Your server and security
Security model
How Setup keeps the apps it runs away from your network and from each other, how it handles secrets, and which gaps are still open.
On this page
Setup runs software that strangers wrote, so it treats every repository, model, docs page and recipe as hostile. Looking at a project never runs it. When you start an app, it runs in a sandbox on a runner, on a private network with no way into your LAN or onto the machine underneath.
No sandbox is perfect. This page describes what Setup does today and what isn't solved yet, so you can decide what to run where.
Where untrusted code runs
| Stage | Where | Runs untrusted code |
|---|---|---|
| Analysis | Setup's server | No. Files are read as data: a GitHub tarball in memory, Hugging Face API JSON, docs pages as text. Nothing is unpacked to disk, installed or run. |
| Recipe drafting | Setup's server and a model provider | No. Repository text goes to the model as quoted data, and the answer has to pass the recipe schema and policy before anyone sees it. |
| Build | Runner, inside BuildKit | Yes, the RUN steps. BuildKit fetches git sources on the Docker host; the runner never clones them. |
| Run | Runner, inside containers | Yes, in the sandbox described below. |
Setup's server never clones repositories and never runs package managers on analyzed code. It doesn't host language models either: drafting a recipe with a model is an optional step that calls an outside provider.
Recipes can't ask for dangerous options
A recipe has no field for any of these:
- host paths and bind mounts
- privileged mode or extra Linux capabilities
- devices, except a GPU flag
- host networking, or the host's PID and IPC namespaces
- sysctls and security options
- the Docker socket
Unknown keys are stripped, paths are checked for traversal, and image references have to match a strict grammar. The runner validates every recipe again before it touches Docker.
The sandbox around each app
Network. Each app gets its own internal Docker network with no route to the internet or your LAN. The network's bridge has no address on the host, so services on the Docker host itself, such as SSH, are out of reach too. Requests come in only through a per-app ingress proxy on a separate network, and only to the HTTP port the recipe exposes.
Outbound traffic. Only services whose recipe declares egress: public reach the internet, and only through the app's egress proxy. The proxy resolves each name itself, refuses private, loopback, CGNAT, link-local (including the cloud metadata address 169.254.169.254), multicast and reserved ranges in IPv4 and IPv6, and then connects to the address it checked, so DNS rebinding doesn't get around it. It carries HTTP and HTTPS only.
Containers. Every service drops all Linux capabilities except six that many images need, for example to switch to their own user, and runs with no-new-privileges, Docker's default seccomp and AppArmor profiles, and limits for CPU, memory, processes and log size. When memory runs out, the kernel kills sandbox processes first. The runner uses gVisor or Kata when the host has one. Storage is named volumes only, scoped to the app.
Raw ports. TCP or UDP ports outside HTTP exist only when the runner's operator allows them (--allow-published-ports). A forwarder relays them, so the service itself never joins a routable network.
Trials delete themselves, after 60 minutes by default.
Runners. A runner you connect only runs apps of its own workspace. Setup Cloud runners are shared between workspaces. Runners and self-hosting covers the host side.
Secrets
App secrets, inputs of type secret, are encrypted with AES-256-GCM in Setup's database. The API never returns them; the dashboard only knows that a value is set. Setup decrypts them when a runner picks up a job and sends them over HTTPS. On the runner they sit in the app's Compose file in the data directory (file mode 0600) and in the containers' environment, so anyone with Docker access on that host can read them.
Runner tokens and API keys are shown once and stored only as hashes. An API key can be limited to one workspace, never carries global admin rights and can't manage keys. See MCP and API keys.
Connected accounts are encrypted the same way, never logged and never returned by the API. They leave Setup's server only inside a job that creates or removes resources at an outside service, as environment variables of a pinned OpenTofu container. That container runs no analyzed code and reaches the internet only through an egress proxy. App sandboxes never get these credentials; an app only gets the key or project Setup created for it. See integrations.
The assistant's model key, if a workspace adds its own, is encrypted the same way.
Accounts and workspaces
You sign in with email and password (the address has to be verified), a sign-in link, a one-time code by email or a passkey, and you can turn on two-factor authentication. Google, GitHub and Apple sign-in appear when the Setup instance has them configured. A password reset signs out all other sessions.
Everything belongs to a workspace: your personal one, or an organization with owners, admins and members. Only admins add or change runners, connect or disconnect accounts and set the assistant's key. When an agent asks for a destructive action, it needs an explicit confirmation.
Public status pages are off by default and exist only for kept apps. They show the app's name, its state, daily uptime and when the last incident started and ended. They never show the workspace, the runner, logs or error text, and the app's address appears only if the owner turns on a second switch.
Known gaps
These are open. Each one says what you can do today.
- Builds can reach your LAN.
RUNsteps need the internet to install dependencies, and build containers can also reach the runner's local network. Block private ranges for build containers on the host (commands) or run the runner in a VM. Running Setup Cloud builds in microVMs is planned, not built. - Shared kernel. Under runc, containers share the host kernel. Install gVisor on hosts that matter; the runner finds and uses it. Each runner in the dashboard shows its isolation, for example "Hardened containers" or "gVisor sandbox".
- Resource exhaustion. Limits bound every container, but a hostile image can still use its whole quota. The runner doesn't cap disk space for volumes; putting
/var/lib/dockeron its own filesystem keeps a full disk away from the rest of the host. - Backups stay on the runner. They are archives in the runner's data directory, not encrypted by Setup and not copied anywhere else yet. Keep the data directory on an encrypted disk.
- Outside-service modules are young. Most of what Setup can create at other services was written from the provider's documentation and hasn't run against a real account yet. The dashboard marks each one.
Report a vulnerability
Email security@setup.run, privately rather than in a public issue. Steps to reproduce help a lot.