docs: add jcs_edge_v1 conformance appendix for Content Integrity Profile (#1140) - #2219
eriknewton wants to merge 4 commits into
Conversation
…ile (a2aproject#1140) Retains chopmob-cloud's jcs_edge_v1 RFC 8785 canonicalization edge-case vectors byte-verbatim under proposals/content-integrity-profile/, using the retention layout established in eriknewton/concordia-protocol#212 (pinned SHA-256, upstream LICENSE/NOTICE, provenance record). The Content Integrity Profile spec text itself has not landed in this repository (PR a2aproject#1141 closed without merging), so this lands as a standalone proposal document rather than inside docs/specification.md. Refs a2aproject#1140.
…batim vector files, ignore commit ids
|
Hi @eriknewton. Thank you for the pro-active work on this. For the major changes and updates like this PR, please present your ideas and proposal in A2A TSC to vet this idea and implementations. As the TSC composes of the teams that maintain the A2A SDKs, it would be super valuable. Till then, I suggest you to mark this PR as DRAFT. |
|
Thanks @msampathkumar. Marked as draft. Happy to bring the conformance appendix to the TSC: a standalone vector corpus with SHA-256 pins and AlgoVoi attribution, no spec text. What is the right way onto the agenda, and is there a preferred write-up? The corpus and cross-language results are on #1140 for anyone reading before then. |
|
Cross-implementation check on the corpus this PR lands — I ran Corpus integrity first. Result: 10/10 byte-identical, on both decode paths (
Method: full file Two notes for the profile, both from this run:
This is offered as cross-implementation evidence for the profile, not as a merge request on the corpus — the appendix is doing its job as written. One caveat I want to state plainly rather than leave to inference: this is one independent implementation agreeing with a corpus authored elsewhere. The reproducibility claim the profile actually needs is three-way (the vector set's own reference implementation, plus at least one implementation per SDK language). If it's useful, I can extend the harness to run the same corpus through the Python |
|
This is the check the profile needs, and the corpus-integrity step first is the part that makes it evidence rather than a copy compared to itself. Ten for ten byte-identical through the Go card-signing path, with vector 003 and the 005/006 pair doing their job, is a strong data point. Yes, please extend it to the three-way table with the Python rfc8785 reference and the JS canonicalize package. A reference plus one implementation per SDK language is exactly the reproducibility bar, and having it posted before the TSC review would help the case directly. Thank you for running it against the retained bytes. |
Cross-implementation results for
|
| vector | expected sha256 (12) | Go (UseNumber) | Go (float64) | Python rfc8785 | JS canonicalize | verdict |
|---|---|---|---|---|---|---|
| jcs-edge-001-sep-in-value | cf1040eecddd |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-002-sep-in-key | 4a95154197d0 |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-003-nonbmp-key-order | 48c5d0987138 |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-004-nonbmp-key-order-multi | 84c14af1e8d9 |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-005-number-one-float | 2bfd14f43d17 |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-006-number-one-int | 2bfd14f43d17 |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-007-mandatory-short-escapes | 86e432364fb5 |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-008-control-u-escape | 3c7d87189d5a |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-009-solidus-and-html-literal | b4f255e4d487 |
✓ | ✓ | ✓ | ✓ | PASS |
| jcs-edge-010-accented-nfc-literal | 298ebe9dfd00 |
✓ | ✓ | ✓ | ✓ | PASS |
✓ = canonical output bytes equal the base64-decoded expected_jcs_bytes_b64 and sha256 equal expected_sha256. All 10 vectors, 4 output columns each (40 cross-checks): all agree.
Method: each vector's preimage was canonicalized independently by each implementation; no vector was special-cased, skipped, or re-generated, and no implementation was seeded with the expected output.
Limits: the Go leg probes the canonicalizer (jcsMarshal) that the Agent Card signing path uses, via a temporary in-package test, but does not exercise the full canonicalPayload end-to-end over a complete AgentCard JWS (the signatures-stripping and payload wiring are separate from JCS byte production and are not what these vectors test). All three legs ran clean; no divergence to report.
|
@msampathkumar quick housekeeping ahead of the TSC.
Thanks. Draft status stays until you say otherwise. |
|
Opened #2246 with |
|
Gentle nudge before Tuesday's TSC: could a maintainer apply |
|
Ran it against the Go SDK. Corpus as downloaded from #2246 and pinned before running:
On the pair you named, we accept both halves. Two things from the run that I would want the profile to have, because they change how a Go reader should interpret the
If the profile settles on 128 counted as open containers, implementing it here is small and contained: a counter advanced when a container opens, checked before the recursive walk, returning an error to the caller. I am glad to do it, as its own change rather than folded into the signing work in #368, and only once the profile's bound is settled rather than while it is a proposal. |
|
Thank you for pinning the corpus and running all eight. This is the result the corpus exists to produce: the at-bound rows match, and 101 to 103 accepted means no bound, as you read from the code. Your two notes change the appendix text and I will fold them: the 104 row must say that a refusal supplied by the decoder (jsontext's 10,000) is a toolchain property, and the profile must require the implementation to count open containers itself, so a Go reader does not mistake the stdlib cap for conformance. The bound's value (128, counted as open containers) is what I am asking the TSC to settle on Tuesday; your counter as its own change after that is the right sequencing. |
The README now says 128 is a proposed profile choice, required by neither RFC 8785 nor an adopted A2A rule, and states the grounds the in-toto proposal gives for it. The unsourced second-serializer claim is withdrawn from both the README and PROVENANCE.json, and replaced by the third-party run against the a2a-go canonicalizer reported on a2aproject#2219, including its finding that the jcs-depth-104 refusal there came from the encoding/json decoder rather than from the canonicalizer. The gowebpki/jcs measurement now names its version, and notes that v1.0.2 adds a limit of 10,000. PROVENANCE.json pins the new README digest and names the source corpus by its current repository path.
Adds the jcs_edge_v1 conformance vectors as a standalone appendix for the
Content Integrity Profile discussed in #1140.
jcs_edge_v1 was authored by chopmob-cloud (AlgoVoi) and posted to #1140 on
2026-07-19. It pins the RFC 8785 edge cases a single self-check misses:
U+2028/U+2029 as literal UTF-8 bytes, property-name ordering by UTF-16 code
unit rather than code point, and 1.0 folding to 1. I reproduced all ten
vectors byte-for-byte on 2026-07-19 and turned that into a standing CI check
in Concordia (eriknewton/concordia-protocol#212). kuangmi-bit's a2a-go#368
canonicalizer already passes the same section 3.2.2.2 cases and is expected
to cross-run this corpus.
The profile spec text itself hasn't landed here yet (#1141 closed without
merging), so this lands as a standalone proposal document under
proposals/content-integrity-profile/ rather than inside
docs/specification.md. It can move once the profile text is accepted.
Retention layout mirrors concordia-protocol#212: vectors, upstream LICENSE
and NOTICE, and a PROVENANCE.json pinning the SHA-256 of each file plus the
source commit.
Refs #1140.