Libra Governor
Never start work you are unlikely to afford to finish.
Libra is a local Governor: a data-plane daemon that sits between
agentic work and the LLM provider it talks to (an agentic coding tool —
Claude Code today — is the current integration). It estimates the cost
and time-to-complete of a task before
admitting it, tracks spend against that estimate as the task runs, and
makes replanning a deliberate, auditable decision rather than an implicit
one. See PRODUCT.md for the full product North Star and
ARCHITECTURE.md for how the pieces fit together.
This is the v0.0.3 release: everything from v0.0.2 (the v0.0.1 MVP 3
local flow, Codex support, local extension points — see below) plus the
Agent Execution Economics work (HORO-1666–1673, HORO-1689): hierarchical
resource accounting, a progressive remaining-cost estimator with shadow
runtime decisions, counterfactual policy replay, and economic-truth
reconstruction (libra-governor economics explain).
Capability status (v0.0.3) — what is production-evidenced and what is not
The founder’s v0.0.3 GO verdict is narrowed to the evidence that actually
exists. Full record: docs/adr/0014-v0-0-3-go-scope-and-exclusions.md.
Production-evidenced (real DogFood evidence, not just synthetic tests —
see experiments/v003_1689_dogfood/README.md):
- Duration estimation (the progressive/bucketed estimator,
libra-governor calibration report). - Admission (preflight cost/time estimation and admission-replay policies).
- Flat / single-tenant resource accounts — the only account shape any real installation has ever produced.
Implemented and tested, but explicitly not yet production-evidenced — do not rely on these for production decisions:
- The shadow-decision / early-warning mechanism (proactive Stop/Degrade proposals). Real-usage measurement has twice (synthetic and real DogFood) produced zero classifiable samples against the pre-registered floors. Tracked: HORO-1693.
- Multi-level / multi-tenant hierarchical custody accounting (organization → principal → session → agent). No real installation has ever created a non-flat account; the code path has no production caller. Intentionally kept research/latent — see HORO-1694. Not wired further without a real multi-level product requirement.
Also in this release (carried from v0.0.2)
- Codex support (HORO-1157/1167) — Claude Code and Codex share the same Governor core through a stable Agent Adapter contract; capability differences between the two are reported honestly, not papered over.
- Local extension points (HORO-1174) — an optional, outbound-only
Business Context Provider fetch and signed event/policy webhooks, plus
one inbound path over the existing 0600 Unix socket for external
Outcome Providers. Off by default; narrowing-only where it touches
policy (see
docs/adr/0005-local-extension-points.md).
Team Alpha (a hosted control plane for shared team policy) is not part of this release. It remains gated on real evidence that the local product sees repeated use — see HORO-1154.
Requirements
- A Rust toolchain (
cargo,rustc) — see https://rustup.rs if you do not have one. - macOS or Linux. Claude Code with hook and statusline support, and/or
Codex with its hooks support (run
libra-governor install --agent codex) — seeintegrations/codex/README.mdfor Codex-specific setup and capability-tier differences. - No Docker. No account or login. No SaaS dependency — everything below runs entirely on your machine.
Install
This repository’s CI (.github/workflows/ci.yml) does not publish a
release binary anywhere — there is no curl | sh-a-prebuilt-binary path
to offer honestly. The real install path is building from a clone with
cargo install:
git clone https://github.com/horonomy/libra-governor.git
cd libra-governor
./scripts/install.sh
scripts/install.sh runs cargo install --path crates/cli --locked,
wires this binary’s hooks and statusline into
~/.claude/settings.json (via libra-governor install — see
“Uninstall” below for exactly what that touches), and finishes by
running libra-governor doctor so you see real, current state rather
than an installer’s own claim of success. If you want the three hooks
without changing Claude Code’s statusline setting, use
./scripts/install.sh --hooks-only or, after installing the binary,
libra-governor install --hooks-only. This mode preserves statusLine
exactly as found, including leaving it absent; default install behavior is
unchanged.
Equivalent manual steps, if you would rather not run the script:
cargo install --path crates/cli --locked
libra-governor install
libra-governor doctor
Quickstart (5 minutes)
-
Run the install steps above.
-
Restart Claude Code (or start a new session) so it picks up the
settings.jsonchange. -
Open any project in Claude Code and submit a prompt.
-
Watch the statusline (bottom of the Claude Code UI) update within a couple of seconds to something like:
libra: task a1b2c3d4 | plan e5f6a7b8 | preflight: Preflight complete | recon: 0.03s | remaining P90: 42s | stable -
Run
libra-governor doctorany time to check on things.
That’s it — the balanced policy preset and no enforcement gateway are
the defaults, and nothing here blocks or interrupts your normal Claude
Code workflow (see “Safe defaults” below).
Your first preflight, walked through
When you submit a prompt, the UserPromptSubmit hook
(libra-governor hook user-prompt-submit) sends it to the local daemon,
which:
- Starts itself on demand if it is not already running (no separate
“start the daemon” step — see
integrations/claude-code/README.md). - Runs bounded, read-only reconnaissance against your repository (file counts, detected build/test tooling — never raw file contents).
- Drafts a Completion Contract and a probabilistic P50/P80/P90
cost/time estimate from your own local history (or an honestly
labeled cold-start estimate on a fresh install — see
crates/domain/src/estimate.rs). - Evaluates admission against your active policy preset and reserves the Completion Reserve required to finish the task, not just start it.
- Surfaces the result into Claude’s own context (the preflight summary) and the statusline.
As you work, PostToolUse cheaply counts tool calls, and if the run
materially deviates from what the plan implied (a possible tool-call
loop, or a call count history says is atypical), the daemon
automatically replans — recomputing and widening the remaining estimate,
subject to a cooldown/max-replan budget — and the statusline shows this
(replanned x2, or awaiting approval once that budget is exhausted).
See
crates/domain/src/replan.rs for exactly
what signals this is (and honestly is not) able to detect from Claude
Code’s hook payloads. On Stop, the session’s task is finalized into an
ExecutionReceipt — the estimate-vs-actual record used for calibration
(libra-governor calibration report).
Policy presets
Four named presets, defined in
crates/domain/src/policy.rs:
balanced (the default), deadline_first, cost_first, and
strict_budget. Select one (and optionally scale its resource/time
target) via an optional config.json in the daemon’s state directory —
full field reference in
integrations/claude-code/README.md.
Absence of config.json is the normal case; every existing install
without one keeps the balanced default unchanged. libra-governor doctor reports which preset is actually active.
Statusline and replanning
The statusline is the visible explanation channel for what the daemon
has decided — not something buried in a log file you never open. It
shows the current task id, plan id, preflight/recon status, the current
remaining P90 estimate, and replan state (stable, replanned xN, or
awaiting approval).
Claude Code gives a scope exactly one statusLine.command, so Libra does
not take it. The supported surface is a provider document composed by a
shared host alongside your own statusline and any other Horonom product’s:
libra-governor statusline provider # one versioned JSON document, read-only, always exit 0
libra-governor statusline explain # the long form the statusline has no room for
docs/statusline.md covers opting in, what the
provider never emits, and migrating off an external wrapper. Bare
libra-governor statusline still prints today’s one-line render
unchanged — see
crates/cli/src/statusline.rs for that
logic and crates/cli/src/statusline_provider.rs
for the provider’s; both are unit-tested independently of a live socket.
Diagnostics: libra-governor doctor
A read-only diagnostic snapshot — never spawns the daemon, never mutates anything, never prints a secret value (only presence/absence — see “Security & privacy” below):
libra-governor doctor # human-readable
libra-governor doctor --json # machine-readable: {"findings": [...], "daemon": {...} | null}
It reports, combining local file checks with a Request::Doctor round
trip to the daemon when one is reachable:
- Daemon availability and version, and whether its protocol version matches this CLI’s own (a version-skewed daemon is reported as an error naming the fix — restart it).
- SQLite ledger schema version vs. this build’s latest known migration.
- Claude Code hook/statusline wiring (
~/.claude/settings.json). - The active admission policy preset.
- Gateway configuration presence, whether it is actually running, and
its honest capability tier (see “Enforcement gateway” in
integrations/claude-code/README.md). config.jsonpresence and validity, checked both locally (so a fresh install with no daemon running yet still catches a corrupt file) and, when a daemon is reachable, against what it actually loaded at startup — a present-but-invalid file is flagged as an error either way (the daemon is silently running on its hardcoded defaults until this is fixed).- Stale config: whether a currently-running daemon’s in-memory policy
preset/gateway presence still matches what’s on disk right now — an
edited
config.jsonwith no daemon restart since is flagged as an error naming the fix (restart it). - Telemetry posture (see “Security & privacy” below).
Exit code 0 unless at least one finding is error-severity — a daemon
that simply has not started yet (the normal state before your first
prompt) is a warning, not a failure.
Evidence export (opt-in, manual): libra-governor evidence-report
This is not telemetry. Nothing here runs automatically, nothing runs in the background, and nothing is ever transmitted anywhere by this tool — it is a manual self-report export you run deliberately, and it makes zero network calls.
It exists for HORO-1154: when a real Claude Code/Codex power user is participating in an evaluation and has agreed to share their local usage signals with the founder, this gives them a way to do that without handing over prompt text, source code, or tool output.
libra-governor evidence-report consent # records explicit opt-in, once
libra-governor evidence-report # refuses without the above
consent writes a timestamped marker to
~/.local/state/libra-governor/evidence_consent.json. Without it,
evidence-report refuses outright and explains why — it never collects
or writes anything on your behalf without this explicit step.
With consent on record, evidence-report:
- Reads coarse, privacy-safe counts out of your local ledger: how
many preflights you’ve run, how many were admitted vs. denied, how
many replans happened, how many tasks you finished (Execution
Receipts). Every one of these is a
COUNT()-style aggregate — seelibra_governor_ledger::EvidenceAggregates, which is structurally incapable of holding a prompt fragment, a file path, or tool output. - Reads whether Claude Code’s hooks/statusline are currently wired into
~/.claude/settings.json— a real, observable fact. It explicitly cannot detect whether you ran a session with the hooks removed or otherwise worked around them; that is invisible from inside this product, and the exported report says so in plain language rather than inventing a signal for it. - Prompts you, interactively, for the open-ended questions HORO-1154 asks about: perceived friction, whether you’d route more work through it, team/Codex demand, and a willingness-to-pay signal. Whatever you type is included verbatim — this is genuinely your own words, never inferred or fabricated on your behalf.
- Writes one JSON file and one Markdown file locally, under
~/.local/state/libra-governor/evidence-reports/, and prints their paths. That’s it. You decide whether and how to send either file to anyone (e.g. attach the Markdown to an email) — this command never does that for you.
See crates/cli/src/evidence_report_cmd.rs for the full contract and
crates/cli/tests/evidence_report_privacy.rs for the automated evidence
that a real prompt nonce, driven through a real Preflight, never appears
anywhere in the exported files.
Troubleshooting
Real, observed failure modes and the exact fix — not invented generic advice:
| Symptom | Cause | Fix |
|---|---|---|
doctor reports a protocol version mismatch | You upgraded the binary while an old daemon was still running (every PROTOCOL_VERSION bump has required this — see crates/protocol/src/lib.rs’s bump history) | libra-governor daemon stop; the next hook invocation respawns it |
Statusline shows libra: - | The daemon is not running; statusline never spawns one by design (a refreshing statusline spawning a daemon would be a race factory) | Submit a prompt — hook user-prompt-submit spawns it on demand |
doctor reports config.json ... rejected | A typo or invalid value in config.json — full detail is in daemon.log, not swallowed | Fix the file (see the field reference in integrations/claude-code/README.md); the daemon keeps running on defaults meanwhile, never crashes on this |
doctor reports stale_config | You edited config.json after the daemon last started, so it’s still running on the old values | libra-governor daemon stop; the next hook invocation respawns it with the new file |
doctor reports stale_runtime | You replaced the installed binary while an old daemon was still running — the daemon’s own executable (hashed at startup) no longer matches what is on disk at the installed path | libra-governor daemon stop; the next hook invocation respawns it running the new binary |
doctor reports orphan_socket | daemon.sock exists but daemon.pid is missing or unreadable — either a stale leftover from an unclean exit, or a live daemon old enough to predate the pid-record feature | Confirm via ps/lsof whether a Governor daemon still holds the state dir; if not, remove daemon.sock and daemon.log by hand (doctor never deletes these itself) |
install says an existing statusLine was left untouched | You already had a non-Governor statusLine configured in ~/.claude/settings.json | You do not have to choose. Register statusline provider with the shared statusline host and both render — see docs/statusline.md. install never overwrites a foreign statusLine |
doctor reports the ledger schema is ahead of this binary | A newer daemon build already migrated the database, and you are now running an older CLI/daemon binary | Rebuild/reinstall this binary at the newer version |
Gateway configured but doctor says not running | Configuration validation or credential resolution failed at daemon startup (fails open, never takes the daemon down) | Run libra-governor gateway status for the exact disabled_reason; the daemon keeps serving hooks/statusline normally either way |
| A gateway request is refused with HTTP 403 | The gateway’s own admission failed closed (see x-libra-decision header) | See “What a refusal looks like” in integrations/claude-code/README.md |
Security & privacy
Truthful, matching the actual implementation — not aspirational copy:
- What stays local: full prompt text, source code, and raw tool
output never leave your machine as part of Libra’s own operation —
this is a structural guarantee, not a policy switch (see
ARCHITECTURE.md). The ledger’s schema has no column for a raw prompt, file content, or tool output — seecrates/ledger/migrations/0001_init.sqlandlibra_governor_domain’s crate-level docs. - What is persisted in SQLite (
~/.local/state/libra-governor/ledger.sqlite3, owner-only0700/0600permissions): task/plan/contract structure, estimates, execution receipts (tool-call counts, duration, outcome), reservation/settlement ledger entries, and — only when the optional gateway is enabled — agateway_requestsprovenance row per proxied request that has no column for a body, a header, a prompt, or tool output (seecrates/ledger/migrations/0007_gateway_requests.sql). - What may pass through the optional gateway: only traffic you
explicitly route through it (
ANTHROPIC_BASE_URLpointed at the gateway’s loopback address). It is off by default (DaemonConfig.gateway: None). Even when enabled, it enforces provider-spend limits — it does not inspect, log, or exfiltrate request/response bodies at any level; there is deliberately no debug body-dump switch anywhere in the code. - What is never uploaded automatically: everything. There is no
telemetry code path anywhere in this repository — nothing runs on a
schedule, in the background, or without your explicit invocation — and
no Team Alpha / cross-machine sync exists yet —
libra-governor doctor’s telemetry finding reflects this as a real, observed absence, not an aspiration. The one exception islibra-governor evidence-report(see “Evidence export” above): a manual, opt-in-gated command you run yourself, that writes a local file and makes no network call of its own — it is a self-report export tool, not telemetry, and it never decides on your behalf to send anything anywhere. - Logs and receipts:
daemon.log(crates/daemon/src/log.rs) never receives raw prompt text or hook payload content. Execution receipts record structural facts (counts, durations, outcome) never the content that produced them. - API/BYOK vs. subscription enforcement — exact limits: reusing
HORO-1144’s model rather than re-describing it inconsistently — see
EnforcementCapabilities/EnforcementTierincrates/domain/src/capability.rsand the capability-tier table inintegrations/claude-code/README.md. In short: a Governor-held API/BYOK credential gets a genuine pre-spend monetary hard cap; a forwarded subscription credential gets exact token observation but no monetary cap (the provider does not expose that quota’s accounting to us); no gateway gets neither — hooks are advisory only.libra-governor gateway statusandlibra-governor doctorreport which one your deployment actually is. - How to disable/remove the integration: see “Uninstall” below.
Safe defaults
Advisory behavior never unexpectedly blocks an existing Claude Code
workflow: every hook is advisory-only (no {"decision": "block", ...}
is ever emitted — see
integrations/claude-code/README.md),
and hard enforcement requires you to deliberately configure and start
the optional gateway with a compatible credential/provider
configuration — configuration validation refuses to start a
combination that would overclaim a monetary cap it cannot honor (see
libra_governor_gateway::config::validate). Conflicting or invalid
configuration is diagnosed (libra-governor doctor, daemon.log)
rather than silently overwritten. libra-governor install merges into
~/.claude/settings.json — it never replaces the file wholesale; see
crates/cli/src/claude_settings.rs for the exact safe read-modify-write
algorithm.
Uninstall
libra-governor uninstall # removes settings.json keys only; asks before deleting state
libra-governor uninstall --yes # also deletes the state directory without prompting
Removes exactly what this integration’s own installer added:
- The
UserPromptSubmit/PostToolUse/Stophook entries,statusLine, and — if present — the gateway’senv.ANTHROPIC_BASE_URL(loopback-only) andapiKeyHelperkeys from~/.claude/settings.json. Every other key in that file — anything from another tool, or your own hand edits — is preserved with its original value untouched (see the ownership predicate incrates/cli/src/claude_settings.rs, and its test suite that seeds foreign content and asserts every foreign value survives). The file itself is rewritten as pretty-printed JSON with keys in alphabetical order, so the surrounding bytes — key order, exact whitespace — are not preserved verbatim, only the values. A timestamped backup of the pre-edit file is written before any change if you need the original byte-for-byte. If the file changes on disk between the moment the command reads it and the moment it would write it back — Claude Code itself, another tool, or your editor saving over it — the write is refused with an error and nothing is changed at all, rather than overwriting that change with an edit computed from the older content; re-run the command to apply it on top of the new content. - The state directory (
ledger.sqlite3,daemon.sock,daemon.log,config.json,gateway.token) — only with--yesor an interactive “yes” confirmation, since it holds your only local record of estimate-vs-actual calibration history and deleting it is unrecoverable. - Nothing else. The daemon binary itself is never deleted by
uninstall— it was put in place bycargo install, so onlycargo uninstall libra-governor-clikeeps cargo’s own package bookkeeping consistent. Wheninstall’s own marker file proves this exact binary path was installed bylibra-governor install,uninstallprints that command for you to run; a binary you built or installed some other way is never mentioned.
Run libra-governor doctor afterward to confirm — it will report
“not installed.”
Known limitations
- Shadow-decision/early-warning and multi-level custody accounting are
not production-evidenced. See “Capability status” above and
docs/adr/0014-v0-0-3-go-scope-and-exclusions.md. - Developer Preview, not a general release. No published binaries
exist yet (see “Install” above);
cargo installfrom a local clone is the real install path. - No Team Alpha / hosted control plane. Shared team policy, a management console, and any cloud component are unbuilt — see HORO-1154/1159/1163/1165. Everything in this release runs entirely on your machine.
- Upgrading directly from v0.0.1 needs one manual daemon restart if
Claude Code was already running before you upgraded. Try
libra-governor daemon stopfirst; if it refuses because no pid record exists (the v0.0.1 daemon predates HORO-1380’s pid-record write and cannot be identity-checked), fall back topkill -f "libra-governor daemon run"for this one specific transition only (or just restart Claude Code).doctorwill tell you if this applies. This is a one-time limitation of that specific transition — the v0.0.1 daemon predates the self-healing fix that makes every later upgrade automatic (seeexperiments/v002_gate/README.md). - The gateway’s session-binding header is unverified against live
Claude Code traffic — see
integrations/claude-code/README.md. - No automated Completion Contract verification — a task is never inferred “done” merely because the model stopped talking (MVP scope).
- No cross-machine sync or telemetry — every install’s history is local to that machine.
- Further limitations (task classification, cross-session merge, MCP
surface, Contract correction UX) are listed in
integrations/claude-code/README.md.
Contributing
See CONTRIBUTING.md for branch/commit/PR
conventions, and CLAUDE.md for repository-specific agent
instructions. Build and test commands:
cargo build --workspace
cargo test --workspace
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
A real, runnable install/uninstall lifecycle smoke test lives at
scripts/smoke-test.sh (not wired into CI —
see that script’s own docs for why).