Skip to content

Gateway Token Minting & Refresh #285

@jrf0110

Description

@jrf0110

Part of #271 — Gastown Cloud Proposal A (Sandbox-per-Town)

Goal

Mint per-town JWT tokens that route all LLM calls through the user's billing context via the Kilo gateway. Implement automatic token refresh so sandboxes never lose gateway access.

Context

The Kilo gateway (POST /api/gateway/chat/completions) accepts Authorization: Bearer <jwt> and extracts user context for billing, model routing, and abuse detection. The existing getUserFromAuth() function handles this — no gateway changes needed. We just need to mint appropriate tokens and keep them refreshed.

Requirements

Token Minting

mintGastownToken(userId, townId, organizationId?) → JWT string

Payload:

{
  "sub": "<user_id>",
  "type": "gastown_sandbox",
  "town_id": "<town_id>",
  "organization_id": "<org_id or null>",
  "iat": 1700000000,
  "exp": 1700086400
}
  • Lifetime: 24 hours
  • Signing: HS256 with NEXTAUTH_SECRET (same as existing stream tickets)
  • Called during createTown (PR 2) to provision the initial token

Token Refresh

The sandbox refreshes its token before expiry via a cron job (every 12 hours).

Cloud-side endpoint (add to gastown tRPC router):

gastown.refreshToken — mutation

  • Input: { townId: string }
  • Auth: x-internal-api-key (sandbox → cloud)
  • Validates town exists and is active
  • Mints a new 24-hour token
  • Returns the new JWT string

Sandbox-side cron:

# Every 12 hours
NEW_TOKEN=$(curl -s "${CLOUD_API_URL}/api/gastown/refresh-token" \
  -H "x-internal-api-key: ${INTERNAL_API_KEY}" \
  -d '{"townId": "'${TOWN_ID}'"}')
echo "KILO_JWT=${NEW_TOKEN}" > /home/gt/.env.gateway

The startup script sources this env file. New agent sessions pick up the refreshed token.

Model Configuration Flow

Users configure models via the dashboard. The flow:

  1. User selects model in dashboard (handled in PR 10)
  2. Cloud app calls sandbox internal API: PATCH /config with model settings
  3. Sandbox writes to gt config files:
    • Town-level: ~/gt/settings/config.json"role_agents" map
    • Rig-level: ~/gt/<rig>/settings/config.json → per-rig overrides
  4. New agent sessions pick up the config on next spawn

No gateway changes needed — the gateway already routes any model the user requests.

Files

  • cloud/src/lib/gastown/auth.ts (new) — mintGastownToken, verifyGastownToken
  • Updates to cloud/src/server/api/routers/gastown.tsrefreshToken endpoint
  • cloud/infra/gastown-sandbox/refresh-token.sh (new) — cron script

Acceptance Criteria

  • mintGastownToken produces a valid JWT with correct payload
  • Token expires after 24 hours
  • Gateway accepts the token and correctly attributes usage to the user
  • refreshToken tRPC mutation validates town ownership and returns new token
  • refreshToken rejects requests for destroyed/stopped towns
  • Sandbox cron script successfully refreshes token and writes to env file
  • New agent sessions pick up the refreshed token (via env file sourcing)
  • Organization context is included in token when applicable

Dependencies

  • PR 2 (Provisioning API) — createTown calls mintGastownToken for initial provisioning
  • PR 3 (Sandbox Internal API) — PATCH /config endpoint for model configuration

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions