Skip to content

Port provenance attestation to the TypeScript, Python, and Go SDKs #93

Description

@macanderson

SPEC.md §6.5.1 defines a normative byte encoding, and contextgraph-types/tests/attestation_vectors.rs publishes the vectors to reconcile against. Right now Rust is the only implementation, so the cross-language claim is untested — and an encoding rule with exactly one implementation is indistinguishable from an implementation detail.

Per SDK: the length-prefixed link encoding, the source-first chain fold, the frame commitment, the RFC 6962 Merkle root and inclusion proofs, and Ed25519 verification. Each must reproduce the published vectors byte-for-byte in its own test suite.

The encoding was chosen over RFC 8785 (JCS) precisely so this port needs no JSON canonicalizer — if any SDK finds itself reaching for one, the encoding has been misread.

Highest value on the two dynamic-language SDKs, where a subtle divergence (UTF-16 length instead of UTF-8 byte length, signed 32-bit prefixes) is easiest to introduce and hardest to notice.

Definition of done

Each box is checkable before the merge, by running the command it names.

The vectors can fail a wrong port. The published set was ASCII-only, its
only multi-leaf Merkle vector had a power-of-two leaf count, and it carried no
signature and no inclusion proof — so a port could get all three of the named
traps wrong and still go green. Fixing that is part of this issue, not a
follow-up.

  • The vector set gains a link whose fields are multi-byte UTF-8 ending in
    an astral-plane character, so the UTF-8-vs-UTF-16-vs-code-point length
    question has an oracle
  • The vector set gains one-, three- and seven-leaf Merkle roots, so RFC
    6962's split is distinguishable from the duplicate-the-last-leaf shortcut
  • The vector set gains an inclusion proof and a fixed Ed25519 key with the
    signature it produces, so §6.5.3 and §6.5.4 have oracles at all
  • No previously published value changes (that would be a wire break,
    SPEC.md §15)
  • The vectors live in one file every language suite reads, with the Rust
    reference asserting the file agrees with what it publishes inline

The ports. Each covers the length-prefixed link encoding, the source-first
chain fold, the frame commitment, the RFC 6962 root and inclusion proofs, and
Ed25519 verification.

  • TypeScript — sdk/typescript/src/attest.ts, exported from the package
    index
  • Python — contextgraph_sdk.attest, exported from the package index
  • Go — sdk/go/contextgraph/attest
  • No port reaches for a JSON canonicalizer (ADR 0010: the encoding was
    chosen so none is needed)
  • No SDK gains a third-party dependency

Each port reproduces the vectors in its own suite.

  • TypeScript: cd sdk/typescript && npx tsc && node --test "dist/test/*.test.js"
  • Python: cd sdk/python && python3 -m unittest discover -s tests -v
  • Go: cd sdk/go && go test ./contextgraph/attest/
  • Rust: cargo test -p contextgraph-types --features attestation
  • Every SDK asserts against the strings the shared fixture publishes, not
    against values it computed itself
  • Each suite is demonstrated to fail on a wrong implementation, not only
    to pass on the right one — transcripts in the PR

Wired into CI.

  • Each of the three SDK jobs in .github/workflows/ci.yml runs its
    attestation suite
  • CI compiles and runs the Rust vectors, which the default-feature
    workspace run never did

Documented.

  • Each SDK README shows verification and names that SDK's own trap
  • CHANGELOG.md records the ports under [Unreleased]

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageAwaiting the triage agent: sizing + priority (SCR-005)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions