Skip to content

Security

Jobs run code that a model writes after reading web pages and tool output, so Umpteenth treats every sandbox as untrusted and keeps credentials on the host wherever it can. Work through the checklist first, and read the section behind any item you can’t tick.

  • Umpteenth sits behind HTTPS, and app.url is the https:// address, see Reverse proxy.
  • Port 8080 is reachable only through the proxy, and port 8081 stays unpublished, as Reverse proxy shows.
  • Every sign-in provider under auth.providers admits only the people who should run jobs, see Sign-in.
  • Only its owner can read config.yml (chmod 600 config.yml), which holds the encryption key and the client secrets of your sign-in providers, and a copy of the key sits next to your backups.
  • Jobs that don’t need the whole internet use Allowed domains only or No network.
  • sandbox.allow_unrestricted_network is false unless a job needs tools that ignore proxy settings, see the unrestricted network.
  • network.blocked_targets lists the host’s own public IP addresses.
  • network.allow_private_targets is false unless a model server, MCP server or webhook receiver lives on your network.
  • Sandboxes run under gVisor if the instance runs jobs you didn’t write.

Umpteenth creates sandboxes, networks and images through the Docker Engine API, so the compose file mounts the Docker socket into its container. Access to that socket amounts to root on the host: whoever takes over the Umpteenth container owns the host too.

A socket proxy between Umpteenth and the engine narrows it, when it allows only the parts of the API Umpteenth calls, write requests included: ping, version, info, containers, exec, images, build and networks. Set DOCKER_HOST to its address, such as tcp://socket-proxy:2375, in place of the socket mount. Creating containers stays as powerful as root, so the proxy shrinks what else a compromised Umpteenth can call.

gVisor protects the host from code that escapes a sandbox, and leaves the socket as it is. On Kubernetes, Umpteenth needs no socket and manages sandbox pods through the API server, with rights limited to one namespace.

Umpteenth creates each sandbox with these settings:

Setting Default
User agent (uid 1000), or root when the job turns on Run as root
Capabilities All dropped for agent, Docker’s default set for root
no-new-privileges On, for root as well
Limits 1 CPU, 1024 MB of memory without swap, 256 processes and 15 minutes per run, see Job settings
Disk No limit, so a runaway job can fill the Docker host’s disk
Lifetime The run’s timeout plus 5 minutes

Each run gets its own Docker network, where the sandbox reaches Umpteenth’s broker, the API that the ump CLI calls for models, MCP tools and job state. The broker accepts the run’s token only while the run executes. The job’s Network setting and its Allow private network switch set what the sandbox reaches beyond that:

Network Internet Private networks and the Docker host Cloud metadata
Internet access Yes, through the egress proxy No No
Internet access with Allow private network Yes, through the egress proxy Yes No
Allowed domains only The listed domains, through the egress proxy No No
Allowed domains only with Allow private network The listed domains Listed domains that resolve to private addresses No
No network No No No
Unrestricted network Yes Yes Yes

Private networks are ranges such as 10.0.0.0/8, 192.168.0.0/16, 100.64.0.0/10 and fc00::/7. Cloud metadata is 169.254.0.0/16, 100.100.100.200 and fd00:ec2::254, and the egress proxy never connects to it or to a loopback address, with or without Allow private network. A sandbox that may reach the Docker host can reach Umpteenth’s published port too, where it needs a session or an API token like any other client.

Sandboxes explains the settings from a job’s point of view.

Internet access and Allowed domains only sandboxes have no route out of their run network, and reach the outside through the egress proxy, which runs inside Umpteenth on the broker port. Before each connection, the proxy checks the address the name resolved to, so a public name that points at a private address gets no further than the address itself. For every job, it also refuses the address of any container on the engine, which keeps one run away from another run’s sandbox. On Kubernetes, it refuses the pod and Service networks you list in the chart’s sandbox.clusterRanges instead, and every private address while that list is empty (Install on Kubernetes).

The proxy treats the Docker host’s public address like any other public address. Add the host’s public IP addresses to network.blocked_targets to keep sandboxes off the services that listen on them:

config.yml
network:
blocked_targets:
- 203.0.113.7
- 2001:db8::7

Entries take single addresses or CIDR ranges, such as 198.51.100.0/24, and Umpteenth refuses to start with anything else. The list applies to Umpteenth’s own outbound calls as well.

An Allowed domains only job gets through only with a name on its list, never with a bare IP address. The proxy matches the name a client asks for and doesn’t look inside the encrypted connection, so a client can use domain fronting to reach other sites behind a CDN that serves a listed name.

Unrestricted network gives a sandbox a direct route out, with no proxy and no checks, so it reaches everything the host can: the internet, your LAN, the Docker host and cloud metadata. Umpteenth keeps these sandboxes apart with a bridge that blocks traffic between its containers. OrbStack doesn’t enforce that block, so run jobs you don’t trust on a Linux engine.

Jobs can pick it while sandbox.allow_unrestricted_network is true, the default. Set it to false to take the option away, and pick another Network in the Sandbox card of each job that still uses it, since their runs fail with Failed to create the sandbox from then on.

Umpteenth, on your hostSandbox, one per runModel providersAnthropic, OpenAI…HTTP MCP serversGitHub, Linear…Agent looppicks the next stepBrokerone token per runKeys and tokensnever enter the sandboxShell and filesbash, files, secretsump CLIfor the job’s scriptsstdio MCP serversa user of their ownpromptsruns commandsMCP, modelsyour tokens
Model API keys and HTTP MCP credentials stay with Umpteenth on the host, and job secrets enter the sandbox as environment variables.

Model API keys stay on the host, and code in the sandbox reaches models only through ump llm and the broker. Umpteenth connects to HTTP MCP servers from the host too, so their headers and OAuth tokens never enter the sandbox. A stdio MCP server runs inside the sandbox as a separate user, whose environment the agent can’t read. In a job with Run as root, Umpteenth skips stdio servers that have environment variables, since root could read them.

Secrets you map to a job enter its sandbox as environment variables, and every process there can read them, the agent included. Umpteenth doesn’t mask them in command output, so a command that prints one puts it in the run’s timeline and in the model’s context. With Internet access or Unrestricted network, code in the sandbox can send a secret anywhere, and Allowed domains only limits where it can go.

Umpteenth masks job secrets of six or more characters in the transcript that reflection reads after a run, and holds back playbook changes that contain a secret or credential-shaped text for your review. It encrypts secrets, provider API keys and MCP logins in the database with keys derived from app.encryption_key, and its API never returns a secret’s value. Sandboxes shows how to map secrets to a job.

Umpteenth calls model providers, HTTP MCP servers with their OAuth endpoints, and notification webhooks from the host, and downloads the skills you add from a link there too. By default it may reach private addresses, since self-hosted models and MCP servers can live on your LAN.

Set network.allow_private_targets to false to refuse them. Saving such a URL then fails with points to a private or local network address, and Umpteenth checks the address again on every connection, so a redirect or a changed DNS answer can’t lead it to a private address either. The addresses in network.blocked_targets stay refused whatever this option says. While either option refuses an address, Umpteenth ignores HTTP_PROXY and HTTPS_PROXY for these calls, since it can only check connections it makes itself. Sandboxes ignore network.allow_private_targets and follow their job’s network setting.