A self-hosted, local AI agent that works while you're away
Run octomind as a service on your VPS or home server, point it at a local model or any of 32 providers, and hand it work from a shell, a webhook or a timer. The logs, the keys and the off switches stay on your box.
curl -fsSL https://octomind.run/install.sh | bash echo "You are the ops agent on this box. Reply: ready." | octomind run developer:general --name ops --daemon --hook deploy --format jsonl -m deepseek:deepseek-flash {"type":"assistant","content":"ready.","session_id":"ops"}
…
{"type":"injected","source_kind":"inject","source_label":"inject","content":"How full is the root disk? One line.","session_id":"ops"}
{"type":"thinking","content":"Check disk usage of root.","session_id":"ops"}
{"type":"tool_use","tool":"shell","tool_id":"call_00_BMqxrdWmjUtfumfbYW242746","server":"octofs","params":{"command":"df -h / | tail -1"},"session_id":"ops"}
{"type":"tool_result","tool":"shell","tool_id":"call_00_BMqxrdWmjUtfumfbYW242746","server":"octofs","content":"/dev/disk3s1s1 926Gi 12Gi 6.2Gi 66% 459k 65M 1% /","success":true,"session_id":"ops"}
{"type":"assistant","content":"Root (`/`) is 66% full — 12Gi used of 926Gi, 6.2Gi available.","session_id":"ops"}
…
How it works
Point your self-hosted AI agent at a model
The first run writes a full config file; edit these keys in it. The local: provider talks to Ollama on localhost:11434, or to LM Studio and any OpenAI-compatible server through LOCAL_API_URL. Set all three model slots, or the supervisor and compression calls still go to octohub.
~/.local/share/octomind/config/config.tomltelemetry = false # default is true max_request_spending_threshold = 0.50 # USD per turn; local models count a cloud-equivalent price [model] name = "local:qwen3-coder" # or a hosted one, e.g. "deepseek:deepseek-flash" [supervisor.model] name = "local:qwen3-coder" # default "octohub:auto" is a hosted call [compression.model] name = "local:qwen3-coder" [[hooks]] # delete a "hooks = []" line if your file has one name = "deploy" bind = "127.0.0.1:9876" script = "/home/you/ops/hooks/deploy.sh" timeout = 30Run it as a systemd user service
The --daemon flag keeps the session waiting for work, but it never detaches and it needs a first prompt on stdin. This unit feeds that prompt, appends every event to a JSONL file and restarts on failure. Each start resumes the ops session and spends one turn on the prompt.
~/.config/systemd/user/octomind-ops.service[Unit] Description=octomind ops agent [Service] WorkingDirectory=%h/ops # provider keys, one VAR=value per line; the leading "-" makes the file optional EnvironmentFile=-%h/.config/octomind-ops.env Environment=OCTOMIND_TELEMETRY=0 StandardInput=data StandardInputText=You are the ops agent on this box. Reply: ready. StandardOutput=append:%h/ops/ops.jsonl StandardError=append:%h/ops/ops.log # install.sh puts the binary in ~/.local/bin ExecStart=%h/.local/bin/octomind run developer:general --name ops --daemon --hook deploy --format jsonl Restart=on-failure RestartSec=10 # SIGTERM exits 130; treat a stop as clean SuccessExitStatus=130 [Install] WantedBy=default.targetHand it work from anywhere on the box
The octomind send command drops a message into the running session over a local socket. A POST to the hook port runs your script, and whatever it prints becomes the next message. Ask the agent for a timer and it sets one with its schedule tool.
mkdir -p ~/ops/hooks cat > ~/ops/hooks/deploy.sh <<'EOF' #!/bin/sh # body on stdin; headers arrive as HOOK_HEADER_* env vars body=$(cat) echo "Deploy webhook ($HOOK_PATH): $body. Check that the service on this box is healthy and report in one line." EOF chmod +x ~/ops/hooks/deploy.sh systemctl --user daemon-reload systemctl --user enable --now octomind-ops loginctl enable-linger "$USER" # keep user services running after you log out octomind send --name ops "How full is the root disk? One line." curl -X POST http://127.0.0.1:9876/deploy -d '{"service":"api","sha":"a1b2c3d"}' octomind send --name ops "Every 30 minutes, check the root disk and only speak up if it is over 80%. Set that up now." tail -f ~/ops/ops.jsonl
What you get
Stays up and takes messages from other processes
A one-shot run exits when its work is done; a --daemon run keeps waiting on its inbox. Every run binds a Unix socket named after the session, so octomind send --name ops from any shell of the same user lands as the next turn. A failed model call is logged and the daemon keeps listening.
src/session/chat/session/main_loop.rs:1902Webhooks become messages through your own script
Each [[hooks]] entry binds an HTTP listener. A POST body goes to your script on stdin and the headers arrive as HOOK_HEADER_* variables, so the script decides what the agent sees. Output means 200 and a new turn, empty output 204, a non-zero exit 500, a timeout 504.
src/session/webhook_listener.rs:191Timers the agent sets for itself
The built-in schedule tool takes a when ("in 5m", "9am", "2026-03-22 15:30", "idle") and an every ("10m", "1h"), plus list, edit and remove. Entries are written to the session log. They fire while the process runs; for a wall-clock job that must happen through downtime, point a systemd timer at octomind send.
src/mcp/orchestration/schedule/core.rs:260Remote clients over one WebSocket port
The octomind server command serves many named sessions, each with its own working directory, from one process. It binds 127.0.0.1 by default and requires a Bearer token when OCTOMIND_SERVER_TOKEN is set. The token is read only from the environment, never from a command-line flag.
src/commands/server.rs:64Your model: local, or any of 32 providers
A local:<model> string reaches Ollama on localhost:11434, and LOCAL_API_URL moves it to LM Studio, vLLM or any OpenAI-compatible server. The same provider:model string picks Anthropic, OpenAI, DeepSeek, OpenRouter and the rest. The repo has an end-to-end test that runs the real binary against a localhost model stub with zero network.
tests/cli_run_e2e_test.rs:15
Four turns from three directions for $0.016
Captured on 2026-10-22 on a macOS laptop, not a VPS, with octomind 0.58.8, deepseek:deepseek-flash and a fresh OCTOMIND_DATA_DIR. One daemon took its stdin prompt, two octomind send messages and one webhook POST. It ran df through its shell tool and armed a 30-minute schedule. No local-model run is shown because the capture machine had no Ollama or LM Studio installed. The zero-network path is covered by tests/cli_run_e2e_test.rs, which runs the real binary against a localhost stub with the supervisor, telemetry and auto-capabilities off.
octomind send --name ops "How full is the root disk? One line." ╭ send
│ session ops
│ chars 36
╰ ✓ send · ops
curl -sS -i -X POST http://127.0.0.1:9876/deploy -H 'content-type: application/json' -d '{"service":"api","sha":"a1b2c3d"}' HTTP/1.1 200 OK
content-length: 3
date: Thu, 22 Oct 2026 08:52:45 GMT
ok
jq -cR 'fromjson? | select(.type=="injected") | {source_kind, content}' ops.jsonl {"source_kind":"inject","content":"How full is the root disk? One line."}
{"source_kind":"webhook","content":"Deploy webhook (/deploy): {\"service\":\"api\",\"sha\":\"a1b2c3d\"}. Check that the service on this box is healthy and report in one line."}
{"source_kind":"inject","content":"Every 30 minutes, check the root disk and only speak up if it is over 80%. Set that up now."}
…
jq -rR 'fromjson? | select(.type=="tool_result" and .tool=="schedule") | .content' ops.jsonl ✅ Scheduled [f9aebb9c] at 2026-10-22 16:23:18 (in 29m 59s)
Description: Root disk watchdog: alert only when / exceeds 80%
Repeats: every 30m
The message will be injected as a user message when the timer fires.
1 scheduled entries:
[f9aebb9c] 2026-10-22 16:23:18 (in 29m 58s) — Root disk watchdog: alert only when / exceeds 80%
Message: Recurring root-disk watchdog (runs every 30 minutes). Do this: run exactly `df -…
🔁 Repeats every 30m
jq -cR 'fromjson? | select(.type=="cost") | {session_cost, input_tokens, output_tokens, cache_read_tokens}' ops.jsonl | tail -1 {"session_cost":0.016463988,"input_tokens":29375,"output_tokens":2445,"cache_read_tokens":309248}
What it doesn't do
Telemetry is on by default, and it names your role
At exit octomind posts command, flag and tool names, the model id, token counts, a machine id and the role or agent tag verbatim to api.octomind.run. Prompts, code, paths and argument values are not fields. Turn it off with telemetry = false in config.toml, OCTOMIND_TELEMETRY=0 or DO_NOT_TRACK=1. Docs →
ollama: means Ollama Cloud, and -m swaps only the main model
The ollama: provider defaults to https://ollama.com. Local Ollama is local: (localhost:11434), or ollama: with OLLAMA_API_URL set. Supervisor and compression stay on octohub:auto until you set [supervisor.model] and [compression.model] too. Docs →
--daemon does not detach or install a service
It keeps the session alive in the foreground and, when stdin is not a terminal (as under systemd), needs a non-empty first prompt on it. Use the systemd unit above or tmux. Schedules fire only while that process runs, and messages still queued when it exits are dropped. Docs →
The webhook listener has no auth and no TLS
Any POST that reaches the bind address runs your script with its body (capped at 10 MiB; larger bodies get 413), and what the script prints becomes an instruction to the agent. Keep bind on 127.0.0.1, put nginx or Caddy in front for TLS, and verify the sender signature from HOOK_HEADER_* before the script prints anything. Docs →
The first run of a tap agent needs the network
Tags like developer:general git-clone github.com/muvon/octomind-tap into ~/.local/share/octomind/taps and run its install scripts; later pulls fail silently offline. For an offline box, run once online and copy taps/, or use a plain role from config.toml, which reads no tap. Also set auto_capabilities = false and skills.auto_activation = false, which otherwise download a small embedding model from Hugging Face on first use. Docs →
Questions
What is a self-hosted AI agent?
It is an agent loop (model calls, tool calls, conversation state) that runs as a process on hardware you control instead of inside a vendor app. With octomind that process is one binary on your server or laptop, and the model behind it can be local through Ollama or LM Studio, or any hosted provider you hold a key for.
Can I run a local AI agent with no internet connection?
Yes, if every model slot is local: set the main, supervisor and compression models to local:<model>, set telemetry = false, use a plain role or a pre-copied taps folder, and turn off auto_capabilities and skills.auto_activation. The repo test tests/cli_run_e2e_test.rs runs the real binary against a localhost model stub with zero network.
What data leaves my machine?
Prompts and tool output go to the model provider you configure, which stays on the box with local: models. Telemetry is on by default and posts usage identifiers, including your role name, to api.octomind.run at exit until you set telemetry = false. Tap agents git-pull from GitHub when they load, and the routing embedding model downloads once from Hugging Face.
How do I keep a self-hosted AI agent running after logout or reboot?
An octomind run --daemon process stays in the foreground, so give it a supervisor: the systemd user unit on this page restarts it on failure, and loginctl enable-linger keeps user services running without a login. On restart, --name ops resumes the same session and spends one turn on the stdin prompt from the unit.
How much does it cost to run?
Octomind is free under Apache-2.0; you pay for model tokens or for your own hardware. The four-turn capture on this page cost $0.016 on deepseek:deepseek-flash. With local: models the cost line shows a cloud-equivalent reference price, not a bill.
Is octomind an open source AI personal assistant?
It is an Apache-2.0 agent runtime, and its default agent, assistant:concierge, routes each request to a specialist agent and remembers you across sessions. The developer:general agent adds shell, file and code-search tools for work on the box itself.
Other ways to run octomind
- Agentic workflows Multi-step agent pipelines in one TOML file: parallel, loop until it passes, fan-out.
- MCP client Plug any stdio or HTTP MCP server into a terminal agent, and add more mid-session.
- Agent orchestration A lead agent hands work to specialists at runtime. You get one bill and one verdict.
- Self-improving agent Correct the agent once. It saves the fix as a file and recalls it next session.
- Agent backend Your app talks to one WebSocket. octomind runs the agent, its tools and the bill per user.
Start with one command
Apache-2.0 and any model. Install it, then run step one above.
curl -fsSL https://octomind.run/install.sh | bash