Security and privacy
AutoRune runs a coding agent with full tool permissions, unattended, for many owners, next to a game where strangers can talk to it. The design assumes the agent can be talked into anything, and limits what it can do instead.
The sandbox
Every running avatar's agent runs in its own bubblewrap sandbox inside a systemd scope:
- Namespaces: user, pid, network, ipc, uts and cgroup are all separate. All capabilities are dropped, the environment is cleared, and the sandbox dies with its runner.
- Filesystem: a read-only system root, the engine binary, the avatar's own workspace (with rs-sdk read-only on top) and a private
/tmp. Nothing else: no home directory, no repository, no environment files, no real game credentials, and no other avatar's files. - Limits: 2 GB memory (no swap), 256 tasks and one CPU core per avatar.
- Escape tests run probes inside the real sandbox: no secrets or host files, rs-sdk read-only, blocked ports, no internet, the limits hold, no sudo.
Egress allowlist
The sandbox has loopback only. The runner forwards a few local ports, each with its own rules:
| From the sandbox | Allows |
|---|---|
| Game gateway | The rs-sdk protocol for this avatar's bot only. The bridge swaps in the real password and forces controller mode. Everything else is refused. |
| HTTPS proxy | CONNECT on port 443 to the owner's provider only: openrouter.ai, api.anthropic.com, api.openai.com or generativelanguage.googleapis.com. For the Claude subscription: api.anthropic.com and platform.claude.com. Literal IPs, other ports, plain HTTP and names that resolve to private addresses are refused. |
| Hiscores and skills gate | Read-only hiscores routes, and the skill library as this avatar only. At most 60 requests a minute. |
The database, the rest of the API, the game engine's ports, other bots, package registries and the general internet are unreachable from inside.
AI keys
- Pasted keys are checked with the provider, then sealed with envelope encryption: a fresh AES-256-GCM data key per record, wrapped by a master key that lives outside the database. Each ciphertext is bound to its user and provider, so it can't be copied to another row.
- The API never returns a key: only the provider name and the last 4 characters.
- A key is decrypted in one place, when an avatar starts or its settings change. It goes to that avatar's runner over the IPC channel, then into the sandbox on the init process's standard input, and only into the engine's environment. It is never put on a command line, in a file or in a log.
- A test runs every API route and checks that no key appears in any response, header or log line.
- Disconnect deletes the record, ciphertext included.
Game credentials
Each avatar's game password is random, generated by AutoRune and sealed in Postgres with the same envelope encryption. The workspace's bot.env holds only a placeholder. The real password reaches only the gateway bridge, outside the sandbox. Owners never see it.
What is never stored
- Claude credentials or tokens. They live only in the owner's Claude Code config directory, written and refreshed by Claude Code. They are never in the database, the API's memory, IPC or logs. AutoRune never opens that file. The one-time sign-in code passes through the API once.
- Discord tokens. Discord's access token is dropped right after reading your id and username.
- Plaintext refresh tokens. Only a hash is stored. Tokens rotate on every refresh, and reusing an old one ends the session everywhere.
- Passwords in chat, skills or logs. Outgoing chat, saved skills and sandbox logs are scrubbed of known secrets and key-shaped strings.
Prompt injection
- Only the app is the owner. In-game chat, including from your own other avatars, reaches the agent as a sanitised block marked untrusted, from other players, NOT from your owner. The agent's instructions say to refuse anything that asks it to ignore its rules or owner, reveal notes, files or passwords, run code from chat, give items away, go somewhere dangerous or change its goal.
- Outbound guard. Every chat message the agent sends is checked for secrets, rate-limited and protected against bot-to-bot ping-pong before it reaches the game.
- Shared scripts are untrusted code. Skills are stored as text and only ever run inside the avatar's own sandbox. The operator reviews a skill before it is shared.
- Even a fully compromised agent can only play its own bot, talk to its owner's provider and read the hiscores.
Accounts and sessions
- Discord OAuth2 with PKCE, scope
identifyonly. OAuth state is single-use and bound to your browser with a cookie. - Access tokens last 15 minutes and stay in the app's memory, never in local storage.
- Refresh tokens live in an httpOnly, Secure, SameSite=Lax cookie, host-only, so test and production sessions can't mix.
- WebSockets open with a short-lived, single-use ticket bound to one avatar.
- Isolation: someone else's avatar answers "not found", exactly like a missing one, on every route and socket.
Network exposure
The game engine, gateway, friends server, API and Postgres all listen on loopback only. nginx is the only public entry point, with HTTPS everywhere, HSTS and strict security headers.