Skip to content

Add relayhistory assertion token route - #58

Merged
kjgbot merged 1 commit into
mainfrom
feat/relayhistory-assertion
Jun 22, 2026
Merged

Add relayhistory assertion token route#58
kjgbot merged 1 commit into
mainfrom
feat/relayhistory-assertion

Conversation

@kjgbot

@kjgbot kjgbot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add POST /v1/tokens/relayhistory-assertion as a fixed-purpose relayhistory assertion mint path
  • require the dedicated relayauth:assertion:create:relayhistory API-key path, rejecting bearer use even with the scope
  • mint access-only aud=relayhistory assertions with <=60s TTL, no refresh token, no persistent identity, jti/audit metadata, signed org/wks/sponsorId, and strict relayhistory scopes only
  • add route coverage for the happy path, bearer rejection, invalid scope strict-reject, and TTL ceiling

Security posture

  • This is the bounded master-capability endpoint for Cloud's dedicated assertion key, not the general WebRelayauthApiKey path.
  • The route is intentionally narrow: aud is fixed to relayhistory, allowed scopes are strictly rth:read/rth:sync, invalid scopes are rejected rather than dropped, and the response never includes a refresh token.
  • Cloud still owns the pre-mint tenant/entitlement check and post-mint fail-closed decode before exchanging with relayhistory.

Verification

  • npm --workspace @relayauth/server test -- --test-name-pattern="POST /v1/tokens/relayhistory-assertion" (369/369 pass in this server test run)
  • npx prettier --check packages/server/src/routes/tokens.ts packages/server/src/tests/tokens-route.test.ts
  • git diff --check

Typecheck note

  • npm --workspace @relayauth/server run typecheck is blocked on the existing nested SDK dependency copy at packages/sdk/node_modules/@relayauth/types, whose dist Action union is missing "trigger" while the local @relayauth/types source/dist include it. The failure is in src/routes/discovery.ts and is unrelated to this route.

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ad3a2f0-4c00-4a78-b091-9a7dec482334

📥 Commits

Reviewing files that changed from the base of the PR and between 6a9cb0d and 1814982.

📒 Files selected for processing (2)
  • packages/server/src/__tests__/tokens-route.test.ts
  • packages/server/src/routes/tokens.ts

📝 Walkthrough

Walkthrough

Adds a new POST /v1/tokens/relayhistory-assertion endpoint to the tokens router. The endpoint mints short-lived, access-only Bearer tokens scoped to relayhistory operations (rth:read, rth:sync), enforces a dedicated API-key authentication path, validates all request fields, persists the token, and writes an audit record. Tests cover the happy path, JWT claims, persistence, audit correctness, and three enforcement error cases.

Changes

Relayhistory Assertion Token Endpoint

Layer / File(s) Summary
Request/response types, constants, and validation helpers
packages/server/src/routes/tokens.ts
Adds RelayhistoryAssertionRequest and RelayhistoryAssertionResponse types, the minting scope constant, fixed audience, max TTL (60s), allowed scope set, and normalizeRelayhistoryAssertionRequest / normalizeRelayhistoryAssertionScopes / normalizeRelayhistoryAssertionExpiresIn validators.
Route handler, token issuance, and audit
packages/server/src/routes/tokens.ts
Adds tokens.post("/relayhistory-assertion", ...) that authenticates, enforces apiKeyVia === "api_key", normalizes the body, and calls issueRelayhistoryAssertion which builds RS256 claims with tokenClass: relayhistory_assertion, persists via persistIssuedToken, and records the event via writeAssertionAudit.
Test suite
packages/server/src/__tests__/tokens-route.test.ts
Adds RelayhistoryAssertionResponse test type and a full test suite covering 201 issuance (JWT claims, access-only token storage, audit log row and metadata), 403 for bearer-auth path, 400 for invalid scopes, and 400 for expiresIn above the 60s ceiling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A bunny hops through the relay gate,
minting tokens before they're late —
sixty seconds, no more, no less,
rth:read and rth:sync in address.
The audit log records each hop,
short-lived assertions never stop! 🐇✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/relayhistory-assertion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@kjgbot
kjgbot merged commit f83a209 into main Jun 22, 2026
3 of 4 checks passed
@kjgbot
kjgbot deleted the feat/relayhistory-assertion branch June 22, 2026 17:28
@agent-relay-code

Copy link
Copy Markdown
Contributor

Review: PR #58 — feat(tokens): mint relayhistory assertions

Summary

The PR adds a POST /v1/tokens/relayhistory-assertion endpoint that mints short-lived (≤60s), access-only JWT assertions scoped to relayhistory, gated behind a dedicated api-key (relayauth:assertion:create:relayhistory). It touches two files: packages/server/src/routes/tokens.ts (route + helpers) and the corresponding test file. I traced the change across types, auth middleware, audit storage, and callers.

What I verified

  • Security gate is sound. The route requires c.get("apiKeyVia") === "api_key", returning 403 assertion_key_required for bearer callers even when the bearer carries the assertion scope. apiKeyVia is only set to "api_key" when auth.via === "api_key" (middleware/api-key-auth.ts:38-41), and /v1/tokens/* is mounted with apiKeyAuth() (server.ts:128-129). The gate is fail-closed and not weakened.
  • No fail-open / default-flip regressions. Scope and TTL validation strictly reject (invalid_scope, invalid_expires_in) rather than silently coercing; expiresIn defaults to the 60s ceiling, and out-of-range/non-finite values are rejected.
  • Type safety. Claims object satisfies RelayAuthTokenClaims; meta is Record<string,string> and every value passed is a string (grantedScopes is JSON.stringify-ed). Audit write entry matches AuditLogWriteEntry.
  • No safety-critical/lifecycle code touched. Change is confined to token issuance; no reaper/dispatch/in-flight code involved.
  • No Cloudflare deps introduced (per repo rule).

CI verification (ran end-to-end locally)

  • turbo typecheck: 11/11 packages pass.
  • turbo build --filter=@relayauth/server: passes. (A full turbo build hit OOM exit 137 on the unrelated ai/cli packages building in parallel — an environment resource limit, not a code defect; the server package and its deps build cleanly.)
  • Server unit tests: 369 pass, 0 fail, including all 4 new relayhistory-assertion subtests.
  • Server e2e tests: 40 pass, 0 fail (93 env-gated skips).

Auto-applied fixes

None. No lint/format/typo/import-order issues were present, and there were no semantic defects to fix.

Addressed comments

  • No bot or human review comments were present in .workforce/context.json (it contained only PR metadata, no review threads). Nothing to validate or address.

Advisory Notes

  • The endpoint hardcodes the assertion subject as agent_relayhistory_assertion and the audience as relayhistory. This is consistent with the feature's intent and tests; no change needed, noted only for downstream consumers expecting these fixed values.

The PR is well-scoped, fully tested, type-safe, and passes the full build/typecheck/test suite. There are no failing checks I could resolve mechanically and no required human-judgment changes outstanding from my analysis. I did not run the actual GitHub CI, so I cannot confirm remote check status, merge-conflict/mergeability state, or that no other reviews are pending — those are post-harness items reported by cloud. Because I cannot confirm every required remote check has completed and passed, I am not printing READY.

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