Skip to content

refactor: relocate QuotaTracker DO behind capability entrypoints - #102

Merged
iamjr15 merged 1 commit into
mainfrom
arch/quota-relocation
Jul 31, 2026
Merged

refactor: relocate QuotaTracker DO behind capability entrypoints#102
iamjr15 merged 1 commit into
mainfrom
arch/quota-relocation

Conversation

@iamjr15

@iamjr15 iamjr15 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes A2 Phase 2 of the architecture-hardening plan: the QuotaTracker Durable Object moves from gateway-worker to agent-worker, where its consumers (run admission, Composio metering, accrual flush) actually live. Cross-worker access is no longer a full cross-script DurableObjectNamespace — it goes through capability-scoped WorkerEntrypoints with strict-Zod ctx.props validation, matching the existing AgentLifecycleEntrypoint/ResourceDeletionEntrypoint house pattern.

worker before after
agent cross-script DO binding into gateway (full 8-method surface) local QUOTA_TRACKER namespace (owner); appended new_sqlite_classes v3
gateway hosts the class + full local namespace GatewayQuotaEntrypoint service binding scoped to {history, peek, setLimit}; class export/binding removed; appended deleted_classes v4
webhooks cross-script DO binding (full 8-method surface) QuotaDeletionEntrypoint service binding scoped to {deleteAllState} only

This is a net privilege reduction: gateway and webhooks previously could invoke every RPC method (including deleteAllState and tryConsume) against any user's DO; each is now confined to its exact baseline call set. QuotaTrackerRpc in @cheatcode/types remains the single contract — both entrypoint binding types are derived projections, zero hand-written method signatures. The dependency-cruiser guard restricting @cheatcode/billing/quota-runtime imports to the agent DO shell was proven firing via an executed probe.

All call-site semantics are preserved byte-for-byte, including every 503 service_maintenance_unavailable translation (entrypoint methods deliberately don't catch, so DO exceptions propagate into the callers' existing handlers).

Deploy plan — roll-forward only

The standard deploy-cloudflare.yml sequential loop (agent → webhooks → preview-proxy → gateway) is exactly the approved rev-5.2 cutover order. Each old deployment's bindings stay valid until its own deploy; the webhooks deploy requires the agent deploy first (entrypoint must exist for deploy-time validation); deleted_classes fires last, after every binding/code reference to the gateway-hosted namespace is gone (zero script_name bindings remain repo-wide). Re-running the loop is idempotent. If a step fails: fix forward and re-dispatch — never roll back.

Quota split-brain window (agent deploy → gateway deploy). From the moment cheatcode-agent deploys until cheatcode-gateway deploys, two QuotaTracker namespaces are live simultaneously. agent-worker writes and reads its NEW, initially-empty local namespace, while the still-old gateway and — until its own deploy — the still-old webhooks read and write the OLD gateway-hosted namespace. During the window: (1) run admission uses the NEW namespace, so exhausted users are re-admitted; (2) new consumption meters into the NEW namespace; (3) gateway /usage and /activity read the OLD namespace, so displayed usage appears frozen; (4) gateway limit writes land in the OLD namespace and are discarded at cutover — harmless, both agent call sites re-issue setLimit before every gate, so limits self-heal; (5) an account deleted during the window leaves residue in the NEW namespace (self-limiting: 366-day retention alarm, or re-run the deletion job). At gateway deploy the deleted_classes migration destroys the old namespace and both surfaces converge.

Pre-cutover quota usage is permanently reset, not just windowed: deleted_classes destroys all counters and usage_event history — /activity charts start empty and /usage totals restart at 0 for every user. Sanctioned pre-launch (no users, disposable data); Postgres remains authoritative for entitlements.

Brief transient at the gateway cutover: old gateway isolates can outlive the migration by seconds and surface 503 service_maintenance_unavailable on /usage//activity/limit-sync. Already caught and retriable at every call site — expected, not a regression.

Verification

  • Gates (independent re-run, pipefail): lint, typecheck, turbo build --force, deadcode, architecture:check, skills:build — all green
  • wrangler deploy --dry-run for all four workers — all pass
  • Adversarial review (independent session): SAFE-TO-DEPLOY, zero defects across deploy-window safety, entrypoint least-privilege (exact baseline match), call-site semantics (3 traced sites), config hygiene, and cruiser-rule liveness
  • Post-merge: single deploy-cloudflare.yml dispatch, convergence on merge SHA, then topology/authorization/function verification (admission, usage, limits)

… entrypoints

Move the QuotaTracker Durable Object from gateway-worker to agent-worker,
where its consumers (run admission, Composio metering, accrual flush) live.
Cross-worker access now goes through capability-scoped WorkerEntrypoints with
strict-Zod ctx.props validation instead of full cross-script DO namespaces:

- agent-worker: local QUOTA_TRACKER binding + appended new_sqlite_classes v3;
  GatewayQuotaEntrypoint {history, peek, setLimit} and QuotaDeletionEntrypoint
  {deleteAllState} exported as named entrypoints
- gateway-worker: class export + local binding removed; service binding to
  GatewayQuotaEntrypoint; appended deleted_classes v4 (fires only after all
  bindings/code refs are gone)
- webhooks-worker: cross-script DO binding replaced by QUOTA_DELETION service
  binding scoped to deleteAllState only
- dependency-cruiser: quota-runtime imports restricted to the agent DO shell
  (rule proven firing via probe)

Net privilege reduction: gateway/webhooks previously held full 8-method DO
namespaces; each is now confined to its exact baseline call set. All 503
service_maintenance_unavailable translations preserved at every call site.

Deploy is roll-forward-only per the approved rev-5.2 sequence (agent ->
webhooks -> gateway). Pre-cutover quota counters are discarded at the gateway
deploy; sanctioned pre-launch with disposable data.
@iamjr15
iamjr15 merged commit a5a2cf4 into main Jul 31, 2026
4 checks passed
@iamjr15
iamjr15 deleted the arch/quota-relocation branch July 31, 2026 04:55
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