
An AI personal assistant with a focus on security.

Stavrobot is a personal AI assistant built with the principle of "all the access an AI assistant needs, and no more".
It has all the nice features of an AI assistant, but focuses on sandboxing, isolation, and minimal permissions. It's lightweight and is deployed with only a docker compose up.
uv, isolated from the host environment.AI-assisted install: Tell your coding AI (Claude Code, Cursor, Windsurf, etc.) to fetch and follow https://stavrobot.stavros.io/INSTALL.md — it will walk you through the whole setup interactively.
Manual install:
Message @BotFather on Telegram to create a bot and copy the token. Message @userinfobot to get your Telegram user ID (this is used as the chat ID).
Copy env.example to .env and change POSTGRES_PASSWORD to something secure and TZ to your timezone.
Copy config.example.toml to data/main/config.toml and fill in the required fields: apiKey, password, publicHostname, [owner].name, [owner].telegram (your chat ID), and [telegram].botToken. Everything else in the file is optional.
docker compose up --build
That's it. Message your bot on Telegram and it will respond. See the detailed setup sections below for Signal, WhatsApp, email, and other options.
config.example.toml to data/main/config.toml.authFile (or apiKey) and publicHostname. Everything else is optional.env.example to .env and set your timezone (TZ). Postgres credentials and other environment settings can also be overridden there. Always set POSTGRES_PASSWORD to something secure — the default is a weak placeholder and should not be used in production.After the app starts, open /settings/config to edit config.toml in the browser. The
page is protected by HTTP Basic Auth and displays the raw configuration, including any
secrets it contains. A valid save creates a single <CONFIG_PATH>.bak backup, saves the
new content, and restarts the app; this interrupts any in-progress agent turn.
plugin-runner, coder, signal-bridge, and python-runner read config.toml only
when they start. Changes that affect them, such as the password, require manually
restarting those containers.
Stavrobot can be pointed at any OpenAI-compatible endpoint (Ollama, LiteLLM, vLLM, etc.) or a custom Anthropic-compatible proxy by setting baseUrl in config.toml. See config.example.toml for the required fields and example configurations.
The app supports two authentication modes: API key or OAuth.
apiKey in config.toml. No login or logout needed.authFile in config.toml (a path where credentials will be stored). The login page works with any OAuth provider supported by Pi.
<your-hostname>/login in a browser. Follow the prompts on the page, and credentials are saved to the auth file. If auth expires while the bot is running, it sends a message with the login URL to you via your messaging platform.authFile path. The bot will detect missing credentials on the next message and prompt you to log in again.The coder container is optional (needed only for the self-programming feature). It uses Claude Code with subscription auth (OAuth), separate from the main app's API key.
Docker Compose profiles are comma-separated, so you can combine them (e.g. COMPOSE_PROFILES=signal,coder).
COMPOSE_PROFILES in your .env file to include coder (e.g. COMPOSE_PROFILES=coder, or COMPOSE_PROFILES=signal,coder if you are also using Signal).docker compose --profile coder up --builddocker compose exec -u coder coder claude (it will prompt you to log in if you haven't).[coder].model in your config to a Claude Code model alias (sonnet, opus, or haiku).Signal requires a separate phone number — not your personal one. A prepaid SIM or VoIP number works.
COMPOSE_PROFILES=signal in your .env file to enable the signal-bridge container.docker compose --profile signal builddocker compose --profile signal run --rm --entrypoint bash signal-bridge -c 'signal-cli link -n "Stavrobot" | tee >(xargs -L 1 qrencode -t utf8)' — scan the QR code with your phone (Signal > Settings > Linked devices).docker compose --profile signal run --rm --entrypoint bash signal-bridge -c 'signal-cli -u +YOUR_NUMBER register', then verify with docker compose --profile signal run --rm --entrypoint bash signal-bridge -c 'signal-cli -u +YOUR_NUMBER verify CODE'.[signal].account in your config.docker compose up --build/settings web UI.docker compose exec app node -e "fetch('http://localhost:3001/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:'Send the message \"Hello from Stavrobot\" to my Signal number.'})}).then(r=>r.text()).then(console.log)"