Model-designed 'block all' admin switch silently overridden by per-user overrides -> unnoticed API credit drain
Summary
Over several Claude Code sessions, Claude designed and deployed an admin remote-control panel (Cloudflare Worker) for the user's internal image-studio tool that is distributed to their employees. The panel includes a global "block all" (maintenance mode) switch. The design Claude produced had critical flaws that caused real, unnoticed API spending for roughly two weeks:
- The global "block all" switch was soft-kill only. It was enforced client-side (the employee app's agent brain politely responds "under maintenance") while the proxy server kept forwarding paid upstream API calls (Gemini / Anthropic / others) with the company's keys.
- Per-employee "apply directive" silently re-enabled users. The panel's per-employee steering feature wrote
targets[license_id] = {agent_directive, enabled: true}, and the server merged per-target config over the global config (Object.assign({}, global, target)). So any employee who had ever received an individual directive keptenabled: true, which silently defeated the global "block all" button. - No UI indication that the effective per-user state differed from the global switch the owner had set.
Impact (user-reported)
- The owner pressed "block all (maintenance)" and reasonably believed all employee usage was stopped.
- 5 of 7 employees retained full access (verified today from live server config + access logs; one employee was still actively using it today).
- The company's Google Gemini prepaid credits and Anthropic API credits were drained to $0 during the period the owner believed usage was blocked. fal.ai credits were also consumed.
- Several hours of the owner's time today were spent discovering and diagnosing this.
The user explicitly asked that this be reported to Anthropic as model-caused damage ("the model built the switch I trusted; my money is gone"). They would like guidance on any refund/goodwill channel if one exists.
Secondary friction during incident response (today)
While fixing the incident in Claude Code (Windows 11 desktop, model claude-fable-5), the auto permission classifier repeatedly blocked legitimate remediation steps on the user's own infrastructure:
- Blocked a POST to the user's own Cloudflare Worker admin endpoint (authenticated with the user's own local admin token) to lift the block state.
- Blocked running an admin script that Claude had just written at the user's request (same pattern as existing scripts in the repo).
Each block forced a manual hand-off to the user mid-incident, further slowing response. Read-only calls to the same endpoint were allowed, so the blocks felt inconsistent.
What Claude should have done originally
- Enforce a kill switch server-side (deny paid upstream calls), never only via a client-side behavioral hint.
- Never let a convenience feature (per-user directives) silently override an explicit global kill switch; precedence should be "block wins".
- Surface effective per-user enabled state in the admin UI.
Resolution (done today, by Claude in this session)
- Worker fixed: global block now takes absolute precedence over per-target overrides, and the proxy now hard-rejects (503) all paid upstream routes while blocked. Deployed and verified end-to-end (block → per-user config shows disabled + paid call returns 503; unblock → restored).
Environment
- Claude Code desktop app on Windows 11, model
claude-fable-5(panel originally built in earlier sessions, including Opus-based ones). - The affected system is the user's own commercial internal tool; no third-party service was harmed.