Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,719 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Heimdall 🛡️

Every PR ships the runnable evidence that the fix passes.

A cloud bot that fixes your GitHub issues and opens a proven PR. You review, you merge. The bot opens it on a heimdall/* branch as a scoped GitHub App — never as you, never on main, and it never self-merges. A human always gates the merge.

Claude Code License: MIT

Version


Get a bot PR on your repo

Once you've installed the Heimdall Maintainer GitHub App on your repo and run claude setup-token, it's two commands:

rr connect                                    # registers your App install + captures your Claude cred
rr "fix the flaky test in payments and open a PR"

What happens: rr signs your task with your own Ed25519 key and enqueues it. A gated worker clones your repo with your team's Claude subscription and your GitHub App installation, runs the issue-resolution loop until the fix passes the gates, and opens a heimdall/* PR on your repo. You review it. You merge it.

Nothing to paste — no token, no URL. The public control plane is baked in and enrollment is automatic: your first signed call registers this device on first use. Just rr connect and go. (Running your own deployment, or need to re-gate enrollment behind a bootstrap token? That's an operator concern — see OPERATORS.md.)

Why it's safe to point at your repo

  • Tenant isolation is a falsifiable oracle, not a promise. Every cross-tenant attack — IDOR by repo slug, cred read across teams, queue drain, installation-id swap, signed-request replay — has a named invariant and a red-line mutant test. Drop any gate and test/heimdall-cp-authz-gate.test.sh goes red; the keystone suite passes only when every mutant is caught. Full invariant + attack matrix: docs/specs/2026-07-03-rr-isolation-invariants.md.
  • BYOC — no shared keys. You pay your own Claude tokens; your credential lands in your own per-team Secret Manager secret and is injected env-only into your job — never logged, never echoed, never readable by another tenant.
  • Least-privilege bot. The App holds exactly Contents + Issues + Pull requests — no Administration, no Actions/Workflows, no merge capability. It can open a PR; it cannot touch branch protection or push to main.
  • Honest bring-up. This loop was hardened over a live multi-tenant bring-up that shook out a run of production-only failures — Google's GFE rejecting GET-with-a-body, cold-start identity drift, jobs starving under scale-to-zero — each now documented as fixed in deploy/cloud-run/README.md and the runbook.

Under the hood — the verification orchestrator

The bot is powered by Heimdall's local engine: a Claude Code plugin that turns one prompt into finished, proven work. Every plan wires an external, falsifiable oracle so the implementation can never grade its own homework, and the merge stays blocked until the work is proven correct. Install it to run the same gates on your own machine.

Install

Three paths. Each is labelled with what it actually does to your machine — pick the risk you are willing to take, not the shortest command.

# Path What it runs Honest risk
1 One-liner (below) Downloads install.sh, checks its sha256, then runs it as you Highest. A script fetched over the network, executing with your user's privileges — it can do anything you can do. The digest check is the only thing between you and whatever those bytes are. Verify it, or take path 3.
2 npmnpx runheimdall The same install.sh, fetched over https and sha256-checked against a digest baked in at publish time (bin/runheimdall.js), then handed to bash Same as path 1. The wrapper is thin and the script still runs as you. You gain not having to hand-copy a digest. You gain no isolation.
3 Docker sandboxDockerfile.install The same install.sh, copied from your own clone — nothing fetched, no digest to trust — run inside a container Lowest, with real caveats. Every $HOME change lands in a layer you delete. It does not isolate a repo you mount, and it is not a macOS sandbox. Caveats below.

Whichever you pick, this is what the installer writes outside the repo. Measured from a real run against a throwaway $HOME, not asserted:

Written What it is
~/.heimdall/ A full git clone of this repo — the installed checkout hmd runs from
~/.local/bin/hmd, ~/.local/bin/heimdall Symlinks into ~/.heimdall/bin/heimdall
~/.claude/settings.json Adds statusLine + subagentStatusLine, and registers the plugin under enabledPlugins / extraKnownMarketplaces. Honors $CLAUDE_CONFIG_DIR, and never clobbers a statusLine you set yourself
~/.zshrc / ~/.bashrc / ~/.profile One appended export PATH=… line for ~/.local/bin — only when it is not already on PATH
~/Library/LaunchAgents/com.heimdall.dream.plist macOS only — a nightly 03:00 LaunchAgent (com.heimdall.dream) that runs the overnight sweep with no session open and survives logout and reboot. Opt out with HEIMDALL_NO_DREAM_SCHEDULE=1
~/.heimdall/team.json An auto-minted solo team secret — a bearer capability. Treat it like a credential

No sudo. Idempotent — re-run to upgrade. hmd uninstall reverses all of it.

Network posture is default-ON. Team presence and the cloud features reach the control plane as soon as you use them: a signed heartbeat carrying your handle, verdict, and current filename — scoped to your team, never your code or file contents. That is a feature, and it is on until you switch it off. hmd presence sever gives zero egress. Field-by-field contract: DATA.md. The precisely scoped claims are under Your code stays yours.

hmd's default module set includes a proxy that can read your prompts, and hmd wrap routes generation traffic through it. Headroom is default_included: true in modules/headroom/manifest.json: a local context-compression proxy which, once traffic is pointed at it, sits between your coding tool and the model provider, reads the prompts and context on their way out, and rewrites them to be smaller. hmd modules add headroom installs the package, then measures each wire the manifest declares and reports what it found: the wrap chain measures ROUTED, so a tool launched via hmd wrap <tool> has its GENERATION traffic carried through the proxy. Bare hmd does not route. The storage-codec wire still measures RECORDED, not routed. JUDGMENT never traverses it — every verdict-producing call is scrubbed back to the real provider, which test/gate-judgment-uncompressed.test.sh goes red on. It runs as a process you own and can inspect, and it introduces no Heimdall-operated destination — that traffic goes to the same provider it went to before. Five things to know before you install:

  • Nothing installs it for you. install.sh has no module code path at all, and the background updater refuses to acquire a consent-required class unattended — it names the module and hands you the command. Until you run hmd modules add headroom, hmd modules status headroom reports NOT ATTEMPTED.
  • That command is a remote code install, and no digest is verified. It runs uv tool install --python 3.13 "headroom-ai[all]==<pin>" against PyPI. hmd hashes nothing on that path and does not claim to: the lifecycle step is named install + provenance, not digest-verify, and the receipt records verified: false alongside the pin it did not check. It also pulls an ML stack — Rust wheels, an ONNX runtime, HuggingFace tokenizers — so this is the one place hmd stops being near-stdlib.
  • The consent question is waived; the disclosure is not. consent_waived sits on that one module's manifest. modules/_classes/traffic-proxy.json still reads consent_required: true, so every other traffic-proxy module hmd ships still asks. The consent text still prints, both declared class contracts still run their invariants, and the receipt records granted_via: manifest-waiver. This is a deliberate maintainer decision: disclosed, not asked.
  • Installing it is not wiring it, and hmd measures the difference out loud. The manifest declares two wires and hmd applies neither. [6/7] wire prints each one as RECORDED, not routed next to the measurement that produced it — bin/heimdall-wrap holds no reference to the module, and the memory-codec seam reports backend=plain — and hmd modules status headroom reads back that same record. A declared wire whose kind the code has no handler for is refused at validate rather than quietly recorded, so a module cannot install while claiming a capability hmd does not deliver (test/wire-kind-dispatch.test.sh).
  • Gates read raw, and signed traffic steps around it. Route generation through a proxy — this one, or your employer's — and judgment still may not follow it. Verdict-producing commands run through hmd_gate_exec, and control-plane, enrollment and presence traffic through hmd_signed_exec (bin/lib/hmd-gate-endpoint.sh); both drop ANTHROPIC_BASE_URL, the proxy pairs and the whole HEADROOM_* namespace before pinning the endpoint to the real provider. test/gate-judgment-uncompressed.test.sh goes red the moment a gate request reaches the proxy.

hmd modules remove headroom returns the tree byte-identically. Threat model, the full reachability table and every way to decline it: SECURITY.md. Mechanics and honest limits: Modules.

Path 1 — the one-liner

curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.3.9/install.sh -o heimdall-install.sh \
  && echo "73c89c9400205a7d32178f7e91854d836f9702708417079b08faea63fa301196  heimdall-install.sh" | shasum -a 256 -c - \
  && bash heimdall-install.sh

Pinned to the v2.3.9 tag and checked against the sha256 of that tag's install.sh. The && chain is load-bearing: if the bytes do not match the digest, shasum -c prints FAILED and nothing runs — so a tag moved under you, a CDN cache poisoning, or a truncated download stops the install instead of executing. On a Linux box without shasum, sha256sum -c - takes the same digest. Re-derive it yourself any time:

curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.3.9/install.sh | shasum -a 256
# 73c89c9400205a7d32178f7e91854d836f9702708417079b08faea63fa301196

No sudo. Idempotent — re-run to upgrade. Reversible:

hmd uninstall    # removes everything; nothing else was touched

Prefer to inspect first? Same download, same check — just read it before you run it:

curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.3.9/install.sh -o heimdall-install.sh
echo "73c89c9400205a7d32178f7e91854d836f9702708417079b08faea63fa301196  heimdall-install.sh" | shasum -a 256 -c -
less heimdall-install.sh  # function-wrapped, no eval, no base64 — what you read is what runs
bash heimdall-install.sh

Signature (stronger than the digest). A digest you copy from this README only proves the bytes match what this README says; a signature proves they came from the maintainer's key. Every release signs install.sh with minisign and publishes install.sh.minisig as a release asset:

curl -fsSL -O https://github.com/randomittin/heimdall/releases/download/v2.3.9/install.sh.minisig
minisign -Vp release/heimdall-signing.pub -m heimdall-install.sh -x install.sh.minisig

The public key ships in this repo at release/heimdall-signing.pub, so that command assumes a clone. SIGNING.md has the full model — including the bundled pure-python verifier for machines with no minisign binary, and the fail-closed behavior of the auto-updater.

Prerequisites: Claude Code 1.0+ · Git · jq (brew install jq)

Path 2 — npm

npx runheimdall — same pinned tag, same sha256 check, zero clone required. It fetches the pinned install.sh, verifies it against the digest baked in at publish time, and aborts before executing anything if the bytes disagree. Convenience, not containment: what finally runs is the same script, with the same privileges as path 1.

Path 3 — the Docker sandbox

For a first look that does not touch your machine. Build from a clone, so the install.sh you read is byte-for-byte the one that runs — nothing is fetched, so there is no digest for you to trust:

git clone https://github.com/randomittin/heimdall && cd heimdall
less install.sh                                     # what you read is what runs
docker build -f Dockerfile.install -t heimdall-sandbox .
docker run --rm -it heimdall-sandbox                # hmd is already on PATH

--rm discards every $HOME mutation in the table above the moment the container exits. The image also drops the auto-minted team.json during the build, so containers never share one team secret.

What the container does not isolate — a sandbox you misunderstand is worse than no sandbox:

  • A mounted repo is not isolated. -v "$PWD:/work" is a hole you punched on purpose: anything hmd writes under /work lands on your real disk. Mount :ro if you only want hmd to read your code.
  • It is not a macOS sandbox. The image is Linux, so launchd and the keychain do not exist inside it and the nightly LaunchAgent step is skipped as unsupported. That is the container being a different OS — not a boundary defending your account. launchctl and the keychain are account-scoped: a fake $HOME relocates only the plist file, while launchctl load still registers the job in your real per-user session. A sandboxed test in this repo learned that the hard way, by rewriting the developer's live LaunchAgent. The switch is HEIMDALL_NO_DREAM_SCHEDULE=1 — never $HOME.
  • The network is open. The build clones from GitHub, and presence is on by default. docker run --network none gives the container zero egress; hmd presence sever does the same at the application level.

Your code stays yours

Gates run 100% locally. Your code never leaves your machine.

Team presence is a feature you can see and switch off: it sends {handle, verdict, current filename} — never code, never file contents — to your team's endpoint. hmd presence off makes you invisible; hmd presence on --no-files hides filenames.

Telemetry is specified, minimal, and yours to kill: DATA.md documents every field. hmd telemetry off. hmd telemetry purge deletes the local spool — nothing is transmitted in this release.

Auto-update checks GitHub Releases for new signed versions. HEIMDALL_NO_AUTOUPDATE=1 (or ~/.heimdall/no-autoupdate) disables it.

rr is the one thing that sends on purpose, and only when you run it: your BYO Claude credential (write-only), your GitHub App installation id, and the literal task text you typed — because that text is the job. It never uploads your working tree; the worker clones your repo from GitHub.

Full data contract and every field collected: DATA.md.


First run

hmd demo --run

Scaffolds a real full-stack task, builds it, ends with a summary card and a follow-up prompt. Safe to run sight-unseen — hmd demo (without --run) prints the plan and does nothing.


Why Heimdall

  • Catches the silent failures — ordering races, whole-sequence invariants, missing subsystems that pass a naive green suite.
  • Falsifiable gates — every gate is proven able to go red before it is trusted green. The corpus of real failure cases replays on every change; a regression that once shipped can never ship twice.
  • Proof of correctness, not just generation — the delta Heimdall sells is the receipt that proves the proof can fail. Generalizes: 0.50 median reuse across 8 cold repos.
  • Full audit trailhmd report produces a machine-readable telemetry report of every gate, mutation score, and corpus catch-rate from the last run.

What's inside

Capability Command Status
Verification gates (secret-scan, bloat, falsify) /hmd:verify in Claude · bin/falsify Shipped
Demo task runner hmd demo / hmd demo --run Shipped
Issue-resolution loop hmd (auto-retries failures against corpus) Shipped
Telemetry report hmd report Shipped
Design match (visual diff vs spec) hmd designmatch Shipped
Redum / conformance checker heimdall-redum · heimdall-check Shipped
Reuse engine (cold-repo analysis) bin/lib/reuse_analyzer.py Shipped
Debloat scanner heimdall-debloat --report-only Shipped
Parallel workers hmd --team N "task" (N tmux panes, independent — no shared state) Shipped (no coordination layer)
Benchmark suite heimdall-bench Shipped
Optional capability modules hmd modules Shipped

Modules

Optional capability modules. The base install ships the module system and zero module payloadsmodules/ holds manifests and class contracts, never vendored code. The registry is this repo, so every pin is a reviewed commit rather than a network lookup, and there is no code path that resolves "latest". Nothing self-installs.

hmd modules                    # list — honest when nothing is installed
hmd modules add <name>         # the full ordered pipeline
hmd modules remove <name>      # total removal
hmd modules update [<name>]    # move to the manifest's human-set pin
hmd modules status <name>      # one module in detail
hmd modules verify [<name>]    # re-run class invariants (the CI entry point)
hmd modules preflight <name>   # can this install happen? read-only, asks nothing
hmd modules repair <name>      # retry an install, recording which stage failed
hmd modules defer <name>       # not now — hmd is ready, the module follows later
hmd modules pending            # what is deferred or awaiting a retry
hmd modules optout <name>      # decline; no install path may re-install it
hmd modules optin <name>       # undo an optout

Every verb accepts --json; add accepts --yes. --registry and --state relocate the registry and install state, which is how the suite runs hermetically.

Four permission classes

A module declares its class — or several — in its manifest, and a missing or unknown class is refused, not defaulted: the class decides consent and which invariants are enforced, so picking one silently would be picking a security posture on the operator's behalf. When a module declares several, the union of their invariants runs, never the first match.

Class Consent Invariants enforced
traffic-proxy required gates read raw · non-interactive passthrough · signed and control-plane traffic never routed through it
tool-adapter required wrap/unwrap byte-identical · hooksPath and AGENTS.md fences preserved
storage-codec not required round-trip fidelity · plain fallback when absent · never touches judgment inputs
rule-pack not required rules ship falsifiers · attribution preserved

The two classes that mutate something you own — the wire, and your own config files — ask. The two that are transparent by contract do not, and their invariants are what make that transparency true rather than asserted: a codec that loses a byte, or a pack that flattens attribution, is rolled back rather than merely disclosed.

The lifecycle order is the contract

[1/7] validate → [2/7] class contract → [3/7] preflight → [4/7] consent
    → [5/7] install + provenance → [6/7] wire → [7/7] class invariants (module active)

Steps 1–4 are read-only, so anything rejected at validate, class, preflight or consent mutates nothing at all. Preflight sits before consent on purpose — nobody should be asked to agree to an install that cannot happen. Step 5 is the first mutation, and from there every failure unwinds through the same removal path remove uses, so a module that fails its own class test leaves a byte-identical tree. Wiring precedes invariants deliberately: the contracts assert behaviour with the module active, so a check run against an unwired module would prove nothing.

Step 5 is called provenance, not digest-verify, and the naming is load-bearing. A digest is verified on exactly one path: a local module, whose artifact ships in this repo and is hashed here against its pin. An upstream module is fetched from a package index and hmd hashes nothing — it records the pin and asks the installer whether the payload arrived. The receipt reads verified: false in every upstream state, and nothing re-checks that pin later: hmd modules verify re-runs the class invariants and reads no digest at all. Printing digest-verify over that would assert a check that never ran.

Consent leaves a receipt

An install that required consent records how it was granted — granted_via is one of interactive, --yes, or manifest-waiver — alongside the exact consent_text shown and its consent_text_sha256. Consent required with a non-TTY stdin is refused, not prompted and not defaulted to yes; --yes is the operator saying so on purpose.

A module in the default set reaches machines the author will never meet, so default_included: true is refused at validate time if any class it claims requires consent and the manifest ships no consent_text. A disclosure that only materialises at a prompt somebody may never be shown is not a disclosure.

Opting out

Two surfaces read opt-out signals, and they do not overlap — each verb honours the signals of the surface it belongs to:

Signal Read by Effect
HEIMDALL_NO_MODULES=1 bin/heimdall-autoupdate no module is acquired automatically, at all
~/.heimdall/modules-optout bin/heimdall-autoupdate one module name per line; # starts a comment
hmd modules optout <name> bin/heimdall-modules persisted; repair, defer and pending all refuse to act
HMD_MODULE_OPTOUT=a,b bin/heimdall-modules the same, for one invocation

install.sh acquires no modules at all — the installer has no module code path, so there is no installer flag to suppress and none is offered. Automatic acquisition lives in the background updater, which is why HEIMDALL_NO_MODULES=1 bites there. The consequence, stated plainly: every one of these signals governs acquisition that happens on your behalf. HEIMDALL_NO_MODULES=1 suppresses automatic acquisition and does not block an explicit hmd modules add <name> — an operator typing the command themselves is not what an opt-out for unattended installs is trying to stop.

Headroom — the one shipped module, and its honest limits

modules/headroom/manifest.json pins Headroom (Apache-2.0), a local context-compression proxy. Depend, don't clone — none of its source is vendored here. The manifest is the single source of truth for the pin and its artifact digest; this page deliberately does not restate the version, because a second hand-maintained copy of a pin is how pins drift.

It is default_included, and its consent question is waived on the module itself. modules/_classes/traffic-proxy.json still reads consent_required: true, so every other traffic-proxy module hmd ever ships still asks; the waiver's blast radius is this one module. Waived is the question, never the disclosure — the consent text still prints, both declared classes still run their invariants with the module active, and hmd modules remove headroom still returns the tree byte-identically.

Being in the default set is a distribution fact, not a claim that it helps, and not an unattended install:

  • The background updater will not install it for you. Acquisition reads the class contract, and traffic-proxy requires consent — so heimdall-autoupdate names the module, states the class, and hands you hmd modules add headroom rather than acquiring it. Until you run that command, heimdall-autoupdate status reports it absent with the reason and hmd modules status headroom reports NOT ATTEMPTED.
  • It is the one place hmd stops being near-stdlib. The fetch is uv tool install --python 3.13 "headroom-ai[all]==<pin>", which pulls an ML stack — Rust wheels, an ONNX runtime, HuggingFace tokenizers. Install size and time are materially larger than hmd's own.
  • A failed Headroom install does not fail hmd install. hmd works; the module reports ABSENT with the blocker and the remedy named, and rolls back through the removal path. It is never silently assumed present.
  • The storage-codec half does not engage via the documented install. uv tool install lands the package in an isolated per-tool venv that hmd's python3 cannot import, so the memory codec stays on its plain backend on every machine that installs it the sanctioned way. The manifest records that as a measurement rather than letting a green add imply otherwise. Treat compression on the storage seam as a contract the seam honours if a backend ever arrives — not as a description of any running machine.
  • The proxy half is declared, not applied — hmd points no traffic at it. bin/heimdall-wrap contains no reference to the module, so installing Headroom leaves your generation traffic going exactly where it went before. The wrap-chain wire records the chain hmd would offer; wiring it up is yours to do, deliberately, by pointing your tool at the proxy. hmd measures that wire at every add and prints RECORDED, not routed while it stays unapplied, and a wire kind with no handler behind it is refused outright rather than recorded — an install may not imply a capability the code cannot deliver (test/wire-kind-dispatch.test.sh).
  • Gates read raw. Generation traffic may traverse a proxy once you route it through one; judgment traffic may not follow. Every verdict-producing execution runs through hmd_gate_exec, which unsets ANTHROPIC_BASE_URL, the HTTP/HTTPS/ALL/NO_PROXY pairs and Headroom's own HEADROOM_* namespace before pinning the endpoint to the real provider. A judge reading compressed context emits confident false greens, which is the failure this whole project exists to prevent.

Full manifest schema, class-contract details and the lifecycle rationale: modules/README.md.


Viral statusline — watchman, team wall, gate animation

Heimdall's status bar is a full-width, four-row watchman HUD. It renders entirely shell-side (zero model, zero context cost) and reads Claude Code's statusLine JSON on stdin. Three surfaces, by how far they spread:

Your sigil — the identity hook. Every Heimdall identity (HAID) gets a unique, deterministic pixel watchman: same identity, same sigil, forever. It anchors the left of the line, prints big on the install card, and shares as a postable block:

python3 sentinels/hmd-sigil.py --seed $HMD_HAID --size large   # share/banner render
bash hooks/hmd-banner.sh --share                               # postable "my watchman" card

The seed is your HAID by default — automatic, stable, no PII in the art. Works solo on day one, before any teammate shows up.

The team watch wall — the headline, and the moat. When teammates also run hmd in the same repo, the bottom row becomes a live wall of their watchmen and what each agent is doing — gate state colored in, a teammate's cell flashing red the instant their gate denies. Nobody else can render this; it needs Heimdall's coordination substrate. The wall is empty until your team joins, so the feature recruits your team for you.

Presence is opt-in per repo — each running hmd heartbeats <repo>/.heimdall/team/<haid>.json (TTL ~30s; a stale file means the agent left). Names live in the repo's team dir and never leave it; default off for non-team repos. The watchman watches your gates, not your team. At squad scale the wall caps at the ~6 most-recently-active teammates plus a +N more tail so a wide terminal never wraps.

The deny flash — the clip. When a gate blocks, hmd-gate-anim.sh redraws the big watchman inline: a scanning pulse settling to a green sparkle on pass, or three red beats and ✗ BIFRÖST CLOSED on deny. TTY-only — in CI or a pipe it collapses to one clean final frame so logs stay readable.

bash sentinels/hmd-gate-anim.sh deny "oracle/falsify" $HMD_HAID

Wiring (settings.json):

{
  "statusLine":         {"type":"command","command":"bash ${CLAUDE_PLUGIN_ROOT}/hooks/statusline.sh"},
  "subagentStatusLine": {"type":"command","command":"bash ${CLAUDE_PLUGIN_ROOT}/sentinels/hmd-subagent-statusline.sh"}
}

install.sh wires this for you — it registers both entries into your ~/.claude/settings.json (honoring $CLAUDE_CONFIG_DIR) using the absolute installed path, idempotently and without clobbering a statusLine you set yourself. The ${CLAUDE_PLUGIN_ROOT} form above is the plugin-hook spelling; a user-level statusLine resolves no such variable, so the installer fills in the resolved absolute path — which is why the HUD now reaches every dev, not just whoever hand-wired it in dev setup.

hooks/statusline.sh drives the full-width watchman and falls back to the legacy single line if python3 is missing — it never errors, never blocks. Already a ccstatusline (9.2k★) user? Keep your line and drop the watchman in as a Custom Command widget:

python3 sentinels/hmd-statusline.py --widget   # just the watchman + verdict segment

The sigil ships solo-first (viral-cheap, no team required); the watch wall is the team-gated headline that lights up once presence is wired into your gate hooks.


Running on your own work

cd /path/to/your/project
heimdall --auto "build a real-time dashboard with auth and charts"

--auto runs a background safety classifier that blocks prompt injection and risky escalation. It is the default. --dangerously-skip-permissions exists but is not the default — only use it in a throwaway sandbox.


Failures visible on purpose

Live flagship status: evals/flagship/STATUS.md — the ❌ rows are kept in view. The corpus dip log and golden provenance are at evals/corpus/CORPUS-STATUS.md and evals/oracles/emulator-gb/fixtures/golden/VERIFICATION.md.

A verification system that can't show you its own failures can't be trusted with yours.


Contributing

  • Stack packs (skills/stacks/) — teach Heimdall a framework's conventions and build commands.
  • Oracle packs (evals/oracles/) — add a falsifiable external gate for a new domain.

See CHANGELOG.md for release history.


License

MIT

Self-maintenance (auto-update + self-heal)

hmd keeps itself and its host current, in the background, on session start — both are throttled (~24h), detached (never block the session), idempotent, and opt-out:

  • Plugin auto-update (bin/heimdall-autoupdate): checks the installed version vs the latest GitHub release; if newer, re-runs the latest installer in the background (takes effect next launch; never hot-swaps the running session). Off: HEIMDALL_NO_AUTOUPDATE=1 or ~/.heimdall/no-autoupdate.
  • Claude Code self-heal (bin/heimdall-cc-selfheal): on a NATIVE Claude Code install, auto-repairs the "✘ Auto-update failed" class — a stale npm-global @anthropic-ai/claude-code conflicting with the native updater. It removes ONLY that conflicting package, ensures autoUpdates:true, and re-runs claude update. Never touches an npm/brew-managed install, never uninstalls anything else, never touches credentials. Off: HEIMDALL_NO_SELFHEAL=1 or ~/.heimdall/no-selfheal. Inspect: heimdall-cc-selfheal status.
  • Default module reconciliation (same updater): compares the installed modules against the default set. A module whose class requires consent is never acquired here — it is named, with the hmd modules add command to run. Off: HEIMDALL_NO_MODULES=1 or ~/.heimdall/modules-optout. Inspect: heimdall-autoupdate status.

About

Verification gates for AI coding agents — nothing ships unproven. Falsifiable oracle gates, team wall, receipts. Works with Claude Code today; Cursor, Codex, Gemini CLI & every git repo next.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages