Security & install transparency

Read it before you run it.

Installing a tool is the moment you decide whether to trust it. So: exactly what the installer does, what it writes, what leaves your machine, how releases are signed, and how to remove all of it. Where something is a limit rather than a guarantee, it says so.

Install & trust

Install runhmd. Read it before you run it.

One line installs the CLI that gives you hmd, rr, and the gates. Pinned to a release tag, so what you read in the source is exactly what runs. The gates run local; the cloud maintainer only ever holds your own credentials, in your own partition. Remove it with hmd uninstall.

Free while we prove it — no plans, no cards. You pay nothing until runhmd has earned it.

BYO credentials Signed requests No sudo Pinned to a tag MIT
$ curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.4.9/install.sh -o heimdall-install.sh \ && echo "98ecc11bd6af7412692a2826d6c35be174a737b748010fce7d98a6b95ecf4934 heimdall-install.sh" | shasum -a 256 -c - \ && bash heimdall-install.sh
$ # the && chain is load-bearing: if the bytes do not match the digest,
$ # shasum -c prints FAILED and nothing runs. On Linux: sha256sum -c -
$ less heimdall-install.sh # short, function-wrapped, no sudo, no prompts
$ hmd demo # a guided first run: deny → fix → pass
What the installer writes

Everything it touches outside the repo.

Measured from a real run against a throwaway home directory, as documented in the project README. No sudo. Idempotent: re-run to upgrade. hmd uninstall reverses all of it.

~/.heimdall/

A full clone of the repository: the installed checkout hmd runs from.

~/.local/bin/hmd

Together with ~/.local/bin/heimdall: symlinks into ~/.heimdall/bin/heimdall.

~/.claude/settings.json

Adds statusLine and subagentStatusLine, and registers the plugin under enabledPlugins and extraKnownMarketplaces. Honors $CLAUDE_CONFIG_DIR. Never overwrites a statusLine you set yourself.

~/.zshrc · ~/.bashrc · ~/.profile

One appended export PATH=… line for ~/.local/bin, only when it is not already on your PATH.

com.heimdall.dream.plist

macOS only. A nightly 03:00 LaunchAgent in ~/Library/LaunchAgents/ that runs the overnight sweep with no session open; it survives logout and reboot. Opt out with HEIMDALL_NO_DREAM_SCHEDULE=1.

~/.heimdall/team.json

An auto-minted solo team secret, written by the post-install health check on every run. It is a bearer credential: treat it like one.

The README's Install section goes further: the optional prompt-reading proxy module, what a pasted team invite writes inside a repo, and the network posture.

Three ways to install

Pick the risk you are willing to take.

Each is labelled with what it actually does to your machine. The shortest command is not the safest.

The one-liner

Downloads install.sh, checks its sha256, then runs it as you. Highest risk of the three: a script from the network, running with your privileges. The digest check is what stands between you and whatever those bytes are.

npx runheimdall

The same install.sh, fetched over https and checked against a digest baked in at publish time, then handed to bash. Same risk as the one-liner; the wrapper is thin.

Docker sandbox

Dockerfile.install runs the same install.sh, copied from your own clone — nothing fetched, no digest to trust — inside a container. Lowest risk, with real caveats: read the README before relying on it.

What leaves your machine

Scoped, not sloganed.

Some things do leave the machine, so this page does not say “no telemetry”. None of it is your source code, and each has an off switch. The exact field list and endpoint for each are in the FAQ.

Presence heartbeat

On by default, per repo. A signed POST of {haid, handle, project, verdict, file, ts, activity_ts} to the control plane: never file contents. Off: heimdall-presence off for this repo, off --global for every repo; --no-files sends no file name.

rr, only when you run it

rr connect sends your Claude credential write-only and your GitHub App installation id. rr "<task>" sends the task text you typed: the one path where real content leaves, because it is the job you asked for.

Auto-update

One unauthenticated GET to the public GitHub Releases API to compare your installed tag with the latest: no credentials, no code. Off: HEIMDALL_NO_AUTOUPDATE=1 or ~/.heimdall/no-autoupdate.

Local only

Local telemetry writes to a local NDJSON file and does not use the network; the corpus spool is a local send-queue. Off: HEIMDALL_TELEMETRY=off, hmd telemetry off, hmd telemetry purge.

Release signing

Updates are fail-closed.

Every release signs install.sh with minisign (Ed25519). The auto-updater applies a release only if the signature verifies against the public key already on your machine; unsigned, tampered or wrong-key releases are refused, loudly. Signing authenticates the channel, not the author's intent: a leaked secret key, or a malicious commit legitimately signed and shipped, is not stopped by it.

SIGNING.md
The merge boundary

runhmd never merges or deploys for you.

That is by design, not a limitation. The cloud maintainer opens PRs on a heimdall/* branch as a scoped GitHub App: Contents, Issues and Pull requests only — no Administration, no Actions or Workflows, no merge capability. It cannot push to main. A human reads the evidence and presses merge.

See what lands on your repo
The phone app

A supervision surface, secured like one.

As of October 2026 the app and hmd's side of it are built on hmd's main branch and are not in a tagged release yet. This is the model they implement.

End-to-end encrypted

Phone and laptop agree a key with X25519 and HKDF-SHA256 and seal frames with ChaCha20-Poly1305. The relay in the middle forwards ciphertext and never holds a session key. It also sends three unencrypted control frames it originates itself (device bound, session ended, keepalive); none carries session content.

Pairing

One QR is one session, claimed with a single-use code that expires in about a minute. The phone's device token and keypair live in the OS secure store. Treat the QR as a bearer credential for that short window.

What the phone can do

Read status, chat, answer a question, and deny a pending approval (opt-in on the laptop). No shell, no remote allow, no merge.

Notifications

Opt-in per session, and sent by your own hmd — not by the relay — as a short, scrubbed summary through Expo's push service. Nothing in a notification carries authority: a Deny tap opens the app and sends the ordinary sealed deny.

Known limits

One live phone per session. A stolen device token can evict the real phone: revoke with hmd app disconnect and re-pair. Revoking ends the session for every device; there is no per-device revoke yet. Push works in relay mode only.

Report a vulnerability

Privately, to security@runheimdall.dev.

Or via a GitHub Security Advisory. Targets: acknowledgement within 72 hours, triage within 7 days, a 90-day coordinated-disclosure window, and a fix before the advisory. These are one maintainer's targets, not a staffed rotation or a contractual SLA. Safe harbor applies to good-faith research through those channels. There is no bug bounty.

SECURITY.md