brainkit · Deployment Plate №1

Where the brain lives

The decided architecture: two boxes. Employees are non-technical and chat is their entire product — they message their own agent on WhatsApp or Telegram and never see git, files, or the word "vault." All agents run server-side, one container per person, on the agents box; the master lives alone on the brain box.

In plain English Every employee texts their own private AI assistant, the way they'd text a person. The assistant keeps their notes in a private notebook only they can see. A separate "librarian" computer 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.
EMPLOYEES — NON-TECHNICAL · CHAT IS THE ENTIRE PRODUCT Alice — WhatsApp messages her own bot Bob — Telegram messages his own bot Carol — Telegram messages her own bot 1 chat — the only user surface; a pairing code binds their phone to their bot Agents box — one VPS, Docker one hermes container per person · ≈300 MB each, idle CPU ≈ 0 · LLM compute happens at the API, not here CONTAINER — ALICE ONLY hermes gateway + agent her bot token · her sessions /vault — clone of alice/ .brain index · brain MCP 2 local reads sync loop: pull → index → push one deploy key → one repo CONTAINER — BOB ONLY hermes gateway + agent his bot token · his sessions /vault — clone of bob/ .brain index · brain MCP local reads sync loop: pull → index → push one deploy key → one repo CONTAINER — CAROL ONLY hermes gateway + agent her bot token · her sessions /vault — clone of carol/ .brain index · brain MCP local reads sync loop: pull → index → push one deploy key → one repo optional, round 2: one shared company bot — its vault is a Company-space-only view, so shared sessions can't leak anything private git over Tailscale / the provider's private network — the only link between the boxes · per-person deploy keys · no public ports (base setup) 3 BRAIN BOX — MASTER HOST · AGENT-FREE ZONE git + cron + the brain CLI — deterministic plumbing; no LLM ever runs here, so nothing here can be talked into leaking Master vault master/ — the one true store _meta/ org.yaml · spaces.yaml People / Teams / Company (the default shared space) promotion queue (pending) brain cycle — every 5 min validate write-backs (fail closed) sweep drafts → promotion queue recompile + reindex all vaults brain doctor — nightly integrity Compiled vaults alice/ — her view only bob/ — his view only carol/ — her view only 4 5 the operator (you) — ssh over the tailnet: brain promotions approve · brain doctor · dashboard through a tunnel employees never see this side; their own vault records when something they shared goes live

1–2Chat is the whole product

"Remember X" → the agent writes it into Inbox/ in its own clone. Questions → local search over the compiled vault. "Share this with the team" → the agent drafts a promotion. Users never touch git, Obsidian, or files; the pairing-code flow is the entire IT setup they ever experience.

2One container per person

Isolation is structural: a container cannot read another person's vault because it was never mounted — the only defense that survives prompt injection. Cost is ≈300 MB RAM each, idle CPU near zero: ~5 people → 4 GB · ~15 → 8 GB · ~30 → 16 GB. Adding an employee is one compose stanza.

3–4Two boxes, one boundary

The master's isolation is hardware, not configuration — there is no compose line that can drift into exposing it. If the agents box is ever compromised: rotate deploy keys, rebuild containers, the master's history is intact. If the brain box is down, agents keep working on local clones and sync drains later.

5The operator is a separate identity

The admin is a dedicated org.yaml identity with no agent container — not anyone's chat account. It curates the shared space and is the only identity that can publish company-wide, from the dashboard or CLI. Narrower calls are delegated on purpose: a share is decided by its recipient, or by the lead of the team receiving it, and a promotion into a team's own space by that team's lead — each from their own vault, re-checked server-side. Everyone who chats with a bot is a plain employee, read-only on the company space, so no one's agent can write shared knowledge directly.

brainkit · Deployment Plate №2

How agents route back

An agent never addresses the master. It addresses its own vault's git remote — which happens to live on the brain box. Everything an agent wants to tell the company is a commit; the master extracts meaning from pushes on its own schedule, asynchronously and fail-closed.

Any agent container on the agents box /vault — local clone .brain/ index — machine-local 1 edit own spaces · commit locally drafts → People/me/Promotions/ · capture → own Inbox/ · works offline Brain box pushes accepted as-is — validation is async 3 brain cycle — validate every path apply what's legal, revert the rest (fail closed — next compile erases it) sweep drafts → pending queue · recompile every vault 2 git push — the entire client interaction one SSH key → one repo the only network crossing 4 git pull — the verdict returns: rejected edits vanish, approved knowledge arrives THE TRANSPORT — WE CHOSE A; B AND C REMAIN AVAILABLE, THE LOOP NEVER CHANGES A · Tailscale + restricted SSH chosen — the link between the boxes no public ports on the brain box at all brain-sync@brain-box: /srv/brain/compiled/alice authorized_keys forced command — each key opens exactly one repo also carries the operator's admin ssh and the dashboard tunnel B · Public SSH + deploy keys kept in reserve port 22 open · keys only, no passwords same per-key repo restriction as A only needed if an agent ever runs outside the tailnet — a serverless sandbox, a partner's machine mixes freely with A — tailnet for pets, public SSH for cattle C · GitHub / Gitea as the hub outsource the multi-tenancy brain box keeps zero inbound surface — it only dials out cycle wrapped in mirror push / pull; one private repo per person, per-repo deploy keys — auth outsourced costs: company knowledge rests at the forge, and sync becomes polling-shaped side channels capture with no agent attached (a forwarding address or brain webhook) — both call brain ingest on the brain box; everything else flows through agents approvals stay human — ssh brain-box "brain promotions approve <id>" · dashboard reviewed over a tunnel, never a public port

2One key, one repo

Agents hold zero credentials to the brain system itself — no API tokens, no service accounts. The only secret in any container is one SSH key that reaches one compiled vault. Compromise an agent and the blast radius is: push garbage to its own vault (reverted by validation) and read what its person could already read.

4Never in the hot path

The brain box needs no availability SLA for agents to function. If it's down, every agent keeps chatting, reading, and capturing against its local clone; commits queue in local git history and drain when it returns. The master is a batch consolidator, not a dependency any agent waits on.

ATransport is swappable

We chose Tailscale (A): both boxes join one tailnet, the brain box exposes no public ports (unless you opt into webhook intake), and the same net carries your admin SSH and the dashboard tunnel. If agents ever run outside the tailnet, add B for just those; move to a forge (C) only if you'd rather not operate SSH at all.

brainkit · Deployment Plate №3

The visual layer — seeing your agent

Chat stays the primary product, but every container can also expose visual surfaces. Because sessions live in the container's shared state.db, a conversation started on WhatsApp continues in the browser or the desktop app — same brain, different doors. The one rule: the auth gate must map identity to container, so Alice can only ever reach Alice's backend.

ALICE'S DEVICES WhatsApp / Telegram the default — zero setup Browser — her dashboard URL no install, works anywhere Hermes Desktop app native chat · files · voice through the chat platforms — exactly as today AUTH GATE Tailscale (pilot) or an SSO reverse proxy (later) identity → route: alice reaches only alice's container CONTAINER — ALICE ONLY hermes gateway — chat Telegram / WhatsApp adapter hermes serve :9119 backend for desktop app + web UI brain dashboard :8787 read-only vault view + capture one state.db — the same sessions on every surface · one /vault — the same knowledge everywhere
SurfaceWhat Alice getsSetup burdenRisk surfaceVerdict
Chat (today) Capture, ask, share — full agent via WhatsApp/Telegram None — pairing code once Nothing new The product. Everything else is optional.
Brain dashboard A quiet read-only view of her own knowledge: what's new, search, the graph, a capture box (brain dashboard, our own surface) None — she opens a URL Lowest: read-only by construction; writes route through ingest Add first. Purpose-built for employees.
Hermes web UI Browser chat with streaming and tool activity, session list, artifacts preview (hermes serve + browser) None — same URL pattern Full agent control — the gate must be per-person and solid Add for power users once auth is SSO-grade.
Hermes Desktop Native app attached to her container: chat, file browser, voice, drag-and-drop — sessions shared with her chat history Install + sign-in on her laptop Same as web UI, plus a laptop install to support Offer to the few who ask. Fully supported.

✓Yes, the desktop app attaches

Hermes Desktop has first-class remote-backend support: the container runs hermes serve --host … --port 9119 (the same headless backend the app uses locally), a non-loopback bind auto-engages the auth gate, and Alice enters her URL under Settings → Gateway and signs in. The app runs as a GUI-only "lite" client — no local agent installed on her laptop.

1The brain dashboard is the sleeper

We already ship the right first visual surface: the vault's own live dashboard — employee-first, read-only, typographic, built to show what's new, what links where, and what needs attention. It answers "what does my assistant actually know?" without handing anyone a control panel, which is exactly the right amount of power for a non-technical org's first step.

!Auth is the real decision

Exposing hermes serve means remote control of that agent, so credentials must be per-person, per-container — never shared. Pilot: keep all visual surfaces tailnet-only. Wider rollout: one SSO reverse proxy (OIDC / Nous OAuth) mapping each login to exactly one container's ports. Basic-auth on the public internet is explicitly discouraged by the hermes docs.

brainkit · Deployment Plate №4

The life of a note

The whole system, traced through one piece of knowledge — twice. Once for the common case (a private capture) and once for the rarer, human-gated case (sharing). Each step says when it happens and who can see it.

① Capturing something private — the everyday case

  1. 9:02 am · Alice's phone

    Alice voice-memos her bot on WhatsApp: "the client agreed to the March timeline."

    visible to: Alice only
  2. seconds later · her container

    Her agent transcribes it, writes a note into her Inbox in its local clone, commits, and replies "Saved." The note is already durable on the agents box — this step works even if the brain box is down.

    visible to: Alice only
  3. within ~5 minutes · both boxes

    Her container's sync loop pushes; the next brain cycle validates the path, applies it to the master, and recompiles her vault. The note now lives in two places — three after the nightly offsite mirror.

    visible to: Alice only
  4. any time after · her phone

    "What did we agree with the client?" — her agent answers from its local index, instantly, network or no network.

    visible to: Alice only

② Sharing with the team — the human-gated case

  1. 2:14 pm · Alice's phone

    Alice: "share the March timeline with the team."

    visible to: Alice only
  2. seconds later · her container

    Her agent drafts a promotion into her Promotions folder and tells her exactly what will happen next. Nothing is shared yet — a draft is still hers.

    visible to: Alice only
  3. within ~5 minutes · brain box

    The push lands; the next cycle sweeps the draft into the master's pending queue.

    visible to: Alice + the approver
  4. when a human decides · operator

    The approver reviews it (dashboard or brain promotions approve) and says yes. This is the gate — no agent, however confused or manipulated, can open it.

    visible to: Alice + the approver
  5. next cycle · brain box

    The note materializes into the Team space and every affected person's vault recompiles to include it.

    visible to: the whole team
  6. within ~5 minutes · Bob's container

    Bob's sync loop pulls. His agent can now answer with it — "when's the client deadline?" → "March." Alice's agent tells her it went live.

    visible to: the whole team

①Private is fast and unconditional

A capture is durable on the agents box in seconds and settled into the master within one cycle (~5 minutes). No approval, no waiting on people — private knowledge flows at machine speed because it never crosses a visibility boundary.

②Shared is minutes + one decision

Sharing costs a few machine-minutes plus however long the human takes. That latency is the feature: the promotion queue is the only path across spaces, so the approver's yes is the exact moment knowledge changes hands — never a sync accident, never an agent's judgment call.

✓What users should be told

Two sentences cover it: "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." That's the entire mental model an employee needs.

brainkit · Deployment Plate №5

The deployment runbook

Everything to stand this up, in order. Phases 1–3 happen once and take an afternoon. Phase 4 is the ~10 minutes you spend per new employee. Phase 5 is the steady state — a few minutes a week.

1Provision — once

  1. Two VPSes, same provider & region. Brain box: 2 GB is plenty. Agents box: ≈300–400 MB per employee plus 2 GB headroom (15 people ≈ 8 GB).
  2. Ubuntu LTS, key-only SSH, unattended-upgrades on both.
  3. Join both to one tailnet; no other inbound ports anywhere — with one opt-in exception: enabling webhook intake (brain webhook) later adds a single TLS-fronted public port on the brain box.
# on both boxes
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up   # names: brain-box, agents-box

2Brain box — once

uv tool install git+https://github.com/joedanz/brainkit  # brainkit is not on PyPI — install from source
brain init /srv/brain/master --company "Acme Co"
$EDITOR /srv/brain/master/_meta/org.yaml     # people, roles, teams
$EDITOR /srv/brain/master/_meta/spaces.yaml  # visibility rules
brain compile --master /srv/brain/master --out /srv/brain/compiled
brain doctor  --master /srv/brain/master --out /srv/brain/compiled

# one sync user; per-person keys locked to one repo each
adduser --disabled-password brain-sync
# crontab — the whole orchestration layer
*/5 * * * * brain cycle --master /srv/brain/master \
  --out /srv/brain/compiled --index --json \
  >> /var/log/brain/cycle.jsonl \
  && curl -fsS $CYCLE_HC || curl -fsS $CYCLE_HC/fail
17 3 * * *  brain doctor --master /srv/brain/master \
  --out /srv/brain/compiled \
  && curl -fsS $DOCTOR_HC || curl -fsS $DOCTOR_HC/fail
23 4 * * *  git -C /srv/brain/master push --mirror offsite

$CYCLE_HC/$DOCTOR_HC are healthchecks.io-style check URLs, pinged on success and on /fail — a dead-man's switch, so a dead box or a stopped cron alerts too, not just a failing job. A nonzero cycle means a held or failed write-back; a nonzero doctor means an integrity error. The nightly mirror push to one private offsite remote backs up the master. Optional: set BRAIN_EMBED_BASE_URL (an embedding API reachable from both boxes) to upgrade keyword search to semantic — cycle --index and each container's brain index pick it up from the environment.

3Agents box — once

  1. Install Docker + the compose plugin.
  2. Build the hermes-brain image with deploy/agents-box/build-image.sh — derived from the official nousresearch/hermes-agent image, with brainkit installed from the public repo (agents need brain index / search / mcp locally). The script resolves the ref to a commit first, so the image is built from an exact revision — which is what lets you ask a container later what it is running.
  3. Set an LLM provider key once, shared via compose env.
# deploy/agents-box/docker-compose.yml — one 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
      - TELEGRAM_BOT_TOKEN=${ALICE_BOT_TOKEN}
      - ANTHROPIC_API_KEY=${LLM_KEY}
      - BRAIN_EMBED_BASE_URL=${EMBED_URL}  # optional: semantic search
    volumes:
      - alice-state:/opt/data   # sessions, pairing, deploy key
      - alice-vault:/vault      # her compiled vault clone

Everything the image bakes in lives in 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 installed from the public repo at build time, not copied from the working tree.

Each container's first boot: install the company-brain hermes profile (SOUL.md, terminal.cwd: /vault, the brain MCP server + skills — with the assistant's built-in memory disabled, so the vault is its only durable memory) and mint the container's 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 that profile — the container records which brainkit build it applied, so a rebuild propagates instead of landing silently. Narrow on purpose: skills refresh; SOUL.md updates only if untouched since the image wrote it, so an edit someone meant survives; config.yaml never, because the assistant rewrites it at runtime and regenerating it would discard live settings — including the model you chose.

4Onboard an employee — ~10 min each

deploy/agents-box/add-agent.sh <name> prints the compose stanza and this exact checklist:

  1. Add them to org.yaml; 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, set the token, docker compose up -d agent-<name>.
  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 30 seconds.
  5. They message their bot; you 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.

5Operate — minutes per week

  1. Approvals: ssh brain-box brain promotions list / approve <id> --approver joe; review in the dashboard over ssh -L on the tailnet.
  2. Watch two numbers: cycle exit ≠ 0 (a held or failed write-back) and doctor exit ≠ 0 (integrity) — both already ping your alert URL.
  3. Updates: uv tool upgrade brainkit on the brain box; build-image.sh then docker compose up -d to roll agents — which also re-syncs each container's profile, since it records the build it applied. The two halves upgrade separately, so they drift — brain --version answers what each is actually on, down to the commit (brain 0.2.0 (rev 69631f68e8c9)).
  4. Backups, both halves, offsite by default: the brain box ships the master vault plus its wiring (proxy/tunnel config, systemd units, the sync authorized_keys) to object storage hourly with deploy/brain-box/backup-master.sh — restic, encrypted client-side. On the agents box, backup-agents.sh zips each container's state nightly (hermes backup inside the container, consistent while the gateway runs — a raw volume tar is not) and backup-agents-offsite.sh ships the zips plus the compose .env and live docker-compose.yml off-box. One bucket per box — object-storage tokens scope to buckets, so a compromised box can't delete the other's history. Vault volumes re-clone themselves — skip them.
  5. Offboarding: remove from org.yaml, delete their authorized_keys line, docker compose rm their container.

6Visual layer — optional, phased

  1. Bake the extras into the image: .[web,pty] for hermes serve; brainkit is already there for the brain dashboard.
  2. Pilot (tailnet-only): expose each container's two ports on the agents box's tailscale IP; work devices join the tailnet.
  3. Wider rollout: one SSO reverse proxy (Caddy/Traefik + OIDC or Nous OAuth via hermes dashboard register) with per-person routes.
# inside each container (supervised alongside the gateway)
hermes serve --host 0.0.0.0 --port 9119   # desktop + web backend
brain dashboard --vault /vault --host 0.0.0.0 --port 8787

# proxy routes — identity must map to container
alice.agents.example.com       → agent-alice:9119
alice-brain.agents.example.com → agent-alice:8787

Per-person credentials in each container's .env (HERMES_DASHBOARD_BASIC_AUTH_* on the tailnet; OAuth beyond it). The brain dashboard has no auth of its own — off-loopback it must sit behind the authenticated proxy, never exposed directly.

✓Failure drills — reassuringly boring

  1. Brain box down: agents keep chatting and capturing on local clones; commits queue and drain on recovery. Users notice nothing.
  2. Agents box down: chat goes quiet, zero data lost; docker compose up -d restores everything from volumes.
  3. Agent volumes lost: fresh container, hermes import <zip> --force with the person's latest backup, then docker restart (the restart re-fixes key file modes) — memories, pairing, and deploy key all return; the vault re-clones itself.
  4. Agents box compromised: rotate deploy keys, rebuild containers from the image — the master's history is intact and trustworthy.
  5. Brain box lost: restic restore the offsite snapshot — vault, config, and the sync authorized_keys all return, so agents reconnect without re-authorizing keys — reinstall brainkit, recompile. The encrypted offsite snapshot is the whole plan.