Skip to content

Wolt auth: credential-copy seeding breaks for concurrent long-running wolts (token rotation race) — adopt setup-token #379

Description

@woltspace-jerpint

Pain point

Wolts are seeded by copying the shared Claude credentials file (/workspace/wolts/.claude/.credentials.json → per-wolt .claude/.credentials.json, done by create_creature_wolt() at creation and wclaude self-heal at spawn). This works as a day-one bootstrap, but breaks structurally for two or more long-running wolts:

  • A Claude OAuth grant has one refresh chain, and refresh tokens are single-use (rotation). When the shared access token nears expiry, every running session independently tries to refresh. The first wins and gets a new token pair; every other copy now holds a dead refresh token — its next refresh trips reuse-detection and its access token is revoked (error says revoked, not expired).

Observed live, twice in 12 hours (2026-07-25/26, uxwolt + raggerrabbit both long-running):

  1. 22:03 — uxwolt refreshed at 22:01; raggerrabbit's refresh 2 min later → 401 OAuth access token has been revoked, refresh token stripped from his file.
  2. ~06:50 next morning — same race, same loser; rabbit's cred file wiped to epoch-zero expiry.

Each recovery is a manual stopgap (kill stale claude proc → copy fresh creds from the healthy wolt → POST /sessions/{name}/resume) that re-arms the same race at the next ~8–12h refresh cycle.

Historical note: this is why the shared seed "never really worked" — the seed had been expired since March (fresh wolts hit login screens, found 2026-07-17); only the one long-running wolt with its own healthy chain survived. Symlinking all wolts to one cred file was already tried and abandoned: Claude Code atomically replaces the file on refresh, which breaks symlinks (see comment in setup_wolt_claude_config).

A second, related pain: all wolts on one grant = one subscription seat's usage limits shared across the colony (org monthly spend limit was hit during incident 2). No token scheme fixes this — it's a capacity/seat question — but it compounds the auth fragility.

Why not share the refresh chain

  • Rotation makes a refresh chain single-owner by design — any second consumer is always one step behind.
  • Symlink/hardlink to one file: broken by Claude Code's atomic replace-on-refresh (platform already learned this).
  • Cred-sync daemon (one canonical refresher, fan-out copies): mostly works, but leaves a race window whenever the canonical session is idle at expiry, and babysits undocumented internal behavior. Fragile forever.
  • Per-wolt /login: clean (own grant per wolt) but interactive — needs the human in the loop per wolt, and doesn't scale with wolt creation.

Proposed solution: claude setup-token as the platform auth mechanism

claude setup-token mints a one-year, non-rotating OAuth token (sk-ant-oat01-…) designed for headless/CI use — nothing to race, so it is genuinely shareable by every wolt. Current official docs explicitly support it on Pro, Max, Team, or Enterprise plans (authentication docs).

This is what the original CLAUDE_CODE_OAUTH_TOKEN in .env was. It failed because its expiry was unmanaged (silent 1-year time bomb, no refresh, shadowed the self-healing file creds when it died) — the mechanism was right, the lifecycle management was missing.

Plumbing (revives the spirit of #249):

  1. Seed: store the setup-token platform-wide with a stamped expiry date (mint date + 1y).
  2. Renewal reminder: a wolf cron warns 1–2 weeks before expiry; renewal is a 2-minute re-mint (claude setup-token → browser click → paste).
  3. Documented precedence: env token is auth precedence test: woltspace account access #5 and shadows file creds (test: approval required #6) — this is by design; with a managed expiry it's a feature (one auth source), not a bomb.
  4. Onboarding: wolt creation stops copying .credentials.json; wolts authenticate via the shared token.

Verify before building (known upstream risks)

  • anthropics/claude-code#8938: fresh containers can ignore CLAUDE_CODE_OAUTH_TOKEN during first-run onboarding — exactly our environment. Our pre-written .claude/ config may dodge it; needs a live test.
  • anthropics/claude-code#23703: recent scope regression in minted tokens.
  • --bare mode does not read CLAUDE_CODE_OAUTH_TOKEN (not currently used by the platform, but worth noting).
  • Historical: setup-token may not have worked on Team plans in early versions — the owner's past failed attempt predates current docs. Hence: 5-minute live test first (mint token → fresh test-wolt session boots on it under the Team plan) before any plumbing lands.

Out of scope

Shared-seat usage limits (the spend-limit hit): setup-token fixes the outages, not the budget. A separate seat for heavy-usage wolts is the capacity answer — separate decision.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions