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 528a482fe8 basic sandboxing support with landrun, fail fast 2026-02-11 08:32:14 +01:00
internal basic sandboxing support with landrun, fail fast 2026-02-11 08:32:14 +01:00
.gitignore cleanup 2026-02-10 22:30:57 +01:00
LICENSE.md initial docs 2026-02-10 22:57:21 +01:00
README.md basic sandboxing support with landrun, fail fast 2026-02-11 08:32:14 +01:00
go.mod basic sandboxing support with landrun, fail fast 2026-02-11 08:32:14 +01:00
go.sum basic sandboxing support with landrun, fail fast 2026-02-11 08:32:14 +01:00
main.go basic sandboxing support with landrun, fail fast 2026-02-11 08:32:14 +01:00
mkfile fix mkfile 2026-02-10 22:31:35 +01:00

README.md

AnviLLM

An Acme-native interface for LLM chat backends

AnviLLM brings Claude, Kiro, and other LLM chat interfaces directly into your Acme editor. It provides:

  • Native Acme integration: Chat sessions appear as Acme windows with familiar tag commands
  • 9P filesystem: Sessions exposed as files (9p ls agent)
  • Multi-backend support: Run sessions for Claude and Kiro (and other supported backends) simultaneously
  • Tmux isolation: Each session runs in its own tmux window for direct access when needed
  • Session management: Save/load transcripts (if supported by backend, otherwise take advantage of acme), alias sessions, switch between multiple conversations .
  • If all else fails: Fire up a terminal and attach to the correct tmux session with a single click.

Architecture

┌─────────────────────────────────────────────────┐
│                 Acme Windows                    │
│  ┌──────────────┐  ┌──────────────┐             │
│  │ /AnviLLM/    │  │ /chat/claude │             │
│  │ Sessions     │  │ Save Load    │             │
│  └──────────────┘  └──────────────┘             │
└─────────────────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────┐
│              Session Manager                    │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐       │
│  │ Backend: │  │ Backend: │  │ Backend: │       │
│  │  Claude  │  │ Kiro-CLI │  │  (more)  │       │
│  └──────────┘  └──────────┘  └──────────┘       │
└─────────────────────────────────────────────────┘
                       │
         ┌─────────────┴─────────────┐
         ▼                           ▼
┌──────────────────┐      ┌──────────────────────┐
│  9P Filesystem   │      │   Tmux Sessions      │
│  /mnt/anvillm/   │      │  anvillm-claude      │
│   agent/         │      │  anvillm-kiro-cli    │
│     <id>/        │      └──────────────────────┘
│       in         │
│       out        │
│       ctl        │
│       backend    │
└──────────────────┘

Requirements

  • Go 1.21+ (for building)
  • Plan 9 from User Space (plan9port) - provides Acme and 9P tools
  • tmux - session isolation and management
  • At least one backend CLI:

Installation

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

Usage

Starting AnviLLM

Type Assist somewhere in Acme, and middle click. This opens the main /AnviLLM/ window in Acme showing all active sessions (probably empty).

Creating Sessions

From the /AnviLLM/ window, use tag commands:

  • Kiro <directory> - Start a kiro-cli session in the specified directory
  • Claude <directory> - Start a Claude Code session in the specified directory

Example: Click on a path, and 2-1 chord on Claude to start a Claude session.

Each session opens a new $PWD/+Chat.<session-id> window where you can:

  • Type prompts in the window body
  • Use Get to send the text as a prompt
  • Use Save to export the transcript
  • Use Load to import a previous transcript
  • Use Attach to open the underlying tmux window

Session Management Commands

Main Window (/AnviLLM/)

  • Open <session-id> - Reopen a session window (2-1 chord with session ID highlighted). Or, just right-click the session ID!
  • Kill <pid> - Terminate a session (2-1 chord with pid highlighted)

Chat Windows (/chat/<backend>/<id>)

  • Send - Send the window body as a prompt to the LLM
  • Save [path] - Save transcript to file (if backend supportrs it, defaults to ~/.config/anvillm/transcripts/<timestamp>.txt)
  • Load <path> - Load a previous transcript (if backend supports it)
  • Alias <name> - Give the session a memorable name (2-1 click with a name argument)
  • Attach - Open the tmux window for direct terminal access
  • Stop - Interrupt the current running prompt (CTRL+C)

9P Filesystem

AnviLLM exports a 9P filesystem at agent:

agent/
└── ctl             # Control file (create new sessions)
└── list            # list of active sessions
└── <session-id>/
    ├── in          # Write prompts here
    ├── out         # Read responses here
    ├── err         # Error output
    ├── ctl         # Session control (stop, close)
    ├── state       # Current state (idle, running, exited)
    ├── pid         # Process ID
    ├── cwd         # Working directory
    ├── alias       # Session alias
    ├── winid       # Acme window ID
    └── backend     # Backend name (claude, kiro-cli)

9P Usage Examples

Create a new session:

echo 'new claude /home/user/project' > agent/ctl

Send a prompt:

echo 'What is the capital of France?' > agent/a3f2b9d1/in
9p read agent/a3f2b9d1/out

Check session state:

9p read agent/a3f2b9d1/state
# Output: idle

Backends

Supported Backends

Claude (Claude Code CLI)

  • Official Anthropic CLI for Claude
  • Supports chat, session save/load, slash commands
  • Install: npm install -g @anthropic-ai/claude-code

Kiro-CLI

  • Alternative Claude interface provided by Amazon for AWS development
  • Supports /chat load and /chat save for session persistence
  • Install: See kiro-cli repository

Adding New Backends

Backends are defined in internal/backends/. To add a new backend:

  1. Create a new function in internal/backends/yourbackend.go:
func NewYourBackend() backend.Backend {
    return tmux.New(tmux.Config{
        Name:    "yourbackend",
        Command: []string{"your-cli-command", "args"},
        Environment: map[string]string{
            "TERM":     "xterm-256color",
            "NO_COLOR": "1",
        },
        TmuxSize: tmux.TmuxSize{
            Rows: 40,
            Cols: 120,
        },
        StartupTime: 15 * time.Second,
        Detector:    &yourDetector{},
        Cleaner:     &yourCleaner{},
        Commands:    &yourCommandHandler{},
    })
}
  1. Implement the required interfaces:

    • Detector - Detect when the backend is ready and when responses complete
    • Cleaner - Strip ANSI codes and noise from output
    • CommandHandler - Handle backend-specific slash commands
  2. Register in main.go:

backendMap := map[string]backend.Backend{
    "kiro-cli":    backends.NewKiroCLI(),
    "claude":      backends.NewClaude(),
    "yourbackend": backends.NewYourBackend(),
}
  1. Add tag command in main.go event loop

See internal/backends/kirocli.go or claude.go for complete examples.

Configuration

Terminal Command

The terminal used for Attach is configured in main.go:

const terminalCommand = "foot"  // Change to: "kitty", "xterm", "alacritty", etc.

Tmux Session Names

Backends create persistent tmux sessions named anvillm-<backend>:

  • anvillm-claude
  • anvillm-kiro-cli

These sessions persist across AnviLLM restarts and are cleaned up on exit.

Sandboxing

AnviLLM uses landrun to sandbox backend processes for enhanced security. Sandboxing restricts filesystem and network access for LLM-controlled processes while allowing backends to run with full permissions (--dangerously-skip-permissions, --trust-all-tools) within the sandbox.

How It Works

landrun --rw /project --rox /usr --connect-tcp 443 -- claude --dangerously-skip-permissions
         ↑                                                   ↑
         Sandbox layer (restricts access)                   Backend runs with full permissions
                                                             (but only within sandbox)

Quick Start

Sandboxing is enabled by default with a locked-down policy:

  • Filesystem: Read/write access to session working directory only
  • System binaries: Read-only + execute access to /usr, /lib, /bin, /sbin
  • Network: HTTPS (port 443) for API calls
  • Config files: Access to ~/.claude, ~/.kiro, ~/.config/anvillm

Requirements

  • landrun: Install with go install github.com/landlock-lsm/landrun@latest
  • Linux Kernel: 5.13+ with Landlock support
  • Fallback: Runs unsandboxed if landrun unavailable (best-effort mode)

Configuration

Configure sandboxing via the Sandbox tag command in the main /AnviLLM/ window:

  1. Click Sandbox to open the configuration window
  2. Click Edit to modify ~/.config/anvillm/sandbox.yaml
  3. Click Reload to reload configuration from disk
  4. Create new sessions to apply changes

Configuration file: ~/.config/anvillm/sandbox.yaml

Important: Changes apply to NEW sessions only. Active sessions are NOT affected.

Path Templates

Use these templates in filesystem paths:

  • {CWD} - Session working directory
  • {HOME} - User home directory (~)
  • {TMPDIR} - Temporary directory (/tmp)

Common Configurations

Development (Permissive)

network:
  enabled: true
  connect_tcp:
    - "443"
    - "80"
    - "3000-9000"

filesystem:
  rw:
    - "{CWD}"
    - "{HOME}/.npm"
    - "{HOME}/.cache"
    - "{HOME}/.local"
  rox:
    - "/usr"
    - "/lib"
    - "/bin"

Code Review (Restrictive)

network:
  enabled: false

filesystem:
  ro:
    - "{CWD}"  # Read-only project access
  rw:
    - "{TMPDIR}"
  rox:
    - "/usr"
    - "/bin"

Disabled (Testing Only)

general:
  enabled: false

Security Model

Default protections:

  • ✓ Cannot read sensitive files outside CWD (e.g., ~/.ssh/id_rsa, ~/.aws/credentials)
  • ✓ Cannot modify system files
  • ✓ Cannot make unauthorized network connections
  • ✓ Cannot access files in other users' directories

User can still:

  • Modify files in session CWD (by design - project workspace)
  • Weaken sandboxing via config (user choice)

Troubleshooting

Landrun not available?

  • Install: go install github.com/landlock-lsm/landrun@latest
  • Check status: Click Sandbox → Status in AnviLLM

Network errors?

  • Ensure connect_tcp = ["443"] in config for API calls
  • Or set unrestricted = true for full network access

File access denied?

  • Add required paths to rw or ro in config
  • Example for SSH keys: ro = ["{HOME}/.ssh"]
  • Example for NPM: rw = ["{HOME}/.npm", "{HOME}/.cache"]

Check sandbox status:

# View current config
cat ~/.config/anvillm/sandbox.yaml

# Test landrun availability
landrun --version

For more details, see landrun documentation.

Troubleshooting

"Backend not found" error

Make sure the backend CLI is installed and in your PATH:

which claude
which kiro-cli

Tmux windows not cleaned up

If AnviLLM exits unexpectedly, you may have orphaned tmux sessions:

tmux list-sessions | grep anvillm
tmux kill-session -t anvillm-claude
tmux kill-session -t anvillm-kiro-cli

Can't connect to 9P filesystem

Make sure plan9port is installed and 9P tools are working:

9p ls agent