omg/docs

Service tokens

Long-lived svc:* JWTs and where they live.

Three service tokens are in production. All have iss=https://auth.omg.dev, aud=vibes-infra, and a 1-year TTL.

SubUsed byLives inPurpose
svc:convexConvex actionsConvex env (VIBES_INFRA_SERVICE_TOKEN)Server-side calls into infra (sandbox, deploys)
svc:workerCF Worker vibes-routerWorker secret (VIBES_INFRA_SERVICE_TOKEN)KV-miss fallback to /v1/deploys-internal
svc:ciGitHub Actions e2eGH secret VIBES_API_KEY (env: e2e)Tier 2/3 tests against real infra

A backup of every token lives in .env.production (gitignored) under VIBES_INFRA_SERVICE_TOKEN_CONVEX, _WORKER, and _CI. Each line carries the jti and expiry as a comment so a quick grep shows whether you're about to need a rotation.

When to rotate

  • Routine: 60 days before expiry. The revoke.ts list command on the auth box prints EXPIRING for tokens within 30 days; grep for that in ops review.
  • Compromise: any token leak (committed to git, sent in an email, etc.) → rotate immediately and revoke the leaked jti. The denylist propagates in ~30s.

See Auth → Rotating a service token for the step-by-step.

Why on-box minting only

The mint script is bun apps/auth/scripts/mint.ts running on the auth box; there is no HTTP mint endpoint. Reasoning:

  • A network-reachable mint endpoint requires an admin bearer, which would itself become the highest-value secret in the stack.
  • On-box CLI access is gated by SSH key — no key, no mint.
  • Compromise blast radius is limited to whatever lateral movement got someone shell on the auth box, which is also where the JWKS private key lives, so the bar is the same either way.