Skip to content

fix(config): fail closed when selected policy cannot load - #223

Merged
tcballard merged 1 commit into
mainfrom
codex/fail-closed-config
Oct 5, 2026
Merged

tcballard merged 1 commit into
mainfrom
codex/fail-closed-config

Conversation

@tcballard

Copy link
Copy Markdown
Contributor

Summary

Explicitly selected missing configuration previously became optional defaults, and discovery skipped unusable paths. Fail at the selected policy boundary instead, for both routing and initial gateway startup.

This is the Wayfinder portion of the 5 October policy-hardening workstream. It is independent of the decision-engine experiment in #222.

Scope

  • Explicit CLI/environment selection is required policy: missing, invalid or unreadable configuration fails closed.
  • Discovery retains broken symlinks, directories and access errors instead of falling through to a parent.
  • Expose loaded/absent/invalid/unreadable states; optional undiscovered configuration retains documented defaults.
  • Share source loading between routing and server startup; redact runtime parser excerpts and configured values.
  • Add a transport-level regression proving that a forbidden backup is not contacted after the primary fails.
  • Document the existing immutable snapshot/last-known-good reload behaviour and host-agent boundaries.
  • Clarify three existing stream must_use annotations for current Rust lint compatibility, matching feat(experiment): compare decision engines behind an opt-in boundary #222.

Findings and source verification

Confirmed loading defects: explicit-missing/default substitution and skipped unusable config discovery. A gateway may already fail later for missing models; this patch removes reliance on that side effect.

Claude Code releases verify allowedProviders and managed-settings read handling in 2.1.285, plus the compound-command, symlink read-denial, MCP metadata and shell-variable fixes in 2.1.289. These prompted the audit; they are not claimed as Wayfinder vulnerabilities.

Wayfinder already enforces model-alias allowlists and destination privacy/locality checks during fallback. The new regression protects that behaviour. Claude's provider setting is not imported: protocol kind does not identify every provider host. Operators must restrict all relevant aliases and control their deployment endpoints.

Shell approvals, IDE file permissions and third-party MCP metadata integrity remain host-agent responsibilities.

Verification

Passed on Linux:

  • cargo +stable fmt --manifest-path rust/Cargo.toml --all -- --check
  • cargo +stable test --manifest-path rust/Cargo.toml --workspace --all-features --locked — 587 tests passed
  • cargo +stable clippy --manifest-path rust/Cargo.toml --workspace --all-targets --all-features --locked -- -D warnings
  • node clients/shared/test/parity.mjs — 21/21 score, route and feature checks
  • Four loader regressions, a real route/serve process test and a forbidden-fallback delivery test
  • Mechanical preflight, diff review and remote/local tree-hash equality

Compatibility and limits

An explicit missing config now errors instead of silently selecting defaults. Valid symlinks remain supported. Failed live reloads retain the last validated snapshot; existing requests finish on their captured snapshot. Protect config directories from untrusted writes: this is not an atomic filesystem sandbox.

No native Apple UI changes or live Claude Code qualification. The host must be updated independently.

Checklist

  • Conventional single-scope title
  • WF-DESIGN-0022 and Unreleased entry
  • Deterministic/keyless scored path preserved
  • Maintainer identity and DCO sign-off; no tool attribution
  • No merge or release

Treat explicit selection as required, retain unusable discovered paths, expose load states and redact runtime diagnostics. Verify the real route/serve boundaries and prove forbidden fallback destinations are never contacted. Clarify must-use stream annotations for the current compiler lint gate.

[design:WF-DESIGN-0022]

Signed-off-by: Tom Ballard <tom@armytage.co>
@tcballard
tcballard force-pushed the codex/fail-closed-config branch from 86d2af4 to af7ab6b Compare October 5, 2026 08:54
@tcballard
tcballard merged commit 075f17c into main Oct 5, 2026
14 checks passed
@tcballard
tcballard deleted the codex/fail-closed-config branch October 5, 2026 09:05
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