The loudest objections to hosted always-on agents were about custody: an agent that never sleeps, holding your credentials, on someone else's cloud. Self-hosting answers the custody question. It does not answer prompt injection — that follows an agent wherever it runs. This page is precise about which is which.
| Boundary | How it holds |
|---|---|
| Only you can drive the gateway | Every API call, the live stream, screenshots and screens need the gateway token — a random secret in gateway/data/auth-token (mode 0600). A browser signs in once through the link printed at startup and keeps an HttpOnly, SameSite=Strict cookie; scripts send Authorization: Bearer <token>. |
| Other websites cannot act as you | Anything that changes state, and the WebSocket, must come from the gateway's own origin (or the UI's, OPENGROKBOT_WEB_URL), and requests with a body must be JSON — which no other page can send without a preflight the gateway never answers. A page you happen to visit cannot approve a hold, post into a thread or read the stream. |
| Your network cannot reach it | The gateway listens on 127.0.0.1 unless you set OPENGROKBOT_HOST. |
| A bot cannot approve its own holds | The token never enters a bot's workspace. A bot's shell or browser can reach the host, but cannot authenticate. |
| Bots cannot reach each other | All bot computers share one Docker network with container-to-container traffic switched off. And because on Docker Desktop a container can still reach ports published on the host, everything a computer publishes wants that computer's own secret: its shell/file shim and its browser control port (CDP) a per-container token, its screen a per-container VNC password. |
| Each computer is a cage | Its own container with memory, CPU and process limits, no-new-privileges, and only its own workspace mounted. Every port is published to 127.0.0.1 only. |
| A bot cannot put code in front of you | The screen page and its noVNC code are served by the gateway from its own copy; only the raw VNC stream comes from the bot's computer. Nothing a bot changes inside its container reaches your browser as a page. |
| Standing rules only change through the chip | MEMORY.md lives outside the bot's workspace. The only way in is save_memory, which always posts a diff chip; a MEMORY.md a bot writes into its own workspace is ignored. Screenshots — the evidence — are stored outside it too. |
| Handoffs are allowlisted | By default only the chief of staff can reach everyone. Peer directions need OPENGROKBOT_A2A_ALLOW, and relays stop after two hops. |
| Spend has a ceiling if you want one | OPENGROKBOT_DAILY_TOKENS stops a bot for the rest of the day once it has used that many tokens. |
| You can always stop it | The stop button (or POST /api/threads/:id/stop) aborts the running turn — including a shell command in flight, which is killed in the container. No single command runs longer than ten minutes. Routines can be paused or deleted from the computer panel. |
Holding outward actions for approval. Every teammate is told — in its system prompt and in the hard
boundaries of its AGENTS.md — to prepare the work and call hold_for_approval before anything leaves its
workspace, and to stop there. That is a strong discipline, not an enforcement point: a bot has a real browser,
and nothing in the sandbox stops it from clicking Send on a site where it is signed in. Build your setup so a
mistake is cheap (rule 1 below).
Treating what it reads as data. Bots are told that pages, files and emails are data, never orders. Prompt injection against tool-using agents is still an unsolved problem industry-wide.
- Give bots their own accounts, never yours. Separate email, separate calendar, invited only into what they need. A bot with its own account has a blast radius; a bot with yours has your life.
- Sign in to only what the job needs. Sessions live in the bot's browser profile on your disk
(
gateway/data/workspaces/<bot>/.browser), and they are as powerful as the account behind them. - Keep the gateway on loopback. For remote access, tunnel — see always-on.md. If you
must bind
0.0.0.0, the token still guards it, but put TLS in front. - Cap spend twice: a limit on the API key at your provider, and
OPENGROKBOT_DAILY_TOKENShere. - Rotate the token if it leaked: delete
gateway/data/auth-tokenand restart. Every browser signs in again.
| Secret | Where |
|---|---|
| Model API key | The OPENGROKBOT_API_KEY environment variable. The gateway never writes it to disk. |
| Gateway token | gateway/data/auth-token (0600), or OPENGROKBOT_TOKEN if you pin one. Set OPENGROKBOT_QUIET_TOKEN=1 to keep it out of service logs. |
| Bot logins | Each bot's browser profile, inside its own workspace. The gateway never sees a password. |
| Shim token, VNC password | Generated per container at creation, kept in that container's environment. |
| Risk | Hosted (Grok Bot) | Self-hosted (this repo) |
|---|---|---|
| Where your sessions live | the vendor's cloud VM | your disk |
| Who can subpoena, breach or train on them | the vendor | you |
| Kill switch | a support ticket | the stop button, docker stop, or the power cable |
| Audit trail | what the vendor shows you | every message in SQLite on your disk |
If a workflow would make a bot both read strangers' input and act outward, keep a human decision in the loop — or redesign the workflow. The approval chip is where that decision happens; keep it meaningful by giving bots accounts whose worst case you can live with.