Skip to content

docs: define Product CASH settlement boundary - #162

Merged
knzeng-e merged 6 commits into
devfrom
feat/product-cash-settlement
Sep 14, 2026
Merged

knzeng-e merged 6 commits into
devfrom
feat/product-cash-settlement

Conversation

@knzeng-e

@knzeng-e knzeng-e commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Outcome

This PR establishes an evidence-backed Product CASH settlement boundary. It
confirms that the current Product Host payment API cannot safely grant Dotify's
Asset Hub listening entitlement, keeps CASH unavailable, and adds a read-only
readiness harness for reevaluating that decision when Product exposes an
authoritative proof.

Issue and context

Refs #85. Implements the research/unsupported-result scope in
W16-product-cash-settlement.md.

CASH is spent through Coinage on People chain, while Classic access is recorded
inside each artist SmartRuntime on Asset Hub. The current Host API returns an
opaque payment id and a Processing, Completed, or Failed status, but no
chain transaction, finalized block, or proof tying that payment to one runtime
entitlement. Treating Completed as access would make the key service trust an
unverifiable signal.

This is a partial slice of the Product compatibility epic. It must not close
#85: real CASH settlement remains dependent on a Product-supported bridge or
attestation.

Architecture and key concepts

flowchart LR
  Host["Product Host payment status"] --> Guard["W16 readiness gate"]
  Cash["People chain CASH"] --> Guard
  Guard --> Unsupported["CASH remains unavailable"]
  Native["Asset Hub native payment"] --> Runtime["SmartRuntime read-back"]
  Runtime --> Key["Content-key access"]
Loading

The harness has two independent inputs:

  • static repository gates prove product-cash is still unsupported and runtime
    writers accept only executable native-payment intents;
  • optional operator evidence is validated for payer, recipient, asset, chain,
    release, finality, entitlement, idempotency, replay, and reconciliation.

Even a perfectly shaped future evidence file remains blocked in this build.
Only a later implementation based on an actual Product authority may enable the
rail.

How it works

  1. smoke:product-cash-settlement reads the checked-in package pins, payment
    model, runtime writer port, and Product architecture boundary.
  2. Without live evidence, it reports cash-settlement-unavailable with the
    external dependency named explicitly.
  3. With evidence, invalid or unsafe facts fail the command; pending finality or
    unreconciled settlement stays blocked.
  4. No path signs, submits, stores, or uploads a payment. Classic access keeps
    using the existing native runtime write and authoritative read-back.

Design decisions and tradeoffs

  • Rejected Host Completed as entitlement because it lacks a verifiable binding
    to the runtime/content/listener tuple.
  • Rejected a Dotify-operated relay because it would introduce custody,
    accounting, replay, and availability trust that is not Product-native.
  • Rejected implicit CASH/native conversion because no approved exchange or
    bridge exists.
  • Chose a dated decision record plus executable fail-closed harness, which makes
    the platform dependency testable without claiming the feature shipped.

The harness is intentionally strict and tied to the current DevNet topology. A
future network reset must update the decision and constants before evidence can
be evaluated.

Security, failure, and operations

The artist SmartRuntime remains the access authority. A Host receipt alone
never reaches the content-key service. Wrong payer, recipient, asset, chain,
reused receipt, duplicate retry, failed payment, reorg-unsafe finality, missing
entitlement, or unreconciled payment cannot produce a supported result.

No secret, permission, environment variable, hosted setting, contract, key, or
storage schema changes. The harness is read-only and reads optional evidence
from a local operator-selected JSON file. Rollback is a code revert; no data
rollback is needed.

Review guide

Suggested order

  1. Read docs/design/product-cash-settlement-decision.md for the platform facts,
    rejected trust models, and required future authority.
  2. Review web/scripts/product-cash-settlement-readiness.mjs for fail-closed
    gate semantics and the absence of transaction side effects.
  3. Review web/scripts/product-cash-settlement-readiness.test.mjs against the
    W16 acceptance matrix.
  4. Check paymentModel.ts, the runbook, and architecture docs to confirm the
    executable/native boundary remains coherent.
  5. Use docs/backlog/implementation/evidence/W16.md for the exact validation
    record and remaining external gate.

Verify carefully

  • Can any Host Completed status yield cash-settlement-supported?
  • Do wrong-chain or replayed receipts fail rather than degrade to success?
  • Does a crash after payment remain visibly unreconciled?
  • Is every future activation still blocked until code implements a
    Product-supported authority?
  • Does this PR avoid changing the current native Classic payment behavior?

Validation

Evidence What it proves
npm --prefix web run test:product-cash-settlement (11/11) Invalid bindings, failed payment, replay, retries, crash, finality, entitlement, relay, and disabled-authority behavior fail closed.
npm --prefix web run smoke:product-cash-settlement (6 pass / 0 fail / 1 blocked / 1 not run) Current local boundary is sound and the missing Product authority remains explicit.
Missing --evidence file exits 1 A path typo cannot be mistaken for an intentionally omitted live check.
npm --prefix web run test:unit (54 files / 430 tests) Existing web behavior remains green.
npm --prefix web run lint 0 errors; 3 inherited hook warnings in App.tsx and ArtistShell.tsx.
npm --prefix web run fmt:check Changed JS/TS remains formatted.
npm --prefix web run build Standalone production build succeeds; inherited Rollup/chunk warnings remain.
CATALOG_API_URL=http://127.0.0.1:9 npm --prefix web run build:product-devnet Product build succeeds while preserving the checked-in catalog bootstrap offline; inherited Rollup/chunk warnings remain.
node scripts/backlog-sync.mjs --check --offline Manifest passes; inherited warnings remain for item 24 mapping and duplicate 08 docs.
git diff --check origin/dev...HEAD No whitespace errors after the first CI run caught two Markdown hard-break spaces.

Implementation SHA tested: 52369d590783d18ca9d8e80867f00b801588e326.

Known limitations and follow-ups

  • No real CASH payment or Product-host settlement rehearsal was run because the
    authoritative cross-chain entitlement mechanism does not exist in the
    documented API.
  • A future implementation must persist idempotency across restarts, define
    support/refund ownership, add a disabled-by-default adapter, and prove one
    funded receipt creates exactly one matching entitlement.
  • This does not block the native-runtime-payment pilot.

Metadata checklist

  • Backlog issue linked with correct close/reference semantics
  • Local backlog document linked
  • Added to Project 5 (Dotify sprints)
  • Project Priority, Track, Phase, Type, and Backlog doc mirror the issue
  • Workflow status matches draft/review state
  • Assignee set
  • Applicable labels set
  • Applicable milestone set, or confirmed none exists
  • Reviewers requested when ownership is known (none known; none requested)
  • Draft/ready state is intentional

@knzeng-e knzeng-e added documentation Improvements or additions to documentation P1 research testing dotify-backlog Tracked by docs/backlog/backlog.json and Project 5 product-sdk Polkadot Product SDK / Host / Playground integration labels Sep 14, 2026
@knzeng-e knzeng-e self-assigned this Sep 14, 2026
@netlify

netlify Bot commented Sep 14, 2026

Copy link
Copy Markdown

Deploy Preview for muzinga ready!

Name Link
🔨 Latest commit e2fcf20
🔍 Latest deploy log https://app.netlify.com/projects/muzinga/deploys/6aa7c017a8c7c8000847eab4
😎 Deploy Preview https://deploy-preview-162--muzinga.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@knzeng-e knzeng-e moved this from Todo to In Progress in Dotify sprints Sep 14, 2026
@knzeng-e
knzeng-e marked this pull request as ready for review September 14, 2026 01:18
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T01:23:01.798496Z 0ecfb51 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0ecfb51494

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +245 to +246
if (isHexAddress(quote.payerH160) && typeof quote.payerProductPublicKey === 'string' && /^0x[0-9a-fA-F]{64}$/.test(quote.payerProductPublicKey)) {
pass(gates, 'quote:payer', 'Payer binding', 'Quote binds the Product account public key and derived H160 runtime identity.', source);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify the public-key-to-H160 payer mapping

When payerH160 is a valid address belonging to one account and payerProductPublicKey is a valid key belonging to another, this gate still passes because it validates the two formats independently without deriving and comparing the H160 address. Consequently, the harness does not actually reject the valid-but-wrong payer case required by W16; use the existing Product deriveH160 mapping to verify the relationship.

AGENTS.md reference: AGENTS.md:L77-L77

Useful? React with 👍 / 👎.

Comment on lines +308 to +311
if (finality.peopleParaId !== PRODUCT_CASH_TOPOLOGY.peopleParaId) {
fail(gates, 'finality:chain', 'Finality chain', 'Finality must be observed on People para 1004.', source);
} else if (finality.status === 'finalized' && isHexHash(finality.blockHash) && finality.reorgSafe === true) {
pass(gates, 'finality:chain', 'Finality chain', 'People-chain CASH settlement is finalized and reorg-safe.', source);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind finality to the quoted payment

For operator evidence containing any finalized, reorg-safe block from People para 1004, this gate reports that CASH settlement is finalized even though evaluateFinality receives neither the quote nor the Host payment and verifies no transaction or extrinsic in that block. An unrelated finalized block can therefore satisfy the finality check; the proof needs to identify the CASH transfer and bind its payer, recipient, asset, and amount to the receipt and quote.

AGENTS.md reference: AGENTS.md:L77-L77

Useful? React with 👍 / 👎.

Comment on lines +331 to +335
if (
isRecord(quote) &&
sameText(entitlement.runtimeAddress, quote.runtimeAddress) &&
sameText(entitlement.contentHash, quote.contentHash) &&
sameText(entitlement.listenerAddress, quote.payerH160)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correlate the entitlement with the CASH receipt

When the listener already has access to the same track from an earlier native-runtime purchase, that existing hasPaid/canAccess state satisfies this binding for a later CASH evidence file. The entitlement transaction hash, block, and timing are never correlated with the Host payment or stable receipt, so the harness cannot establish its claimed invariant that one real CASH receipt produced the entitlement rather than merely finding pre-existing access.

AGENTS.md reference: AGENTS.md:L77-L77

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

if (isHexAddress(quote.payerH160) && typeof quote.payerProductPublicKey === 'string' && /^0x[0-9a-fA-F]{64}$/.test(quote.payerProductPublicKey)) {
pass(gates, 'quote:payer', 'Payer binding', 'Quote binds the Product account public key and derived H160 runtime identity.', source);

P2 Badge Verify the public-key-to-H160 payer mapping

When payerH160 is a valid address belonging to one account and payerProductPublicKey is a valid key belonging to another, this gate still passes because it validates the two formats independently without deriving and comparing the H160 address. Consequently, the harness does not actually reject the valid-but-wrong payer case required by W16; use the existing Product deriveH160 mapping to verify the relationship.

AGENTS.md reference: AGENTS.md:L77-L77


if (finality.peopleParaId !== PRODUCT_CASH_TOPOLOGY.peopleParaId) {
fail(gates, 'finality:chain', 'Finality chain', 'Finality must be observed on People para 1004.', source);
} else if (finality.status === 'finalized' && isHexHash(finality.blockHash) && finality.reorgSafe === true) {
pass(gates, 'finality:chain', 'Finality chain', 'People-chain CASH settlement is finalized and reorg-safe.', source);

P2 Badge Bind finality to the quoted payment

For operator evidence containing any finalized, reorg-safe block from People para 1004, this gate reports that CASH settlement is finalized even though evaluateFinality receives neither the quote nor the Host payment and verifies no transaction or extrinsic in that block. An unrelated finalized block can therefore satisfy the finality check; the proof needs to identify the CASH transfer and bind its payer, recipient, asset, and amount to the receipt and quote.

AGENTS.md reference: AGENTS.md:L77-L77


if (
isRecord(quote) &&
sameText(entitlement.runtimeAddress, quote.runtimeAddress) &&
sameText(entitlement.contentHash, quote.contentHash) &&
sameText(entitlement.listenerAddress, quote.payerH160)

P2 Badge Correlate the entitlement with the CASH receipt

When the listener already has access to the same track from an earlier native-runtime purchase, that existing hasPaid/canAccess state satisfies this binding for a later CASH evidence file. The entitlement transaction hash, block, and timing are never correlated with the Host payment or stable receipt, so the harness cannot establish its claimed invariant that one real CASH receipt produced the entitlement rather than merely finding pre-existing access.

AGENTS.md reference: AGENTS.md:L77-L77

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@knzeng-e
knzeng-e force-pushed the feat/product-cash-settlement branch from 0ecfb51 to e2fcf20 Compare September 14, 2026 09:36
@knzeng-e

Copy link
Copy Markdown
Owner Author

Addressed the review feedback in e2fcf20: Product public-key-to-H160 derivation is now verified, People finality must bind the exact CASH transfer/quote/payment, and Asset Hub entitlement must correlate to the same receipt/finality so pre-existing access cannot satisfy CASH evidence. CI is green.

@knzeng-e
knzeng-e merged commit f6e6ee7 into dev Sep 14, 2026
15 checks passed
@knzeng-e
knzeng-e deleted the feat/product-cash-settlement branch September 14, 2026 09:43
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Dotify sprints Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation dotify-backlog Tracked by docs/backlog/backlog.json and Project 5 P1 product-sdk Polkadot Product SDK / Host / Playground integration research testing

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant