Skip to content

security: control-plane bearer auth, dependency audit gate, secrets hardening - #1

Open
DanielSoCra wants to merge 20 commits into
mainfrom
codex/control-plane-hardening-build
Open

security: control-plane bearer auth, dependency audit gate, secrets hardening#1
DanielSoCra wants to merge 20 commits into
mainfrom
codex/control-plane-hardening-build

Conversation

@DanielSoCra

Copy link
Copy Markdown
Owner

Summary

This PR addresses the production-readiness findings from the 2026-07-07 external engineering review in four focused hardening areas:

  1. Auth boundary — the daemon control plane now enforces Authorization: Bearer $RUNFORGE_CONTROL_TOKEN on every route except GET /health (both control and degraded servers). A startup bind-host gate refuses non-loopback binds without a token; loopback-without-token keeps working in legacy mode with loud deprecation warnings. X-Requested-By is retained as a CSRF/provenance defense on mutating methods. All clients (dashboard, briefing-summarizer, concierge, daemon CLI) forward the bearer token.
  2. Dependency hygiene — upgraded next to 16.2.10, hono to 4.12.28, and better-auth to 1.6.23; added root package.json pnpm.overrides for remaining high-severity transitive packages (picomatch, path-to-regexp, fast-uri, vite, undici) so pnpm audit --prod --audit-level high exits 0.
  3. Secrets handlingscripts/install-daemon.sh now provisions RUNFORGE_CONTROL_TOKEN into .env.mac (0600) and the launchd plist; scripts/sync-claude-creds.sh writes its validation log to a 0600 file instead of world-readable /tmp.
  4. Spec/traceability coverage — extended STACK-AC-OPERATOR-AUTH (and its ARCH/FUNC parents) to own the daemon-side bearer model, bind-host gate, legacy loopback mode, and /health exemption; updated .specify/traceability.yml with the new control-plane auth files.

Design spec: docs/superpowers/specs/2026-07-07-control-plane-hardening-design.md
Implementation plan: docs/superpowers/plans/2026-07-07-control-plane-hardening.md

Test plan

  • Acceptance tests (pnpm --filter @runforge/daemon exec vitest run src/control-plane/__acceptance__) pass
  • Daemon request matrix (server.test.ts, control-auth.test.ts, degraded-server.test.ts) covers token set/unset × mutating/sensitive/health/halt routes
  • Dashboard daemon-fetch unit tests cover bearer injection, non-overridability, and 401/403 → DaemonAuthError
  • Representative proxy-route tests cover auth-error JSON responses
  • Installer idempotence: RUNFORGE_ENV_MAC_PATH override produces identical token and 0600 mode on re-run
  • pnpm audit --prod --audit-level high exits 0
  • gitleaks detect --redact is clean
  • node scripts/check-ci-workflows.mjs exits 0
  • Full gates: pnpm typecheck && pnpm lint && pnpm test && pnpm build plus pnpm check:traceability && pnpm check:workflows

Notes

  • Docker deployments bind the daemon on 0.0.0.0; the compose file now requires RUNFORGE_CONTROL_TOKEN via ${RUNFORGE_CONTROL_TOKEN:?…}. This is a deliberate fail-closed upgrade: deployments without a token will fail fast at compose time until the operator sets the token in the selected env file.
  • Native loopback deployments without a token continue to work (legacy mode) but log deprecation warnings on startup and on each request. The installer provisions a token automatically on the next run.
  • Built-in HTML dashboard remains functional only in legacy loopback mode; in token mode it requires the bearer like every other route, so the Next.js dashboard is the supported UI.

🤖 Generated with Claude Code

DanielSoCra and others added 20 commits July 7, 2026 15:30
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Move RUNFORGE_CONTROL_TOKEN resolution into a shared helper
  (packages/daemon/src/control-plane/resolve-control-token.ts) so
  both main.ts and the stub cli.ts use identical logic.
- Locate the repo root by walking up from the module file to a
  pnpm-workspace.yaml or .git marker, instead of using process.cwd().
  This fixes status/pause/resume CLI failures when invoked via
  pnpm --filter (cwd = packages/daemon).
- Add unit tests covering env precedence, cwd independence, and
  empty-value handling.
- Update traceability.yml for the new files.
Restore the legacy /halt behavior so Authorization: bearer <token> and
Authorization: BEARER <token> are accepted. Prevents external clients that
send lowercase schemes from getting 403 after the hardening migration.
app/api/metrics/escalation and app/api/decisions/pending were treating
DaemonAuthError the same as DaemonConfigError and silently returning a
degraded 200, masking misconfigured RUNFORGE_CONTROL_TOKEN. Now they log
and return a 500 JSON response with the actionable RUNFORGE_CONTROL_TOKEN
message, matching every mutating daemon proxy route.

- Add regression tests for both GET proxies.
- Update traceability.yml for the new metrics escalation test file.
- install-daemon.sh: check .env.mac for a RUNFORGE_CONTROL_TOKEN line
  directly, instead of sourcing the file and trusting inherited shell
  env. Fixes a daemon/dashboard token mismatch when the operator has
  RUNFORGE_CONTROL_TOKEN exported in their shell but .env.mac lacks it.
  Also chmod 600 the env file on every run, not only on generation.
- sync-claude-creds.sh: mkdir -p CREDS_DIR before creating the
  validation log with mktemp, so a fresh bind-mount dir no longer aborts
  the script before credentials are written.
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