Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Conceptual documents that help you understand why Dotify works the way it does.
| [Royalty Settlement](./explanation/royalty-settlement.md) | All | How native runtime payments settle, become claimable on recipient failure, and stay separate from Product CASH |
| [Listening Rooms](./explanation/listening-rooms.md) | All | WebRTC peer-to-peer streaming, signaling protocol, known limitations |
| [Product DevNet Architecture](./explanation/product-devnet-architecture.md) | Maintainers | Dual-host boundaries, Product account capabilities, rooms, storage, and the proposed contract port |
| [Product CASH Settlement Decision](./design/product-cash-settlement-decision.md) | Maintainers | Why CASH remains unavailable until Product supplies a verifiable CASH-to-runtime entitlement path |

---

Expand Down
154 changes: 154 additions & 0 deletions docs/backlog/implementation/evidence/W16.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# W16 - evidence and handoff

## Identity

- Sequence and scope: W16 - prove a real Product CASH settlement path.
- Date: 2026-09-14.
- Starting dev SHA: `383f75f80c21b29e8a63c5d96eb054fd44d1240f`.
The branch was rebased onto current `origin/dev`
`49efda042c544414816e451a5b061e6b83c97103` after W13 merged, then onto
`26da9c85405973ff4cd54ff72ebcfb429065fb36` after W14 and the dev
Playwright fix merged.
- Implementation SHA actually tested:
initial implementation `52369d590783d18ca9d8e80867f00b801588e326`; review
hardening `ab4abf541325d8132ef6909749151cc0dd85015f`.
- Branch / PR / issue: `feat/product-cash-settlement`; PR #162
(`https://github.com/knzeng-e/dotify/pull/162`); existing Product feasibility
epic #85 (reference only, do not close).
- Related dependency evidence: W05 in
`docs/backlog/implementation/evidence/W05.md` / PR #146 and W11 in
`docs/backlog/implementation/evidence/W11.md` / PR #153 are both present in
current `dev` history.
- Code readiness: locally verified.
- Release readiness: blocked. Current Product APIs do not provide an
authoritative proof connecting a People-chain CASH payment to the matching
Asset Hub SmartRuntime entitlement.

## Result and decisions

W16 completes the allowed unsupported-result path from the implementation
contract. Product CASH remains an explicit, non-executable payment rail. Classic
access continues to use the native Asset Hub runtime payment and its on-chain
`hasPaid` / `canAccess` read-back.

The dated decision record rechecks the September 2026 Product DevNet topology,
current registry, CASH asset identities, and the current published Product SDK
surface. `PaymentManager.requestPayment` returns an opaque payment id and the
status subscription reports `Processing`, `Completed`, or `Failed`; it does not
provide the chain transaction, finalized block, amount/recipient proof, or an
Asset Hub entitlement proof. A `Completed` callback therefore cannot authorize
content-key delivery.

The new read-only readiness harness verifies the local fail-closed boundary and
models the minimum evidence a future Product mechanism must supply: quote,
Product-public-key-to-H160 payer derivation, recipient/asset/release binding,
People-chain finality for the exact CASH transfer, exactly-once runtime
entitlement correlated to the same receipt and finality proof, stable receipt
identity, retry/replay protection, and reconciliation. It always keeps even
perfectly shaped future evidence blocked until Dotify implements a mechanism
supported by Product. Trusted relays and operator attestations fail rather than
masquerading as Product-native settlement.

Alternatives rejected in this sequence are treating a Host `Completed` status
as access, operating a private Dotify settlement relay, and silently converting
CASH to the runtime native asset. Each introduces custody, exchange-rate, replay,
or entitlement authority that the current platform does not provide.

## Verification

| Command or real scenario | Environment and build | Observed result | Artifact |
| --- | --- | --- | --- |
| Official Product money, address, Host API, and 2026-09-09 DevNet update review | Public Product documentation, reviewed 2026-09-14 | Confirmed People asset `1`, Asset Hub protected asset `50000413`, 6 decimals, EVM chain `420420417`, current CDM registry `0x05662b3dbd5dd9f2ff92d67630477e84b0b37c1f`, and no documented CASH-to-runtime entitlement proof. | `docs/design/product-cash-settlement-decision.md` |
| `npm view @parity/product-sdk version` and `npm view @parity/product-sdk-host version` | npm registry, 2026-09-14 | Passed: current versions match repository pins `0.27.0` and `0.19.1`. | Terminal output in this run |
| `npm --prefix web run test:product-cash-settlement` | macOS, Node `v22.13.1`, review hardening `ab4abf5` | Passed 14/14 Node tests. Covered unsupported static boundary, Host completion without entitlement, Product public-key/H160 mismatch, wrong payer/recipient/asset/context/finality chain, unrelated finalized People block, failed payment, replay, duplicate retry, crash after payment, delayed/reorg-unsafe finality, pre-existing access, wrong/repeated entitlement, trusted relay rejection, and disabled future-authority shape. | Terminal output in this run |
| `npm --prefix web run smoke:product-cash-settlement -- --json-out /tmp/dotify-w16-product-cash-settlement-review.json --md-out /tmp/dotify-w16-product-cash-settlement-review.md` | Same, read-only local snapshot | Passed with expected result `cash-settlement-unavailable`: 6 pass, 0 fail, 1 blocked, 1 not run. Missing authoritative Product mechanism is visible and does not become a false failure of the native pilot rail. | `/tmp/dotify-w16-product-cash-settlement-review.json`, `/tmp/dotify-w16-product-cash-settlement-review.md` |
| `node web/scripts/product-cash-settlement-readiness.mjs --evidence /tmp/dotify-w16-evidence-does-not-exist.json` | Same | Exited 1 with an explicit missing-evidence error; a mistyped evidence path is not treated as an omitted live check. | Terminal output in this run |
| `npm --prefix web run test:unit` | Same, review hardening `ab4abf5` | Passed 55 files / 435 tests. | Terminal output in this run |
| `npm --prefix web run lint` | Same | Passed with 0 errors and 3 inherited React hook dependency warnings in `App.tsx` and `ArtistShell.tsx`. | Terminal output in this run |
| `npm --prefix web run fmt:check` | Same | Passed. | Terminal output in this run |
| `npm --prefix web run build` | Same | Passed. Existing Rollup warnings remain for the `@scure/base` annotation, mixed static/dynamic `protectedAudio.ts` import, and large chunks. | `web/dist/` ignored build output |
| `CATALOG_API_URL=http://127.0.0.1:9 npm --prefix web run build:product-devnet` | Same, deterministic offline catalog refresh | Passed. Generator reported the expected failed fetch and preserved the existing checked-in bootstrap; no generated drift. Existing Rollup/chunk warnings remain. | `web/dist-product/` ignored build output |
| `node scripts/backlog-sync.mjs --check --offline` | Repository root | Passed with inherited warnings for active item 24 lacking an issue mapping and duplicate numbered backlog docs for 08. | Terminal output in this run |
| `git diff --check origin/dev...HEAD` | Repository root | Passed after removing two Markdown hard-break spaces caught by the first PR CI run. | Terminal output in this run |

No wallet, funded Product account, payment request, runtime write, deployment, or
real participant interaction was used. The evidence fixtures are synthetic and
prove validator behavior only.

## Compatibility and operations

- Supported devices, browsers, and Product host versions: no real Product host
payment was attempted. The decision covers the currently published SDK
versions and keeps all hosts on the existing native runtime path.
- New config/permissions and documented defaults: no environment variable,
Product permission, origin, or hosted setting changed. The Product deployment
runbook now includes the read-only readiness command.
- Storage/key/contract migration and compatibility evidence: none required;
no storage, key, ABI, runtime, or contract state changed.
- Deployment identifiers: none. W16 did not deploy or submit transactions.
- Rollback procedure and rehearsal evidence: revert the W16 PR to remove the
harness and decision text. Runtime behavior remains the pre-W16 native payment
path throughout, so no data rollback is required.
- Data collected, retention, and user controls: no user or payment data was
collected. Optional evidence is read from an operator-selected local file and
is not uploaded by the harness.

## Acceptance mapping

| Sequence criterion | Passed / failed / not run | Supporting evidence or exact blocker |
| --- | --- | --- |
| Recheck Product APIs, asset identifiers, topology, receipt authenticity, and finality | Passed for current documented surface | Dated decision record and package/version checks. The review found no authentic settlement proof surface. |
| Model quote through reconciliation, including replay and crash handling | Passed locally | Readiness schema and 14 deterministic tests cover each boundary. |
| Wrong payer, asset, recipient, or chain fails closed | Passed locally | Combined invalid-binding test checks Product context, Product public-key/H160 derivation, quote, Host amount/recipient, and People finality chain. |
| Reused receipt and duplicate retry do not grant access twice | Passed locally | Replay/retry test rejects prior use and retry count above one; entitlement test requires `issuanceCount=1`. |
| Reorg and delayed finality do not grant access | Passed locally | Included or reorg-unsafe finality remains blocked. |
| Crash between payment and entitlement and unreconciled payment stay visible | Passed locally | Host payment may pass while missing entitlement and `unreconciledPayment=true` remain blocked. |
| Unrelated finality or pre-existing access cannot satisfy CASH settlement | Passed locally | New review-hardening tests require finality to identify the exact CASH transfer and entitlement to bind the same quote, Host payment id, finality block, and finality transaction hash. |
| One real CASH receipt creates exactly one correct entitlement and reconciled value | Not run / externally blocked | Product exposes no authoritative proof or bridge connecting the two chains. No authorized funds were spent. |
| UI never claims access from an unverified payment signal | Passed locally | `product-cash` remains `status: unsupported`; runtime writers accept only `ExecutableTrackAccessPaymentIntent`; static harness gates pass. |

## Remaining gates

- Product must publish a settlement or attestation mechanism binding the payer,
recipient, CASH asset, People-chain finality, stable receipt id, Asset Hub
runtime/content hash, and exactly-once entitlement.
- Once that exists, a new bounded implementation must persist idempotency state
across restarts, define support/refund responsibility, add the disabled-by-
default adapter, and run one authorized funded testnet rehearsal.
- Product CASH must remain unavailable until that follow-up passes. This does not
block the native-runtime-payment pilot.

## Next agent

- Next eligible sequence(s), based on merged prerequisites: W15, W17, W19, and
W20. W15 is now unblocked because W14 / PR #161 has merged.
- Files/interfaces changed that the next agent must inspect:
`docs/design/product-cash-settlement-decision.md`,
`web/scripts/product-cash-settlement-readiness.mjs`,
`web/src/features/payments/paymentModel.ts`, and
`docs/operations/product-devnet-deployment.md`.
- Decisions that must not be silently reversed: Host `Completed` is not runtime
entitlement; no private relay or implicit exchange is Product-native; content
keys remain gated by runtime read-back.
- Exact command/scenario to reproduce a remaining issue:

```bash
npm --prefix web run smoke:product-cash-settlement -- \
--md-out /tmp/dotify-product-cash-settlement.md \
--json-out /tmp/dotify-product-cash-settlement.json
```

- Project/metadata updates completed and any inaccessible fields: PR #162
is assigned to `knzeng-e`, labeled `P1`, `product-sdk`, `research`,
`documentation`, `testing`, and `dotify-backlog`, and added to Project 5 with
Status `In Progress`, Priority `P1`, Track `Product SDK`, Phase
`Product feasibility`, Type `Research`, and Backlog doc
`docs/backlog/implementation/W16-product-cash-settlement.md`. No milestone is
set because the repository has no open milestone; no reviewer is requested
because no separate owner is known. Issue #85 remains an open Product
feasibility epic and must not be closed by this research slice.

Implementation summary: W16 establishes an evidence-backed, fail-closed Product
CASH boundary and a reusable readiness harness without inventing settlement.
Exact next step: review the draft PR, then keep CASH unavailable until Product
provides the required authoritative mechanism.
142 changes: 142 additions & 0 deletions docs/design/product-cash-settlement-decision.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Product CASH Settlement Decision

Status: W16 decision record.

Date: 2026-09-14.

Decision: Product CASH access payments remain unavailable in Dotify until Product
exposes an authoritative settlement or attestation path that can grant and
verify Asset Hub runtime entitlement.

This review was performed against the current published SDK versions:
`@parity/product-sdk@0.27.0` and `@parity/product-sdk-host@0.19.1`.

## Official Sources Rechecked

- Product DevNet update, 2026-09-09:
https://forum.polkadot.network/t/polkadot-product-devnet-update/18602
- Product money model:
https://docs.polkadotcommunity.foundation/architecture/money/
- Product addresses and registries:
https://docs.polkadotcommunity.foundation/reference/addresses/
- Product SDK Host API:
https://paritytech.github.io/product-sdk/api/host/
- Product SDK transaction API:
https://paritytech.github.io/product-sdk/api/tx/

## Current Facts

Product DevNet is a Paseo system-parachain suite:

| Component | Current W16 fact |
| --- | --- |
| Product app | `dotify-test01.dot` |
| Contracts and DotNS | Asset Hub para `1000` |
| Identity and CASH | People para `1004` |
| App bundle storage | Bulletin para `1010` |
| Asset Hub EVM chain id | `420420417` |
| CASH on People | local pUSD asset id `1` |
| CASH on Asset Hub | protected asset id `50000413`, 6 decimals |
| Current CDM registry | `0x05662b3dbd5dd9f2ff92d67630477e84b0b37c1f` |

The September 2026 Product DevNet reset moved DotNS/CDM registries and required
fresh descriptors. That matters for any payment proof because stale registry or
descriptor evidence can decode the wrong chain state without throwing a useful
Dotify-level error.

The Product Host payment surface is intentionally high-level. `PaymentManager`
exposes balance subscription, top-up, `requestPayment(amount, destination,
from?)`, and payment-status subscription. The documented terminal status is
`Completed` or `Failed`. `requestPayment` returns an opaque payment id, while
the status subscription does not expose a chain transaction hash, finalized
block, amount/recipient binding, or an Asset Hub entitlement proof. That is
enough to drive a Host-mediated payment experience, but it is not the same fact
as a Dotify runtime access grant.

Dotify's Classic entitlement is on the artist SmartRuntime. Today it is granted
by `musicRoyPayAccess(contentHash)` on Asset Hub and then read back through
`musicAccHasPaid(contentHash, listener)` plus
`musicAccCanAccess(contentHash, listener)`. The key service opens full playback
only after the runtime read-back confirms access.

## Options Considered

| Option | Trust assumption | Decision |
| --- | --- | --- |
| Treat Host `PaymentManager` `Completed` as access | Trusts an off-chain/high-level Host status without runtime entitlement | Rejected. A CASH receipt alone does not update `musicAccHasPaid` and cannot authorize key release. |
| Dotify-operated relay receives CASH and calls `musicRoyPayAccess` | Trusts Dotify custody, exchange/accounting, relay uptime, and replay controls | Rejected for W16. This is a trusted payment processor, not Product-native settlement, and requires an explicit product decision. |
| Implicit CASH-to-PAS/native conversion | Trusts an exchange rate and asset bridge not present in Dotify | Rejected. W16 forbids implicit exchange rates and fake CASH settlement. |
| Product-confirmed CASH-to-runtime entitlement bridge or attestation | Product supplies a verifiable proof binding payment and runtime grant | Required future path. Not available in current official docs/API snapshot. |
| Keep native runtime payment for Classic and leave CASH disabled | No new payment trust; runtime remains entitlement authority | Accepted for W16. |

## Required Future Proof Shape

A future CASH path must prove all of these before Dotify can label access as
acquired:

- quote id and idempotency key;
- payer Product public key and H160 runtime address, with the H160 derived from
that exact public key through Product's address mapping;
- recipient address;
- CASH amount in atomic 6-decimal units;
- People asset id `1`, Asset Hub protected asset id `50000413`, and Asset Hub
EVM chain id `420420417`;
- exact runtime address and content hash;
- Host payment id and terminal status;
- People-chain finality for the exact CASH transfer, including quote id, Host
payment id, payer, recipient, amount, People asset id, transaction hash, and a
reorg-safe block identity;
- one Asset Hub runtime entitlement for the same payer/runtime/content hash,
correlated to the same Host payment id and finalized People-chain transfer;
- entitlement timing proving the runtime grant was issued after Host completion
and People-chain finality;
- replay protection proving the same receipt was not reused;
- reconciliation result for crashes, timeouts, duplicate retries, and payments
that settled without entitlement.

An implementation can pass W16's future "one real receipt creates exactly one
entitlement" requirement only when this proof is provided by a Product-supported
mechanism or an explicitly approved trust design. A private Dotify relay is not
allowed to pretend to be that mechanism.

## Implemented Boundary

The current repository keeps the rail closed in three places:

- `web/src/features/payments/paymentModel.ts` models `product-cash` as
`status: 'unsupported'` with settlement
`pending-product-confirmation`.
- `RuntimeWritePort.payForAccess` accepts only
`ExecutableTrackAccessPaymentIntent`, which is currently the native runtime
payment intent.
- `web/scripts/product-cash-settlement-readiness.mjs` validates this static
boundary and can inspect future evidence. The CLI reports missing official
support as `blocked`, rejects unsafe evidence as `fail`, and exits non-zero
only for invalid/unsafe evidence.

Run:

```bash
cd web
npm run smoke:product-cash-settlement
```

Optional future evidence can be checked with:

```bash
cd web
npm run smoke:product-cash-settlement -- \
--evidence /path/to/product-cash-evidence.json \
--json-out /tmp/dotify-product-cash-settlement.json \
--md-out /tmp/dotify-product-cash-settlement.md
```

The default result should remain `cash-settlement-unavailable` until Product
documents or ships the authoritative bridge/attestation path.

## Operational Consequence

Classic support stays on the native runtime payment rail for the current pilot.
Product CASH can be shown as planned/future work, but the UI and backend must
not claim a listener acquired access from CASH unless the SmartRuntime read-back
confirms the matching entitlement.
Loading
Loading