AnviLLM was an experimental 9P interface for common CLI-based AI agents. It is no longer actively maintained, but kept for progeny.
This repository has been archived on 2026-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
Go to file
Levi Neely 92a90c2175 Add MCP tools configuration to anvillm-agent
- Configure anvilmcp MCP server tools in agent config
- Enables read_inbox, send_message, list_sessions, set_state tools
2026-02-18 19:58:29 +01:00
cfg Add agent system configuration and AGENT_ID support 2026-02-18 16:23:30 +01:00
claude improve send message api 2026-02-17 18:20:01 +01:00
cmd Add set_state tool to anvilmcp 2026-02-18 19:54:36 +01:00
elisp Remove deprecated endpoints and fix mailbox auto-completion 2026-02-17 23:06:07 +01:00
internal Add agent system configuration and AGENT_ID support 2026-02-18 16:23:30 +01:00
kiro-cli Add MCP tools configuration to anvillm-agent 2026-02-18 19:58:29 +01:00
scripts Add centralized audit log system 2026-02-17 22:07:39 +01:00
services feat: split monolith into anvilsrv daemon and Assist client 2026-02-14 11:30:57 +01:00
skills Remove deprecated endpoints and fix mailbox auto-completion 2026-02-17 23:06:07 +01:00
workflows improve messaging 2026-02-17 18:58:06 +01:00
.gitignore Fix 9p-read-inbox to strip .json extension when marking messages complete 2026-02-17 14:37:46 +01:00
IDEAS.md improve send message api 2026-02-17 18:20:01 +01:00
LICENSE.md initial docs 2026-02-10 22:57:21 +01:00
README.md improve send message api 2026-02-17 18:20:01 +01:00
SECURITY.md feat: split monolith into anvilsrv daemon and Assist client 2026-02-14 11:30:57 +01:00
go.mod improve send message api 2026-02-17 18:20:01 +01:00
go.sum improve send message api 2026-02-17 18:20:01 +01:00
main.go Remove deprecated endpoints and fix mailbox auto-completion 2026-02-17 23:06:07 +01:00
mkfile Fix 9p-read-inbox to strip .json extension when marking messages complete 2026-02-17 14:37:46 +01:00

README.md

AnviLLM

9P-based LLM orchestrator. Front-ends for Acme, Emacs, TUI, and web.

Architecture

AnviLLM consists of:

  • anvilsrv: Background daemon managing sessions via 9P
  • Assist: Acme UI client (auto-starts daemon if needed)
  • anvillm.el: For our emacs friends ;)
  • anvillm: Terminal UI (curses-based) for everyone else
  • anvilweb: Web interface for browser-based access

This separation allows:

  • Multiple clients to connect to the same sessions
  • Server survives client crashes/restarts
  • Scriptable workflows via 9P without UI dependencies
  • Service management (systemd/runit integration)
  • Choice of interface: Acme, terminal, Emacs, or web browser

Requirements

Installation

git clone https://github.com/lneely/anvillm
cd anvillm
mk  # installs anvilsrv and Assist to $HOME/bin

Service Integration

Install as system service (optional):

systemd:

# User service (recommended)
cp services/systemd/anvilsrv-user.service ~/.config/systemd/user/
systemctl --user enable --now anvilsrv

# System service
sudo cp services/systemd/anvilsrv.service /etc/systemd/system/
sudo systemctl enable --now anvilsrv

runit:

sudo cp -r services/runit /etc/sv/anvilsrv
sudo ln -s /etc/sv/anvilsrv /var/service/

See services/*/README.md for details.

Usage

Starting the Server

Automatic: Assist auto-starts anvilsrv if not running.

Manual:

anvilsrv start       # Daemonize (background)
anvilsrv fgstart     # Foreground (for debugging)
anvilsrv status      # Check if running
anvilsrv stop        # Shutdown

Multiple Namespaces

anvilsrv respects the $NAMESPACE environment variable, allowing multiple independent instances for different Acme sessions (similar to running multiple Acme instances with acme -a).

Default namespace: /tmp/ns.$USER.:0

Example: Running multiple instances

# Start default instance (namespace :0)
anvilsrv start

# Start second instance (namespace :1)
NAMESPACE=/tmp/ns.$USER.:1 anvilsrv start

# Check status of each instance
anvilsrv status                           # :0
NAMESPACE=/tmp/ns.$USER.:1 anvilsrv status  # :1

# Each instance has isolated:
# - PID file: $NAMESPACE/anvilsrv.pid
# - 9P socket: $NAMESPACE/agent
# - Session list (completely independent)

# Connect Assist to specific instance
NAMESPACE=/tmp/ns.$USER.:1 Assist

This enables running separate AnviLLM environments for different projects or Acme workspaces.

Terminal UI (anvillm)

Run anvillm for a curses-based interface (no Acme required):

anvillm

Keyboard shortcuts:

  • s - Start new session (shows backend menu)
  • p - Send prompt to selected session
  • t - Stop selected session
  • R - Restart selected session
  • K - Kill selected session
  • a - Set session alias
  • r - Refresh session list
  • d - Daemon status
  • ? - Help
  • q - Quit

To attach to the anvillm session, simply open a different terminal and use tmux attach.

Navigation:

  • Arrow keys (↑/↓)
  • Vim-style (j/k)
  • Emacs-style (C-n/C-p)

Like Assist, the TUI reads/writes the 9P filesystem for all operations.

Emacs Interface (anvillm.el)

Run M-x anvillm in Emacs for a native Emacs interface:

Installation:

Copy anvillm.el to ~/.emacs.d/lisp/ or wherever your elisp files live. Then, add to your emacs init:

(add-to-list 'load-path "/path/to/anvillm.el/")
(require 'anvillm)

Keybindings:

  • s - Start new session (select backend)
  • p - Send prompt to selected session
  • P - Send prompt to selected session using minibuffer
  • t - Stop selected session
  • R - Restart selected session
  • K - Kill selected session
  • a - Set alias for selected session
  • r - Refresh session list
  • g - Refresh session list (standard Emacs binding)
  • d - Daemon status
  • q - Quit
  • ? - Help

Navigation:

  • Standard Emacs navigation (n, p, C-n, C-p)
  • Sessions displayed in a sortable table (click column headers)

The Emacs interface uses the 9p command from plan9port to interact with the filesystem.

Web Interface (anvilweb)

Run anvilweb for a browser-based interface:

anvilweb              # starts on :8080
anvilweb -addr :3000  # custom port

Features:

  • Start new sessions (Kiro/Claude)
  • View all sessions with live state updates
  • Send prompts, edit context and aliases
  • Stop/restart/kill sessions
  • Auto-refreshes every 5 seconds

Security Warning:

⚠️ The web interface has NO authentication. Anyone who can reach the server port can:

  • View all sessions
  • Send prompts to any session
  • Start/stop/kill sessions
  • Modify session contexts

Only run anvilweb on localhost or trusted networks. For remote access, use SSH port forwarding:

# On remote machine
anvilweb -addr localhost:8080

# On local machine
ssh -L 8080:localhost:8080 user@remote
# Then browse to http://localhost:8080

Or use a reverse proxy with authentication (nginx, caddy, etc.).

Acme Interface (Assist)

Type Assist in Acme and middle-click to open the /AnviLLM/ session manager.

Commands

Main window (/AnviLLM/) tag: Get Attach Stop Restart Kill Alias Context Log Daemon Sandbox

Command Description
Kiro <dir> Start kiro-cli session
Claude <dir> Start Claude session
Stop <id> Stop session (preserves tmux)
Restart <id> Restart stopped session
Kill <id> Terminate session
Attach <id> Open tmux terminal
Alias <id> <name> Name a session
Context <id> Edit context (prepended to prompts)
Log <id> View session log
Daemon Manage anvilsrv daemon (start/stop/status)
Sandbox Configure sandbox settings

Right-click a session ID to open its prompt window. Select text anywhere and 2-1 chord on a session ID for fire-and-forget prompts.

Note: Assist reconnects to the running anvilsrv daemon, so sessions persist across Assist restarts.

Prompt window (+Prompt.<id>) tag: Send

Type prompt, click Send.

9P Filesystem

The anvilsrv daemon exposes sessions via a 9P filesystem at $NAMESPACE/agent (typically /tmp/ns.$USER/agent).

agent/
├── ctl             # "new <backend> <cwd>" creates session
├── list            # id, alias, state, pid, cwd
└── <id>/
    ├── in          # Write prompts
    ├── out         # Read responses
    ├── ctl         # "stop", "restart", "kill"
    ├── state       # starting, idle, running, stopped, error, exited
    ├── context     # Prepended to prompts (r/w)
    ├── alias       # Session name (r/w)
    ├── pid         # Process ID
    ├── cwd         # Working directory
    └── backend     # Backend name

Path Validation: Session creation validates and cleans paths (e.g., /../../../etc → /etc), and rejects nonexistent directories.

Example:

echo 'new claude /home/user/project' | 9p write agent/ctl
echo 'Hello' | 9p write agent/a3f2b9d1/in
9p read agent/a3f2b9d1/state  # → "running"
echo 'stop' | 9p write agent/a3f2b9d1/ctl
9p read agent/a3f2b9d1/state  # → "stopped"

Security: See SECURITY.md for 9P socket authentication limitations.

Backends

Backend Install
Claude npm install -g @anthropic-ai/claude-code
Kiro Kiro CLI

Both run with full permissions inside the sandbox (--dangerously-skip-permissions, --trust-all-tools).

Adding Backends

Create internal/backends/yourbackend.go, implement CommandHandler and StateInspector interfaces, register in main.go. See existing backends for examples.

Self-Healing

The anvilsrv daemon monitors all sessions every 5 seconds and automatically restarts sessions that crash unexpectedly. This ensures long-running workflows remain resilient to backend failures.

Behavior:

  • Detects when a session process terminates without explicit Stop command
  • Automatically restarts the session in the same working directory
  • Preserves session context and alias
  • Rate-limited to prevent restart loops (minimum 5 seconds between attempts)
  • Does not restart sessions stopped intentionally via Stop command

Sessions that crash are transparently restarted without user intervention. Check logs with anvilsrv fgstart to see auto-restart activity.

Sandboxing

Backends run inside landrun sandboxes. Sandboxing cannot be disabled — it's the only safety layer.

Defaults

  • Filesystem: CWD, /tmp, config dirs (rw); /usr, /lib, /bin (ro+exec)
  • Network: disabled
  • Mode: strict (best_effort: false) — sessions fail if sandbox unavailable

Configuration

Click Sandbox in main window, then Edit to modify ~/.config/anvillm/sandbox.yaml.

Path templates: {CWD}, {HOME}, {TMPDIR}

# Enable network
network:
  enabled: true
  unrestricted: true

# Add paths
filesystem:
  rw:
    - "{CWD}"
    - "{HOME}/.npm"

Changes apply to new sessions only.

Kernel Requirements

Kernel Landlock ABI Features
5.13+ v1 Filesystem
6.7+ v4 Improved FS
6.10+ v5 Network

Best-Effort Mode

Set best_effort: true for graceful degradation on older kernels.

Warning: If landrun is missing or kernel lacks Landlock, sessions run completely unsandboxed. Only enable if you accept this risk.

Reproducible Workflows

The 9P filesystem enables scripted multi-agent workflows. Create sessions, set contexts, and wire agents together programmatically.

Example: Developer-Reviewer

Two agents collaborate on code changes — one implements, one reviews:

./scripts/DevReview claude /path/to/project

Creates paired sessions with contexts that instruct agents to:

  1. Developer implements and stages changes
  2. Developer sends review request via agent/{reviewer-id}/in
  3. Reviewer examines diff, sends feedback or "LGTM"
  4. Loop until approved

Example: Planning

Three-agent workflow for documentation tasks:

./scripts/Planning kiro /path/to/docs
  • Research: Queries codebase and knowledge base
  • Engineering: Writes/updates documentation, queries research when needed
  • Tech-editor: Reviews for quality, requests changes from engineering

Writing Your Own

Key patterns from the example scripts:

# Create session, capture ID
echo "new claude /project" | 9p write agent/ctl
ID=$(9p read agent/list | tail -1 | awk '{print $1}')

# Set context (defines agent behavior)
cat <<EOF | 9p write agent/$ID/context
You are a code reviewer. When you receive code...
EOF

# Agents communicate via mailboxes
cat > /tmp/msg.json <<'EOF'
{"to":"$PEER_ID","type":"REVIEW_REQUEST","subject":"Review","body":"Please review staged changes"}
EOF
9p write agent/$ID/mail < /tmp/msg.json

See workflows/DevReview and workflows/Planning for complete examples.

Troubleshooting

Problem Solution
"Failed to connect to anvilsrv" Check anvilsrv status; try anvilsrv start
Session won't start Check stderr; verify which claude or which kiro-cli
Landlock ABI error Set best_effort: true or upgrade kernel
Permission denied Add paths to sandbox config
Orphaned tmux tmux kill-session -t anvillm-0 (or anvillm-N for namespace :N)
9P not working Verify plan9port: 9p ls agent
Stale PID file anvilsrv stop cleans up automatically
Daemon won't stop Check logs with anvilsrv fgstart
anvilweb can't connect Ensure anvilsrv is running; check namespace matches
anvilweb port in use Use -addr :PORT to specify different port

Debugging: Run anvilsrv fgstart to see daemon logs in foreground.

Terminal: Attach command uses $ANVILLM_TERMINAL or defaults to foot. Configure in environment or see cmd/Assist/main.go.