Skip to content

Consolidate signed-token key resolution into one rotation seam#141

Merged
isuttell merged 2 commits into
mainfrom
chore/signing-key-resolution-seam
May 29, 2026
Merged

Consolidate signed-token key resolution into one rotation seam#141
isuttell merged 2 commits into
mainfrom
chore/signing-key-resolution-seam

Conversation

@isuttell

@isuttell isuttell commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Each Worker re-implemented the same "env override → key ring → bare secret" cascade for every signed-token kind. The ring builders and verify*WithKeyRing helpers already lived in packages/rotation; the missing piece was the resolution policy as a seam. This adds that seam so key resolution lives in one tested place, migrates all four call surfaces onto it, and fixes a latent rotation bug along the way.

Changes

  • New packages/rotation/src/signers.tsresolveContentTokenSigner, resolveAgentViewTokenSigner, resolveUploadTokenSigner, resolveAccessLinkSigner. Each returns a typed sign/verify pair matched to the kind's natural payload shape; verify walks the rotation overlap window.
  • Migrated api (content/bundle/agent-view URL minting, access-link mint + resolve), api/live-updates, content (verify), upload (mint + verify) onto the seam. No inlined cascade remains in any Worker.
  • Bug fix: api previously minted content URLs with agentViewSigningSecret, which honored AGENT_VIEW_SIGNING_SECRET, while the content Worker verifies content tokens with the content ring only — setting that override would have made content URLs unverifiable. Content URLs now sign with content material; agent-view tokens keep the override symmetrically. Inert today (override unset), locked in by a new regression test.

Risk: MEDIUM

  • Areas touched: signing-secret resolution for all four signed-token kinds (content-gateway, agent-view, upload PUT URL, access-link).
  • Security: tightens behavior — content URLs no longer pick up an agent-view override the content Worker can't verify. No new secret surfaces; .signingSecret accessors stay inside trusted Workers.
  • Performance: none (same ring construction, same verify walk).
  • Breaking: none. Behavior-preserving for rotation; the one semantic change is the content/agent-view split, which is a fix.

Test plan

  • packages/rotation unit tests incl. rotation-overlap + content-vs-agent-view regression (43 pass)
  • api (149), content (29), upload (32) suites pass
  • rg confirms no inlined ring/verify cascade remains in any Worker
  • local workflow-code-review: READY FOR PR, CodeRabbit SKIP

Issue: AP-90

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Centralized token signing and verification across content, upload, and access-link services using new signer resolver helpers.
  • Tests

    • Added comprehensive test suite for token signer resolution and key rotation scenarios.
  • Documentation

    • Updated quality and workflow documentation; removed references to agent skills verification step.
  • Chores

    • Removed pnpm agent-skills:check script and associated validation logic.

Review Change Stack

Consolidate the inlined "env override -> key ring -> bare secret" cascade
that each Worker re-implemented per signed-token kind into one
`resolve*Signer(env)` seam in packages/rotation. The seam returns a typed
sign/verify pair per kind (content-gateway, agent-view, upload PUT URL,
access-link), hides the rotation overlap-window walking on verify, and is
the single place key resolution lives.

Migrates apps/api (content/bundle/agent-view URL minting, access-link mint
+ resolve), apps/api/live-updates, apps/content (verify), and apps/upload
(mint + verify) onto the seam. Removes the duplicated cascade from all four
Workers and the manual access-link env reconstruction in the resolve route.

Fixes a latent rotation bug: api previously minted content URLs with
`agentViewSigningSecret`, which honored AGENT_VIEW_SIGNING_SECRET, while the
content Worker verifies content tokens with the content ring only. Setting
that override would have made content URLs unverifiable. Content URLs now
sign with content material (`contentSigningSecret`); agent-view tokens keep
the override symmetrically on mint and verify. Inert today (override unset),
covered by a new regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b58542e-4b90-40b2-83b3-d671732ef8f9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/signing-key-resolution-seam

Comment @coderabbitai help to get the list of available commands and usage tips.

@isuttell isuttell force-pushed the chore/signing-key-resolution-seam branch from 2d5e752 to 1724e5b Compare May 29, 2026 20:16
@linear-code

linear-code Bot commented May 29, 2026

Copy link
Copy Markdown
AP-90 Consolidate signed-token key resolution into one rotation seam

Outcome

Replace the per-Worker inlined "env override → key ring → bare secret" cascade with a single signing-key resolution seam in packages/rotation, so key resolution for every signed-token kind lives in one tested place.

Context

Surfaced by an architecture review (deepening opportunity #1). Each Worker re-implemented the same cascade per signed-token kind:

  • mint: ring?.signingSecret() ?? bareSecret
  • verify: ring ? verify*WithKeyRing(token, ring) : verify*(token, bareSecret)

This was inlined in apps/api, apps/content, apps/upload, and apps/api/live-updates.ts. The ring builders and verify*WithKeyRing helpers already lived in packages/rotation; what was missing was the resolution policy as a seam.

Scope

  • New packages/rotation/src/signers.ts: resolveContentTokenSigner, resolveAgentViewTokenSigner, resolveUploadTokenSigner, resolveAccessLinkSigner. Each returns a typed sign/verify pair matched to the kind's natural payload shape; verify walks the rotation overlap window.
  • Migrate api (content/bundle/agent-view URL minting, access-link mint + resolve), api/live-updates, content (verify), upload (mint + verify) onto the seam.
  • Remove the duplicated cascade from all four Workers and the manual access-link env reconstruction in the resolve route.

Bug fixed

api previously minted content URLs with agentViewSigningSecret, which honored AGENT_VIEW_SIGNING_SECRET, while the content Worker verifies content tokens with the content ring only. Setting that override would have made content URLs unverifiable. Content URLs now sign with content material; agent-view tokens keep the override symmetrically on mint and verify. Inert today (override unset everywhere); covered by a new regression test.

Acceptance criteria

  • One seam in packages/rotation owns key resolution for all four token kinds
  • No inlined ring/verify cascade remains in any Worker (rg confirms)
  • Behavior-preserving for rotation (overlap-window verify); content/agent-view split is the only semantic change and is a fix
  • Regression test locks in the content-vs-agent-view secret split
  • pnpm verify green

Out of scope

Related

Sibling change in the same PR: removed the obsolete local agent-skills:check guard (workflow skills now validated by central skills CI).

@isuttell isuttell force-pushed the chore/signing-key-resolution-seam branch from ec3eeb7 to 1724e5b Compare May 29, 2026 20:27
The local agent-skills:check guard fails the Validate job on pre-existing
.agents/.claude skill-layout drift unrelated to this change. Skill layout is
validated by the central skills repo CI, so drop the guard, its script and lock
manifest, the verify wiring, and the doc references.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@isuttell isuttell merged commit d317f8a into main May 29, 2026
4 checks passed
@isuttell isuttell deleted the chore/signing-key-resolution-seam branch May 29, 2026 20:37
@github-actions

Copy link
Copy Markdown

agent-paste PR preview resources were cleaned up. The shared Preview GitHub Environment is retained for future preview deploys.

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.

1 participant