Skip to content

feat(api): rate-limit legacy admin and public Agent View routes (AP-14)#85

Merged
isuttell merged 6 commits into
mainfrom
cursor/rate-limit-admin-public-bearer-f4ae
May 26, 2026
Merged

feat(api): rate-limit legacy admin and public Agent View routes (AP-14)#85
isuttell merged 6 commits into
mainfrom
cursor/rate-limit-admin-public-bearer-f4ae

Conversation

@isuttell

@isuttell isuttell commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes AP-14 by adding explicit rate limits to legacy admin-token routes and the public Agent View bearer read surface, using the existing route-contract registrar and worker-runtime rate-limit primitives (ADR 0072 / ADR 0064).

Legacy admin (/admin/*)

  • All contract-mounted admin routes use rateLimit: "actor".
  • /admin/whoami applies the same actor limit after auth.
  • Keys: platform:admin:{admin_id} on ACTOR_RATE_LIMIT.
  • Over limit: 429 with rate_limited_actor and Retry-After: 60.
  • Admin-token principals without a usable admin id fail closed with not_authenticated (no workspace bucket).

Public Agent View (GET /v1/public/agent-view/{token})

  • Contract uses rateLimit: "artifact" with rate_limited_artifact in errors.
  • api mounts ARTIFACT_RATE_LIMIT using the same namespace IDs as content, so per-artifact budgets are shared across read surfaces.
  • Over limit: 429 with rate_limited_artifact and Retry-After: 60.

Documented exception

  • GET /v1/usage-policy remains rateLimit: "none" (static policy; no separate abuse surface).

Files changed

  • packages/contracts/src/routes.ts — rate-limit classes and error codes on admin/public routes
  • packages/worker-runtime/src/rate-limit.ts — admin-token and signed-agent-view-token artifact keys
  • apps/api/src/index.ts, apps/api/wrangler.jsoncARTIFACT_RATE_LIMIT binding on API worker
  • apps/api/src/index.test.ts, packages/worker-runtime/src/rate-limit.test.ts, packages/contracts/src/mvp-contracts.test.ts — allowed + limited request coverage
  • Status ledgers: docs/ops/project-status.md, docs/ops/status/hosted-ops.md, docs/ops/status/phase-backlog.md

Verification

  • pnpm --filter @agent-paste/worker-runtime test (22 passed)
  • pnpm --filter @agent-paste/api test (107 passed)
  • pnpm verify (72 tasks passed)

Hosted verification

Not run (no production credentials in this environment).

Linear Issue: AP-14

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Admin API routes (including admin identity) and the public Agent View are now rate-limited; excess requests return 429, a 60s Retry-After, and distinct error codes.
  • API Contract

    • OpenAPI updated: public Agent View 429 response documents an artifact-specific rate-limit error payload.
  • Tests

    • Added tests asserting 429 responses, Retry-After headers, and correct actor/artifact error codes.
  • Documentation

    • Ops and agent docs updated to reflect the new rate-limit behavior and workflows.

Review Change Stack

@linear-code

linear-code Bot commented May 26, 2026

Copy link
Copy Markdown
AP-14 Security: Rate-limit legacy admin-token and public bearer read routes

Context

Legacy admin-token routes and public bearer read surfaces currently lack explicit rate limits in places, especially /admin/* and public Agent View.

Source docs

  • docs/ops/status/phase-backlog.md
  • docs/ops/status/hosted-ops.md
  • docs/adr/0039-authenticated-rate-limits-under-usage-policy.md
  • docs/adr/0064-native-ratelimit-bindings-for-authenticated-counters.md

Scope

Inventory affected routes, add explicit route-contract rate-limit classes/bindings where needed, and cover admin-token plus public bearer read surfaces.

Out of scope

Do not redesign rate-limit policy or billing-tier rate limits.

Dependencies

None.

Implementation notes

Use existing ADR 0072 route registrar and worker-runtime rate-limit primitives. Keep error codes stable and include Retry-After on limit responses.

Acceptance

Routes identified in the status docs have explicit limits or a documented reason for no limit. Tests cover allowed and limited requests.

Verification

Run focused worker-runtime/API/content tests and pnpm verify.

Remote Cursor handoff

Start by reading AGENTS.md, then docs/agents/remote-cursor-agent.md, then this issue. Fetch current repo status from docs/ops/project-status.md and relevant ADR/spec docs named above. Keep the change scoped to this issue. Run the ticket-specific verification plus pnpm verify unless the issue explicitly says a narrower check is acceptable. Do not run hosted production deploys or smokes unless the issue explicitly grants credentials and approval.

Review in Linear

@isuttell isuttell temporarily deployed to pr-preview-85 May 26, 2026 16:42 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR enforces actor-scoped rate limiting for legacy admin_token principals and artifact-scoped limiting for public Agent View tokens. It adds an ARTIFACT_RATE_LIMIT env binding and Wrangler entries, exports and uses applyRateLimit in the API (including adminWhoami and registrar wiring), updates route contracts and OpenAPI (ArtifactRateLimitErrorEnvelope / artifact 429), adds unit/integration tests, and updates ops/docs.

Sequence Diagram(s)

sequenceDiagram
  participant AdminClient as Admin Client (admin_token)
  participant AdminWhoami as adminWhoami endpoint
  participant ApplyRate as applyRateLimit
  participant ActorBucket as Actor Bucket (platform:admin:id)
  participant Response as Response Handler

  AdminClient->>AdminWhoami: GET /admin/whoami
  AdminWhoami->>ApplyRate: check actor limit
  ApplyRate->>ActorBucket: increment/check
  alt Rate limit exceeded
    ActorBucket-->>ApplyRate: blocked
    ApplyRate-->>Response: {ok:false, code:"rate_limited_actor"}
    Response-->>AdminClient: 429 + Retry-After: 60
  else Within limit
    ActorBucket-->>ApplyRate: allowed
    ApplyRate-->>AdminWhoami: {ok:true}
    AdminWhoami-->>Response: 200 OK with actor
    Response-->>AdminClient: 200 OK
  end
Loading
sequenceDiagram
  participant PublicClient as Public Client
  participant AgentViewAPI as GET /v1/public/agent-view/{token}
  participant ApplyRate as applyRateLimit
  participant ArtifactBucket as Artifact Bucket (artifact_id)
  participant DBLoad as Load Agent View
  participant Response as Response Handler

  PublicClient->>AgentViewAPI: GET /v1/public/agent-view/{token}
  AgentViewAPI->>ApplyRate: check artifact limit
  ApplyRate->>ArtifactBucket: increment/check
  alt Rate limit exceeded
    ArtifactBucket-->>ApplyRate: blocked
    ApplyRate-->>Response: {ok:false, code:"rate_limited_artifact"}
    Response-->>PublicClient: 429 + Retry-After: 60
  else Within limit
    ArtifactBucket-->>ApplyRate: allowed
    ApplyRate->>DBLoad: load public Agent View
    DBLoad-->>Response: 200 OK + AgentView
    Response-->>PublicClient: 200 OK
  end
Loading

Possibly related issues

  • AP-14: Security: Rate-limit legacy admin-token and public bearer read routes — This PR implements AP-14 by adding actor/artifact bindings, wiring, tests, and Retry-After responses.
  • AP-7: Remote cursor and agent handoff docs — PR updates to remote-cursor and issue-tracker docs align with AP-7's conventions for agent handoffs.

Possibly related PRs

Poem

🐰 I nibble tokens, one by one, with care,

Sixty hops per minute — pause if you dare.
Admin gates hold, public views take breath,
Retry-After hums: wait, then step ahead.
A rabbit hops, pacing traffic with care.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The pull request implements all primary coding objectives from AP-14: inventoried affected routes, added explicit rate-limit classes/bindings via route contracts and worker-runtime, included Retry-After headers, and covered admin-token and public bearer surfaces with comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are scoped to AP-14 requirements: route contract updates, worker-runtime rate-limit logic, Cloudflare bindings, API implementation, OpenAPI contracts, and tests. Documentation updates and agent workflow changes are supplementary to the core rate-limiting work and remain focused.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding rate limits to legacy admin routes and the public Agent View, matching the changeset's primary objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 cursor/rate-limit-admin-public-bearer-f4ae

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

@cursor cursor Bot marked this pull request as ready for review May 26, 2026 16:43
@github-actions

Copy link
Copy Markdown

@cursor cursor Bot temporarily deployed to pr-preview-85 May 26, 2026 16:46 Inactive

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/ops/project-status.md`:
- Around line 22-23: Update the stale bullet that claims "rate limits still
needed for legacy admin-token/public bearer reads" (the later bullet in the same
project-status section) to reflect that the debt has been addressed—either
remove the sentence or change it to indicate the rate-limit requirement is
resolved; ensure the remaining text matches the new status change that removed
the "rate limits still needed" note earlier in the document so the file is
internally consistent (search for the bullet containing "legacy
admin-token/public bearer" or "rate limits still needed" to locate the line to
edit).

In `@docs/ops/status/hosted-ops.md`:
- Around line 68-70: The Open Ops Items list still marks adding rate limiting as
pending while the new bullet claims legacy admin-token routes and public Agent
View now use ARTIFACT_RATE_LIMIT / ACTOR_RATE_LIMIT bindings on api; update the
Open Ops Items entry (the pending item at the top-level Ops list) to reflect
completion by either removing the pending item or rewording it to a follow-up
task (e.g., "verify and audit ARTIFACT_RATE_LIMIT / ACTOR_RATE_LIMIT deployment
for legacy admin-token routes and public Agent View") so the ledger no longer
contradicts the new bullet referencing ARTIFACT_RATE_LIMIT and ACTOR_RATE_LIMIT.

In `@packages/worker-runtime/src/rate-limit.test.ts`:
- Around line 45-55: The test for "rate-limits legacy admin-token principals by
actor only" currently doesn't assert that the workspace contract isn't invoked;
add a mock for the workspace rate-limit function (e.g., workspace =
vi.fn().mockResolvedValue(...)) and pass it into applyRateLimit's contract map
alongside actorContract, then add an assertion that workspace was not called
(expect(workspace).not.toHaveBeenCalled()) to explicitly verify "actor only"
behavior for applyRateLimit and the actorContract.

In `@packages/worker-runtime/src/rate-limit.ts`:
- Around line 41-45: The branch handling principal.kind === "admin_token"
currently returns { ok: true } when adminIdForPrincipal(principal) yields falsy,
which incorrectly bypasses actor throttling; change this to fail closed by
returning the same not_authenticated result used for missing/invalid actors
(i.e., return the not_authenticated error shape instead of ok true) so
admin-token paths without a usable admin id are rejected; update the branch in
rate-limit.ts where adminIdForPrincipal(principal) is checked (the admin_token
handling block) to return the not_authenticated result consistent with
actor-scoped behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7b086173-766e-4916-947b-beeae331994c

📥 Commits

Reviewing files that changed from the base of the PR and between db00315 and da7b5c4.

📒 Files selected for processing (14)
  • apps/api/src/index.test.ts
  • apps/api/src/index.ts
  • apps/api/src/worker-configuration.d.ts
  • apps/api/wrangler.jsonc
  • docs/ops/project-status.md
  • docs/ops/status/hosted-ops.md
  • docs/ops/status/phase-backlog.md
  • packages/contracts/openapi/api.json
  • packages/contracts/src/mvp-contracts.test.ts
  • packages/contracts/src/openapi/api.ts
  • packages/contracts/src/routes.ts
  • packages/worker-runtime/src/index.ts
  • packages/worker-runtime/src/rate-limit.test.ts
  • packages/worker-runtime/src/rate-limit.ts

Comment thread docs/ops/project-status.md
Comment thread docs/ops/status/hosted-ops.md
Comment thread packages/worker-runtime/src/rate-limit.test.ts
Comment thread packages/worker-runtime/src/rate-limit.ts
@github-actions

Copy link
Copy Markdown

@cursor cursor Bot temporarily deployed to pr-preview-85 May 26, 2026 16:51 Inactive
@cursor cursor Bot temporarily deployed to pr-preview-85 May 26, 2026 16:53 Inactive
@cursor cursor Bot temporarily deployed to pr-preview-85 May 26, 2026 16:55 Inactive
@github-actions

Copy link
Copy Markdown

@cursor cursor Bot temporarily deployed to pr-preview-85 May 26, 2026 17:06 Inactive
@github-actions

Copy link
Copy Markdown

cursoragent and others added 6 commits May 26, 2026 17:10
Apply actor-scoped limits to all contract-mounted /admin/* routes and
/admin/whoami, and artifact-scoped limits to public Agent View reads.
Share the content worker ARTIFACT_RATE_LIMIT namespace on api so per-artifact
budgets apply across read surfaces.

Closes AP-14.

Co-authored-by: Isaac Suttell <isaac@isaacsuttell.com>
Co-authored-by: Isaac Suttell <isaac@isaacsuttell.com>
Co-authored-by: Isaac Suttell <isaac@isaacsuttell.com>
Co-authored-by: Isaac Suttell <isaac@isaacsuttell.com>
Co-authored-by: Isaac Suttell <isaac@isaacsuttell.com>
- Fail closed when admin_token principal lacks a usable admin id
- Assert workspace bucket is not used for legacy admin rate limits
- Remove stale pending rate-limit ops bullets from status ledgers

Co-authored-by: Isaac Suttell <isaac@isaacsuttell.com>
@cursor cursor Bot force-pushed the cursor/rate-limit-admin-public-bearer-f4ae branch from 1f07faa to ae7639d Compare May 26, 2026 17:11
@cursor cursor Bot temporarily deployed to pr-preview-85 May 26, 2026 17:12 Inactive
@cursor cursor Bot changed the title feat(api): rate-limit legacy admin-token and public Agent View routes (AP-14) feat(api): rate-limit legacy admin and public Agent View routes (AP-14) May 26, 2026
@github-actions

Copy link
Copy Markdown

@isuttell isuttell merged commit fc472ab into main May 26, 2026
4 checks passed
@isuttell isuttell deleted the cursor/rate-limit-admin-public-bearer-f4ae branch May 26, 2026 17:16
@github-actions

Copy link
Copy Markdown

agent-paste PR preview resources were cleaned up. The pr-preview-${context.issue.number} environment is left in place; remove it from the GitHub UI if desired.

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.

2 participants