Skip to content

feat(runtime): validate account tokens on add + per-key usage; auto-disable a bad token (v0.282.0) - #508

Merged
vikasprogrammer merged 2 commits into
mainfrom
feat/runtime-account-validate
Jul 31, 2026
Merged

feat(runtime): validate account tokens on add + per-key usage; auto-disable a bad token (v0.282.0)#508
vikasprogrammer merged 2 commits into
mainfrom
feat/runtime-account-validate

Conversation

@vikasprogrammer

Copy link
Copy Markdown
Owner

Why

An owner added a Claude subscription token to the instapods runtime-account pool, but every session kept hitting /login. Root cause: the enabled account's token value was not a valid setup-token (Z3ug…, not sk-ant-oat01-…); it was vaulted as-is with no validation, injected as CLAUDE_CODE_OAUTH_TOKEN, and Claude rejected it (401 Invalid bearer token) — so rotation overrode the working box default with a broken token on every launch. Nothing caught it: no add-time check, and teardown only reacted to usage-limits, not auth failures.

What

1. Add-time validation (the core fix). POST /api/runtime-accounts now probes a token against Claude's own GET /api/oauth/usage (the endpoint Claude Code's status line uses; read-only, no quota consumed) before storing it:

  • 401 → rejected with a clear message; never vaulted.
  • 403-permission_error (scope) or 200 → valid, stored.
  • 429 / network → added and badged "could not verify" (never wedge on a transient blip).

2. Per-key usage in Settings → Runtime accounts. New Usage column (weekly 7d + session 5h utilization, amber ≥80% / red ≥100%, reset on hover), a Refresh action, and the add form echoing the result. Usage populates for interactive-login credential-dirs (which carry the user:profile scope). A claude setup-token is validated the same way but can't report usage (Anthropic scopes it out of the profile endpoint) — surfaced honestly as valid · usage n/a (setup-token lacks the user:profile scope).

3. Launch-time backstop. When an unattended run's pane shows a credential-rejection banner (not a usage-limit), the account it launched under is auto-disabled (markInvalid) — an invalid token won't self-heal at a reset the way a usage-limit does — so the launcher rotates to another account / the box default instead of looping into /login. A Refresh that re-authenticates re-enables it.

Schema

New idempotent columns on runtime_accounts: last_checked_at, check_ok, check_note, usage_json. New route POST /api/runtime-accounts/:runtime/:name/check.

Verification

  • Live oauth-usage endpoint across all outcomes: bad token → 401 (rejected), valid setup-token → 403-scope (valid, usage n/a), login token → 200 with real weekly/session %.
  • Store + migration on a scratch DB: markInvalid disables + drops from pick/enabledCount; recordCheck(null) doesn't clobber a known-good badge; pick skips invalid/limited.
  • Full HTTP flow with an owner session: reject bad · accept valid token · credential-dir usage populated at add · Refresh on both kinds.
  • npm run build, web build, npm run typecheck, 130/130 governance.

Note on the live instapods box

Already hot-patched separately (disabled the broken tools2 account → pool inert → sessions fall back to the working box default). This PR prevents the class of failure going forward. The fleet uses setup-tokens, which validate here but can't show usage — to get live usage numbers an account must be added as a login credential-dir.

🤖 Generated with Claude Code

vikasprogrammer and others added 2 commits July 31, 2026 20:06
…uto-disable a bad token (v0.282.0)

A mis-pasted Claude subscription token was vaulted as-is and then silently sent
every future session to /login (invalid CLAUDE_CODE_OAUTH_TOKEN) — the outage seen
on the instapods pool. Three changes close that:

- Add-time validation: POST /api/runtime-accounts probes the token against Claude's
  own GET /api/oauth/usage (the source Claude Code's status line uses; no quota
  consumed) and rejects a definitive 401 with a clear message instead of storing it.
  A transient 429/network is added and badged "could not verify", never blocked.
  New src/edge/runtime-account-check.ts.

- Per-key usage in Settings → Runtime accounts: a Usage column (weekly 7d + session
  5h utilization, amber ≥80% / red ≥100%, reset on hover), a Refresh action, and the
  add form echoing the result. Usage populates for login credential-dirs (user:profile
  scope); a setup-token is validated the same way but can't report usage (Anthropic
  scopes it out) — shown honestly as "valid · usage n/a". New runtime_accounts columns
  last_checked_at/check_ok/check_note/usage_json; POST …/:runtime/:name/check.

- Launch-time backstop: when an unattended run's pane shows a credential-rejection
  banner (not a usage limit), the account it launched under is auto-disabled
  (markInvalid) — an invalid token won't self-heal at a reset — so the launcher rotates
  to another account / the box default instead of looping into /login. A Refresh that
  re-authenticates re-enables it.

Verified against the live oauth-usage endpoint (401 invalid / 403-scope valid / 200
usage), the store + migration on a scratch DB, and the full HTTP add/reject/refresh
flow with an owner session. 130/130 governance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…validate

# Conflicts:
#	CHANGELOG.md
#	package-lock.json
#	package.json
@vikasprogrammer
vikasprogrammer merged commit 43cb6fb into main Jul 31, 2026
1 check passed
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