# Security model

> How sitesolide walls in each app, keeps secrets on the server, uses tokens instead of SSH, and what its design does not protect.

Every app runs behind its own walls, and every component on the server has one job. A compromised app should reach nothing but its own data.

```diagram
[Visitor] a browser
-> HTTPS
[Caddy] certificate and sign-in check
-> loopback, identity headers
[Your app] its own account and folder
```

## Isolation

Each app runs in a systemd unit generated from its manifest. No containers: the walls are systemd's and the kernel's.

| Wall | What it does |
|---|---|
| Account and folders | The app runs as `site-<slug>`, in an empty `/srv` where only its project's folders are bound back in. |
| Read-only system | The file system is read-only, the app's own code included, except its `data/` folder and a private `/tmp`. |
| Loopback rule | An nftables rule reserves ports 3000 to 3099, and Caddy's admin API on 2019, to Caddy and root. An app cannot connect to another project's port. |
| Listening ports | A server listens on its own port alone among 2019, 3000 to 3099, 3128 and 3129. Any other bind there fails with `EPERM`. |
| System calls | systemd's `@system-service` set: no mount, kernel module, reboot, swap, clock change or tracing. A blocked call fails with `EPERM`, and the process keeps running. |
| Network | The loopback alone, with no DNS, unless the manifest sets `egress` or `"network": "outbound"`. |
| Secrets folder | An app that declares no secret cannot see `/etc/sitesolide`, not even the file names. |

The services of one project reach each other's ports, and a neighbour reaches none of them. That is why a Restricted app can trust the `X-Sitesolide-User` and `X-Sitesolide-Role` headers Caddy sets.

## Secrets

Secrets live on the server only, in `/etc/sitesolide`, never in git. systemd reads a project's files as root when the service starts, and hands it the variables. A manifest may declare only its own project's files, and `deploy` never pushes a value. See [Secrets](secrets.md).

## Tokens instead of SSH

Only the owner holds SSH, as an account with passwordless sudo. Colleagues and agents deploy over HTTPS with a personal token: scoped, revocable, and never stronger than its holder's roles. The server reads those roles at every request, so a role lowered to Viewer stops that project's deployments from the next one.

## Adversarial audit

Before v0.6.0, ten Claude Code agents each attacked one component, proving every finding with a test that failed first, then fixing it. Red-team agents then attacked the fixes, and the release was verified on a real Debian VM. About forty issues were fixed in v0.6.0 and v0.6.1: the [upgrade notes](https://github.com/cthiriet/sitesolide/blob/main/docs/upgrading.md#what-a-project-a-token-or-a-cloned-folder-could-reach) list them.

## Limits

These are limits of the design, not vulnerabilities:

- **One kernel.** A kernel exploit, or an escape from systemd's confinement, gets root on the server and every project on it.
- **The owner's SSH key holds the server.** Its account has passwordless sudo.
- **A preview code is about 30 bits**, and nothing limits the attempts. Use Restricted for anything that deserves authentication.
- **A project can slow its neighbours.** A server has no CPU cap unless its manifest sets `cpu`.
- **A project sends what it likes to the hosts its manifest lists.** The list is the boundary.
- **Some parties are trusted:** your identity provider, Cloudflare and its DNS token, your cloud provider. Releases are checked against checksums, not signed.

## Report a vulnerability

Report it privately, at [github.com/cthiriet/sitesolide/security/advisories/new](https://github.com/cthiriet/sitesolide/security/advisories/new). Never in a public issue: a hole made public before its fix is open on every installation at once.

In scope: anything that lets a component, a person, a token or a project's code do more than its job allows. Give the version (`sitesolide --version`), the component, who the attacker is, and the steps. Only the latest release is supported; fixes are not backported.

## Related

- [How it works](how-it-works.md): the parts, and the helpers that each do one job.
- [Teammates and tokens](tokens.md): deploy without SSH, for a colleague or an agent.
- [Calling outside services](network.md): let an app reach the hosts it needs.

Full reference: https://github.com/cthiriet/sitesolide/blob/main/SECURITY.md
