Repository navigation
feat(identity): one delegation-chain rule for demand-side grants - #601
Merged
Merged
Conversation
Align GrantEvaluator with adcontextprotocol/adcp#8473, the erratum to ratified R6 now in AdCP 3.3.0-beta.3, and run its cross-SDK delegation-chain vectors. - Implicit authority: account.operator and the signer's operator are each the brand or its house, in either combination. - One chain rule: at most two grants from the house's record to the signer's operator, every grant covering the claim, with account.operator the brand, the house, or the first grant's grantee. Brand- and house-operated accounts can use two-grant chains, and the brand's own record can supply the second grant. An unreadable intermediary is an outage only if no other chain authorizes. - exclusive_grants on the house's record makes grants[] the complete list whatever the scope. This replaces the per-scope reading. - GrantAuthorization.Source reports what authorized a claim. The 48 vectors are vendored byte for byte from v3.3.0-beta.3 and pinned by checksum in testdata/delegation-chains/UPSTREAM.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve.
Checked:
- GrantEvaluator aligned with adcp#8473 (erratum to ratified R6, experimental in 3.3); 48 cross-SDK delegation-chain vectors pinned by sha256.
- grants.go rewrite (hunk +326..498) is fail-closed: authorization expiry defaults to zero time so
now.Before(expiry)denies on absence;firsts()enforces account.operator ∈ {brand, house, first-grant grantee}; isRegistrableDomain guard applied at both call sites (no fail-open); authorizing chain decided beforeoutageso unreadable intermediary yields SERVICE_UNAVAILABLE only when no other chain authorizes; record-wide exclusive_grants gates grant_absent vs seller discretion off the house flag. - Operational readiness: bounded fetchIntermediaries (8-way sem), per-fetch trustDiscoveryTimeout=10s, wg.Wait + mutex-guarded map,
go test -race ./identitypasses. - No schema/types_gen.go, tmproto, or identity-agent surface touched; additive Source field;
feat(identity)correct.
Non-blocking follow-up noted by reviewer: intermediary fetch count grew from 1 to N distinct first-grantees — cold-cache latency on a pathological house record, acceptable given caching and the 10s per-fetch bound.
No critical/high/medium findings. gated_paths false; no author team gate; high_risk false. Falls through to row 9.
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
This aligns
identity.GrantEvaluatorwith adcontextprotocol/adcp#8473 (an erratum to ratified R6, shipped in AdCPv3.3.0-beta.3) and runs its 48 cross-SDK delegation-chain vectors. It ports adcontextprotocol/adcp-client#3452.account.operatorand the signer's operator each only need to be the brand or its house, in either combination. Mixed cases are now implicit, for example a brand-operated account where the house's own agent signs.account.operatormust be the brand, the house, or the first grant's grantee. This replaces the three fixed shapes. Brand- and house-operated accounts can now use two-grant chains, and the brand's own record can supply the second grant.SERVICE_UNAVAILABLE) only if no other chain authorizes. The authorization expires with the latest-expiring chain that authorizes.exclusive_grants: trueon the house's record makesgrants[]the complete list whatever the scope. An uncovered claim getsgrant_absent, andSellerAuthorizationis never called. An exclusive list with no grants authorizes no one. This reverses the per-scope reading. Only the house's flag counts.GrantAuthorization.Source(implicit,grant,seller,legacy) reports what authorized a claim. The vectors need it to tellimplicitfromauthorized.Tests
identity/grants_vectors_test.goruns all 48 vectors fromstatic/test-vectors/delegation-chains.json. They're vendored byte for byte fromv3.3.0-beta.3, which is identical to the #8473 head that adcp-client vendors, and pinned by sha256 intestdata/delegation-chains/UPSTREAM.json. The vectors use.examplenames, which have no registrable domain, so the harness maps them to registrable placeholders, as adcp-client does.TestGrantExclusiveAndHooksandTestNonRegistrableGranteeStaysExclusive(renamed).TestUnreadableIntermediaryIsAnOutageOnlyIfNoOtherChainAuthorizes.account.operatorrule and record-wide exclusivity on purpose: 9 vectors failed.go test -race ./identity ./signingpasses, andgolangci-lint run ./identity/...(go1.26.2) reports 0 issues.docs/agent-identity-3-3-migration.mdnow describes the new rule.🤖 Generated with Claude Code