Repository navigation
CI run for adcontextprotocol/adcp-client-python#1537 - #2
Closed
KonstantinMirin wants to merge 51 commits into
Closed
KonstantinMirin wants to merge 51 commits into
KonstantinMirin wants to merge 51 commits into
Conversation
Update the experimental AdCP 3.3 identity preview schemas from the open spec branches, with provenance in UPSTREAM.json: - relationship.status uses pending_approval from its own enum, and relationship is emitted on get_principal only when served at 3.3 or later. - The buyer onboarding posture lives only in trust.json, at profiles.adcp.onboarding on the record and on an agent entry; sellers and buyers share one mode enum. - The agent_identity requirement kind and documentation_url are gone, and aao_verified uses verified_specialisms. Regenerate the bundle and the preview models, and export PrincipalRelationshipStatus in place of BuyerOnboardingMode. Refs adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…final spec Bring the experimental AdCP 3.3 agent identity up to date with the final spec decisions and with the Go and TypeScript SDKs. Signature path, per DR-0023 and WBA 5.5/6.10: - Answer with WBA statuses and headers only, with one generic AUTH_INVALID body and never SERVICE_UNAVAILABLE. A revoked key is 403 without Accept-Signature. - Key-directory discovery failures never evict a cached directory, which keeps verifying up to max_stale_seconds; with nothing cached the request is unverified: 403 with Accept-Signature by default, or anonymous via wba_discovery_failure, except operations in required_for. - verify_wba_request needs only the directory and replay store. Revocation, bindings and grants are the authorization layer; replay and revocation store failures are application-level SERVICE_UNAVAILABLE. Authorization layer: - OriginRevocationChecker.status reports revoked, current, no_list, stale or unavailable. No list means identity-established and unbindable; a stale list fails spend closed. - An unbound caller outside unbound_for gets what an anonymous caller gets. A delisted bound origin falls back to identity-established with no task-control exception, keeps its binding against downgrade, and cannot be bound again while unlisted. - Trust records: the last record read stays authoritative; a 404 or invalid record after a read is an outage, never a removal; spend gets SERVICE_UNAVAILABLE and non-spend continues within a configurable bound. A grantor that served 200 never falls back to authorized_operators and an outage is never grant_absent. - Grants are read from the house record, with registrable-domain comparison, self-operated brands, narrowing and window intersection. Interop: 64-byte nonces, the target URI as received, unsigned JWKS directories by thumbprint, RSA-PSS-SHA512, and the vendored Cloudflare interop fixtures. Hardening: per owner/host trust budgets with a separate foreign-hop budget, PSL PRIVATE-aware per-operator history quotas, backdated directory created, replay at exactly expires plus skew, and fail-closed account claims. Refs adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rewrite the AdCP 3.3 agent identity migration guide for the final spec decisions: errors split by layer, discovery failures, revocation states, delisting and outage continuation, house-record grants, interop details, and the deliberate differences from the Go and TypeScript SDKs. Refs adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lignment - A reused WBA nonce is 429 without Accept-Signature, as the spec requires; replay_status=403 only reproduces the older #7894 vector. The signer enforces 64-byte nonces too. - Spend classification fails closed: update_media_buy and control_media_buy commit spend unless every change is a rename, pause or cancellation. - Grantees compare as registrable domains, and the house resolver can map the brand to its child-brand id in the house portfolio. - Unsigned requests to required operations get the WBA Accept-Signature challenge, and a web-bot-auth-only verifier answers them and RFC 9421 signatures with 403. - authorities refuses signatures captured for another host, with a boot warning when unset. - Revocation list state is an LRU cache with revoked facts kept separately, so a flood of origins cannot lock new buyers out; concurrent overflow is unavailable. Trust history admits up to 65536 operators. - Seller-side claim failures are PERMISSION_DENIED without details, and grant_absent is reserved for uncovered claims. - Document the breaking experimental API changes, bearer fallback for signed callers, and the missing durable history seams. Refs adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Compare the admitted origins as an ordered list rather than by substring, which CodeQL reads as URL sanitization. Refs adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment the expected decode failures in valid_wba_nonce and give the revocation status protocol an explicit body. Refs adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The verifier accepts 22 to 256 character nonces again: 64 bytes is RECOMMENDED for signers and httpsig-02 adds no requirement. The signer still sends 64 bytes by default and accepts the same range from callers. - Binding, trust-record and spend logic move out of the transport middleware behind one injected WbaAuthorizer; OriginBindingGate is the default, derived from the config so dataclasses.replace rebuilds it. - The RFC 9421 DeprecationWarning fires only once the SDK advertises 3.3. - Snapshot adcp.signing and adcp.types.experimental_identity in the public API snapshot, with the PgReplayStore stub resolving like the real class. Refs adcontextprotocol#1511, adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d libraries The SDK keeps AdCP domain rules and takes these protocols from libraries: - jws.py verifies with joserfc. The AdCP profile stays on top: the alg allow-list, exact typ, no crit, a protected kid, strict JSON in header and payload (joserfc keeps the last duplicate key), EdDSA pinned to Ed25519, and the fetcher's own size cap instead of joserfc's defaults. - keygen.py generates keys and exports public JWKs with joserfc; the curve policy, AdCP JWK members and the CLI stay in the SDK. - standard_webhooks.py signs with the specification's standardwebhooks package. Verification calls Webhook.sign and matches v1 tokens in constant time, because Webhook.verify reads the wall clock with a fixed window and cannot honor the caller's now and tolerance. Adds joserfc and standardwebhooks, and raises the cryptography floor to 45.0.1, which joserfc requires. Refs adcontextprotocol#1511, adcontextprotocol#1500 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… Auth with adcp#7894 Moves every RFC 9421 layer onto http-message-signatures 2.0.1 and RFC 8941 onto http-sf, replacing the archived http-sfv: - adcp.signing._rfc9421 holds the component resolver that supplies the ;key and ;req parameters (pyauth/http-message-signatures#29) and the AdCP @target-uri/@authority canonicalization, plus thin sign/verify wrappers over HTTPMessageSigner and HTTPMessageVerifier. - The 3.2 request and webhook profiles verify at step 10 through HTTPMessageVerifier and sign through HTTPMessageSigner.sign; the async SigningProvider path takes the library's own base. Error codes and step order are unchanged; every conformance vector passes. - crypto.py dispatch uses the library algorithm classes; Signature, Signature-Input and Content-Digest parse with http-sf, which refuses duplicate keys and parameters through on_duplicate_key. - Governance JWS signs and verifies with joserfc; publisher-pin thumbprints use joserfc; JWS splitting uses joserfc extract_compact. Aligns the experimental Web Bot Auth profile with the final adcontextprotocol/adcp#7894 at e24eeb09: one signature and one Signature-Agent member per request, signed directories only, ed25519 and rsa-pss-sha512 only, a 64-byte nonce, and 429 with Accept-Signature carrying a fresh nonce for a replay. Re-vendors the wba-profile vectors, governance vectors included, with provenance. Refs adcontextprotocol#1511 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Read one header spelling everywhere: the library request takes the first spelling of a name, as the profile lookups do, and the step-1 precheck refuses a signed field that a mapping carries under two spellings. - Refuse parameters on a Signature member and a req parameter that is not a true flag. - verify_wba_governance_issuer registers the action-binding crit markers, checks marker/claim pairing, and checks the issuer before key discovery. - Typed duplicate-field errors, an explicit window guard on the shared verify helper, a tab-tolerant profile-selection walk, and contract tests for the http-sf primitives it uses. Refs adcontextprotocol#1511 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add tests/wba_interop/adapter.py, which answers the four operations of the AdCP interop harness (sign_request, sign_directory, verify_request, and verify_directory) through the public adcp.signing API. Add a wba-interop CI job. It checks out tests/wba-interop and the wba-profile vectors from adcontextprotocol/adcp at ADCP_REF, the head of the harness pull request (adcontextprotocol/adcp#8298), installs the harness with npm ci on Node 24, and runs run.mjs against the adapter.
Apply the Web Bot Auth rules that the Cloudflare web-bot-auth reference and the profile state: - Sign and verify with ed25519 only. RSA-PSS leaves the WBA signer, verifier, and directory, and the dead RSA branch of the governance issuer check goes with it. ES256 stays on the AdCP RFC 9421 profile. - Refuse a signature that has no alg parameter. The verifier no longer infers alg from the key. - Ignore a directory key the profile can't use, and the signature that key makes (RFC 7517 section 5). Refuse a directory in which a usable key doesn't sign (WBA Appendix B.1, one signature per key). - Refuse a directory served with any media type other than application/http-message-signatures-directory+json (WBA section 5.5.1).
b64url_decode used the standard library's lenient decoder, which drops characters outside the alphabet and ignores padding and trailing bits. Verifier step 8 decodes the signer's JWK through public_key_from_jwk, so it accepted a JWK whose x member had "!!" inserted. Build b64url_decode and b64url_encode on joserfc.util, and import Ed25519 and P-256 JWKs with joserfc's OKPKey and ECKey through one helper, crypto.import_jwk. The JWS and governance paths use the same helper and decoder instead of their own copies. The SDK keeps one check that joserfc leaves open: a P-256 member is exactly 32 bytes (RFC 7518 section 6.2.1.2).
Remove hand-written code that joserfc, http-sf, and http-message-signatures already provide: - public_directory_key exports the JWK with joserfc's OKPKey.as_dict and thumbprint. - jwk_thumbprint passes joserfc the members that the key type's registry marks as required, instead of its own RFC 7638 member table. It returns the base64url thumbprint, so thumbprint_key_id no longer decodes and re-encodes it. - A directory key's x member is checked by importing it with joserfc, and the nonce check decodes base64url strictly, so neither re-encodes a value to compare it. - The async signer and the verifier build Signature-Input with http-sf (signature_input_field) instead of an f-string. - sign_key_directory adds each key's signature to the same message with HTTPMessageSigner.sign(append_if_signature_exists=True) instead of joining the fields with ", ".
Under -00 section 5.2.1, a Signature-Agent member with type=directory names a key directory, and the verifier ignores a member with any other type (profile rule 6). The verifier already reads type=directory as a Token and as a String, as Cloudflare's parser does, and ignores jwks_uri and cimd, which leaves the signature without a member keyed to its label (403, harness scenarios R18 and R18b). No test covered either spelling. The nonce test's signing moves into a helper, _sign_vector_request, that signs the positive vector's request with the library, so a test can sign a Signature-Agent shape the SDK signer never emits.
RFC 9421 sections 4.1 and 4.2 define every Signature-Input member as an inner list and every Signature member as a Byte Sequence with no parameters. The verifier checked this only for the web-bot-auth member, so a sibling such as cdn=token verified. Cloudflare's http-message-sig refuses such a field as MalformedField, which the profile answers with 400 (harness scenario R20b). _check_signature_members runs on both fields before a member is selected, for requests and for key directories, so a malformed web-bot-auth member is 400 too. A member with another tag is still not read otherwise. _verify_crypto no longer checks its own member.
The Signature-Agent member value is the signer's https origin, and the profile compares it as a string: a lowercase host, no default port, no trailing slash, and no path, query, fragment, or userinfo. The verifier canonicalized the value first, so https://BUYER.example.com:443/agents/a verified as https://buyer.example.com. The verifier now refuses any value that differs from its canonical origin with 403, before key discovery (harness scenarios R17, R17c, R17d, and R17e). http:// and userinfo were already refused. sign_wba_request already sends the canonical origin, and a test pins it.
The governance vectors leave the Web Bot Auth profile's vectors, and the profile drops its governance checks: verify_wba_governance_issuer is an AdCP-layer check that runs after WBA verification. The function stays. Move the three governance vectors, with the e24eeb09 keys file they sign with, to vectors/wba-governance, with their own provenance record. Move their tests from test_wba_profile.py to test_wba_governance.py, which shares the profile tests' directory resolver.
Re-vendor static/compliance/source/test-vectors/wba-profile from adcontextprotocol/adcp at 49b3a315, the head of #7894. The request and directory vectors keep their signatures and change only their comments. The directory no longer carries governance vectors or the governance key, and it adds scenarios.json, the failure scenarios that the interop harness signs at run time.
…hook ProfileSigner reaches HTTPMessageSigner's protected base builder because the library has no public way to get a signature base before signing. pyauth/http-message-signatures#31 asks for one, so the module and the class docstrings name it as the condition for dropping the hook.
…ed helpers The governance verifier had its own compact JWS splitter, its own strict JSON parser, and two checks that decoded base64url and re-encoded it to refuse a non-canonical spelling. Split the token with jws.parse_compact_jws and parse each segment with parse_strict_json, the splitter and parser that the revocation-list and Web Bot Auth paths use. b64url_decode is strict, so the re-encode checks go too. Outcomes and error codes are unchanged, with two exceptions where the old parser was weaker than every other JWS path in the SDK: a segment with an unpaired surrogate escape is refused (governance_token_invalid), and nesting deep enough to raise RecursionError is governance_token_invalid instead of an uncaught exception. Some failure messages change, because the splitter refuses a malformed header before the JSON step. The governance authorization tests (tests/test_governance_authorization.py and tests/conformance/vectors/governance-authorization.json) gate it.
The governance verifier and the Web Bot Auth governance issuer check each carried a copy of security.mdx checklist step 4, and each checked the crit syntax by hand. Add governance.critical_claims_failure and call it from both. It hands the crit list and the markers to joserfc's JWSRegistry.check_header, which refuses a crit that isn't a list of registered names and a marker that is missing or isn't a boolean. The helper keeps the rules joserfc leaves open: no repeated entry, a claim present exactly when its marker is true, and authorized_task with authorized_payload_hash. The 3.2 verifier still requires crit, and the WBA check still treats an absent crit as empty. Behavior change in the 3.2 governance path, and in the WBA issuer check: a crit entry whose marker is absent, or isn't a boolean, is now refused (governance_token_invalid) even when its claim is absent too. RFC 7515 section 4.1.11 forbids a crit name that doesn't occur in the header, so refusing it is correct. Every other outcome is unchanged. The governance authorization tests (tests/test_governance_authorization.py and tests/conformance/vectors/governance-authorization.json) and tests/conformance/signing/test_wba_governance.py gate it.
The Web Bot Auth governance issuer check built its own joserfc registry, a second copy of governance._governance_jws_registry with its own algorithm table. Use the governance registry for both verifiers, and let _AdcpJwsRegistry know RFC 9864's Ed25519 name, which a Web Bot Auth governance token may carry. Behavior is unchanged. Each caller still narrows the registry to its own algorithms: the revocation-list paths to EdDSA and ES256, the 3.2 governance verifier to the token's EdDSA or ES256, and the WBA check to EdDSA or Ed25519 after it matched the alg to the key. The WBA table's PS512 entry is dead: with Ed25519 keys only, no PS512 token reaches signature verification. joserfc's extract_compact doesn't read the registry's algorithms, so splitting is unchanged too. The governance authorization tests, tests/conformance/signing/test_wba_governance.py, and the JWS and revocation-list tests gate it.
crypto.import_jwk hands the whole JWK to joserfc, and joserfc derives the public key from d when d is present. So public_key_from_jwk, the request and webhook verifiers' step 8, and the JWS verifier used the key that d names, not the key that x names. A kid or a publisher pin names a key by its RFC 7638 thumbprint, which covers x (and y) only. A JWKS entry that carried the pinned x beside another key's d passed the pin check, and the signature that d made verified. Before adcontextprotocol#1537, public_key_from_jwk read x alone. import_jwk now drops the private members unless private=True, which only private_key_from_jwk passes. The Web Bot Auth module takes the private-member set from crypto instead of keeping its own copy. test_public_key_is_read_from_the_public_members and test_reject_signature_by_a_private_member_beside_the_key failed before this change.
… table Commit 3f0c801 rewrapped one annotated assignment in governance.py into a shape that black, the formatter that .pre-commit-config.yaml runs at 24.10.0, refuses. This puts back the line as black writes it, which is the line on the base branch.
…s claim together security.mdx checklist step 4 (AdCP 3.2.1 to 3.2.3) requires "each boolean protected-header marker, its crit entry, and its payload claim to appear together", and the unknown-extension rule says verifiers "MUST also reject when the critical marker and its corresponding claim do not appear together". critical_claims_failure compared the claim only against a crit entry with a true marker, so it accepted a marker for a claim the payload doesn't carry: a crit entry with a false marker, or a marker with no crit entry. Both governance verifiers share the helper. The helper now requires the three to be present together, with the marker true. test_critical_marker_crit_entry_and_claim_appear_together failed before this change. A repeated crit entry with its marker present is now a case of test_critical_claims_follow_rfc_7515_crit_rules, so the duplicate check has a case that only it refuses.
With Web Bot Auth on ed25519 only, nothing reaches the rsa-pss-sha512 entry of _rfc9421.ALGORITHMS. The AdCP request and webhook profiles check alg against ALLOWED_ALGS (ed25519 and ecdsa-p256-sha256) before the table, and the Web Bot Auth signer and verifier take ed25519 from the key. The table and the module text now name the two algorithms the SDK signs and verifies with.
…head adcontextprotocol/adcp#7894 moved from 49b3a315 to 2546cf5f, which adds scenarios R18c and R18d: a Signature-Agent member with type=directory as a Token and as a String verifies. Only scenarios.json changes; the request and directory vectors are byte-identical. The provenance record, the vectors README, and the provenance test name the new commit. docs/agent-identity-3-3-migration.md still named e24eeb09, the commit before 49b3a315, as the profile it follows; it names 2546cf5f now.
adcontextprotocol/adcp#8298 was force-pushed: its head is 6bf069b6, and a7708e41, the commit ADCP_REF named, is no longer on the branch. A commit no ref reaches can stop being fetchable. The new head also grades scenarios R18c and R18d, type=directory as a Token and as a String, which raises the check count from 52 to 54. The SDK adapter passes all 54 at this commit.
verify_wba_governance_issuer screened the token's alg against a literal set that still held PS512 after the profile went to Ed25519 only. A PS512 token passed the screen, made the verifier fetch the issuer's key directory, and failed only at the key-to-alg match. The screen now reads the profile's own table, _GOVERNANCE_JWS_ALGS, so the token is refused with governance_token_invalid before any fetch. test_governance_alg_outside_the_profile_is_refused_before_key_discovery failed before this change.
…token_invalid verify_wba_governance_issuer tested the header's alg for membership in a set. A JSON list or object there is unhashable, so the verifier raised TypeError instead of refusing the token. It now refuses an alg that isn't a string as governance_token_invalid, the code the other malformed-header cases get. test_a_non_string_governance_alg_is_an_invalid_token failed before this change. The 3.2 governance verifier gets the same fix on main.
…litter Commit 4e07819 moved the AdCP 3.2 governance verifier onto jws.parse_compact_jws and strict segment parsing, but verify_wba_governance_issuer kept its own sequence: joserfc's extract_compact, a base64url decode of the header segment, and two parse_strict_json calls with a separate object check. It now calls parse_compact_jws and governance._decode_json_segment, as the 3.2 verifier does. Every outcome and error code is unchanged. A token whose header or claims is valid JSON but not an object now gets the reason "governance token is not a compact JWS" instead of its own reason; both are governance_token_invalid. tests/conformance/signing/test_wba_governance.py gates it.
…directory Commit d4ca112 moved the vectors of verify_wba_governance_issuer out of the wba-profile vectors into tests/conformance/vectors/wba-governance/. The migration guide still said the governance/ vectors grade the check.
_parse_directory_keys refuses an entry that carries d, p, q, dp, dq, qi, k, or oth before it skips the keys the profile can't use. Only an oct key with k graded that order. The new test publishes a P-256, an RSA, and an Ed25519 key with its private members beside a usable Ed25519 key that signs, and expects "private key material" for each. If the scan moved after the skip, each directory would verify on the usable key, and all three cases fail, as moving the check showed. _directory_signed_by takes published_private, the signers it lists with their private members.
…at f311e1b5 adcontextprotocol/adcp#8298 moved to ef966de1 and #7894 to f311e1b5. ADCP_REF names the new harness commit. The wba-profile vectors are vendored from f311e1b5: scenarios.json and the upstream README change, and the request and directory vectors are byte-identical. The provenance record, the vectors README, the provenance test, and the migration guide name the new commit. The new scenarios include D14 and D14b: a directory in which an extra P-256 key, or the signing Ed25519 key, carries its private d is refused. The profile at f311e1b5 also records a nonce only after the request verifies; verify_wba_request claims the nonce after the signature verifies. The SDK adapter passes all 63 checks at ef966de1.
fetch_key_directory sent "Accept: application/http-message-signatures- directory+json, application/jwk-set+json;q=0.9, application/json;q=0.8", while _verify_directory accepts only the directory media type (WBA section 5.5.1). A server that negotiates content could answer with application/json, which the verifier then refuses. The request now offers DIRECTORY_MEDIA_TYPE alone. test_directory_fetch_asks_for_the_directory_media_type_only failed before this change.
private_key_from_jwk imported the JWK with joserfc, which requires x. A d-only JWK, which WebhookSender.from_jwk took before adcontextprotocol#1537, raised ValueError, and a JWK that paired d with another key's x loaded without complaint: the sender then signed with d while its JWK named x's key, and only the receiver noticed. The function now builds the key from d alone (strictly decoded, 32 bytes) and treats the public members as optional. When x (or y) is present, it must be the public key of d, or ValueError is raised at load time. test_a_private_jwk_needs_only_d and the Ed25519 case of test_a_private_jwk_whose_public_members_name_another_key_is_refused failed before this change; a mismatched P-256 pair was already refused.
… only
OriginRevocationChecker built its AsyncCachingRevocationChecker without
an algorithm list, so averify_jws_document fell back to ALLOWED_JWS_ALGS
(EdDSA and ES256). Directory keys are Ed25519 only and joserfc refuses
ES256 with an OKP key, so no ES256 list verified, but the verifier still
resolved the list's key from the origin's directory before refusing it.
The profile's revocation-list JWS is EdDSA only, as the #7894 answer for
Python stated.
AsyncCachingRevocationChecker and its from_issuer_origin take
allowed_algs, defaulting to ALLOWED_JWS_ALGS, and pass it to
averify_jws_document. OriginRevocationChecker passes {"EdDSA"}, so a list
with any other alg is refused before key resolution.
test_a_revocation_list_is_eddsa_only failed for ES256 before this change.
…er case The #8298 D14c case publishes a P-256 key's private scalar as "D". The private-member scan lower-cases each member name, so the SDK already refuses it, but no in-repo test spelled a member in upper case. test_a_private_member_refuses_the_directory_whatever_its_case covers D, P, and Q. It fails when the scan compares names case-sensitively.
…efusal
The adapter turned every exception into {"ok": false}, and the harness
grades a directory scenario with no expected status as passed when ok is
false. A TypeError in the SDK therefore graded as a correct refusal on
D08 to D14c.
The adapter now reports only the SDK's refusals: WbaVerificationError,
and the ValueError it raises when it refuses to sign
(WbaVerificationError subclasses ValueError). Any other exception
propagates and the adapter exits with a traceback.
test_the_interop_adapter_reports_only_sdk_refusals failed for TypeError
and KeyError before this change.
adcontextprotocol#1537 changes four exported adcp.signing functions that also exist on main 9.x. docs/request-signing-migration.md section 7 lists each change and what an adopter checks before upgrading. BREAKING CHANGE: adcp.signing.b64url_decode accepts only RFC 7515 base64url (no padding, no standard-alphabet characters, no non-zero trailing bits) and raises ValueError for anything else. public_key_from_jwk reads a JWK through joserfc, so its ValueError text changes. private_key_from_jwk raises ValueError when a JWK's x (or y) isn't the public key of its d. adcp.signing.publisher_pins.jwk_thumbprint returns the RFC 7638 thumbprint as a base64url str instead of the digest bytes.
…only The migration guide now says that Web Bot Auth signs with ed25519 following the current #7894 page (5c3876073f). The working group has not ratified that page, so a reversal there restores the removed algorithm here too.
…K's resolvers http-message-signatures 2.1.0 resolves the RFC 9421 ;key and ;req component parameters (pyauth/http-message-signatures#30) and adds HTTPMessageSigner.prepare and apply for a signature produced elsewhere (adcontextprotocol#31). It also replaced the resolver's url attribute with url_parts, so the SDK's resolver subclasses, which read url, failed every signature base under 2.1. The SDK now uses the library for what the subclasses did: - Every profile resolves components with HTTPSignatureComponentResolver. ComponentResolver (the ;key and ;req branches, a derived-component allow-list, and its own trimming and control-character checks), ComponentError, AdcpComponentResolver, and Web Bot Auth's _WbaComponentResolver are removed. - The AdCP RFC 9421 profile hands the library a request whose URL is the canonical target URI (canonical.signing_message), so the library derives the AdCP @target-uri and @authority from it. Web Bot Auth hands it the target URI as received. - The async signer uses prepare and apply instead of the protected base hook. build_signature_base still builds a base through the protected _build_signature_base: the library has no public call for a base over parameters the caller orders. The library is the reference for every observable change, and each one follows it: - A covered derived component beyond @method, @target-uri, and @authority, such as @path, resolves and verifies instead of being refused. Neither profile refuses extra components. test_an_extra_covered_derived_component_verifies replaces test_unsupported_derived_component_wraps_as_header_malformed. - A Web Bot Auth @target-uri for a URL with no path ends in "/", and one with a fragment or userinfo drops them. @authority strips a zero-padded default port. - A covered header's obsolete line folding is unfolded (RFC 9421 section 2.1) instead of refused. - A lowercase method, a malformed percent escape, and a raw "{", "}", or "|" in the URL, which RFC 3986 doesn't allow, are refused. The signers' ValueError says to percent-encode the URL. - A Web Bot Auth target URI that isn't a valid URI is 403, the profile's status for a failed verification, instead of 400. Tests that asserted on the removed internals are rewritten against the library: test_rfc9421_library_contract.py drops four tests and adds test_library_resolves_key_and_req and test_verify_tells_a_base_failure_from_a_bad_signature, and the WBA test helpers sign with the library resolver. BREAKING CHANGE: adcp.signing requires http-message-signatures 2.1.0 or later. sign_request, async_sign_request, and sign_wba_request raise ValueError for a lowercase method or a URL that isn't valid RFC 3986, such as one with a raw "{", "}", or "|" in its query, and the verifiers refuse such a request. A covered derived component beyond the required ones verifies.
…at e5929470 ADCP_REF moves to 306cdfa6, the #8298 head, where the harness grades an adapter crash as a failure, never as a refusal. The wba-profile vectors move to e5929470, the #7894 head: scenarios.json adds D14c (a private member spelled in upper case) and equals the harness's copy at 306cdfa6. The request and directory vectors, keys.json, and the upstream README are byte-identical to f311e1b5. With http-message-signatures 2.1.0 the adapter passes 64 of 64 checks, with no adapter crash.
…g-storage Ports the driver change from adcontextprotocol#1521 (bde770b) on main. The JS SDK moved createFilesystemReportingRowObjectProviderV1 out of the ledger package, so the row-storage interop lane fails on any branch cut before adcontextprotocol#1521. The same change is already on main and next, so the sync merges cleanly.
…ntity-3-3-alignment
…o claude/issue-1511-rfc9421-library Brings in next through adcontextprotocol#1514's branch. The one conflict was the import block of src/adcp/signing/verifier.py: this branch imports `_rfc9421`, and next (adcontextprotocol#1479, AdCP 3.2.4) imports `target_uri_precheck`. The resolution keeps both, and keeps the precheck's call site, so a malformed wire authority is still refused as request_target_uri_malformed at step 6 before key lookup and replay claims. A new regression test in test_target_uri_malformed_boundary.py fails if the precheck is dropped. http-message-signatures 2.1.0, released 2026-10-19, rewrote the component resolver this layer subclasses (no `url` attribute). Cap the dependency below 2.1 until the SDK moves to 2.1, which adcontextprotocol#1537 carries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…511-rfc9421-library next's tree at 2b08ced equals adcontextprotocol#1514's head 6637374, which this branch already merged, so the merge records the squash commit without changing the tree. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…x/wba-standard-interop
…ta alongside the 2.1 pin
KonstantinMirin
force-pushed
the
conductor/reporting-webhook-activity-1168b
branch
from
October 19, 2026 18:12
87f3956 to
0701c8b
Compare
Owner
Author
|
adcontextprotocol#1537 merged. |
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.
This PR exists only to run CI for adcontextprotocol#1537 in this fork. Its base is adcontextprotocol#1523's head, under a branch name that ci.yml triggers on, because upstream CI doesn't run on adcontextprotocol#1537's base. Don't merge it.