docs(trusted-match): bind seller_agent_url into the TMP request signature#5660
Merged
ohalushchak-exadel merged 1 commit intoJun 22, 2026
Merged
Conversation
…ture seller_agent_url selects which seller's registered active package set the receiver evaluates and is the lookup key receivers use to resolve the publisher's signing keys; leaving it out of the signed bytes lets a holder of a valid registry key replay a captured signature under a different seller's URL to read another seller's offers or active package set. Adds seller_agent_url to the Context Match and Identity Match signed-fields tables and extends the Context Match cached-signature reuse key to (seller_agent_url, placement_id, provider_endpoint_url). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Clean fix. Closes a real replay gap by binding the signature to the seller it already claims to be scoped to — fail-closed beats fail-open.
The structural argument holds: seller_agent_url is the lookup key receivers use to resolve a seller's active package set, and it was already required on both request bodies but absent from the signed bytes. That gap let a holder of a valid registry key replay a captured signature under a different seller_agent_url to read another seller's active set. Putting it on the wire-signed surface is the right shape.
Things I checked
- Field exists where the spec says it does.
seller_agent_urlisrequiredin both wire schemas —static/schemas/source/tmp/context-match-request.json:228andstatic/schemas/source/tmp/identity-match-request.json:196. The "From the request body" source claim is accurate. No schema is touched, so no spec drift. - Anchor and reference resolve.
#seller-agent-attributionexists (specification.mdx:404);/docs/reference/url-canonicalizationresolves todocs/reference/url-canonicalization.mdx. The canonicalization-not-byte-equality framing matches the field's own schema description. - Context Match order is load-bearing; the insertion is deliberate. Context Match is newline-joined in table order, so inserting
seller_agent_urlat position 2 (aftertype, beforeproperty_rid) defines the canonical signing order. Identity Match is RFC 8785 JCS, which sorts keys lexicographically — so its table position is cosmetic, not wire-affecting. Both placements are safe. - Cache-key extension is correct. Widening the reuse key from
(placement_id, provider_endpoint_url)to(seller_agent_url, placement_id, provider_endpoint_url)is exactly what keeps a cached signature from crossing sellers. Without it the new binding would be defeated by the cache. - Changeset gate.
docs/trusted-match/is not inPROTOCOL_SCOPED_PATHS(scripts/check-changeset-protocol-scope.cjs), so CI does not require a changeset here. No block.
Follow-ups (non-blocking — file as issues)
- Breaking interop, no version marker. The PR's own compatibility note is right: this changes the signed bytes for both request types, and updated signers/verifiers reject un-updated ones during rollout. The repo's changeset gate keys off schema source, and this PR is docs-only, so it slips the gate cleanly — every prior
seller_agent_urlchange (#5410/#5411 at895f74a19) carried a changeset only because it also editedcontext-match-request.json. Worth a coordinated-rollout note in the changelog even though the gate doesn't compel one, so adopters don't desync silently. - Test-plan cross-check has no in-tree target. The unchecked item — "Cross-check that the in-tree reference implementation's signing input matches the updated tables field-for-field" — has nothing to verify against in this repo; there is no in-tree TMP signing implementation. The field-for-field check that actually matters is the Context Match position-2 insertion against the external reference impls you cite. That can't be confirmed from this diff. Confirm it lands in lockstep with the signer/verifier code.
Minor nits (non-blocking)
- Identity Match "immediately after
request_id" reads as ordering when it isn't. Since the Identity Match input is JCS, key order in the table is documentation-only — an implementer who treats the table row position as significant for the hash would be misled. The section already explains JCS upstream; a half-sentence pinning that the table order is presentational for this request type would remove the ambiguity.specification.mdx:540.
LGTM. Follow-ups noted below.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
5 tasks
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
Adds
seller_agent_urlto the Context Match and Identity Match signed-fields tables in the TMP specification.seller_agent_urlis the asking seller's agent URL on the request. It selects which seller's registered active package set the receiver evaluates (Seller Agent Attribution) and is the lookup key receivers use to resolve the publisher's signing keys for verification. Leaving it out of the signed bytes lets a holder of a valid registry key replay a captured signature under a different seller's URL to read another seller's offers or active package set — a structural break of the "binds the request to a specific seller" property the rest of the section already assumes.Both reference implementations of TMP signing already cover
seller_agent_urlin the signed input — this change brings the normative spec into alignment.Changes
seller_agent_urlimmediately aftertypein the newline-joined input, compared using the AdCP URL canonicalization rules.seller_agent_urlimmediately afterrequest_idin the JCS canonical object.(placement_id, provider_endpoint_url)to(seller_agent_url, placement_id, provider_endpoint_url)so that a cached signature under one seller cannot be reused under another.provider_endpoint_urlbinding explanation describing the same role forseller_agent_url.Compatibility note
This changes the signed bytes for both request types. Verifiers that have been following the spec literally (without
seller_agent_url) will reject signatures from updated signers and vice versa during rollout. Both signers and verifiers should land the change together.Test plan
🤖 Generated with Claude Code