skip to content

Genie overview

genie-agent is the support agent behind the @genie bot in Matrix. People ask it questions about Nix, NixOS, den and the gen libraries, often in public rooms. It answers in the thread.

Genie is built on xmsg. Each of its parts is a separate process that registers on an xmsg bus as a service, and the parts talk to each other by xmsg messages and replies.

TermMeaning
Matrix botmatrix-xmsg, a separate program. It reads the Matrix room, decides which messages to forward, and posts genie’s answers back.
Sender tiertrusted or public. The Matrix bot computes it from the authenticated Matrix sender id: senders on its trusted list are trusted, everyone else is public. Message text never sets it.
Thread tierThe lowest sender tier among everyone whose text is in the thread. One public line makes the thread public.
GuardA separate model session that screens text. guard-in screens text coming in; guard-out screens answers going out. See Guard model.
RedactorDeterministic code (no model) that removes credential-shaped strings from anything genie posts.
Tier 1The first answering model. It runs as one pi process per thread against a local, OpenAI-compatible model endpoint.
Expert tierA stronger model (Claude Code) that tier 1 can escalate to. There are two experts, @public and @trusted, chosen by the thread tier.
Rootgenie-root, the composition root. It owns tier 1’s processes, the thread state, the credentials and every call to the guards.
Dispatchergenie-dispatcher, one per expert tier. It receives escalations over xmsg and runs one Claude Code session per thread.
BrokerThe only component holding the GitHub token. Models ask it for gists and pull requests by proposal; they never hold the token.
flowchart LR
  room["Matrix room"] --> bot["matrix-xmsg<br/>(tags each line with its sender tier)"]
  bot -->|"guard-in"| guard["genie-guard"]
  bot --> root["genie-root"]
  root --> t1["tier 1 (pi, one per thread)"]
  t1 -->|"draft"| out["redactor, then guard-out"]
  out --> bot
  t1 -->|"escalate"| esc["escalation guard"]
  esc -->|"xmsg"| disp["genie-dispatcher@public or @trusted"]
  disp --> claude["Claude Code session (one per thread)"]
  claude -->|"xmsg reply"| root
  1. Someone mentions @genie in a room. The Matrix bot admits the message, tags every line with its sender’s tier, and sends it to genie-guard for review.
  2. The bot forwards the reviewed text to genie-root over xmsg. Root reads the tiers from the raw envelope and sets the thread tier. If the bot did not already screen the text, root sends it to the guard itself.
  3. Root finds or starts the thread’s tier-1 process. Tier 1 reads public sources, may evaluate a Nix configuration in a sandbox, and writes a draft: an answer, a confidence between 0 and 1, and a list of gaps.
  4. Root runs the redactor over the draft, then sends the redacted text to guard-out. Only a passed draft is posted, with its confidence line.
  5. If the draft’s confidence is low, a hard trigger fires, or the asker reacts 🔍 (or types !deeper), root builds an escalation package, screens its untrusted parts, and sends it over xmsg to the expert the thread tier selects. The expert’s answer returns as an xmsg reply, passes the redactor and guard-out, and is posted marked “expert review”.
  6. The asker reacts ✅ to accept. The thread closes after acceptance or 24 hours idle.
  • It evaluates Nix configurations; it never builds them on the untrusted path.
  • It never asks anyone for a secret. When a secret blocks an evaluation, it stubs the secret or says what the secret blocked.
  • No model holds a credential. The model key, the GitHub token and the expert tokens stay with the processes that need them.
  • No model decides trust. Tiers come from Matrix authentication, never from text or a tool argument.
palette
dark
light
↑↓ select↵ apply esc close

Palettes adapted from Catppuccin (Macchiato) (MIT), Tokyo Night (Apache-2.0), gruvbox (MIT), Catppuccin (Latte) (MIT), Rosé Pine (Dawn) (MIT).