Sandbox
@NERVsystems/llm9p

9P filesystem server for LLM access

llm9p turns an LLM into a virtual filesystem that 9P clients can mount. A prompt goes into `ask`, the response comes back from `ask`, and other files like `model`, `temperature`, `system`, and `context` control the conversation.

46 stars4 forksGoUpdated 16d ago
Who it's for

Builders who want their agent or shell to talk to an LLM through mounted files instead of SDK calls.

What it delivers

You can script LLM calls with standard file reads and writes instead of wiring up API clients.

What it does

Filesystem prompt and reply

Write to `ask` to send a prompt, then read the same file to get the response.

Multiple backends

Uses Anthropic API or the Claude Code CLI today, with local LLM support planned.

Conversation state files

Exposes `model`, `temperature`, `system`, `tokens`, `context`, and `new` for controlling and resetting chat state.

Streaming output

Provides `stream/ask` and `stream/chunk` for reading long answers as they arrive.

9P client compatibility

Works with plan9port, Infernode, Linux 9P mounts, and other 9P-capable clients.

How to get it

  1. 1Run
    go install github.com/NERVsystems/llm9p/cmd/llm9p@latest
  2. 2Or build from source
    git clone https://github.com/NERVsystems/llm9p
    cd llm9p
    go build -o llm9p ./cmd/llm9p
  3. 3Option A: Using Anthropic API (requires API key)
    export ANTHROPIC_API_KEY=sk-ant-...
    ./llm9p -addr :5640
  4. 4If you have a Claude Max subscription and the Claude Code CLI installed
    ./llm9p -addr :5640 -backend cli
  5. 5Linux has built-in 9P filesystem support via the 9p kernel module.
    # Mount via kernel 9p module
    sudo mount -t 9p -o port=5640,version=9p2000 localhost /mnt/llm

README

llm9p

LLM access via the 9P filesystem protocol.

llm9p enables users, scripts, and AI agents to interact with Large Language Models through standard filesystem operations. Write a prompt to a file, read the response from the same file. Supports multiple backends including Anthropic API, Claude Code CLI, and local LLMs (planned).

Supported Backends

BackendStatusDescription
Anthropic APIAvailableDirect API access with your API key
Claude Code CLIAvailableUses Claude Max subscription via claude command
Local LLMs (Ollama)PlannedRun models locally without cloud dependencies

The pluggable backend architecture makes it easy to add new LLM providers.

What is 9P?

9P is a simple, lightweight network filesystem protocol originally developed for Plan 9 from Bell Labs. It lets you access remote resources as if they were local files. This means you can interact with LLMs using basic file operations (cat, echo, >, <) instead of SDKs or HTTP APIs.

Why use 9P for LLM access?

  • Universal: Any language or tool that can read/write files can use it
  • Scriptable: Chain LLM calls with standard Unix pipes and shell scripts
  • Composable: Mount multiple 9P services and combine them
  • Simple: No libraries, no dependencies, just files

Installation

go install github.com/NERVsystems/llm9p/cmd/llm9p@latest

Or build from source:

git clone https://github.com/NERVsystems/llm9p
cd llm9p
go build -o llm9p ./cmd/llm9p

Usage

Start the Server

Option A: Using Anthropic API (requires API key)

export ANTHROPIC_API_KEY=sk-ant-...
./llm9p -addr :5640

Option B: Using Claude Max Subscription (via Claude Code CLI)

If you have a Claude Max subscription and the Claude Code CLI installed:

./llm9p -addr :5640 -backend cli

This uses your Claude Max subscription instead of API tokens. No API key required.

Requirements for CLI backend:

  • Claude Code CLI installed and authenticated (claude command available)
  • Active Claude Max subscription

Mount the Filesystem

There are several ways to mount the filesystem depending on your environment.

Option 1: Plan 9 from User Space (plan9port)

plan9port provides Plan 9 tools for Unix systems.

# Install on macOS
brew install plan9port

# Install on Debian/Ubuntu
sudo apt-get install 9base

# Mount using 9pfuse
mkdir -p /mnt/llm
9pfuse localhost:5640 /mnt/llm

# Or use the 9p tool directly (no mount needed)
9p -a localhost:5640 ls llm
9p -a localhost:5640 read llm/model
9p -a localhost:5640 write llm/ask "What is 2+2?"
9p -a localhost:5640 read llm/ask

Option 2: Infernode (Inferno OS)

Infernode is a hosted Inferno OS environment with native 9P support. This is an excellent way to explore 9P filesystems.

# Start infernode
cd /path/to/infernode
./emu

# Inside infernode shell, mount llm9p
# Note: Use IP address 127.0.0.1, not "localhost"
mkdir /n/llm
mount -A tcp!127.0.0.1!5640 /n/llm

# List available files
ls -l /n/llm

# Check current model
cat /n/llm/model

# Ask a question
echo 'What is the capital of France?' > /n/llm/ask
cat /n/llm/ask

# View conversation history
cat /n/llm/context

# Reset conversation
echo reset > /n/llm/new

Infernode Tips:

  • Always use 127.0.0.1 instead of localhost for the server address
  • Create the mount point with mkdir /n/llm before mounting
  • The -A flag to mount enables anonymous authentication (no auth required)
  • Use single quotes around prompts to avoid shell interpretation issues

Option 3: Linux 9P Mount

Linux has built-in 9P filesystem support via the 9p kernel module.

# Mount via kernel 9p module
sudo mount -t 9p -o port=5640,version=9p2000 localhost /mnt/llm

Interact with the LLM

# Ask a question
echo "What is 2+2?" > /mnt/llm/ask
cat /mnt/llm/ask

# View token usage
cat /mnt/llm/tokens

# Change model
echo "claude-3-haiku-20240307" > /mnt/llm/model

# Adjust temperature
echo "0.5" > /mnt/llm/temperature

# Set a system prompt (persists across conversation resets)
echo "You are a helpful coding assistant." > /mnt/llm/system

# View current system prompt
cat /mnt/llm/system

# View conversation history
cat /mnt/llm/context

# Add a system message
echo "You are a helpful coding assistant." > /mnt/llm/context

# Reset conversation
echo "" > /mnt/llm/new

# View help
cat /mnt/llm/_example

Filesystem Schema

/llm/
├── ask              # Write prompt, read response (same file)
├── model            # Read/write: current model name
├── temperature      # Read/write: temperature float (0.0-2.0)
├── system           # Read/write: system prompt (persists across resets)
├── tokens           # Read-only: last response token count
├── new              # Write anything to start fresh conversation
├── context          # Read: conversation history; Write: add system message
├── _example         # Read-only: usage examples
└── stream/          # Streaming interface
    ├── ask          # Write-only: starts a streaming request
    └── chunk        # Read-only: blocks until next chunk, EOF on completion

File Behaviors

FileReadWrite
askReturns last LLM responseSends prompt to LLM (sync), stores response
modelReturns current model nameSets model for subsequent requests
temperatureReturns current temperatureSets temperature (0.0-2.0)
systemReturns current system promptSets system prompt (persists across resets)
tokensReturns last response token countPermission denied
newPermission deniedAny write resets conversation (keeps system prompt)
contextReturns JSON conversation historyAppends system message to history
_exampleReturns usage examplesPermission denied
stream/askPermission deniedStarts a streaming request
stream/chunkBlocks until next chunk, returns itPermission denied

Streaming

For long responses, use the streaming interface to see output as it's generated:

# Start a streaming request (using 9p tool)
echo "Write a poem about the moon" | 9p -a localhost:5640 write stream/ask &

# Read chunks as they arrive
while chunk=$(9p -a localhost:5640 read stream/chunk 2>/dev/null); do
  [ -z "$chunk" ] && break
  printf "%s" "$chunk"
done

With a mounted filesystem:

# Start streaming in background
echo "Explain quantum computing" > /mnt/llm/stream/ask &

# Read chunks
while read -r chunk < /mnt/llm/stream/chunk 2>/dev/null; do
  printf "%s" "$chunk"
done

Note: Start reading chunks immediately after writing to stream/ask. If you wait too long, the stream may complete and you'll get EOF.

Shell Scripting

#!/bin/sh
# ask.sh - Simple LLM query script

if [ -z "$1" ]; then
    echo "Usage: $0 <question>"
    exit 1
fi

echo "$1" > /mnt/llm/ask
cat /mnt/llm/ask

Configuration

Command Line Flags

FlagDefaultDescription
-addr:5640Address to listen on
-backendapiBackend: api (Anthropic API) or cli (Claude Code CLI)
-debugfalseEnable debug logging

Environment Variables

VariableRequiredDescription
ANTHROPIC_API_KEYFor api backendYour Anthropic API key

Default Settings

  • Model: claude-sonnet-4-20250514 (API) or sonnet (CLI)
  • Temperature: 0.7
  • Max Tokens: 4096

Backend Differences

FeatureAPI BackendCLI Backend
AuthenticationAPI key requiredClaude Max subscription
Token countingAccurateNot available (always 0)
Model namesFull namesAliases (opus, sonnet, haiku)
StreamingTrue streamingSimulated (full response)
Rate limitsAPI limits applySubscription limits apply

Requirements

  • Go 1.21+
  • LLM backend (one of the following):
    • Anthropic API key (for api backend)
    • Claude Code CLI with Max subscription (for cli backend)
  • 9P client (one of the following):
    • plan9port - Plan 9 tools for Unix (macOS, Linux)
    • Infernode - Hosted Inferno OS with native 9P
    • Linux 9P kernel module - Built into Linux kernel
    • Native Plan 9 or Inferno OS

Troubleshooting

Port already in use

# Check what's using the port
lsof -i :5640

# Use a different port
./llm9p -addr :5641

Connection refused

Ensure the server is running and listening on the expected port:

# Start with debug logging to see connections
./llm9p -addr :5640 -debug

Infernode: "localhost" not resolving

Inferno's DNS resolution works differently. Use the IP address directly:

# Wrong
mount -A tcp!localhost!5640 /n/llm

# Correct
mount -A tcp!127.0.0.1!5640 /n/llm

Infernode: Mount point doesn't exist

Create the mount point before mounting:

mkdir /n/llm
mount -A tcp!127.0.0.1!5640 /n/llm

Permission denied on write

Some files are read-only by design:

  • tokens - Read-only (token count from last response)
  • _example - Read-only (usage examples)
  • stream/chunk - Read-only (streaming output)

Empty response from ask

The ask file only contains content after you write a prompt to it:

# First write a prompt
echo "Hello" > /mnt/llm/ask

# Then read the response
cat /mnt/llm/ask

API errors

Check that your API key is set and valid:

# Ensure the key is exported
export ANTHROPIC_API_KEY=sk-ant-...

# Verify it's set
echo $ANTHROPIC_API_KEY

How It Works

  1. Server starts: llm9p listens for 9P connections on the specified port
  2. Client connects: A 9P client (9pfuse, Infernode, etc.) connects and negotiates the protocol
  3. Filesystem exposed: The client sees a virtual filesystem with files like ask, model, tokens
  4. Write prompt: Writing to ask sends the text to the configured LLM backend
  5. Read response: Reading from ask returns the LLM's response
  6. State persists: Conversation history is maintained until you write to new

The 9P protocol handles all the complexity of making this look like a regular filesystem, so any tool that can read and write files can interact with the LLM.

Related Projects

  • Infernode - Hosted Inferno OS with native 9P support
  • plan9port - Plan 9 from User Space
  • u9fs - 9P file server

License

MIT

Files in the repo

Repository payload9 top-level entries
  • cmd
  • docs
  • internal
  • .gitignore
  • CLAUDE.md
  • go.mod
  • go.sum
  • LICENSE
  • README.md

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More tools

JuliusBrussee/
caveman

🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman

105k
1 add
MemPalace/
mempalace

The best-benchmarked open-source AI memory system. And it's free.

59k
stablyai/
orca

Orca is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop, mobile and remote runtime.

66k

A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io

132k

Never stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors

64k
headroomlabs-ai/
headroom

Compress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.

71k