Config subsystem (koanf): typed config, fail-closed, no self-disable knob#7
Open
Tombar wants to merge 7 commits into
Open
Config subsystem (koanf): typed config, fail-closed, no self-disable knob#7Tombar wants to merge 7 commits into
Tombar wants to merge 7 commits into
Conversation
This was referenced Jun 10, 2026
… = defaults)
Introduces internal/config: a layered koanf-based config loader (defaults →
file → env → flags) with typed Config struct, FAILSAFE_LOG back-compat shims,
tilde/\${VAR} path expansion, and Validate() enforcing safety invariants
(log.redact fixed-true, control_plane.* reserved-v1, mode.default fail-safe
normalisation with explicit disabled allowed per O1).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…= identical behavior)
Wire internal/config into the hook enforcement path:
- cmd/failsafe/main.go: load config once at top of run(), fail-closed on bad
config file, pass cfg into HookOptions{Cfg: cfg} for the hook case (both
explicit 'hook' subcommand and the default bare invocation).
- internal/subcommand/hook.go: add Cfg *config.Config to HookOptions; if nil,
load defaults via config.Load (single code path). Replace defaultModeChain()
with buildModeChain(cfg, home) driven by cfg.Mode.PaneDir / cfg.Mode.Default.
Replace auditlog.DefaultLogger with loggerFromConfig(cfg). Replace hardcoded
trust path / user policy path / tools dir with cfg.Trust.Path,
cfg.Policy.UserPath, cfg.Policy.ToolsDir.
- internal/policy/chain.go: add UserPolicyPath to DiscoverOpts so callers can
supply a config-driven path without knowing HOME.
- internal/trust/trust.go: add LoadFromPath(path) so the hook can load from the
config-driven path; Load(home) delegates to it (no behavior change).
- internal/config/config.go: fix injectableEnvProvider to skip single-segment
FAILSAFE_* keys (e.g. FAILSAFE_MODE) that map to struct fields, not leaves —
prevents unmarshal errors when tests set FAILSAFE_MODE in the env.
All existing hook/parity/mcp/corpus tests pass unchanged (no-config-file path
= defaults = identical behavior). New TestBuildModeChain_CustomPaneDir proves
config.Mode.PaneDir drives the chain.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…from one source) Load config.Config once in run() and wire cfg.Trust.Path → TrustOptions, cfg.Policy.ToolsDir → ToolsListOptions, cfg.Log.Path → ReportOptions.LogPath, cfg.Mode.Default → ExplainOptions.Mode, and build the mode chain from cfg for toggle/mode/mode-set via the new exported ModeChainFromConfig helper. Removes the auditlog.DefaultLogger import from main.go. Adds TestTrust_CustomTrustPath and TestToolsList_CustomToolsDir to pin that config-driven paths are actually honoured. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… (hardcoded, no self-disable vector) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…OG, self-protection note + fs-guard roadmap Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…*_* now honored) Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… rebase onto report/log #10)
c9caa7a to
4c001ae
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a unified config subsystem (koanf, local providers) that replaces scattered env-vars + hardcoded paths with one typed
Config, loaded once at startup and consumed by the hook and every CLI subcommand. Fully backward-compatible — no config file means identical behavior.internal/config) — typedConfig, precedence flags > env > file > defaults, providersfile(YAML) +env(FAILSAFE_prefix) +posflag. Config file at~/.config/failsafe/config.yaml. koanf versions pinned.Validate()— a bad/invalid config blocks (the hook exits non-zero before any command runs):log.redact:falserejected,control_plane.*reserved/rejected, malformed YAML rejected. Safety is never negotiable at the config layer.mode.defaultis not configurable; the default guard mode is hardwiredenabled(protected). The only way to allow writes is the per-panetoggle, which is already self-protected.mode.pane_dir,log.{enabled,path,redact}(redactsafety-fixed true),telemetry.{enabled,otlp_endpoint}(off by default, no-op stub — no OTLP SDK),policy.{user_path,tools_dir},trust.path.control_plane.*reserved.FAILSAFE_MODEstays a mode-chain source (not collapsed into config);FAILSAFE_LOG(off/<path>) reproducesauditlog.DefaultLoggerexactly.Validaterejects fail-open knobs; documented residual surface + a filesystem-access guard roadmap entry (the real fix for protectingconfig.yaml/~/.aws/~/.ssh/policies from agent writes — separate future PR).Config) + CLI (toggle/mode/trust/tools/report); docs (configuration.mdrewrite) + CHANGELOG.Test Plan
go test ./...— 14/14 packages pass (incl. precedence, missing-file-defaults, FAILSAFE_LOG/MODE back-compat, fail-closed Validate, multi-word env mapping, telemetry off-by-default)failsafe test ./test/corpus— 30/30 (hook exercised with no config → defaults → identical)🤖 Generated with Claude Code