Infernode is Inferno OS running natively on AMD64 and ARM64 (macOS, Linux, Windows). The AI
agent stack runs entirely inside the Inferno emulator (emu), using Plan 9's "everything is
a file" model to integrate the LLM API, tool execution, wallet, and GUI through a unified 9P namespace.
Host OS (macOS / Linux / Windows)
│
├── emu (Dis VM + JIT compiler)
│ │
│ └── Inferno namespace (rootfs = project root)
│ │
│ ├── /n/llm ← llmsrv (LLM providers as 9P)
│ ├── /n/ui ← luciuisrv (GUI state 9P server)
│ ├── /n/wallet ← wallet9p (crypto wallet 9P server)
│ ├── /tool ← tools9p (39 tool modules as 9P)
│ ├── /mnt/factotum ← factotum (key agent, secstore-backed)
│ ├── /n/local/ ← agent-visible host paths (via sys->bind)
│ ├── /dis/ ← compiled Limbo bytecode (815 modules)
│ ├── /lib/ ← runtime data (fonts, tool docs, resources)
│ └── /tmp/ ← scratch (writable at /tmp/veltro/scratch/)
│
└── secstored (TCP 5356) ← encrypted key persistence (AES-256-GCM)
1. secstored starts (TCP port 5356)
2. factotum starts (with secstore backing if $SECSTORE_PASSWORD set, otherwise empty)
3. wm/logon displays login screen (skipped if keys already loaded or headless)
- First boot: password + confirmation → creates secstore account
- Normal boot: password → PAK auth → keys loaded into factotum
- Wrong password: retry or continue without secstore
- Escape (double-press): skip with warning
4. llmsrv, tools9p, wallet9p, lucibridge, lucifer start
5. System fully operational with all keys (wallet, API, email) available
- Source:
appl/cmd/llmsrv.b; runs inside Inferno emulator - Presents LLM providers (Anthropic API or Ollama/OpenAI-compatible) as a 9P file server
- Self-mounts at
/n/llm; can also be accessed remotely via 9P dial+mount - Session lifecycle: clone from
/n/llm/new→ session directory/n/llm/{id}/ - Files per session:
ask(write prompt → read response),tools(write tool schemas),history(full conversation history) - Response format:
STOP:tool_use\nTOOL:<id>:<name>:<args>orSTOP:end_turn\n<text> - Native
tool_useprotocol: LLM receives proper JSON tool schemas, returns structured tool calls
The shared configuration and execution server for the agent stack. Both the GUI (lucictx) and the agent bridge (lucibridge) interact with it as their common intermediary.
Filesystem layout at /tool:
/tool/
├── tools (r) Newline-separated list of currently active tool names
├── help (rw) Write tool name → read documentation
├── ctl (rw) Control: add/remove tools; bindpath/unbindpath paths
├── paths (r) Newline-separated list of registered namespace paths
└── _registry (r) Space-separated list of tool names (for spawn validation)
└── <name> (rw) Per-tool file: write args → blocking execute → read result
Key design properties:
- Pre-loads all tool modules before namespace restriction — allows
ctl addwithout needing access to/dis/veltro/tools/ - Async tool execution —
asyncexec()runs tool in a spawned goroutine; the write() call blocks until complete but the serveloop remains responsive to other 9P traffic - Shared server — tools9p runs in its own Inferno thread and is mounted in the shell's
namespace; both lucictx (GUI) and lucibridge (agent bridge) inherit
/toolfrom there
The agent bridge: connects the GUI conversation UI to the LLM.
- Runs in background, started by Lucifer
- Reads user input from
/n/ui/activity/{id}/conversation/input - On each turn: re-reads
/tool/toolsand/tool/pathsto pick up GUI-side changes - If tool set changed: calls
initsessiontools()to update the LLM's active tool list - If path set changed: calls
applypathchanges()to bind/unmount paths in its namespace - Sends LLM responses and streaming tokens back via
/n/ui/activity/{id}/conversation/ctl
GUI state server — a 9P file server for the three-zone Lucifer UI.
Mounted at /n/ui. Presents conversation messages, presentation artifacts, and context
zone state as a filesystem. No draw/display dependency — fully testable headless.
/n/ui/
├── ctl Global control
├── event Global event stream
├── catalog/ Resource catalog (from /lib/veltro/resources/*.resource)
└── activity/{id}/
├── label Activity name
├── status idle / working / error
├── event Per-activity event stream
├── conversation/
│ ├── ctl Write messages / update streaming token
│ ├── input Blocking read: next user message
│ └── {N} Indexed message files
├── presentation/
│ ├── ctl Create / update / append / center artifacts
│ ├── current ID of centered artifact
│ └── {id}/ Per-artifact directory
│ ├── type text / markdown / pdf / diagram
│ ├── label Display label
│ └── data Artifact content
└── context/
├── ctl Add resources / gaps / bg tasks
├── resources/{N} Context resources
├── gaps/{N} Knowledge gaps
└── background/{N} Background tasks
CLI agent. One-shot or REPL mode.
- Reads
-p pathsand registers them in tools9p (bindpathctl commands) - Forks namespace (
sys->pctl(FORKNS)) - Calls
nsconstruct->restrictns()— reduces namespace to only what the agent needs - Creates an LLM session via
/n/llm/new - Runs
repl.bloop: prompt → LLM → tool calls → results → repeat
Namespace restriction engine, called by both tools9p and veltro.
Policy applied after FORKNS:
/dis→ reduced tolib/,veltro/(+sh.disifexectool active)/dis/veltro/tools/→ only registered tool.disfiles visible/dev→ reduced tocons,null,time/n→ capability-gated:/n/llmalways;/n/git,/n/speechonly if incaps.paths/tmp→ writable only at/tmp/veltro/scratch/
Cryptocurrency wallet exposed as a 9P file server at /n/wallet/.
/n/wallet/
├── ctl rw "network <name>", "default <name>", "rpc <url>"
├── accounts r newline-separated account names
├── new rw write: "eth chain name" or "import eth chain name hexkey"
└── {name}/
├── address r public address (EIP-55 checksummed)
├── balance r live balance from blockchain RPC
├── chain rw chain name
├── sign rw write: hex hash → read: hex signature
├── pay rw write: "amount recipient" → read: txhash
├── ctl rw "budget maxpertx maxpersess currency"
└── history r recent transactions
Key design properties:
- Factotum-backed — private keys stored in factotum (
service=wallet-eth-{name}), never in wallet9p's memory. Signing writes a hash, reads back a signature. - Secstore persistence — new accounts trigger factotum sync to secstore (async). Keys survive emu restart.
- Budget enforcement — server-side spending limits; agents cannot bypass.
- Namespace-gated — agents need
"/n/wallet"incaps.pathsto access. Unlike/n/llm(always granted), wallet access is explicitly opt-in. - Multi-network — supports Ethereum Mainnet, Sepolia, Base, Base Sepolia with per-network RPC endpoints and USDC contract addresses.
Built-in text editor with 9P IPC for agent integration. Mounts at /edit/.
/edit/
├── ctl rw open <path>, new, quit
├── index r list of open document IDs
└── {id}/
├── body rw document text
├── ctl rw save, saveas, goto, find, insert, delete, replace, replaceall
├── addr rw cursor position ("line col")
└── event r blocking read for events (modified, opened, quit)
The Veltro editor tool uses this IPC to let agents read, navigate, and modify open
documents without needing direct Draw access.
Three-zone window: Conversation | Presentation | Context.
Starts the following pipeline:
luciuisrv— mounts at/n/uitools9p— mounts at/tool(with full default tool set)lucibridge— connects conversation input → LLM → conversation outputlucictx— renders the context zone (tool toggles, namespace browser)
Additional features:
- Live theme sync — theme changes propagate to all running apps in real time
- HiDPI fonts — antialiased combined fonts for Retina/HiDPI displays
- App slots — up to 16 GUI apps (wallet, editor, fractals, etc.) in the presentation zone
- Activity tracking — per-activity event streams, status indicators, tool-call tiles
User types in Conversation zone
→ lucifer writes to /n/ui/activity/{id}/conversation/ctl
→ luciuisrv stores message, fires "conversation N" event
→ lucifer re-renders conversation zone
→ lucibridge (blocking read on /conversation/input) receives message
→ lucibridge re-reads /tool/tools, /tool/paths
→ lucibridge calls LLM via /n/llm/{id}/ask
→ LLM returns tool_use or end_turn
→ lucibridge executes tools (writes to /tool/<name>, reads result)
→ lucibridge writes response back to /n/ui conversation/ctl
→ lucifer renders response
User clicks [-] on "diff" in context zone
→ lucictx writes "remove diff" to /tool/ctl
→ tools9p moves diff from active set to alltools
→ /tool/tools no longer lists "diff"
→ On next LLM turn: lucibridge re-reads /tool/tools, calls initsessiontools()
→ LLM no longer receives diff tool schema
User browses to /Users/pdfinn/docs, clicks [Bind]
→ lucictx writes "bindpath /Users/pdfinn/docs" to /tool/ctl
→ tools9p adds path to boundpaths list; /tool/paths now lists it
→ On next LLM turn: lucibridge reads /tool/paths, calls applypathchanges()
→ lucibridge: sys->bind("/Users/pdfinn/docs", "/n/local/docs", MBEFORE)
→ Agent can now read files under /n/local/docs
Each agent session has a restricted namespace:
Full Inferno namespace (shell, GUI)
│
├── tools9p runs in this namespace (shared)
└── FORKNS ──► Agent namespace (restricted copy)
├── /n/llm (always)
├── /n/local/<base> (only granted paths)
├── /tool (inherited, read-only in practice)
├── /dis/veltro/ (tool .dis files only)
├── /dev/cons (console only)
└── /tmp/veltro/scratch/ (only writable area)
Subagents (via spawn tool) can only NARROW further — they cannot re-grant permissions
their parent didn't have.
The same stack runs on Linux ARM64 (e.g. Jetson) over ZeroTier with Ed25519 authentication
and RC4-256 encryption. 9P mounts work cross-host: the remote Inferno namespace is
accessible locally via mount -A tcp!<host>!<port> /n/remote.
| Component | Source | Compiled |
|---|---|---|
| tools9p | appl/veltro/tools9p.b |
dis/veltro/tools9p.dis |
| lucibridge | appl/cmd/lucibridge.b |
dis/lucibridge.dis |
| lucictx | appl/cmd/lucictx.b |
dis/lucictx.dis |
| lucifer | appl/cmd/lucifer.b |
dis/lucifer.dis |
| luciuisrv | appl/cmd/luciuisrv.b |
dis/luciuisrv.dis |
| lucitheme | appl/cmd/lucitheme.b |
dis/lucitheme.dis |
| veltro | appl/veltro/veltro.b |
dis/veltro/veltro.dis |
| nsconstruct | appl/veltro/nsconstruct.b |
dis/veltro/nsconstruct.dis |
| agentlib | appl/veltro/agentlib.b |
dis/veltro/agentlib.dis |
| llmsrv | appl/cmd/llmsrv.b |
dis/llmsrv.dis |
| wallet9p | appl/veltro/wallet9p.b |
dis/veltro/wallet9p.dis |
| factotum | appl/cmd/auth/factotum/factotum.b |
dis/auth/factotum.dis |
| secstored | appl/cmd/auth/secstored.b |
dis/auth/secstored.dis |
| logon | appl/wm/logon.b |
dis/wm/logon.dis |
| editor | appl/wm/editor.b |
dis/wm/editor.dis |