People tell AI agents what to build; the agents build, deploy, and manage static sites here. The platform's whole job is to make that safe — by construction, not by vigilance.
Simple Host is one Go service, one Postgres database, and one directory on disk, behind a single reverse proxy that fronts every *.simple-host.app subdomain. No CDN, no per-site infrastructure, no third-party hosting. Uploads are static data that can never execute on the server; every version is immutable, attributable, and instantly revertible; and the whole thing runs on a box with 1 CPU and ~1 GB of RAM. This page is that argument, visualized.
The defining constraint: user uploads are data, never code — they can never execute on the server. That is what makes agent-generated ("vibe-coded") sites safe to host.
No execution path exists. The server has no interpreter, no SSR runtime, no plugin loader, no per-site processes. Uploaded files are written to disk and later returned as bytes with a content type — a hostile upload has no more power than an email attachment sitting in a folder.
Archives are validated before extraction. Path traversal is blocked (no entry may escape its site directory), archive and file sizes are capped, and extensions are restricted to a static-content allowlist — scripts, binaries, and server configs (.sh, .php, .env …) are rejected at upload time, before anything touches disk.
Serving is jailed per site. Requests for site content are path-checked and rooted at that one site's current/ directory. Directory listings are disabled; a directory without an index serves nothing. One site can't read another's files through the server.
Everything is versioned and revertible. Every upload is a new immutable version tied to an authenticated owner and timestamped. Bad content rolls back instantly — the current symlink flips atomically — and can be deleted by the owner or an admin.
Top to bottom: an agent authenticates with the user's key and uploads; the validation gate enforces size caps, the static-extension allowlist, and traversal protection — scripts and binaries die there, before anything touches disk. Accepted content extracts to a new immutable vN/, the current symlink flips atomically. Serving is a separate read-only path: path-safety check, then static bytes. No user code ever runs server-side.
Every surface is gated; the gates differ in strength. Sites are public on the internet — the protection is that they're static, owned, and revertible.
| T1 | Deploy & manage sites | Per-user API key, issued by email magic-link registration. The key authenticates the owner of every upload, rollback, and delete. |
| T2 | Admin | A single admin key held in the service's environment file (/etc/simple-host.env, root-owned) — never in code, config, or the repo. |
| T3 | Viewing sites | Public by default. Any site can be made private with a password (view-lock): a login page sets a signed cookie that gates the page and its data. |
| T4 | Per-site state | Origin-checked — only browser calls originating from that deployed site's own subdomain are accepted. Attribution-grade, not a secret store. |
| T5 | Create with AI | Sign-in-gated and rate-limited, per user and per IP. Runs off-box; the platform stores no model credentials on served pages. |
| T6 | DB & secrets | Postgres credentials and keys live in the root-owned env file, read at boot by the non-root service user. Not reachable from any served page. |
Known, accepted limitation: the origin check on state is attribution-grade, not authentication-grade — it prevents accidental cross-site misuse and gives accounting, not access control. Documented as such; secrets never belong in state.
Static hosting is commoditized. The difference here is that every site gets just enough backend — with no database to set up.
Per-site JSON state. One shared document per site (GET/PUT/PATCH /v1/sites/{site}/state), with atomic ops (increment, append, set) and ETags for cheap polling — counters, votes, saved app state, guestbooks.
Append-only collections. Growing lists (/collections/{name}) for signups, RSVPs, and form submissions — cheap appends, paginated reads.
Drop-in widgets & templates. Threaded comments and feedback pins as one script tag each, password-locked private pages, and starter templates served from /v1/templates.
All of it is backed by the same single Postgres — no per-site database, no separate service. The store is public to the site's own audience by design; skills tell agents never to put secrets in it.
The homepage builds a site for you through a chat — and the model runs off-box, so the platform never holds AI credentials on a served surface.
POST /v1/generate keeps the sign-in gate and rate limit on the edge, then hands the turn to a separate agent service as a background job (poll for the result — no request waits out a proxy timeout). The agent runs with its built-in file/shell tools disabled — only a scoped deploy_site tool — so a prompt-injected page can't run commands or exfiltrate keys. You preview in a sandboxed opaque-origin iframe, then publish through the same authenticated files API.
The agent server holds no user secrets, is reachable only with a shared secret, and binds each job to the caller's key.
Where this actually runs: one small VM, one process, no orchestration.
One process. A single Go binary under systemd — deliberately not a cluster. An nginx edge terminates TLS and routes every *.simple-host.app subdomain to it.
Hardened & non-root. Runs as a dedicated simplehost user with NoNewPrivileges, ProtectSystem=strict, and write access limited to the sites directory. Liveness (/healthz) and readiness (/readyz, DB-connected) gate traffic.
Small by design. 1 CPU, ~1 GB RAM, 25 GB disk. Static serving and pass-through proxying — no heavy compute in-process.
Hot-swap releases. Build, copy the new binary in, atomic rename over the old one, restart — site data and versioned files untouched. Old binaries kept for instant rollback.
Local state. Postgres on the same box (users, sites, versions, per-site state); the versioned site tree on a local disk. No external object store — small enough not to need one.
| N1 | Not general compute | No user-code execution, by construction. No SSR, no backends, no PHP, no processes per site. |
| N2 | Static-only serving | HTML/CSS/JS/images/fonts served as bytes. Framework output is welcome; framework servers are not. |
| N3 | Not a secret store | Per-site state is readable by the site's audience and documented as such. Skills tell agents never to put credentials in uploads or state. |
| N4 | Not bypassable by scale | Per-site size and state caps are enforced server-side; AI create is rate-limited per user and IP. |