Skip to content

feat(signing): accept any well-formed adcp_use purpose in keygen - #1710

Merged
bokelley merged 1 commit into
mainfrom
claude/issue-1677-keygen-open-purpose-9x
Oct 29, 2026
Merged

bokelley merged 1 commit into
mainfrom
claude/issue-1677-keygen-open-purpose-9x

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Backport of #1708 (into next) to SDK 9.x. #1677 asked to check whether 9.x needs the same fix. It does: main has the same closed Literal["request-signing", "webhook-signing"] on generate_signing_keypair, pem_to_adcp_jwk and adcp-keygen --purpose. The change is additive (Literal widened to str), so it isn't breaking.

Change

  • purpose is validated syntactically (a lowercase hyphenated token of at most 64 characters) instead of by membership. That makes response-signing, governance-signing and any not-yet-named purpose expressible. Malformed input still raises ValueError, and the CLI exits 2 before writing.
  • adcp.signing exports ADCP_USE_REQUEST, ADCP_USE_WEBHOOK, ADCP_USE_RESPONSE and ADCP_USE_GOVERNANCE.
  • The docstrings explain the fail-closed property that makes an open vocabulary safe.

Differences from #1708: main's keygen still uses the hand-rolled cryptography JWK export (no joserfc), and governance.py keeps its string literal (cosmetic only, left out to keep the backport minimal).

Tests

These run against main: test_keygen_programmatic.py, test_signing_provider.py, test_keygen.py (75 passed). They include the fail-closed property: a key under any non-request purpose is refused by the request and webhook verifiers.

Closes #1677

🤖 Generated with Claude Code

generate_signing_keypair, pem_to_adcp_jwk and adcp-keygen --purpose took a
closed Literal["request-signing", "webhook-signing"] and refused every other
value, so the SDK could not mint or publish a key for response-signing (the
designated-task response JWS profile, core/response-payload-jws-envelope.json),
governance-signing (which governance.py already verifies), or a surface the
vocabulary does not name yet. The helpers they delegate to already took a str.

purpose is now validated by syntax instead of membership: a lowercase
hyphenated token of at most 64 characters. Malformed values (empty, upper
case, underscores, whitespace, leading/trailing/double hyphens, non-str)
still raise ValueError, and the CLI exits 2 before writing a file. An open
vocabulary is safe because purpose matching is fail-closed: a verifier
accepts a key only when adcp_use equals the purpose of the surface it
verifies, so a key under any other purpose verifies nothing there. A test
pins that against the request and webhook verifiers.

adcp.signing exports ADCP_USE_REQUEST, ADCP_USE_WEBHOOK (marked deprecated
upstream), and the new ADCP_USE_RESPONSE and ADCP_USE_GOVERNANCE, so callers
need not retype literals. The docstrings no
longer say request- and webhook-signing keys must be distinct for every
verifier: 3.3 webhook verifiers accept request-signing keys, and new webhook
signers publish request-signing.

Backport of #1708 to 9.x. Closes #1677 on main.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve.

Additive change: feat(signing) widens keygen purpose from a closed Literal to a syntactically-validated open token (lowercase hyphenated, ≤64 chars) and adds four ADCP_USE_* constant exports. Correct non-breaking semver prefix for a purely additive public-surface change.

Checks confirmed:

  • Open-vocabulary safety holds: verifier.py:849-857 fails closed (adcp_use must equal the verified surface's purpose), so a key under any other purpose authorizes nothing.
  • Validation ordering is safe (non-str short-circuits before regex/length); CLI exits 2 before writing.
  • Tests cover open acceptance, malformed rejection, CLI, and the fail-closed property against request and webhook verifiers.

Decision table: no critical/high/medium findings. gated_paths is false; high_risk is true only via (modified) reasons on src/adcp/signing/**, but with no medium-or-higher finding the modifications are presumed safe (rows 4/5 do not fire). No author team gate, no prior escalation. Falls through to row 9 — approve.

@bokelley
bokelley merged commit 787cfb2 into main Oct 29, 2026
47 checks passed
@bokelley
bokelley deleted the claude/issue-1677-keygen-open-purpose-9x branch October 29, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

keygen: purpose is a closed Literal, narrower than the spec vocabulary (cannot express response-signing) and narrower than the SDK's own internals

1 participant