Platform
Security specifications
Every claim below is either pinned by a test or points at the file that enforces it, so a security review doesn't have to take our word for it. See EU data residency for where the platform runs and which entities can be compelled to produce data.
| Control | What foro.sh does | Backed by |
|---|---|---|
| Per-project bearer token | Every deployed server requires Authorization: Bearer <token> on every request, unless the project is explicitly made public - and the check fails closed on any error. | infra/templates/foro-proxy.mts (authorized, authorizedByToken) |
| No standing container privileges | Every container drops all Linux capabilities, runs with a read-only root filesystem outside a capped scratch directory, runs as an unprivileged user, and can never regain a privilege it started without. | apps/api/src/services/docker.ts, pinned by docker.test.ts:198 |
| One private network per project | A project's container sits on a network reachable by nothing except the platform's own routing layer - not other projects' containers, and not the platform's own database, queue, or internal API. | apps/api/src/services/docker.ts (ensureProjectNetwork, connectToNetwork) |
| Secrets encrypted at rest | Project secrets are sealed with libsodium crypto_secretbox before they reach the database, with the master key held outside it in a Docker secret, and are injected only at container start. | apps/api/src/services/secrets.ts |
| Metadata-only tracing | A deployed server's tool arguments and results are tokenized inside the container; only token counts, timing, and a truncated error string ever leave it. The metrics table has no column for a raw argument or result. | infra/templates/foro-proxy.mts (countTokens, emit), apps/api/src/db/schema.ts (mcpRequests) |
Where each control actually runs
foro.sh terminates TLS and routes every request to the right container - that's the part that makes it a gateway in front of your server. What it is not is a policy proxy that inspects or filters individual tool calls at that layer: the bearer-token check, the sandboxing, and the metadata-only tracing all run inside your container, injected at build time. That keeps a deployed server self-sufficient - it keeps authenticating and reporting correctly even if the platform's own API is briefly unreachable - and it's also why none of the claims above describe edge-level tool filtering we don't do.
The deeper reference for each control
This page restates five controls in one place for a reviewer scanning quickly; it doesn't replace their dedicated pages. Authentication covers token rotation and what making a project public turns off. Isolation & networking covers the container and network model layer by layer. Secrets covers encryption at rest and injection at container start.
Is authentication enforced at the edge, in front of every server, or inside my container?
Inside the container. Requests are routed straight through to your server; a gate injected at build time reads the bearer token from the environment and decides there. That keeps a deployed server authenticating correctly even if the platform API is unreachable, but it also means foro.sh is not a policy proxy that filters or rewrites individual tool calls at the edge - the routing layer terminates TLS and forwards; your container is where the token check lives.
Does tracing store the arguments my tools are called with?
No, and this is the claim we verified most carefully before publishing it. Arguments and results are tokenized inside your container by the injected shim; only the token counts, call duration, client identity, and a truncated error string are emitted, and the metrics table has no column for a raw argument or result - there's nothing to retrieve even if you wanted it. That does not extend to everything a deployed server does: if your own code prints an argument to stdout, that line is persisted like any other container log for your plan's retention window, and PII scrubbing of persisted logs is a Starter-and-up feature, not applied on Free. Scope your own logging accordingly if that matters to you.
What's actually dropped from a container to sandbox it?
Every Linux capability (CapDrop: ALL - Docker grants roughly fourteen by default even to a non-root process, including CAP_NET_RAW, which none of that container removes on its own), a writable root filesystem (read-only outside a capped /tmp), the ability to gain new privileges (no-new-privileges), and root itself - the process runs as an unprivileged user. None of it is added back for any plan.
Found a gap in one of these controls? Report it privately to [email protected] rather than opening a public issue. We acknowledge within 3 business days.