Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Reference Deployment: Two Boxes, Chat-Only

This guide is the end-to-end worked example of the deployment rule's container branch: every personal agent runs server-side, in a container that mounts only that person's compiled vault. It fits teams where employees are non-technical and chat is the entire product — they message their own agent on WhatsApp or Telegram and never see git, files, or the word "vault."
In plain English: every employee texts their own private AI assistant. The assistant keeps their notes in a private notebook only they can see. A separate "librarian" machine holds the company's shared knowledge: when someone chooses to share something, a human approves it, and it then appears in everyone's notebook. Nothing is shared by accident, because each assistant is physically given only its own person's notebook — there is no setting to get wrong.

The shape

Two small hosts joined by one private git link. Several transports work — pick by situation; with a private network or a tailnet, there are no public ports anywhere.
Employees ── WhatsApp / Telegram ──┐ (chat is the only user surface) ▼ ┌─ AGENTS BOX — one VPS, Docker ─────────────────────────────┐ │ ┌─ container: alice ─┐ ┌─ container: bob ─┐ ┌─ carol… ─┐ │ │ │ hermes gateway │ │ hermes gateway │ │ │ │ │ │ + agent │ │ + agent │ │ │ │ │ │ /vault = alice/ │ │ /vault = bob/ │ │ │ │ │ │ .brain index + MCP │ │ .brain index+MCP │ │ │ │ │ └────────────────────┘ └──────────────────┘ └──────────┘ │ └───────────────▲─────────────────▲──────────────────────────┘ │ git over SSH — the only link between the boxes, │ per-person deploy keys, one key → one repo ▼ ┌─ BRAIN BOX — master host, AGENT-FREE ──────────────────────┐ │ master vault ─→ brain cycle (cron) ─→ compiled/<person> │ │ promotion queue · brain doctor · localhost dashboard │ └─────────────────────────────────────────────────────────────┘
Brain box — plumbing
git + cron + the brain CLI, nothing else. No LLM ever runs here, so nothing here can be talked into leaking. 2 GB of RAM is plenty.
Agents box — one container per person
Each container holds a complete personal world: hermes gateway + agent, its own bot token and sessions, and a clone of only that person's compiled vault — which is also the agent's only durable memory. ≈300 MB each; LLM compute happens at the API, not here.
Why two boxes instead of one? The agents box runs LLM-driven processes with chat-reachable attack surface; the brain box holds every person's private space. Separate hardware makes the boundary structural — there is no compose line that can drift into exposing the master. The entire cost of that guarantee is one extra $5–10/month VPS.

How the sync works

An agent never addresses the master. It addresses its own vault's git remote, which happens to live on the brain box:
  1. The agent edits its own spaces in its local clone and commits — captures go to its Inbox/, share drafts to People/<me>/Promotions/. This works even when the brain box is down.
  2. git push — the entire client interaction. Pushes are accepted as-is; validation is asynchronous.
  3. brain cycle (cron, every few minutes) validates every pushed path server-side, applies what's legal, reverts the rest on the next compile (fail closed); provisions any self-service client requests and routes share requests (queuing shares, auto-applying revokes); sweeps promotion drafts into the pending queue; recompiles every vault; and finally routes actionable brain doctor findings to each responsible person as a rolling Inbox digest (brain triage) their agent fixes through the same gated paths.
  4. git pull — the verdict returns: rejected edits vanish, approved shared knowledge arrives.
Never in the hot path. Agents hold zero credentials to the brain system — the only secret in a container is one SSH key that reaches one repo. And the brain box needs no availability SLA: if it's down, agents keep chatting and capturing against local clones; commits queue in git history and drain on recovery.

Choosing the transport

The link between the boxes carries exactly one thing: git over SSH, one deploy key per person, each key locked to its one repo by a forced command. Anything that can carry restricted SSH works, so choose by situation — including the situation where the agents and the brain run at different providers and you can't install a VPN on one side.
TransportBrain box exposureChoose it when
Tailscale (or any WireGuard-based mesh)No public portsYou can install it on both hosts. Nice bonus: the same network carries your admin SSH and the dashboard tunnel.
Provider private networkNo public portsBoth hosts sit at the same provider and region — the private LAN is free and there's nothing extra to install or trust.
Public SSH + deploy keysOne public port (22), keys onlyThe hosts can't share a private network — agents on a managed platform, a different provider, or anywhere you can't install a VPN. The forced-command restriction does the same work either way.
GitHub / Gitea as the hubNone — the brain box only dials outYou'd rather not operate SSH at all. One private repo per person, per-repo deploy keys; both boxes push and pull against the forge. Costs: company knowledge rests at the forge, and sync becomes polling-shaped.
These mix freely within one deployment: containers that share a private network with the brain box can use it, while an agent running somewhere else uses public SSH or the forge.
The security properties don't depend on the transport. One key still opens exactly one repo, the master still runs no agents, and validation is still asynchronous and fail-closed. The transport only decides what the network between the boxes looks like.

Sizing

HeadcountAgents box RAM
~54 GB
~158 GB
~3016 GB
The driver is one idle hermes gateway process per person (≈300–400 MB, near-zero idle CPU). Containers themselves add roughly nothing — they share the base image and have no per-container VM. If headcount outgrows one box, split the compose file across two; nothing about the architecture changes.

Disk

Disk does not scale with the master vault. It scales with readers × shared content, and that surprises people.
Every person gets a real copy of every space they can read, and every vault is its own git repo. A shared note that 20 people can read exists 20 times, each inside a separate object store.
Measured, on a master holding 1 MB of content — 60 shared notes plus 20 private notes each for 10 people:
sizevs. master content
master content1.05 MB1×
all 10 compiled vaults9.2 MB8.8×
…after --index13.8 MB13×
Per vault that is 876 KB, and the split is the part worth knowing:
per vault
shared content (Company/)252 KB
own private content88 KB
.git500 KB — 57%
.brain/ index (with --index)+432 KB
Git history dominates, not the notes. Each vault accumulates one commit per compile that changes anything, forever, in its own repo. The content is small and compresses well; the history behind it is what grows.
Three consequences for provisioning:
  • Estimate from readers per space, not from master size. Moving a large space from one team to everyone multiplies its cost by headcount.
  • Budget roughly 10–15× master content for a fleet of ten, and scale that with headcount. Vaults are independent, so it is linear.
  • Only .brain/ is disposable. It is rebuilt from the vault by the next brain index, is never backed up, and costs nothing to delete.
Do not rm -rf a compiled vault to reclaim space. It is not a derived artifact you can regenerate — it is the git remote that person's clone pushes to. Deleting it throws away any commits pushed since the last cycle (write-back reads them from there), and the recompiled replacement is a new repo with unrelated history, so every existing clone breaks and has to be re-cloned. If a vault genuinely must be rebuilt, run a cycle first so pending edits reach master, then re-clone on the person's side.

Visual surfaces (optional, phased)

Chat stays the primary product, but each container can expose visual surfaces. Sessions live in the container's shared state.db, so a conversation started on WhatsApp continues in the browser or desktop app — same brain, different doors.
SurfaceWhat the employee getsSetupRisk surface
Chat (default)Capture, ask, share — the full agentPairing code, onceNothing new
Brain dashboardRead-only view of their own knowledge: what's new, search, the graph, a capture boxNone — a URLLowest: read-only by construction
Hermes web UIBrowser chat with streaming, tool activity, sessions (hermes serve)None — a URLFull agent control — gate it well
Hermes DesktopNative app attached to their container: chat, files, voiceLaptop install + sign-inSame as web UI
Add the brain dashboard first — it answers "what does my assistant actually know?" without handing anyone a control panel. Hermes Desktop attaches to a remote backend natively: the container runs hermes serve, and the user signs in under Settings → Gateway.
Exposing hermes serve means remote control of that agent. Credentials must be per-person, per-container — never shared — and the auth gate must map each identity to exactly one container. Pilot: keep visual surfaces reachable only over your private network (tailnet or equivalent) or an SSH tunnel. Wider rollout: one SSO reverse proxy (OIDC / Nous OAuth) with per-person routes. The brain dashboard has no auth of its own — off-loopback it must sit behind the authenticated proxy.

The life of a note

The whole system, traced through one piece of knowledge — twice.
Capturing something private (the everyday case):
  1. 9:02 am — Alice's phone. She voice-memos her bot: "the client agreed to the March timeline."
  2. Seconds later — her container. Her agent transcribes it, writes a note into her Inbox in its local clone, commits, and replies "Saved." Already durable — this works even if the brain box is down.
  3. Within ~5 minutes — both boxes. Her container pushes; the next brain cycle validates and applies it to the master and recompiles her vault.
  4. Any time after. "What did we agree with the client?" — answered from the local index, instantly. Visible to Alice only, at every step.
Sharing with the team (the human-gated case):
  1. 2:14 pm. Alice: "share the March timeline with the team."
  2. Seconds later. Her agent drafts a promotion — nothing is shared yet; a draft is still hers.
  3. Within ~5 minutes. The next cycle sweeps the draft into the master's pending queue. Now visible to Alice + the approver. Her own Shares.md note (generated into her vault) now lists it as awaiting approval — and will show the decision, including a rejection reason, once made.
  4. When a human decides. The approver reviews and approves — the gate no agent can open.
  5. Next cycle. The note materializes into the Team space; every affected vault recompiles.
  6. Within ~5 minutes. Bob's container pulls; his agent can now answer with it. Alice's agent tells her it went live. Now visible to the whole team.
The two-sentence mental model for employees: anything you tell your assistant is yours alone, instantly. If you ask it to share something, a person approves it first — you'll get a message when it's live. Private knowledge moves at machine speed (~one cycle); shared knowledge costs a few machine-minutes plus one human decision — and that latency is the feature.

The runbook

Phases 1–3 happen once and take an afternoon. Phase 4 is ~10 minutes per new employee. Phase 5 is the steady state.
  1. Provision — once
    Two VPSes — same provider and region if you can, so the private LAN is an option. Ubuntu LTS, key-only SSH, unattended-upgrades. Then connect them with whichever transport fits and open no other inbound ports. (One documented exception exists: opting into webhook intake, below, adds at most a single TLS-fronted public port on the brain box — and zero when the receiver sits behind an edge-terminated tunnel; see the two fronting patterns below.)
    # e.g. with Tailscale, on both boxes: curl -fsSL https://tailscale.com/install.sh | sh tailscale up # names: brain-box, agents-box # or with a provider private network / public SSH: no install — # just make "brain-box" resolve on the agents box: echo "10.0.0.2 brain-box" >> /etc/hosts
  2. Brain box — once
    Install brainkit, scaffold the master, and let cron be the whole orchestration layer (see Getting Started for the org.yaml / spaces.yaml details):
    uv tool install git+https://github.com/joedanz/brainkit # brainkit is not on PyPI brain init /srv/brain/master --company "Acme Co" brain compile --master /srv/brain/master --out /srv/brain/compiled brain doctor --master /srv/brain/master --out /srv/brain/compiled adduser --disabled-password brain-sync # per-person keys, one repo each
    # crontab — $CYCLE_HC / $DOCTOR_HC are healthchecks.io-style check URLs # flock -n -E 75: one cycle at a time; a tick that finds the lock held exits # 75 and pings neither endpoint (see below) */5 * * * * flock -n -E 75 /var/lock/brain-cycle brain cycle \ --master /srv/brain/master --out /srv/brain/compiled --index --json \ >> /var/log/brain/cycle.jsonl 2>&1; rc=$?; [ $rc = 75 ] || \ { [ $rc = 0 ] && curl -fsS -m 10 $CYCLE_HC >/dev/null \ || curl -fsS -m 10 $CYCLE_HC/fail >/dev/null; } 17 3 * * * brain doctor --master /srv/brain/master \ --out /srv/brain/compiled >> /var/log/brain/doctor.jsonl 2>&1 \ && curl -fsS -m 10 $DOCTOR_HC >/dev/null || curl -fsS -m 10 $DOCTOR_HC/fail >/dev/null 7 * * * * /usr/local/sbin/backup-master.sh \ >> /var/log/backup-master.log 2>&1 # encrypted offsite backup
    A nonzero cycle means a held edit or failed write-back, a tampering signal, or a vault that failed to compile (the output names which); a nonzero doctor means an integrity error. Ping the check on success and its /fail endpoint on failure (a dead-man's switch), rather than only pinging on failure — a box that dies or a cron that stops firing can't report its own failure, but a missed success ping still alerts. Give each job its own check, with the period matched to that job's cadence (cycle: 5 minutes; doctor: 1 day; hourly backup: 1 hour) plus a small grace. The 5-minute cycle check doubles as box-liveness monitoring.
    brain cycle takes no lock of its own, so flock supplies one: two runs at once would both write master and rebuild the same vaults through the same swap directories. -E 75 is what keeps the lock from lying to your monitoring. Without it a tick that finds the lock held exits 1, which the dead-man's switch reads as a failed cycle — so a merely-busy box pages someone. With it, a skipped tick is distinguishable and pings neither endpoint: an occasional skip stays silent, while a cycle that wedges for good stops producing success pings and trips the check on its own. If skips become routine, the cycle has outgrown its interval — lengthen it, or find out what got slow.
    Two companion scripts extend the pattern to things without their own cron: brain-liveness.sh watches this box's long-running services (every enabled brain-* systemd unit — dashboards, webhook receiver — plus $EXTRA_UNITS such as a fronting proxy), and agents-liveness.sh watches the agent-container fleet. Each pings one check per box: success while everything runs, /fail naming the dead units or containers otherwise.
    Recommended — an embedding provider, so --index builds semantic (not just keyword) indexes. All four BRAIN_EMBED_* variables must reach the cycle's environment — and any long-running brain dashboard services, which resolve the provider once at startup. A single env file covers both (systemd units add EnvironmentFile= pointing at it):
    # ~/.config/brain/embed.env — KEY=value lines, 0600 BRAIN_EMBED_BASE_URL=https://ai-gateway.vercel.sh/v1 BRAIN_EMBED_API_KEY=... BRAIN_EMBED_MODEL=voyage/voyage-4 BRAIN_EMBED_DIM=1024 # crontab: source it before the cycle line (inside the flock, as above) */5 * * * * set -a; . $HOME/.config/brain/embed.env; set +a; \ flock -n -E 75 /var/lock/brain-cycle brain cycle ...
    Verify the dimension with one curl before committing to BRAIN_EMBED_DIM: some gateways ignore the dimensions parameter for some providers (the voyage models above always return their native 1024, whatever you ask for), and the value you configure must match what the endpoint actually returns.
    Backfilling an existing keyword-only index. Turning a provider on does not re-embed notes that are already indexed — brain cycle --index only touches changed files and has no full-rebuild flag. After enabling the provider, run a one-time brain index --vault <compiled/person> --full for each compiled vault (and inside each agent container against /vault); from then on the cycle keeps vectors current incrementally.
    Recommended — webhook intake. A brain that only receives what people remember to send it starves; letting services push — every Fathom meeting transcript, a Zapier or Composio trigger on email and chat — is usually the single highest-value intake. brain init scaffolds a commented _meta/webhook.yaml.example next to org.yaml; copy it to webhook.yaml, fill in your sources, and run brain webhook on this box as a supervised service with a TLS reverse proxy in front of its loopback port. Both halves ship ready to install in deploy/brain-box/: brain-webhook.service (systemd, secrets from an EnvironmentFile) and Caddyfile.webhook (TLS, exposes only /hook/* and /healthz).
    Fronting the receiver — two TLS patterns. Caddyfile.webhook assumes the box terminates its own TLS: DNS points at the box's public IP, Caddy binds 80/443 and manages certificates. That's pattern A. If the box instead sits behind edge-terminated TLS — a CDN in front, with an outbound-only tunnel connector (Cloudflare Tunnel or similar) delivering traffic to a loopback listener — that's pattern B, and the box keeps zero inbound ports.
    Inbound portsChoose it when
    A — Caddy terminates TLS (Caddyfile.webhook as shipped)One (443)The box has a public IP and DNS points straight at it. No third party in the intake path.
    B — TLS ends at a CDN edgeZero — the connector only dials outYou already front the box with a tunnel (say, for dashboards) and the hooks hostname can ride it. Trade: the CDN vendor becomes part of your intake path.
    Under pattern B, don't install Caddyfile.webhook — it would bind public ports and manage certificates the edge already owns. Route the hostname through what's already running instead:
    # tunnel ingress (e.g. cloudflared config.yml) — before the catch-all rule - hostname: hooks-team.example.com service: http://127.0.0.1:8080 # the existing loopback listener
    # inside the existing host-routed Caddy site block @hooks host hooks-team.example.com handle @hooks { reverse_proxy 127.0.0.1:8766 }
    Two edges worth knowing either way: never put basic_auth (or any browser-login gate) on /hook/* — sources authenticate themselves with HMAC signatures or bearer tokens, and a password prompt only breaks machine senders. And free universal certificates at most CDN edges cover a single subdomain level, so hooks-team.example.com gets a certificate where hooks.team.example.com needs a paid tier.
    Go in eyes open: outside providers can't reach your private network, so this is the one place the brain box gains a public HTTPS surface. The receiver authenticates every request before parsing a byte, every delivery lands through the same brain ingest primitive the cron loop already audits, and the failure drills below bound the worst case — but if your deployment genuinely has no push sources, skip it and keep zero inbound.
  3. Agents box — once
    Install Docker and build the hermes-brain image with deploy/agents-box/build-image.sh — based on the official nousresearch/hermes-agent image, with brainkit installed from the public repo. Use the script rather than a bare docker build: it resolves the ref to a commit first, so the image is built from an exact revision and tagged with it (see below). One compose stanza per person:
    services: agent-alice: image: hermes-brain:latest restart: unless-stopped command: gateway run environment: - BRAIN_PERSON=alice - BRAIN_GIT_REMOTE=brain-sync@brain-box:/srv/brain/compiled/alice # "brain-box" = the brain host on your chosen transport - TELEGRAM_BOT_TOKEN=${ALICE_BOT_TOKEN} - ANTHROPIC_API_KEY=${LLM_KEY} - BRAIN_EMBED_BASE_URL=${EMBED_URL} # optional: semantic search… - BRAIN_EMBED_API_KEY=${EMBED_API_KEY} # …all four, or containers - BRAIN_EMBED_MODEL=${EMBED_MODEL} # fall back to defaults - BRAIN_EMBED_DIM=${EMBED_DIM} volumes: - alice-state:/opt/data # hermes state: sessions, pairing, deploy key - alice-vault:/vault # her compiled vault clone
    Everything this image bakes in lives in that one directory — deploy/agents-box/ holds the Dockerfile, the compose file, the s6 scripts, and the company-brain-profile/ staged into each container. brainkit itself is not copied from the repo; it is installed from the public repo at build time.
    Each container's first boot installs that company-brain hermes profile (SOUL.md, terminal.cwd: /vault, the brain MCP server and skills — with hermes's built-in memory and its bundled llm-wiki skill disabled (skills.disabled: [llm-wiki]), so the vault is the agent's only durable memory and knowledge base and every "remember this" or distilled article lands in a synced file) and generates its own deploy key — the public half lands in docker logs, the private half never leaves the container. A supervised sync loop then clones $BRAIN_GIT_REMOTE, runs brain index, and keeps syncing: pull → index → push every five minutes.
    Rolling a new image re-syncs the profile. The container records which brainkit build it applied, so a rebuild propagates rather than landing silently. What re-syncs is narrow on purpose: skills/ is refreshed; SOUL.md only if it is untouched since the image wrote it, so an edit someone meant is preserved and the refusal logged; and config.yaml never, because hermes rewrites it at runtime and regenerating it would discard live settings including the model an operator chose. A SOUL.md that is missing rather than edited is restored on the next boot, so a partial restore heals itself.
    Email as a second channel (optional). Hermes ships a stock email channel — the same agent, reachable by mail. It slots in per person with zero brainkit-side changes: give the agent its own mailbox and add the EMAIL_* variables to that person's stanza. Email is just another door into the same container, so captures, attachments (vision / OCR), and article distilling all land in the same synced vault. Two deployment rules the hermes docs won't tell you: pin EMAIL_ALLOWED_USERS to the one employee's real address — mailbox-to-person pinning does the same job as the per-person bot token — and plan on real IMAP/SMTP mailboxes with app passwords on a domain you control. Forward-only services (Cloudflare Email Routing and kin) can't carry the channel, which must fetch and send.
    environment: # …plus, for the email channel (full variable list in the hermes docs): - EMAIL_ADDRESS=alice@bots.example.com - EMAIL_ALLOWED_USERS=alice@example.com
  4. Onboard an employee — ~10 minutes
    deploy/agents-box/add-agent.sh <name> prints the compose stanza and this exact checklist:
    1. Add them to org.yaml as a plain employee (no roles: — read-only on Company by default; the admin role stays on a separate curation identity). The next cycle compiles their vault. Set receive.denyCurrentBranch updateInstead on the compiled repo so it accepts the agent's pushes.
    2. Telegram: @BotFather → /newbot → copy the token (WhatsApp: link a number via the hermes WhatsApp adapter).
    3. Agents box: paste the compose stanza, docker compose up -d.
    4. docker logs agent-<name> shows the container's deploy key — append one authorized_keys line on the brain box (the brain-serve-repo forced command locks the key to that one repo). The clone lands within 30s.
    5. They message their bot; approve the one-time pairing code.
    6. Send them one line: "This is your assistant — tell it things, ask it things." That's their entire onboarding.
  5. Operate — minutes per week
    • Approvals: ssh brain-box brain promotions approve <id> --master /srv/brain/master --approver admin (admin is the curation identity from org.yaml); or review in the dashboard over an SSH tunnel and approve there — the dropdown offers whoever may approve that item. Promotions into a team's own space can also be cleared by that team's lead from their own vault, so not everything in the queue needs you — see Who approves.
    • Knowing there's anything to approve: the queue is pull — nothing tells you it has items. Schedule the admin queue nudge from this box's root crontab. It reports depth and age, splits the promotion count by who can clear it, and deliberately refuses to approve anything — company-wide publishing stays at the dashboard, where you see the live diff and the warning naming exactly who will be able to read the result. Run it after the per-person nudges so a lead's own decision has already cleared its item.
    • Watch two numbers: the cycle and doctor exit codes (already wired to your alert URL).
    • Backups, both halves: the brain box ships the master vault and its wiring offsite hourly with deploy/brain-box/backup-master.sh; on the agents box, deploy/agents-box/backup-agents.sh zips each container's state nightly (hermes backup inside the container, consistent even while the gateway runs) and backup-agents-offsite.sh ships the zips — plus the compose .env and live docker-compose.yml — off-box encrypted. Vault volumes need no backup: they re-clone from the brain box. The full picture, including why it's two buckets and how to restore each failure, is in Backups & Restore.
    • Offboarding: remove from org.yaml, delete their authorized_keys line, remove their container.

Knowing what is deployed

Two boxes upgraded on different days drift, and the drift is silent unless you can ask each one what it is running. brain --version is that question:
# brain box ssh brain-box sudo -u brain-sync brain --version # brain 0.2.0 (rev 69631f68e8c9) # any agent container ssh agents-box docker exec agent-alice brain --version # brain 0.2.0 (rev 69631f68e8c9) # the image itself, without starting anything ssh agents-box docker inspect --format \ '{{index .Config.Labels "org.opencontainers.image.revision"}}' hermes-brain:latest
The revision is what matters here, not the version. Neither box installs a release, and a version reads the same for every commit between two tags. So 0.2.0 on both boxes tells you nothing; rev 69631f68e8c9 on both tells you they match.
Both halves know their commit for the same reason. PEP 610 requires an installer to record the resolved commit of a VCS install, so uv tool install git+… on the brain box and the image's uv pip install git+… both write it to direct_url.json beside the package. Neither answer is stamped in by hand, so neither can drift from what is actually installed.
The image adds two conveniences on top: the commit as an OCI label, so you can read it without starting a container, and a hermes-brain:<short-sha> tag alongside :latest for a rollback target. build-image.sh resolves the ref to a commit before building, so the label names a revision rather than a branch.
A bare docker build still works; it installs whatever main points at, and the container can still say which commit that turned out to be.
Containers built from an older image — before brainkit was installed from git — report brain 0.1.2 with no revision. That is the honest answer: the information was never recorded. It resolves itself on the next rebuild.

Failure drills

ScenarioOutcome
Brain box downAgents keep chatting and capturing on local clones; commits drain on recovery. Users notice nothing.
Agents box downChat goes quiet; zero data lost. docker compose up -d restores everything from volumes.
Agent volumes lostFresh container, hermes import <zip> --force with the person's latest backup, then docker restart (re-fixes key file modes) — sessions, pairing, and deploy key all return; the vault re-clones itself.
Agents box compromisedRotate deploy keys, rebuild containers from the image — the master's history is intact and trustworthy.
Brain box lostrestic restore the offsite snapshot — vault, proxy/tunnel config, and the sync authorized_keys all return — reinstall brainkit, recompile. Agents reconnect without re-authorizing keys. See Backups & Restore.
Webhook secret leaked (if webhook intake is enabled)Worst case is note spam into the Inboxes that source routes to, capped at the source's rate limit (60/minute by default) — a webhook source can never read anything or write outside an Inbox, and past the cap deliveries get 429s while the receiver logs a warning. Rotate the secret_env value and restart the receiver; the old secret is dead immediately. Every accepted delivery is its own audited Brain Ingest commit, so cleanup is git revert.
Webhook endpoint probedUnauthenticated requests are refused before the payload is parsed (401/404), nothing is written, and oversized or compressed bodies are rejected outright. The receiver holds no read paths — there is no query to leak a note through.
The full plate-style version of this plan — diagrams, the transport menu, and the visual-layer wiring — is in Where the brain lives.