Skip to content

Align the spec with the SDKs: free-text fields, x402 ids, rail_ref, conformance - #2

Merged
hedge-urk merged 1 commit into
mainfrom
spec/sdk-alignment
Oct 23, 2026
Merged

hedge-urk merged 1 commit into
mainfrom
spec/sdk-alignment

Conversation

@hedge-mustafa

Copy link
Copy Markdown
Contributor

Why

The server and three SDKs (Node.js, Python, and the new Dart SDK in halvorAI/ingest-dart) each implement APE v0.1. Where the spec is silent or contradicts itself, each SDK picked its own answer. This PR writes the answers down here, the contract they all follow, and adds shared test data so they can be checked against it.

The JSON Schema (ape/v0.1.json) is unchanged. Every change is to the docs, plus a new conformance/ folder.

Changes

1. The card-number check lists its fields (docs/FIELDS.md, README.md)

The spec said receivers reject "free-text fields" containing a possible card number, but never said which fields. Each SDK copied the TypeScript list. This PR lists them exactly, and adds payee.website, payee.resource_url and labels keys: query strings and label keys can carry anything.

It also says what is not checked, and why:

  • Identifiers (payment_id, rail_ref.*, instrument.*, related.*): card networks issue numeric transaction ids that pass the Luhn check about one time in ten, so checking them would corrupt or reject real events.
  • evidence[].value: a third party's signed proof, which redaction would break.

2. x402 payment_id for every stage (docs/MAPPINGS.md)

x402:<network>:<nonce> already covered the later stages. This PR adds that it is never the tx hash (the Python SDK uses the tx hash on settlement today, which breaks the lifecycle join). It also defines payment.requested, which was unspecified:

  • Node.js uses x402:<network>:requested, Python x402:<network>:challenge. Both give every challenge on a network the same id.
  • Proposed: x402:<network>:requested:<event id>, with the payment.authorized event that answers it setting related.previous_event_id.

3. rail_ref keys are closed (docs/FIELDS.md, README.md)

The README said receivers ignore unknown rail_ref keys, but the schema sets additionalProperties: false for every rail, and all three SDKs reject them. The docs now match the schema. labels and evidence[].value stay open. Forward compatibility comes from ape_version: receivers pick the schema by version.

4. Shared test data (conformance/, scripts/, CI)

  • conformance/events.json: 40 events with the verdict every implementation must give (valid, or invalid with rule schema or pan).
  • conformance/sign-vector.json: the cross-language signing vector, the same one ingest-python uses.
  • conformance/jcs-vectors.json: canonical-JSON vectors from the TypeScript canonicalize, including RFC 8785 appendix B.
  • npm test checks all of it, plus examples/, against the schema and the card-number rule. A new workflow runs it on every PR. 82 checks pass locally.

Where the SDKs stand

The 40 cases were run through each SDK's own validator:

SDK Cases matched Misses
Dart (halvorai_ingest) 40 / 40 none
Node.js (zod model) 37 / 40 payee.website, payee.resource_url, label keys (new in this PR)
Python 36 / 40 the same three, plus null-optional-field: it accepts null for optional fields, which the schema rejects (an existing bug)

Decisions needed from reviewers

  • The extended card-number fields (change 1). This makes receivers stricter. The server's check should change in the same release as the SDKs; until Node.js and Python redact these fields, the server would refuse their events with a card number in a URL or label key with pan_detected.
  • The payment.requested id (change 2). Node.js and Python both need to change. The alternative is to keep x402:<network>:requested and accept that challenges can't be told apart.
  • Closed rail_ref (change 3). The alternative is opening rail_ref in the schema, which changes every validator.

Notes

  • The repo is served as GitHub Pages (schema.halvorai.com). conformance/ will be public there, which is useful because SDKs can fetch it. package.json and scripts/ will be too, which is harmless.
  • docs/FIELDS.md says it is generated from the schema, but no generator is in this repo. If one exists elsewhere, it needs the same "Cross-field rules" text.
  • Follow-up work for the SDK teams is tracked in the fix lists: Node.js JS-16 and Python PY-17 (new fields), PY-4 (x402 id), PY-20 (null).

Please don't merge until the decisions above are agreed.

🤖 Generated with Claude Code

…formance

- docs/FIELDS.md lists the free-text fields checked for card numbers,
  adding payee.website, payee.resource_url and label keys, and says
  identifiers and evidence values are never checked.
- rail_ref keys are closed per rail, as the schema already says; the
  docs no longer say receivers ignore unknown rail_ref keys.
- docs/MAPPINGS.md defines the x402 payment_id for every stage,
  including payment.requested, which was unspecified.
- conformance/: 40 events with expected verdicts, the signing vector and
  canonical-JSON vectors, checked in CI by npm test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@hedge-urk hedge-urk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

confirmed

@hedge-mustafa
hedge-mustafa marked this pull request as ready for review October 23, 2026 08:56
@hedge-urk
hedge-urk merged commit 7af74dc into main Oct 23, 2026
1 check passed
@hedge-waleed
hedge-waleed deleted the spec/sdk-alignment branch October 27, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants