Tunnel
Local models
Use Ollama, LM Studio, vLLM or llama.cpp on your own computer as a model provider for your agents, through the tunnel.
A model server running on a connected computer can power your agents. The server stays on
localhost, nothing is exposed to the internet, and the prompts and answers travel only over the tunnel's own
outbound connection.
Two kinds of local server are supported:
| Harness | Servers | Found at |
|---|---|---|
ollama |
Ollama | http://localhost:11434 |
openai-compatible-local |
LM Studio, vLLM, llama.cpp, or anything that speaks the OpenAI chat API | http://localhost:1234 (LM Studio), :8000 (vLLM), :8080 (llama.cpp) — the first that answers |
(Codex and Claude Code can also run on the computer as models. They work differently; see Codex and Claude Code.)
Register
Run register once, as the daemon's run-as user, with the model server running:
sudo -u <run-as-user> agentparley register
It checks each harness and prints one line per harness:
– codex not found
– claude not found
✓ ollama registered — 3 models
✓ openai-compatible-local registered — 1 model
Each registered harness becomes a provider in your console, named after it and the computer (for example "Ollama (local, via tunnel) on homelab"), with one model per model the server lists. Pick those models for an agent like any other model. You change nothing in the console: the server's address and its model list stay on your computer.
Pulled a new model, or removed one? Run register again; it is safe to repeat. agentparley unregister ollama
removes the provider.
Context windows
AgentParley needs each model's context window to know when to compact a long chat. register reads it from the
server:
- Ollama: the model's
context_length, from/api/show. - llama.cpp: the context the server was actually started with (
n_ctxfrom/props), which can be smaller than the model's trained context. - vLLM:
max_model_lenfrom/v1/models.
When a server doesn't report one (LM Studio, for example), give it in the config, or the model is not registered:
harnesses:
openai-compatible-local:
url: http://localhost:1234 # optional; overrides discovery
models:
- id: qwen3-32b
label: Qwen3 32B
context_window_tokens: 32768
ollama:
url: http://localhost:11434 # only needed for a non-default address
Give the server root as url; a trailing /v1 is trimmed for you.
How a request travels
agent turn ──▶ SSH egress ──▶ your daemon ──▶ POST localhost:<port>/v1/chat/completions
(messages + tool definitions)
◀── answer or tool calls ◀──────────┘
Each model step is one request. The daemon sends the chat's messages and the agent's tools to your server's OpenAI-compatible chat endpoint, and returns the answer or the tool calls the model asked for. AgentParley runs the tools itself (SSH commands, web search, plugins, other agents) and sends the results back in the next step. Your local model therefore uses every tool your agent has, but the model server never runs a tool itself.
Things to know:
- Use a model that supports tool calling. Agents depend on tools; a model without tool calling can only chat.
- Text only. Images are not sent to local models.
- One step can take up to 10 minutes. Slow hardware with a big model is fine, within that limit.
- No per-token price. You run the hardware, so AgentParley records token counts for these models at no cost.
- Local model requests share the computer's operation limit (
max_concurrent_operations, default 8) with SSH commands.
Keeping a harness off
To stop register from ever offering a harness on a computer, deny it in the config:
deny_harnesses: ["openai-compatible-local"] # deny always beats allow
allow_harnesses: [] # empty = all harnesses considered
The daemon also checks this list on every request, so denying a harness that was already registered stops it at once.