fix(tmproto): accept optional $schema/format_kind, relax wire-ID charset, fix verifier error envelope - #509
Merged
ohalushchak-exadel merged 3 commits intoSep 8, 2026
Conversation
… charset, fix verifier error envelope
Three related Trusted Match Protocol conformance fixes shipped together
so both agents pick them up via one pin bump.
Accept schema-valid $schema and format_kind. The context-match and
identity-match request schemas declare an optional top-level $schema
and Artifact declares format_kind with additionalProperties: true. The
generator previously skipped $schema and the hand-written Artifact
had no FormatKind, so any schema-legal request carrying either was
400'd by DisallowUnknownFields() in the verifier middleware. Both are
now known fields; the strict-decode guarantee for signature
reconstruction stays.
Relax wire-ID charset to match the schema. validateSafeID had
rejected ':' '/' '\\' on request_id, placement_id, package_id, and
property_id. The TMP schemas place no charset constraint on those,
and real callers use them (urn:uuid:... request ids, homepage/atf
placement slugs, tenant-prefixed package ids like acme:q1). Split
into a new validateEchoID (length + C0/DEL rejection only) for the
four wire IDs. validateSafeID stays on property_rid — that one flows
into store keys and is UUID-shaped by spec.
Fix verifier error envelope. writeVerifierError was emitting
{"type":"","request_id":"","code":...}, violating error.json (which
requires type == "error" and echoes request_id). It now emits the
correct discriminator and echoes the request_id through
SafeRequestIDForEcho. Pre-parse failures still pass an empty
request_id, since there is nothing to echo.
Tests in router, contextagent, and identityagent that pinned the old
strict-charset behavior now trigger via property_rid (still strict)
or a DEL byte in the request_id.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaced by `go fix ./...` while running the tooling gate on the tmproto conformance changes. Independent of that work; bundled to keep the working tree clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — TMP conformance fixes with two non-blocking test-coverage gaps.
What I checked:
- Charset relaxation via
validateEchoIDis safe: relaxed wire IDs never reach a separator-interpreting sink (context cache uses NUL,property_ridstays strict), and control-byte/log-injection protection plus signing semantics are preserved. - Accepting optional
$schema/format_kindand the verifier error-envelope fix preserve wire-shape fidelity and TMP signing/verification semantics — no signature/replay/nonce/canonicalization weakening. - No hand-edits to generated types requiring regen;
tmproto/types_gen.gochange is consistent with the schema source.
Medium findings (non-blocking):
- Missing test coverage for
$schema/format_kindacceptance (headline behavior; manual test-plan checkbox unchecked). - Missing test coverage for the corrected error-envelope shape.
Two medium findings fall through to approve (row 9). high_risk is true only via (modified) test/impl files, but with no medium finding on a specific modified sensitive file tied to a risk (findings list is empty), row 5 does not fire. gated_paths is false; no author-team gate; no deletions.
Cover the two behaviors this branch's tmproto changes ship: * Error envelope: on both post-parse (bad signature) and pre-parse (invalid JSON) verifier rejections, assert the response body has type == "error" and, when a request_id was parsed, echoes it back; empty otherwise. Prevents a future refactor from silently dropping either field and re-violating the error.json contract. * Strict decode: feed decodeStrict a context-match request carrying a top-level $schema and an artifact with format_kind, and an identity-match request carrying $schema. Both must decode without error. Prevents a regen that re-skips $schema in the generator, or a hand-edit removing FormatKind, from silently reintroducing a 400 on schema-valid traffic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ohalushchak-exadel
dismissed
aao-secretariat[bot]’s stale review
via
September 8, 2026 17:32
30b70cb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three related Trusted Match Protocol conformance fixes, shipped together as one
tmprotomodule change so both agents pick them up via a single pin bump.Accept schema-valid
$schemaandformat_kind. The context-match and identity-match request schemas declare an optional top-level$schema, andArtifactdeclaresformat_kindwithadditionalProperties: true. The generator previously skipped$schema, and the hand-writtenArtifactstruct had noFormatKind, so any schema-legal request carrying either was 400'd byDisallowUnknownFields()in the verifier middleware. Both are now known fields on the Go structs.DisallowUnknownFieldsstays on — the verifier reconstructs the signing input from the parsed struct, so silently dropping unknown fields would let a future extension produce a signature the verifier could never reproduce.Relax the wire-ID charset to match the schema.
validateSafeIDhad rejected:/\onrequest_id,placement_id,package_id, andproperty_id. The TMP schemas place no charset constraint on those, and real callers use them:urn:uuid:…request ids,homepage/atfplacement slugs, tenant-prefixed package ids likeacme:q1. Split into a newvalidateEchoID(length + C0/DEL rejection only) for the four wire IDs.validateSafeIDstays onproperty_rid— that one flows into store keys and is UUID-shaped by spec anyway.SafeRequestIDForEchonow uses the relaxed rule too, so aurn:uuid:request_id can be echoed on error responses and structured logs (control bytes still stripped).Fix the verifier error envelope.
writeVerifierErrorwas emitting{"type":"","request_id":"","code":…}, violatingerror.jsonwhich requirestype == "error"and echoesrequest_id. It now emits the correct discriminator and echoes the request_id throughSafeRequestIDForEcho. Pre-parse failures still pass an emptyrequest_id(nothing to echo yet); post-parse failures echo the parsed one.The second commit is an unrelated
go fix ./...autofix (slices.Backwardloop migration) that the tooling gate surfaced. Kept in-PR so the working tree stays clean.Release order
After merge:
tmprotosubmodule tag.cmd/context-agent,cmd/identity-agent,cmd/router,e2e, andtargeting.Two router-side follow-ups (no-follow-redirect on fan-out clients; complete asset-credential stripping for the unknown-asset variant and signed-URL fields) will stack on this branch as a separate PR.
Test plan
go test ./...intmproto,targeting, and root (router) — all green$schemaat the top level no longer 400srequest_id: "urn:uuid:..."no longer 400s"type":"error"and echoesrequest_id