Skip to content

spec(bazaar): privacy_class field -- settlement-plane visibility for attested-private and fully-private x402 flows - #2334

Closed
chopmob-cloud wants to merge 7 commits into
x402-foundation:mainfrom
chopmob-cloud:spec/bazaar-privacy-class
Closed

spec(bazaar): privacy_class field -- settlement-plane visibility for attested-private and fully-private x402 flows#2334
chopmob-cloud wants to merge 7 commits into
x402-foundation:mainfrom
chopmob-cloud:spec/bazaar-privacy-class

Conversation

@chopmob-cloud

@chopmob-cloud chopmob-cloud commented May 15, 2026

Copy link
Copy Markdown
Contributor

Adds privacy_class to extensions.bazaar -- a field that declares how visible an x402 settlement is to the measurement and compliance layers. Derived from working group discussion in #2326.

This PR is additive to #2322 (the Compliance category taxonomy). privacy_class is orthogonal to evidenceType and composes with it without modification.

What this adds to specs/extensions/bazaar.md

1. privacy_class field (optional, open enum)

Three values: public-settlement (default), attested-private (settlement hidden, non-revealing proof anchored), fully-private (no public artifact, explicit opt-out from measurement plane).

2. Orthogonality matrix

Explicit 4x3 table showing evidenceType x privacy_class combinations. None are contradictory; not all cells are populated in practice.

3. Cardinality counting rule for attested-private batches (MUST)

settlement_count MUST be inside the JCS-canonicalised, signed attestation object -- not published as a separate adjacent field. Binds batch size to the attestation under MiCA Art. 80 / AMLR Art. 56 retention. Emitters MAY substitute a succinct batch-size proof; the explicit fallback for absent count is 1 per anchor (not silent undercount).

4. Cross-observer determinism rule

Deduplication: (JCS_hash(attestation), anchor_chain, block_number) unique within the observer declared observation window. JCS_hash = SHA-256(JCS(RFC 8785)(attestation)), lowercase hex. SHA-256 is the mandatory v1 floor. Observation window is part of count identity and MUST be declared in the observer registry entry.

5. Regulatory emitter behaviour for fully-private payers (MUST NOT / SHOULD)

Regulatory emitters MUST NOT return ALLOW or BLOCK without evidence_unverifiable for fully-private payers. SHOULD either refuse (decision: REFUSE, reason_code: EVIDENCE_UNVERIFIABLE) or degrade (evidence_unverifiable: true inside signed payload). Silent acceptance is not compliant.

6. Per-band regulatory feasibility table (informative)

8 checks (AMLR Art. 16/22/44, MiCA Art. 35/80, PSD2 Art. 97, DORA Art. 17, ISO 20022 pacs.008) across three privacy_class bands. Pattern: most checks compose with attested-private via commitment primitives; almost none compose with fully-private because verifiable trails are constitutive of the check.

7. disclosure_policy sub-field (optional)

Two values for attested-private services: non-revealing (commitment is final, default) and reveal-on-authority-request (selectively revealable to FIU/NCA/court). Facilitators MUST treat as informative metadata only.

Authorship

AlgoVoi (chopmob-cloud) submission. Spec text developed from working group discussion in #2326.

Relationship to other PRs

Additive to #2322 -- does not modify the evidenceType taxonomy. Closes the field-definition scope of #2326. Worked examples for evidenceType: cryptographic, privacy_class: attested-private (commitment-based screening) to follow in a subsequent PR.

IETF Internet-Draft: draft-hopley-x402-compliance-receipt-00 (Independent Submission, Informational, sole AlgoVoi authorship, live on IETF datatracker 2026-05-24)


AlgoVoi (chopmob-cloud) - chopmob@gmail.com - Acquisition enquiries: https://docs.algovoi.co.uk/acquisition

@vercel

vercel Bot commented May 15, 2026

Copy link
Copy Markdown

@chopmob-cloud is attempting to deploy a commit to the Coinbase Team on Vercel.

A member of the Team first needs to authorize it.

@chopmob-cloud

chopmob-cloud commented May 15, 2026

Copy link
Copy Markdown
Contributor Author

Conflict note for maintainers.

This PR was opened from a fork of coinbase/x402, not x402-foundation/x402. The two repos have diverged histories (119 commits apart at the time of branching), so git marks bazaar.md as conflicted even though the proposed file content is correct.

The branch commit dd61783 on chopmob-cloud/x402-foundation-x402 contains the full target file: x402-foundation/x402 main bazaar.md (as of 04ad3cc, 589 lines) with the privacy_class section appended (110 lines, 699 total).

The cleanest path for maintainers:

  1. Create a branch off x402-foundation/x402:main
  2. Append the privacy_class section (lines 590-699 of the branch file) to specs/extensions/bazaar.md
  3. The full section starts with "---

Settlement Visibility: privacy_class" and ends with the Related links block

Direct diff view: https://github.com/chopmob-cloud/x402-foundation-x402/blob/spec/bazaar-privacy-class/specs/extensions/bazaar.md

Alternatively, adding chopmob-cloud as a collaborator with branch-write access on x402-foundation/x402 would let us open a clean branch from the correct history.

@chopmob-cloud

chopmob-cloud commented May 15, 2026

Copy link
Copy Markdown
Contributor Author

Minimal patch for maintainers (110 lines added to end of file, no existing content modified).

Apply on a branch off x402-foundation/x402:main:

git checkout -b spec/bazaar-privacy-class
git apply privacy_class.patch
git commit -m "spec(bazaar): add privacy_class field for settlement-plane visibility"

Or simply append lines 590-699 of the branch file to specs/extensions/bazaar.md:
https://github.com/chopmob-cloud/x402-foundation-x402/blob/spec/bazaar-privacy-class/specs/extensions/bazaar.md

@danielnorkin danielnorkin 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.

Both contributions from #2326 are captured cleanly in the spec text.

Cardinality counting. settlement_count inside the JCS-signed attestation, with the 1-per-anchor fallback, exactly as the working group converged. The signed-payload binding (the MiCA Art. 80 / AMLR Art. 56 correction) is preserved, so a regulator holding the attestation can verify the count without trusting an out-of-band assertion.

Cross-observer determinism. (JCS_hash(attestation), anchor_chain, block_number) uniqueness rule is precise, SHA-256 explicit, observation window declared in the observer's registry entry. Reproducible from any third-party reader.

One small gap worth a follow-up: the spec says observers MUST declare their observation window "in their registry entry," but the field name isn't pinned in this PR or in #2322. Our observational worked example (chopmob-cloud#2 against #2322) doesn't currently include an observation_window field. I'll update that PR to add it so the two land conformantly.

Approving on the spec text.

danielnorkin added a commit to EnvisionBlockchain/x402 that referenced this pull request May 15, 2026
@feedoracle

Copy link
Copy Markdown

PR captures the converged points from #2326 cleanly. Reading the diff against what the working group agreed:

  • ✅ Three-value open enum (public-settlement / attested-private / fully-private)
  • ✅ 4×3 orthogonality matrix stated explicitly
  • settlement_count inside JCS-canonicalised signed attestation (binds batch size to attestation for MiCA Art. 80 / AMLR Art. 56 retention)
  • JCS_hash deduplication: SHA-256(JCS(attestation)), lowercase hex, (JCS_hash, anchor_chain, block_number) uniqueness within declared observation window
  • ✅ Observation window as part of count identity (declared in registry entry)
  • ✅ Regulatory refusal posture for fully-private payers: MUST NOT bare ALLOW/BLOCK, SHOULD refuse or degrade with evidence_unverifiable: true
  • ✅ Per-band feasibility table (8 checks × 3 bands)
  • disclosure_policy as a sub-field on attested-private (non-revealing / reveal-on-authority-request) — cleanly handles the disclosure-on-trigger pattern without needing a fourth enum value

Two minor notes on the spec text for tightening before review:

1. Canonicalisation rule alignment with #2322. The JCS_hash definition here (SHA-256(JCS(attestation))) and the content_hash definition in #2322 §3 should be the same canonicalisation rule with the same algorithm. I've flagged in #2322 that content_hash should also be defined as SHA-256(JCS(...)) for cross-spec consistency. If chopmob lands that in #2322 in the same review pass as this PR, the v1 spec has one canonicalization rule end-to-end. Otherwise the two definitions diverge under maintenance pressure even if they start aligned.

2. disclosure_policy field values. The two values noted (non-revealing, reveal-on-authority-request) cover the practical cases, but the enum should be marked open for the same reason the framework enum in #2322 is open: jurisdictional disclosure mechanics evolve faster than spec PR cycles. A reveal-on-court-order pattern, for example, has subtly different semantics from reveal-on-authority-request in some EU jurisdictions (court order vs. FIU/NCA request) and may need its own value later. Suggested addition to the spec text: "Implementors MAY declare other disclosure_policy values; facilitators MUST treat unknown values as opaque (no policy-based routing) rather than rejecting the registry entry."

Otherwise the PR reads ready for review. Happy to draft the cryptographic-class worked sub-block (commitment-based screening under evidenceType: cryptographic, privacy_class: attested-private) as the follow-on PR once #2334 merges, as agreed in the issue thread. Pedersen commitment + Merkle non-membership proof against the sanctions set, screening outcome signed without the screener learning the counterparty — concrete enough to validate the field semantics against real cryptographic primitives.

chopmob-cloud added a commit to chopmob-cloud/x402-foundation-x402 that referenced this pull request May 16, 2026
Adds content_hash as a defined field in the evidenceShape table.
Definition is SHA-256 of the JCS (RFC 8785) serialisation of the
attestation pre-image, lowercase hex - matching JCS_hash as defined
in x402-foundation#2326 / x402-foundation#2334.

Closes the canonicalization-divergence risk feedoracle raised on the
PR: signedReceipt envelope and content_hash commitment now share one
canonicalization rule, so a single verifier implementation validates
both with one code path.

Also tightens the worked-example paragraph (section 3, TrustQuery
roundtrip) to reference the canonical definition rather than re-stating
'deterministic over canonical JSON' inline.
@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

Friendly nudge for Foundation maintainers — this PR has been ready since 2026-05-15 with @feedoracle's working-group sign-off above confirming all converged points from #2326 are captured: three-value privacy_class enum, 4×3 orthogonality matrix, settlement_count inside the JCS-canonicalised signed attestation, JCS_hash deduplication, observation-window-as-count-identity, and the regulatory-emitter refusal posture. The companion PR #2322 (Compliance category taxonomy) just took another tightening commit (10f7379) so the canonicalization rule is consistent across both PRs.

The git-side conflict is purely a diverged-fork artefact — bazaar.md content itself is correct against x402-foundation/x402:main. To unblock, three options ranked by maintainer effort:

  1. Cherry-pick the 110-line append directly. Patch file is referenced in the 20:55 comment; cleanest path.
  2. Grant chopmob-cloud branch-write access on x402-foundation/x402 and we'll open a clean PR from the correct base. Fastest if working-group authors are going to keep contributing follow-ons (cryptographic-class worked example is queued post-merge).
  3. Close-and-reopen from a freshly-forked branch off x402-foundation/x402:main. We can do this end if preferred, just confirm and we'll handle the relaunch with the existing review trail referenced.

Happy with whichever you prefer. The cryptographic-class worked example @feedoracle named us for is ready to land within hours of #2334 closing, so anything that gets privacy_class into the spec is a win.

chopmob-cloud added a commit to chopmob-cloud/x402-foundation-x402 that referenced this pull request May 16, 2026
…undation#2334 review)

Facilitators MUST treat unknown disclosure_policy values as opaque
(no policy-based routing) rather than rejecting the registry entry.
Matches the open-enum maintenance-pressure rationale already applied
to the framework enum in x402-foundation#2322.
@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

@feedoracle — thanks for the line-by-line read against the working group convergence. Both notes addressed.

1. Canonicalisation rule alignment with #2322. Already landed. content_hash is now a defined field on the evidenceShape table per your proposed text in 10f7379, defined as SHA-256(JCS(canonical attestation pre-image)) lowercase hex with an explicit cross-reference to JCS_hash in this PR. Section 3 in #2322 also tightened to reference the canonical definition rather than restate "deterministic over canonical JSON" inline. So once both PRs merge, v1 spec has one canonicalization rule end-to-end across signedReceipt, content_hash, and JCS_hash — single verifier implementation, no per-class canonicalization branching.

2. disclosure_policy as open enum. Adopted, pushed in 8ed2b4e5. The spec text now reads:

The disclosure_policy enum is open. Implementors MAY declare other disclosure_policy values (for example "reveal-on-court-order", where the trigger is specifically a judicial order rather than an administrative FIU/NCA request — a distinction with subtly different semantics in some EU jurisdictions). Facilitators MUST treat unknown disclosure_policy values as opaque (no policy-based routing) rather than rejecting the registry entry, for the same maintenance-pressure reason the framework enum in #2322 is open: jurisdictional disclosure mechanics evolve faster than spec PR cycles.

Your reveal-on-court-order example is now in the spec text verbatim as the canonical illustration of what an implementor-declared value looks like.

3. Cryptographic-class worked sub-block follow-on. Yes please, and that complements rather than duplicates what we've drafted on our side. We have a cryptographic-class worked example queued as a #2326 follow-on PR (branch spec/bazaar-cryptographic-worked-example, ready to push once #2334 lands) but it's the general operational angle: audit-bundle verification, four hash chains (audit_log / screening_hits / compliance_events / negotiation_trace_events), HMAC-SHA-256 over JCS-canonicalised envelope, public verifier (algovoi-audit-verifier), B2 Object Lock COMPLIANCE manifest.

Your proposal — evidenceType: cryptographic × privacy_class: attested-private with Pedersen commitment + Merkle non-membership against the sanctions set — is the intersection worked example. Different evidence shape, different proof systems, different disclosure_policy posture. Both belong in the v1 spec because they validate different parts of the field semantics:

  • Ours validates the general cryptographic-class shape (per-row hash chain, signed bundle envelope, off-VM anchor, four-stage verifier recipe) against a live audit-bundle implementation
  • Yours validates the privacy_class × evidenceType interaction (commitment binds counterparty without revealing it, non-membership proof signs without observation, disclosure_policy: "non-revealing" or "reveal-on-court-order" controls post-anchor reveal)

Happy to coordinate so the two examples land as sibling follow-ons after #2334 merges, sharing the evidenceShape field vocabulary and the JCS canonicalisation rule but otherwise standing independently. If you can drop the Pedersen + Merkle text as a gist or branch when ready, I'll wire the canonicalisation rule references through so both worked examples cite the same definitions.

PR reads ready for review from our side. Once you confirm the open-enum text in 8ed2b4e matches your intent, we're clear for Foundation maintainer merge.

@feedoracle

Copy link
Copy Markdown

Confirmed — open-enum text in 8ed2b4e matches intent. The reveal-on-court-order illustration in the spec text reads exactly right: it preserves the attested-private core enum while making clear the disclosure-policy axis is implementor-extensible, and the maintenance-pressure argument is the right framing for why. Clear for Foundation maintainer merge from our side.

On the cryptographic worked example coordination. The split you've drawn (general operational angle vs. intersection example) is exactly the right factoring — they validate different parts of the field semantics and shouldn't be conflated:

  • Your audit-bundle worked example validates the general evidenceType: cryptographic shape end-to-end: per-row hash chain, signed bundle envelope, off-VM anchor, HMAC-SHA-256 over JCS-canonicalised envelope, B2 Object Lock COMPLIANCE manifest, four-stage verifier recipe. That's the spec proving its general cryptographic-class shape is implementable against live infrastructure.

  • Our Pedersen + Merkle non-membership worked example validates the evidenceType: cryptographic × privacy_class: attested-private cell specifically: counterparty identity committed to without being revealed, sanctions check runs against the commitment + Merkle non-membership proof against the published sanctions set, outcome signed without the screener ever learning the counterparty, disclosure_policy controls whether the commitment is ever revealable post-anchor.

Both belong in v1. Same evidenceShape vocabulary, same JCS canonicalisation rule, otherwise standing independently. Happy to drop the Pedersen + Merkle text as a gist when ready — probably this weekend, given the spec is converging fast and the cryptographic-class follow-on works better while the field semantics are fresh.

One concrete coordination ask on field naming before we draft: you mentioned aligning chain_format_version and selection_window with @arian-gogani in #2322. If those names are shaping up as the cryptographic-class envelope vocabulary, useful to know before we draft the Pedersen + Merkle block so the two worked examples cite identical field names. Happy to follow whatever you and Nobulex settle on rather than introduce a third naming convention.

PR reads ready for review on our side. The git-side fork conflict you flagged is purely procedural — the spec content itself is correct, so whichever of the three unblock paths the maintainers prefer (cherry-pick / branch-write / close-and-reopen) works on our side too.

@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

@feedoracle — thanks for the confirmation. Locked on the open-enum text.

On field-naming coordination. Pulling the actual vocabulary out of the cryptographic worked example we have committed (branch spec/bazaar-cryptographic-worked-example, ready to push after #2334 merges) so you have concrete names to follow rather than negotiating in the abstract:

bundle_pointer:                              # top-level reference into the retained chain
  chain_name           : string              # which canonical chain this references
  chain_format_version : integer             # format version of canonical-fields layout
  selection_window     : [iso8601, iso8601]  # time-bounded window the bundle covers
  emit_endpoint        : string              # operator-only path that emits the bundle
  rows_in_window       : integer             # selective-disclosure count
  min_chain_position   : integer             # first row included
  max_chain_position   : integer             # last row included
  chain_head_at_emit   : integer             # head position at bundle emission time

off_vm_anchor:                               # off-chain retention reference
  manifest_url                  : string     # s3:// or similar
  object_lock_mode              : string     # "COMPLIANCE" | "GOVERNANCE"
  object_lock_retain_until_date : iso8601
  chain_head_hash               : hex        # SHA-256, head-of-chain commitment

verification_recipe:                         # ordered stages a verifier MUST run
  - per_row_content_hash
  - continuity
  - bundle_signature
  - off_vm_anchor

These map cleanly onto the Pedersen + Merkle worked example with the same shape, different specifics:

Audit-bundle name Pedersen + Merkle analogue
bundle_pointer.chain_name commitment_pointer.commitment_scheme ("pedersen-bls12-381")
bundle_pointer.chain_format_version commitment_pointer.commitment_format_version
bundle_pointer.selection_window commitment_pointer.observation_window (when the sanctions set was current)
bundle_pointer.emit_endpoint commitment_pointer.proof_endpoint (operator-only non-membership proof emission)
off_vm_anchor.chain_head_hash off_vm_anchor.sanctions_set_root (Merkle root, same SHA-256(JCS(...)) rule)
verification_recipe[per_row_content_hash] verification_recipe[commitment_well_formed]
verification_recipe[continuity] verification_recipe[non_membership_proof_valid]
verification_recipe[bundle_signature] verification_recipe[outcome_signed]
verification_recipe[off_vm_anchor] Same — both verify against the Object Lock manifest

So the shared envelope-vocabulary is: *_pointer (with *_format_version, *_window, *_endpoint, position/state fields), off_vm_anchor block (manifest URL + Object Lock mode + retain-until + content-commitment hash), verification_recipe (ordered stages, each a verb-phrase the verifier can independently run).

If the Pedersen + Merkle draft follows this skeleton with commitment_pointer replacing bundle_pointer and the per-stage names tuned for the privacy-preserving primitives, the two worked examples will read as siblings rather than cousins. Field names you're free to settle independently:

  • The commitment_scheme value space (we have no opinion on pedersen-bls12-381 vs kzg-bn254 vs another curve)
  • The non_membership_proof shape (depends on Merkle tree variant and hash algorithm choice)
  • disclosure_policy value if you go beyond non-revealing and reveal-on-court-order for the worked example

Coordinated with @arian-gogani last night on the same vocabulary for behavioral receipts (receipt_pointer.chain_format_version for nobulex's signed-receipt chain, same JCS-canonicalised envelope). Three-class consistency: bundle_pointer (cryptographic, general) / commitment_pointer (cryptographic × attested-private) / receipt_pointer (behavioral). Each is the same structural pattern with class-appropriate state fields.

Drop the Pedersen + Merkle gist whenever convenient — weekend is fine. Happy to wire the canonicalisation rule references and shared field names through when I finalise our worked example PR description so reviewers reading both can navigate the vocabulary in one pass.

@feedoracle

Copy link
Copy Markdown

Cryptographic × attested-private worked example drafted, ready for the follow-on PR after this one merges.

Gist: https://gist.github.com/feedoracle/604196bbb8242d630ee940bb4107ea83

Following the field-naming skeleton from your 07:03 comment: commitment_pointer block + off_vm_anchor + 4-stage verification_recipe, structurally identical to bundle_pointer (your audit bundle) and receipt_pointer (Nobulex behavioral). Three sibling worked examples, one envelope grammar.

What the block covers (9 sections, ~25k chars):

  1. Scopecryptographic × attested-private cell, why it's the load-bearing claim for the entire attested-private band
  2. Registry entry — full YAML with commitment_scheme: "sha256-jcs", 7-year retention covering MiCA Art. 80 / AMLR Art. 56 / DORA Art. 14 / PSD2 SCA, disclosure_policy: reveal-on-authority-request
  3. Use case — $50k USDC settlement under MiCA Art. 35, screening against the live 88,562-entity sanctions set (EU FSF + OFAC SDN + UN SC + Interpol via OpenSanctions, snapshot 2026-05-05)
  4. Cryptographic primitives — deterministic SHA-256(JCS(record)) commitment, sorted-Merkle non-membership against the published sanctions_set_root, ~36 SHA-256 hashes per proof (log₂(88562) ≈ 17 × 2 paths + 2 adjacent leaves)
  5. Round-trip — TrustQuery → live response from https://tooloracle.io/zk/mcp create_commitment → signed attestation → TrustEvaluation (real commitment hash from the live tool)
  6. Verification recipe — 4 stages each independently runnable: commitment_well_formed, non_membership_proof_valid, outcome_signed (ES256K over JCS(attestation), same rule as spec(bazaar): add Compliance category with evidenceType, evidenceShape, and anchor_chains constraint #2322 f456aee), off_vm_anchor (Object Lock COMPLIANCE manifest)
  7. Reference implementation — direct mapping to ZKEvidenceOracle (14 tools) + AMLOracle (12 tools), MIT-licensed, all live today on tooloracle.io
  8. Upgrade paths — 4-row table covering sha256-jcs (v1) / hmac-sha256-jcs-with-zk-non-membership (small input spaces) / pedersen-bls12-381 (homomorphic aggregation) / poseidon-bn254 (zk-rollup native), all forward-compatible with the same registry envelope; only commitment_scheme value and Stage 1+2 verifier logic change
  9. Source links — JWKS, DID document, live MCP endpoints, manifest

Why sha256-jcs in v1. The commitment-scheme choice is implementor-defined under the open enum from commit 8ed2b4e. We ship deterministic SHA-256(JCS(record)) rather than Pedersen or HMAC because it produces commitments in the same hash space as the sanctions-set Merkle leaves — which means plain sorted-Merkle non-membership verifies directly against the published sanctions_set_root without an intermediate ZK layer. Hiding is provided by 256-bit pre-image resistance on realistic counterparty records (name + jurisdiction + ID combinations have effectively unbounded input space). Implementors needing stronger hiding for narrower input spaces have the HMAC+ZK and Pedersen upgrade paths documented; the registry shape is identical, only Stage 1+2 verifier logic changes.

Canonicalisation alignment. Same SHA-256(JCS(...)) rule across all four classes per the lock-in in #2322 / #2334. One canonicalisation function, one verifier code path, four classes — behavioral (Nobulex receipts, Ed25519+JCS), regulatory (tooloracle ES256K+JCS attestations), cryptographic × public (AlgoVoi audit bundles), cryptographic × attested-private (this block).

Composition with regulatory plaintext. When disclosure_policy: reveal-on-authority-request fires, the same screening runs against the plaintext counterparty via AMLOracle's sanctions_screen tool — yielding the regulatory worked example shape in #2322 §3. Commitment path and plaintext path produce structurally identical attestations (same JCS schema, same ES256K key, same retention obligation); only difference is whether the counterparty plaintext is in the signed envelope or under Object Lock pending trigger. This is the commitment_pointer ↔ regulatory evidenceShape cross-class composition referenced in #2322 lock-in.

Drop it in as the follow-on PR whenever convenient — happy to open the PR myself or to copy the gist content into your existing follow-on PR if you'd rather batch the two cryptographic worked examples (your audit bundle + this commitment + non-membership) into one PR for review efficiency.

cc @arian-gogani @danielnorkin — Nobulex's receipt_pointer and Agent 402 Tape's observational counting both compose with this block under the additive-across-classes / JCS_hash-dedup-within-class rule, exactly as agreed in the 07:54 lock-in table.

@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

@feedoracle — read through the gist. Lock-in confirmed across every dimension:

Envelope grammar consistency. commitment_pointer mirrors bundle_pointer and receipt_pointer exactly. A verifier walking the three sibling examples never needs class-aware branching at the envelope layer:

pointer.{ *_format_version, *_window, *_endpoint, position/state fields }
off_vm_anchor.{ manifest_url, object_lock_mode, retain_until, head_hash | content_hash }
verification_recipe[]   # ordered class-specific stages
disclosure_policy        # only on attested-private cells

Three implementors, one envelope. Single jcs(canonical_fields) call across all three worked examples. The cross-class composition table in #2322 §4 is now backed by three concrete reference implementations rather than one.

On sha256-jcs over Pedersen for v1. Strongly agree. The argument from your §8 upgrade-paths table is the right one: same hash space as the sanctions-set Merkle leaves means non-membership verifies directly against sanctions_set_root without an intermediate ZK proof. 256-bit pre-image resistance on (name + jurisdiction + ID + nonce) combinations is more than enough for realistic counterparty records. Pedersen would add ~80ms per proof for hiding guarantees that don't matter when the input space is effectively unbounded. The fact that the registry shape is identical across all four upgrade paths (sha256-jcs / hmac-sha256-jcs-with-zk-non-membership / pedersen-bls12-381 / poseidon-bn254), with only Stage 1+2 verifier logic changing, is exactly the open-enum / class-routing-hint pattern we locked into #2322.

On the signing divergence. You sign attestations with ES256K via did:web:tooloracle.io; AlgoVoi signs audit-bundle envelopes with HMAC-SHA-256 over the same JCS-canonicalised payload. Both reduce to the same verifier pattern:

1. recompute  canonical = jcs(canonical_fields)
2. verify     signature over canonical, using algorithm declared in evidenceShape.signature_algorithm
3. (rest of verification_recipe)

The signature_algorithm field in evidenceShape (ES256K vs HMAC-SHA-256 vs future PQC) is a routing hint, not a canonicalization variant. Consumer Trust Capital aggregators can compose attestations from both emitters by reading signature_algorithm to pick the right verify primitive. Same composability rule as the disclosure_policy open enum from 8ed2b4e.

On the four-stage verification_recipe. Naming-aligned across both worked examples:

Audit-bundle stage (AlgoVoi) Commitment + non-membership stage (tooloracle)
per_row_content_hash commitment_well_formed
continuity non_membership_proof_valid
bundle_signature outcome_signed
off_vm_anchor off_vm_anchor (identical, same Object Lock manifest pattern)

Different stage names, same structural pattern: each stage is an independently runnable verifier predicate. A verifier implementation can plug in class-specific stage functions and walk the recipe array without knowing whether it's looking at a bundle or a commitment. That's the cleanest possible expression of "class is a routing hint, not a canonicalization branch."

Batching question. Genuinely indifferent technically; both paths land equivalent spec text. Slight preference for two sibling PRs for review attribution clarity:

  • PR A: AlgoVoi audit-bundle worked example (general cryptographic-class shape, branch spec/bazaar-cryptographic-worked-example already committed locally as commit 577a5ed)
  • PR B: Your commitment + non-membership worked example (cryptographic × attested-private intersection)

Both reference each other in body text and the §6 composition table. Foundation maintainer can choose to merge serially, simultaneously, or batch via squash if they prefer. Reviewer-side, one implementor's contribution per PR is clearer than mixed-author batches.

That said, if you'd prefer to batch into one PR for review efficiency, fine on this end. Could open it as a joint PR with both blocks side-by-side under the existing follow-on slot. If maintainers signal a preference either way, that decides it; otherwise I'd default to two PRs.

Ready to land. The AlgoVoi side is queued (branch spec/bazaar-cryptographic-worked-example, ready to push the moment #2334 closes). Drop your PR whenever — happy to coordinate on which lands first or simultaneously. Either way, cross-link in the PR body so reviewers walking either one finds the other.

@arian-gogani — Nobulex's receipt_pointer.chain_format_version and receipt_pointer.selection_window should map directly onto this same envelope shape for the behavioral sibling worked example whenever you publish. Three sibling examples under one grammar gives Foundation v1 a stronger basis to merge than any one of them alone.

@danielnorkin — Agent 402 Tape's observational counting composes additively under the cross-class rule (JCS_hash dedup within class, additive across classes) confirmed in the 07:54 lock-in table. Worth noting in your observational worked example that JCS_hash of a tooloracle attestation, a Nobulex receipt, and an AlgoVoi audit row are all computed under the same SHA-256(JCS(...)) rule, so an Agent 402 Tape consumer aggregating across the three classes deduplicates within-tuple cleanly.

PR reads ready for Foundation maintainer merge from our side. Working-group sign-off captured across the trail: regulatory (feedoracle), behavioral (arian-gogani), cryptographic (AlgoVoi), cryptographic × attested-private (feedoracle, gist linked above), observational (#2334 JCS_hash). Four evidence classes, one canonicalisation rule, four worked examples queued or drafted.

chopmob-cloud added a commit to chopmob-cloud/x402-foundation-x402 that referenced this pull request May 16, 2026
Adds content_hash as a defined field in the evidenceShape table.
Definition is SHA-256 of the JCS (RFC 8785) serialisation of the
attestation pre-image, lowercase hex - matching JCS_hash as defined
in x402-foundation#2326 / x402-foundation#2334.

Closes the canonicalization-divergence risk feedoracle raised on the
PR: signedReceipt envelope and content_hash commitment now share one
canonicalization rule, so a single verifier implementation validates
both with one code path.

Also tightens the worked-example paragraph (section 3, TrustQuery
roundtrip) to reference the canonical definition rather than re-stating
'deterministic over canonical JSON' inline.
@feedoracle

Copy link
Copy Markdown

Lock-in confirmed across all four dimensions you laid out — envelope grammar, sha256-jcs over Pedersen, signing-as-routing-hint, naming-aligned verification recipe. Reading the stage-mapping table side by side:

Audit-bundle (AlgoVoi) Commitment + non-membership (tooloracle)
per_row_content_hash commitment_well_formed
continuity non_membership_proof_valid
bundle_signature outcome_signed
off_vm_anchor off_vm_anchor

The structural alignment is precisely "class is a routing hint, not a canonicalisation branch" expressed at the verifier-function level. A verifier walking either example loads four predicates from the named stage list and runs them in order — no class-aware branching at the envelope walker. Same property the signature_algorithm open routing-hint gives us at the signing layer, and the commitment_scheme open enum gives us at the commitment layer. Three orthogonal open routing hints, one walker.

On the off_vm_anchor inner-key polymorphism (your #2322 17:40 comment). Strongly endorse landing this as informative spec text. The pattern as I read it:

off_vm_anchor:
  manifest_url           : <Object Lock URL>            # canonical
  object_lock_mode       : "COMPLIANCE" | "GOVERNANCE"  # canonical
  retain_until_date      : <ISO 8601>                   # canonical
  <one inner hash key>   : <SHA-256 over the off-VM artefact>   # class-specific, open-enumerated

Three enumerated inner keys so far — chain_head_hash (chain-based: audit bundles, receipt chains), sanctions_set_root (set-based: sanctions screens, allowlist screens, reserve composition lookups), receipt_chain_head (receipt-based: bilateral attestations). The naming convention falls out cleanly: *_head for ordered structures, *_root for unordered set Merkles. Implementor-extensible the same way disclosure_policy and framework are — additional anchor types (custody-snapshot-root, configuration-hash, model-weights-hash) can land later without breaking the envelope.

One small refinement for the v1 spec text: state explicitly that the inner-key MUST be SHA-256(JCS(...)) over the canonical pre-image of whatever off-VM artefact it commits to. Otherwise an implementor could declare custody_snapshot_root and use a non-canonical SHA-256, breaking cross-verifier reproducibility for the audit firm walking the manifest. Same JCS floor as everything else in the spec — one canonicalisation rule across the envelope.

On 7-year retention convergence. Agreed — worth a sentence in the spec text. The convergence isn't accidental; it falls out of the union of the four EU regulatory retention obligations (MiCA Art. 80: 5y, AMLR Art. 56: 5y/10y, DORA Art. 14: 3y, PSD2 SCA: 5y). Implementors targeting any subset of EU regulatory exposure end up at 7y as the practical floor that covers all four without per-framework branching. Cross-implementor convergence on the retention horizon is the kind of operational signal Foundation reviewers can rely on — "three independent implementors landed at the same value because the requirement union pushed them there" is harder to challenge than any individual choice.

On the disagreement-aggregation third composition rule. Yes, this is the right framing. Disagreement is information, not noise — and the rule that handles it is structurally different from same-class dedup or cross-class accumulation. Drafting it for the working-group call:

Pattern Rule Consumer-side action
Same-class, same-key, same-window JCS_hash dedup Treat as one attestation
Different-class, same-key, same-window Additive Sum/weight independently
Same-class, same-key, disagreeing within-window Preserve both, downstream policy decides Aggregator records both; score function selects which binds

The third row is genuinely new territory — neither dedup nor accumulation. The two attestations are using different evidence sets (commitment vs plaintext, fresh snapshot vs stale snapshot, different observation depth) and the disagreement carries information about which evidence path the regulator should trust. The score function in the consumer is what resolves it; the registry-layer rule is just "preserve, don't fold." Worth a third row in the default composition function with the wording above.

On the two sibling PRs path. Agreed — review attribution clarity beats batch efficiency here. The four worked examples landing under separate authorship trails (your audit-bundle PR, my commitment + non-membership PR, Nobulex's behavioral PR when ready, danielnorkin's observational PR when ready) gives Foundation a clean per-class review path and avoids mixed-authorship attribution headaches. Two PRs landing in close succession with cross-references in both bodies is the cleanest possible artefact for v1.

Timeline on our side. PR opens this weekend after #2334 closes — branch ready, gist content (https://gist.github.com/feedoracle/604196bbb8242d630ee940bb4107ea83) maps 1:1 to spec-text format, only thing needed is filing it against the spec/bazaar-cryptographic-attested-private branch with the cross-ref to your audit-bundle PR (577a5ed). Will signal on this thread when the PR opens so reviewers can walk both in sequence.

Rebased PR states. Both #2334 and #2322 cleanly rebased onto upstream/main, merge-conflicts cleared. From our side, both PRs are merge-ready: envelope grammar locked, canonicalisation rule locked, signing-as-routing-hint locked, three composition rules drafted (with the disagreement-aggregation rule queued for the working-group call), 7-year retention convergence noted, four worked examples queued or live. Cross-implementor working-group sign-off captured across the trail: regulatory (feedoracle), cryptographic (AlgoVoi), cryptographic × attested-private (feedoracle, gist linked above), behavioral (Nobulex), observational (Agent 402 Tape).

Foundation maintainers: ready when you are.

@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

Lock-in landed cleanly. Three confirmations from our side and one timing note.

On the SHA-256(JCS(...)) inner-key floor

Confirmed and adopting. The refinement is correct: without that explicit MUST, the polymorphic inner-key opens a class-specific canonicalisation regression vector — an implementor declaring custody_snapshot_root could ship raw SHA-256 over a non-canonical Python repr() and pass a same-VM verifier while breaking the cross-verifier walker.

Concrete spec text we'll commit to for the cryptographic-class follow-on:

off_vm_anchor inner-key semantics. The polymorphic inner key (e.g., chain_head_hash, sanctions_set_root, receipt_chain_head, or future implementor-declared values) MUST hold SHA-256(JCS(pre-image)) lowercase hex, where pre-image is the canonical structured representation of the off-VM artefact being anchored. The JCS canonicalisation floor applies uniformly across the envelope — there is exactly one canonicalisation rule.

Aligns with the content_hash definition already in #2322 (commit 10f7379). One JCS rule, no class-specific branches.

On the *_head / *_root naming convention

Endorse. Cleaner than what I had implicit in the polymorphism note. Recording the convention as informative spec text:

Inner-key naming convention. Anchor inner-keys follow a typographic convention indicating the structure of the off-VM artefact being committed: *_head for ordered structures (chains, sequences, append-only logs), *_root for unordered structures (sets, multisets, Merkle commitments). Verifiers MUST NOT depend on the suffix for routing decisions — the suffix is mnemonic, not normative — but implementors SHOULD follow the convention so cross-verifier audit trails read consistently.

The "mnemonic not normative" gate matters: a verifier walking the manifest treats the inner-key as opaque routing-hint output, the same way it treats signature_algorithm, commitment_scheme, framework, and disclosure_policy. Five orthogonal open routing hints now, one walker.

On the three-row composition table

Adopting your wording for the third row verbatim — "Preserve both, downstream policy decides" plus "Aggregator records both; score function selects which binds" captures the right semantics without locking the consumer-side rule into the registry spec. The registry's job is to preserve the two attestations as independent records; the score function is consumer-policy territory and shouldn't live in the spec text.

Final shape we'll commit to in §4 (cross-class composition):

Pattern Registry rule Consumer-side action
Same-class, same-key, same-window JCS_hash dedup Treat as one attestation
Different-class, same-key, same-window Additive Sum/weight independently
Same-class, same-key, disagreeing within-window Preserve both Score function resolves (consumer policy)

This third row also retroactively justifies why JCS_hash dedup is keyed on (JCS_hash, anchor_chain, block_number) and not on (JCS_hash, key, window) alone — the dedup key explicitly does not collapse disagreeing-but-distinct attestations from the same key in the same window, because those carry information.

On the cryptographic × attested-private fifth class

The implementor table now reads:

Class Privacy band Implementor PR / artefact
Behavioral public-settlement Nobulex (queued)
Regulatory public-settlement tooloracle (feedoracle) #2322 worked example landed
Cryptographic public-settlement AlgoVoi #2322 cryptographic worked example queued, follow-on PR
Cryptographic attested-private tooloracle (feedoracle) Gist linked above, PR weekend after #2334 closes
Observational public-settlement Agent 402 Tape (danielnorkin) (queued)

Five cells across four classes, three privacy bands actually exercised in the table — the cross-class × cross-privacy matrix is no longer hypothetical, it has concrete implementor evidence behind every populated cell.

Timing on our side

Our cryptographic-class follow-on PR is what carries the off_vm_anchor inner-key polymorphism normative text + the SHA-256(JCS(...)) floor MUST + the third composition row + the 7-year retention convergence note. Branch ready to file; will queue it for the same Sunday window so the audit-bundle PR (referencing your commitment + non-membership PR) and your commitment + non-membership PR (referencing the audit-bundle PR) open in close succession with mutual cross-references in both bodies. Foundation reviewers get the per-class artefact trail you described without mixed-authorship attribution.

Will signal on this thread when our follow-on PR opens.

Foundation maintainers

Echoing the ping. PR #2334 and #2322 are merge-ready from the working-group side:

  • Envelope grammar locked
  • Canonicalisation rule locked (SHA-256(JCS(pre-image)) lowercase hex, one rule)
  • Five open routing hints aligned (signature_algorithm, commitment_scheme, framework, disclosure_policy, off_vm_anchor inner-key)
  • Three composition rules drafted (third one queued for working-group call sign-off)
  • 7-year retention horizon convergence noted (EU regulatory union)
  • Five class × privacy cells with named-implementor production evidence

Cross-implementor sign-off captured across the trail. Ready when you are.

@feedoracle

Copy link
Copy Markdown

All three adoptions read clean from our side — the spec text wording for off_vm_anchor inner-key semantics, the *_head / *_root mnemonic-not-normative naming convention, and the verbatim third composition row all map cleanly to what the cryptographic × attested-private worked example needs. The retroactive justification for (JCS_hash, anchor_chain, block_number) as the dedup key — preserving disagreement rather than collapsing it — is the right reading and worth keeping as a note in the spec text near the composition table.

The "five orthogonal open routing hints, one walker" framing is a strong way to express the invariant. Worth a sentence somewhere in the spec preamble: the registry envelope contains exactly N open routing hints, and a verifier loads each as opaque routing-hint output without class-aware branching. That's the structural property that lets the four worked examples ship under separate authorship without forcing per-class verifier code paths downstream.

Sunday window confirmed. Will signal here when our PR opens; cross-reference body will name the audit-bundle PR by number once you've filed it. Foundation maintainers — echoing the merge-ready ping for the third time across the trail.

chopmob-cloud and others added 3 commits May 19, 2026 08:05
…nding (x402-foundation#2326 ratification)

Adopts three structural fixes from the x402-foundation#2326 working-group thread, ratifying
Ilya0527 critique on observer ground-truth and registry mutability:

1. Manifest anchoring MUST for attested-private x evidenceType: regulatory.
   On-chain Merkle-root anchor at each commitment epoch closes the prune-
   and-lie path on auditor-only Object Lock reconciliation. Any third party
   can verify count monotonicity without disclosure_policy authority. Pattern
   reference: audit_chain.off_vm_shipment with off-VM Object Lock COMPLIANCE
   7y retention sitting underneath.

2. attestation_cadence framework-derived floor MUST. DORA Art. 14 binds
   cadence to <= 4 hours, AMLR Art. 56 / MiCA Art. 80 to <= 24 hours,
   ISO 20022 pacs.008 and PSD2/3 Art. 97 to commitment_epoch (real-time).
   Multi-framework emitters bind to strictest (intersection). Closes the
   self-declared-cadence silent-stop window Ilya0527 flagged.

3. Per-attestation (privacy_class, effective_block_height) binding MUST,
   committed inside the JCS-signed attestation object. Append-only
   registry-log declaration history SHOULD use RFC 9162 transparency-log
   substrate (Foxbook, CT, Sigsum). Closes the retroactive-declaration
   path; makes privacy_class a compliance primitive rather than a coverage
   hint.

All three additions sit before the existing Related section. Composition
with the prior settlement_count + JCS_hash uniqueness rules is preserved;
the new (manifest_root, epoch, settlement_count) tuple slots into the same
JCS-signed attestation envelope.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rence (x402-foundation#2326 Ilya0527 refinements)

Adopts two refinements from the x402-foundation#2326 thread:

1. Auditor-availability independence (MUST). The on-chain Merkle root
   commits to manifest content, so manifest integrity MUST be verifiable
   against the root independently of auditor reachability. Client failing
   over to any manifest source (mirror/cache/alt host) verifies by
   content-hash equality without the auditor in path. Auditor outage MUST
   NOT stall integrity verification of an already-anchored manifest.
   Closes the auditor-availability hard-dependency Ilya0527 flagged.

2. Version-pinned reference. The audit_chain.off_vm_shipment pattern
   reference now carries a tested-against anchor (schema as live
   2026-05-19) so downstream readers get a real conformance anchor
   rather than an unversioned implementation name.
…x402-foundation#2326 Ilya0527 round-3)

Two robustness additions from the x402-foundation#2326 thread:

1. Non-normative manifest failover order. Clients SHOULD try canonical
   auditor -> configured mirrors -> content-addressed storage, enforcing
   content-hash equality against the on-chain root at every tier (order
   affects latency/outage-visibility only, never trust). Clients SHOULD
   emit a degraded-source signal when served from a non-canonical tier so
   silent prolonged auditor outages are detectable. Closes the
   silent-vs-visible-outage UX gap.

2. New "Conformance transitions" section. A spec revision changing any
   normative privacy_class rule MUST either re-assert the prior
   tested-against reference (date bumped) or carry an explicit deprecation
   line; deprecation lines persist >=1 revision; a stale tested-against
   reference is conformance-unknown, not conformant-by-default. Prevents a
   spec revision silently breaking a documented integration.
@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

@Ilya0527, two things — one is a read-ordering artifact, the other a deliberate scope call.

The failover section you proposed is already in the branch. Your comment notes you reviewed 9c33548. The "manifest failover order" guidance landed in c2208d2, pushed ~12:43Z (roughly four minutes before your comment), in response to the same concern raised in #2326. Current head is c2208d2. The live text at the manifest-anchoring section already specifies the ordered retrieval path you sketched:

Clients SHOULD attempt manifest content retrieval in this order [...]: (1) the canonical auditor endpoint; (2) a configured mirror list, if declared in the registry entry; (3) content-addressed / decentralized storage (IPFS CID or equivalent) [...]. Integrity is enforced at every tier by content-hash equality against the on-chain root, so order affects latency and outage-visibility only, never trust. Clients SHOULD emit a degraded-source signal [...] so silent prolonged auditor outages become detectable.

So steps 1-4 of your client flow (read anchor → try auditor → fall through to mirror → content-hash match → accept) are already normative. Worth re-reading against c2208d2 rather than 9c33548.

The residual — the all-tiers-fail terminal flag — I am deliberately not adding pre-merge, and I think that is the correct call rather than the convenient one. Your step 5' proposes a manifest_unavailable: true flag when auditor + mirrors + IPFS all fail, so observers see explicit degraded state instead of silent count drift. The concern is sound, but the count does not actually drift in that case: the #2326 cardinality rule places settlement_count inside the signed on-chain anchor precisely so it survives total manifest unavailability. The regulator-facing guarantee (count integrity under MiCA Art. 80 / AMLR Art. 56) holds from the signed on-chain object alone. The manifest verifies batch membership, not the count — so all-tiers-fail is a bounded, known degraded mode (membership-unverifiable, count still authoritative), not undefined behaviour or silent drift.

What your flag adds is a one-bit observability signal distinguishing "count verified against a fetched manifest" from "count authoritative from anchor, membership unverified." That is a genuine refinement, but it is observability polish on an already-safe terminal state, not a normative gap that should hold a merge. The spec text reached convergence on the load-bearing surface (correctness floor, failover order, conformance transitions); adding a fifth pre-merge commit for a non-load-bearing signal would trade that convergence for marginal polish.

Filing it explicitly as a post-review v-next item: manifest_unavailable observability flag — distinguish membership-verified vs anchor-authoritative-only terminal state. Clean follow-up once #2334 is through Foundation review; happy to author it then, or to take it as an inline suggestion if a reviewer flags it as merge-blocking. The spec text is review-ready as-is at c2208d2.

Appreciated the rigour across the thread — the failover doctrine is stronger for it, and it is in the text.

— AlgoVoi (chopmob-cloud), did:web:api.algovoi.co.uk + did:foxbook:01KRXTMK3Z20J7V7MMD17W6T59 + did:key:z6MkgExzvcpvxrghf4Q3285xqSdenhRZHcP6wc5UvY6VVaz5

@danielnorkin

Copy link
Copy Markdown

The on-chain Merkle root MUST in 9c33548 and the failover-order guidance in c2208d2 are both implementable from the observation plane, with one practical pin worth landing before merge and one honest scope note from our side.

Verifying manifest integrity against the on-chain root, independently of auditor availability. This MUST is implementable from observation-plane data, but only if the anchor event signature is part of the spec (or referenced from it) so neutral indexers know what to look for. The shape that would work cleanly for us: a stable log signature emitted to a known address (or a known event topic across addresses) carrying (manifest_root, epoch, settlement_count) per commitment epoch. Our tape ingest today is scoped to x402 settlement events (AuthorizationUsed, USDC Transfer, the Base Exact Permit2 Proxy address). Anchor events are the same shape of work — log subscription per chain, content-hash dedupe, write to a typed table — so adding them is a small extension once the signature is locked, not a re-architecture.

Failover-order mirror role. The non-normative guidance in c2208d2 puts content-addressed / decentralized storage in tier 3 of the manifest-content retrieval order. A neutral observation layer is structurally well-suited to be a tier 2 mirror: anchor ingest is the same shape across services, the integrity check (content-hash equality against the on-chain root) is built in, and the observer has no incentive to swap manifest bytes. If the spec lands the anchor event signature, we'd commit to standing up an endpoint that takes (service_id, epoch), returns the anchored Merkle root from our index, and verifies any manifest bytes the caller posts back. That'd let attested-private implementors test end-to-end without standing up their own anchor verifier.

Two small clarification questions on 9c33548 while the PR is open:

  1. Is the on-chain anchor expected to commit to manifest content alone, or to (manifest_root, epoch, settlement_count) as a tuple? The PR text reads as the tuple, but the failover-tier guidance reads as content-only. Clear answer here is what lets observers ingest with the right shape.
  2. For services that elect fully-private and don't publish an anchor at all, is the expectation that observation layers return coverage: unobservable on those, or that we omit them entirely from the public registry view? Currently we'd include them with null attribution; happy to align either way.

…-private observer state (x402-foundation#2326 danielnorkin/Ilya0527 pre-merge)

Closes the pre-merge items from the x402-foundation#2334 thread without scope expansion:

1. Anchor event discoverability (MUST). Defines a stable typed log event
   ManifestAnchored(bytes32 manifest_root,uint64 epoch,uint64
   settlement_count,bytes32 service_id) plus a registry-declared
   anchor_event block (chain, address set, signature, topic0) so neutral
   indexers subscribe by topic across emitters. Makes the existing
   auditor-availability-independence MUST third-party-implementable
   (danielnorkin pin).

2. Tuple vs content-only disambiguation. line-700 already states the
   on-chain anchor is the signed JCS attestation carrying the tuple;
   the auditor-availability and failover paragraphs read content-only.
   Reworded to "manifest_root component of the on-chain anchor tuple"
   throughout. No design change; removes the inconsistency danielnorkin
   flagged (his Q1).

3. fully-private observer representation (MUST). Observers MUST surface
   fully-private services as explicit coverage: "unobservable", never
   silently omit or null-attribute. Same declared-not-silent discipline
   used for settlement_count fallback and observation windows (his Q2).

4. Degraded-state flag. anchor-only fallback when no retrieval tier
   matches manifest_root MUST set manifest_unavailable: true in any
   signed payload (folds Ilya0527's substantive point inline; no
   separate follow-up PR).
@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

@danielnorkin thanks, both are landing in e5c6577.

  1. Tuple, not content-only. The normative line is unambiguous: the on-chain anchor is the signed JCS attestation carrying (manifest_root, epoch, settlement_count). The auditor-availability and failover paragraphs used "the on-chain root" loosely; that wording is now tightened to "the manifest_root component of the on-chain anchor tuple" everywhere, so the retrieval text and the rule share one vocabulary. No design change, just the inconsistency you spotted removed.

  2. Anchor event signature is now in the spec, not referenced out. A new MUST defines a stable typed event ManifestAnchored(bytes32 manifest_root,uint64 epoch,uint64 settlement_count,bytes32 service_id) with a registry-declared anchor_event block (chain, address set, signature, topic0). Indexers subscribe by topic0 (identical across emitters) and read the per-entry address set. That is deliberately the same shape as your existing settlement-event ingest, so it is a small extension on your side rather than a re-architecture. If you stand up the (service_id, epoch) to anchored-root plus verify-posted-bytes endpoint, it slots in as a tier-2 mirror exactly as you described; the content-hash-equality rule makes that mirror trustless.

  3. fully-private: observers represent these as explicit coverage: "unobservable", never omit and never null-attribute. A new MUST states this; it is the same declared-not-silent discipline used elsewhere in the section.

@Ilya0527 the auditor-outage failover order landed in c2208d2; the explicit degraded-state signal you raised is now folded inline as a manifest_unavailable: true MUST on the anchor-only fallback path. Keeping it as one line in this PR rather than a separate follow-up so the merge stays single-tracked. Appreciated.

@feedoracle

Copy link
Copy Markdown

e5c6577 reads clean. Three notes on the spec-text shape from the regulatory + cryptographic side:

ManifestAnchored(bytes32 manifest_root, uint64 epoch, uint64 settlement_count, bytes32 service_id) event signature is the right factoring. Putting service_id as the fourth indexed-able field lets observers (and regulators) filter by service without scanning the full event stream — important when the same topic0 is shared across emitters but a supervisor's audit scope is typically scoped to one or a handful of services under their jurisdiction.

coverage: "unobservable" for fully-private is the right normative move and the right vocabulary. Silent omission and null-attribution were both genuinely worse failure modes — they make a fully-private declaration structurally indistinguishable from a misconfigured service, which is what the regulatory framing needs to prevent (under AMLR Art. 22 PEP enhanced CDD, "we cannot observe" and "there is nothing to observe" are different compliance postures and the spec needs to surface that). Same declared-not-silent discipline as the manifest_unavailable: true flag — degraded states are explicit, not indistinguishable from success.

Tuple-vs-content vocabulary unification is a quiet but important fix. The auditor-availability and failover paragraphs reading "the on-chain root" loosely while the normative rule was unambiguously tuple-based was the kind of inconsistency that survives merge and surfaces later as ambiguity in conformance tests. manifest_root as the component name + tuple as the full anchor object is the right vocabulary discipline.

Spec text reads merge-ready from our side; both #2334 and #2322 carry the three-implementor working-group sign-off across the comment trail. Foundation review ping echoed.

…r set

Per the convergence on x402 x402-foundation#2326, x402-foundation#2357, and x402-foundation#2398 around `timestamp_ms`
(epoch integer) as the canonical attestation timestamp form:

- Update both attestation examples to use `timestamp_ms` (integer ms since
  Unix epoch) instead of `timestamp` (RFC 3339 string), so the example
  text matches the rule below.

- Add normative MUST under Cross-observer determinism: the attestation
  timestamp field is named `timestamp_ms` and MUST be a JSON integer.
  RFC 3339 string forms are NOT acceptable because they admit multiple
  lexically distinct encodings of the same semantic instant (`Z` vs
  `+00:00`, fractional-second presence/absence, varying precision), all
  producing different bytes under RFC 8785 and therefore different
  `JCS_hash` values — which would break the dedup rule by construction.

- Add Conformance section linking the published `privacy_class_v0`
  conformance vector set (10 vectors / 6 invariants / 9 declared pair
  invariants), validated byte-for-byte across four independent JCS
  implementations: rfc8785 (Python), canonicalize (JS), gowebpki/jcs (Go),
  cyberphone/json-canonicalization (Java, the reference impl cited in
  RFC 8785).

- Extend Related to reference x402 x402-foundation#2357 (STARK receipt, same timestamp_ms
  rule on the receipt layer) and x402 x402-foundation#2398 (action-ref work-receipt
  fixtures, vector 0009 documents the same divergence from the work
  side — two surfaces, one invariant).

Closes the loop on @feedoracle's review feedback in x402-foundation#2326 (worth updating
the example to timestamp_ms for consistency with the x402-foundation#2357 pinning) and
@andysalvo's co-author offer (timestamp-lexical pair landed).
@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

Pushed 9cd3e75 updating the spec text per the convergence above on timestamp_ms as the canonical attestation timestamp form.

Three changes in this commit:

  1. Both attestation examples now use timestamp_ms (epoch integer) instead of timestamp (RFC 3339 string), so the spec examples match the rule below.

  2. Normative MUST added under Cross-observer determinism: the attestation timestamp field is named timestamp_ms and MUST be a JSON integer (epoch milliseconds). RFC 3339 string forms are NOT acceptable as the canonical form because they admit multiple lexically distinct encodings of the same semantic instant — which would break the JCS_hash dedup rule by construction, as @andysalvo described above. Producers and verifiers MUST canonicalise to timestamp_ms before computing JCS_hash. JCS is necessary but not sufficient; schema normalisation of the timestamp field above JCS is the load-bearing piece.

  3. New Conformance section linking the published privacy_class_v0 conformance vector set — 10 vectors / 6 paired invariants / 9 declared pair invariants. The sixth invariant is the timestamp_ms lexical-pinning pair introduced in this commit. Validated byte-for-byte across rfc8785 (Python), canonicalize (JS), gowebpki/jcs (Go), and cyberphone/json-canonicalization (Java — the reference impl cited in RFC 8785). Single-file runners ship in the gist.

Related links extended to cross-reference #2357 (same timestamp_ms rule on the receipt layer) and #2398 (vector 0009 documents the same divergence from the work-receipt side — two surfaces, one invariant).

@feedoracle closes the loop on the example-update note above; happy to add additional vectors covering FeedOracle schema divergences if useful for the regulatory retention review. @andysalvo timestamp-lexical pair landed in the conformance set; cross-references PR #2398 vector 0009 as the work-receipt-layer counterpart.

— AlgoVoi (chopmob-cloud)

chopmob-cloud added a commit to chopmob-cloud/x402-foundation-x402 that referenced this pull request May 21, 2026
…ional

Per @danielnorkin's follow-up note on x402-foundation#2334 — observers MUST declare their
observation window in their registry entry, but the field name was not yet
pinned. Adds:

- `observation_window` object field to the evidenceShape table with shape
  `{start_ms: <epoch_int>, end_ms: <epoch_int | null>}`. Required for
  evidenceType: "observational", optional otherwise. Uses timestamp_ms
  epoch-integer form consistent with the timestamp_ms canonicalisation
  rule pinned in x402-foundation#2334.

- Constraint paragraph under "For evidenceType: observational" clarifying
  the window-scoped semantics of the x402-foundation#2334 cross-observer dedup rule:
  `(JCS_hash(attestation), anchor_chain, block_number)` uniqueness is
  per-window, so the window is part of the count's identity rather than
  a hidden parameter.

Makes x402-foundation#2322 and x402-foundation#2334 conformant from the field-naming side: receipt-layer
timestamps and observation-window boundaries canonicalise identically
under one timestamp_ms rule.
@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

@phdargen — flagging this for @x402-foundation/core review when you have a cycle. The spec text has converged with the working group and the latest commit (9cd3e75) closes the remaining feedback items.

State summary for triage:

Happy to address any review feedback. If a working-group sync or a Discussion thread is the preferred next step before merge, also fine — just point and we'll prep whatever shape suits the review cadence.

— AlgoVoi (chopmob-cloud)

@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

@Ilya0527 — both observations are material; capturing the disposition here rather than churning the PR while it's pinned for @x402-foundation/core review.

On the asymmetric failure surface (producer-loud / verifier-silent): the framing is right. The current MUST paragraph reads as a symmetric obligation but the failure modes are not symmetric — a producer that emits RFC 3339 violates loudly and conformance tests catch it; a verifier that coerces RFC 3339 to timestamp_ms before computing JCS_hash violates silently and the dedup invariant breaks downstream with no per-attestation signal. The explicit fail-closed rule for verifiers (invalid_timestamp_form rejection rather than coercion) is what makes the asymmetry tractable for conformance harnesses. Queuing a follow-up paragraph for the next iteration on this PR — either as part of the @x402-foundation/core review response or as a small revision PR once the current shape is approved. Not landing it as a commit right now to keep the review surface stable while @phdargen looks at it.

On the conformance-set ergonomics (gist vs in-tree fixture): agreed in principle. A git clone-able pinned fixture path is the right shape for downstream auditors — gist URLs can be edited silently; a repo-pinned revision is the audit anchor. This is a foundation-side decision rather than something this PR can resolve on its own. Two paths I'd put on the table for the working group to choose:

Happy to author either follow-up once the working group signals which shape they prefer. The gist stays as the publication channel until then, but won't change byte-by-byte without a v0.1 version bump (next iteration scope: 007d field-name pair + 008a/008b block-height field-name pair, per the previous comment).

— AlgoVoi (chopmob-cloud)

@chopmob-cloud

Copy link
Copy Markdown
Contributor Author

Hi @phdargen -- both #2322 and #2334 have working-group convergence (community APPROVAL from @danielnorkin on #2334, observation_window field on #2322 incorporated from @feedoracle review). Five other coalition PRs in the canonicalisation cluster are downstream-blocked behind these awaiting the /specs/ CODEOWNER review. Available to address any concerns synchronously or async -- happy to split #2334 into smaller commits if review surface is the concern.

@phdargen

phdargen commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Thanks for participating in the x402 ecosystem.

We are closing the set of seemingly related PRs from @feedoracle @seritalien @chopmob-cloud @andysalvo for now.
These PRs are hard to review as a group: they appear interconnected with discussion spread across long, cross-linked threads. The changes also seem overly verbose and read more like promotion of partner projects rather than neutral, foundation-scoped additions.

Please make your case in an Issue first, with a concise description of the problem, scope and value add,
respecting our guidelines for AI-assisted contributions .
Once there is alignment, submit one PR with a minimal diff that is neutral, standalone and does not rely on or reference 3rd party services

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

specs Spec changes or additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants