Skip to content
sitesolide

Docs Concepts

Security model

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.

  1. Visitor

    a browser

  2. HTTPS

    Caddy

    certificate and sign-in check

  3. loopback, identity headers

    Your app

    its own account and folder

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

WallWhat it does
Account and foldersThe app runs as site-<slug>, in an empty /srv where only its project's folders are bound back in.
Read-only systemThe file system is read-only, the app's own code included, except its data/ folder and a private /tmp.
Loopback ruleAn 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 portsA server listens on its own port alone among 2019, 3000 to 3099, 3128 and 3129. Any other bind there fails with EPERM.
System callssystemd'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.
NetworkThe loopback alone, with no DNS, unless the manifest sets egress or "network": "outbound".
Secrets folderAn 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 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.

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.

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 list them.

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 it privately, at 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.