Multi-tenant · one control plane, many teams

Your keys. Your repos. Your team.

The cloud maintainer is multi-tenant by construction. Every team brings its own Claude subscription, its own GitHub App install, and its own Secret Manager partition — and team_id is derived server-side from a signed request, so no team can ever act as another. Isolation isn't a promise here; it's an oracle that kills 6/6 attacks.

Bring your own credentials

Nothing shared. Three things you own.

There is no shared Claude key and no shared repo token. The bot only ever holds what you hand it, in a partition keyed to your team.

Your Claude subscription

Run claude setup-token, then rr connect POSTs the credential write-only into your team's Secret Manager partition. The maintainer fixes issues with your tokens — you pay your own usage, no shared key.

write-only · never read back or logged

Your GitHub App install

Install the App on only the repos you choose. rr connect registers the installation id; the bot's token is scoped so it can push only to heimdall/* branches — it cannot touch main or branch protection.

heimdall/* only · never merges

Your Secret Manager partition

Your team_id is derived server-side from your Ed25519 signature — you never send it. Your cred lives in a secret named for your team, injected per-execution, and there is no cross-team enumeration path.

team_id server-derived · INV-1
The enroll token

How a teammate joins.

The enroll token is a one-time bootstrap credential, not a signing key. Your admin hands it out over a trusted channel; a teammate turns it into their own signed identity in three commands.

1

Point rr at the control plane

$rr setup --mode control-plane --endpoint <endpoint> --enroll-token <enroll-token>

The endpoint + enroll token come from your admin. The token bootstraps enrollment and is then spent — it never becomes your key.

2

Mint a signing identity

$heimdall-presence beat

Generates your local Ed25519 keypair and enrolls it. Your team membership is now bound to your public key — the server derives team_id from your signature on every request.

3

Connect your own credentials

$rr connect --gh-app-installation-id <id> --repo <owner/repo>

Registers your Claude credential and GitHub App install for the team. Now rr "<task>" from any terminal reaches the maintainer.

What admins control

The dials, and their defaults.

The public surface is rate-, budget-, and registry-capped by construction — so opening enrollment is a knob, not a risk.

Enrollment gate
Issue an enroll token out-of-band, or flip HEIMDALL_ENROLL_OPEN for tokenless self-serve — the caps below stay load-bearing either way.
Registry & membership caps
A hard ceiling on total enrolled keys (HEIMDALL_ENROLL_MAX_KEYS, default 1000) and per-team members (HEIMDALL_TEAM_MAX_MEMBERS).
Per-team token & daily-spend budget
Each team's spend is bounded by HEIMDALL_RR_TEAM_BUDGET_TOKENS (default 600,000) and a per-team daily spend cap — one team can't burn the plane.
Rate limits
/rr-task is capped per-IP (default 60) and per-team (default 30); per-team dispatch drain is bounded per window.
Least-privilege split
Two services: the gated worker holds the secrets; the public surface runs least-priv and provably cannot dispatch or read a cred.
Replay & nonce gate
Every signed request carries a {nonce, ts} — a captured request cannot be replayed, and a post-signature tamper fails verification.
Presence · private by construction

You are never on a wall you didn't choose.

The presence roster is private by the team secret — the secret is the boundary, and only its holders can see each other. On your first run a solo team is auto-minted for you, so presence is never repo-public by default. And it's opt-out to the bone.

Per-repo & global invisibility
heimdall-presence off hides you in this repo; off --global is a machine-wide kill switch. OFF means invisible, not blind — you still see the wall; the team can't see you.
Retire-beat — vanish promptly
Going invisible fires one signed retire-beat: the server writes a tombstone and drops you from teammates' walls on the next read — no waiting for a TTL to expire.
--no-files privacy sub-toggle
heimdall-presence on --no-files keeps you present but sends file:null — handle and verdict only. Presence never carries file contents or code; --no-files withholds the filename too.
The team secret is the boundary
A private presence team is defined by a secret only teammates hold. No secret, no roster — and it rides a request header, never the URL. Presence is never repo-public by default.
The statusline says so
When you're invisible, the watchman HUD shows an invisible-hint — you always know your own presence state at a glance, no surprises.
Solo by default
First run auto-mints a solo team so you're a team of one until you deliberately share a secret. Nobody is enrolled onto a shared wall without opting in.
Live

See your team on the wall.

Beyond the maintainer, Heimdall keeps a private presence roster — mint a team secret in your browser, and watch who's online, their HAID, and the verdict the watchman just gave them. Only holders of the secret can see each other.

Generate your team secret

A strong random team secret — it defines a private presence team. Same shape as the server's secrets.token_urlsafe(32) — 43 base64url characters. Generated locally; it is never transmitted from this page.

click generate — a fresh team secret appears here

This is your team secret. It is what teammates use to join the presence team and to see each other — anyone holding it can read the team's private roster (HAID + activity). Share it only with teammates, over a trusted channel. Treat it like a password.

Teammate join — one command
curl -fsSL https://raw.githubusercontent.com/randomittin/heimdall/v2.0.16/install.sh \ | HEIMDALL_CP_URL='https://heimdall-cp-public-eqfrs7sfuq-uc.a.run.app' \ HEIMDALL_TEAM_SECRET='<generate a team secret above>' bash

Your team members

Paste your team secret and project id to see your teammates — who's online right now, their HAID, and their latest verdict. Private: only holders of the team secret can see each other. The secret is sent in a request header, never in the URL. Refreshes every 15s.

Saved to localStorage — it persists across visits and is readable by any script on this page (XSS-exfiltratable). Don't use on a shared or untrusted machine.
enter a project + your team secret, then hit View team