3747fe2f49
CTO directive: "nothing should be fail-open." Remove the dev-mode fail-open auth hatch so AdminAuth/WorkspaceAuth (and the discovery caller) ALWAYS require a real credential — fail-CLOSED in every environment, dev included — fix local dev to stay AUTHENTICATED (not open), and add a regression gate so fail-open cannot return. Removed fail-open call-sites (workspace-server): - internal/middleware/wsauth_middleware.go WorkspaceAuth — deleted the isDevModeFailOpen() short-circuit that let a bearer-less /workspaces/:id/* request through when MOLECULE_ENV=dev + ADMIN_TOKEN unset. - internal/middleware/wsauth_middleware.go AdminAuth — deleted BOTH fail-open branches: the Tier-1 lazy-bootstrap (no live tokens + no ADMIN_TOKEN ⇒ pass, the C4 /org/import pre-empt hole) and the Tier-1b isDevModeFailOpen() dev hatch. HasAnyLiveTokenGlobal is still probed for the 503-on-outage semantics but opens no path. - internal/handlers/discovery.go validateDiscoveryCaller — deleted the IsDevModeFailOpen() allow branch; discovery now requires a verified CP session or valid bearer in every env. - Removed the isDevModeFailOpen()/IsDevModeFailOpen() helper entirely. The two legitimately non-auth uses (rate-limit relaxation in ratelimit.go, loopback bind default in cmd/server) now key on a new NON-security isLocalDevEnv() predicate (MOLECULE_ENV only, decoupled from ADMIN_TOKEN). CanvasOrBearer's cosmetic-only behaviour (PUT /canvas/viewport) is unchanged. Dev path stays authenticated, not open: - scripts/dev-start.sh provisions a deterministic ADMIN_TOKEN into .env and exports the matching NEXT_PUBLIC_ADMIN_TOKEN so the dev Canvas sends a real bearer (canvas/src/lib/api.ts already attaches it; next.config.ts pair-guard). - Docs updated: .env.example, docs/quickstart.md, docs/architecture/overview.md. Regression gate: - internal/middleware/no_fail_open_test.go — asserts AdminAuth + WorkspaceAuth fail CLOSED (401) under the EXACT old-hatch conditions (ADMIN_TOKEN unset + MOLECULE_ENV=dev/development × hasLive 0/1). Proven RED against a temporarily restored hatch, GREEN after. Plus a source-guard test forbidding the isDevModeFailOpen(-style helper from re-appearing. - Converted the stale fail-open assertions in wsauth_middleware_test.go, discovery_test.go, security_regression_685_686_687_688_test.go and the devmode/bind tests to pin the fail-closed contract. Audit (other fail-open patterns on the auth surface): CanvasOrBearer and validateDiscoveryCaller retain a fail-open-on-DB-error (and CanvasOrBearer a no-token lazy-bootstrap) — both are documented availability tradeoffs on cosmetic / low-sensitivity routes, left as-is and flagged for follow-up. Verify: go build ./... ok; go vet middleware/cmd/handlers clean; full module go test ./... = 46 ok / 0 fail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>