Architecture 01 Safety property 02 Trust model 03 The little backend 04 Create with AI 05 Runtime 06 Non-goals
00.
Architecture brief

One small service,
safe by construction.

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.

simple-host.app · public playground · companion: /llms.txt · /openapi.yaml
01.

The core safety property

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.

1.1

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.

1.2

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.

1.3

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.

1.4

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.

Fig 01 · Upload, validation & serve pipeline Platform Accepted Rejected
People & agents
Coding agent
holds the user's API key
uploads .tar.gz / .zip · or JSON files
Viewer's browser
GET https://{site}.simple-host.app/
public on the internet
1 · upload  ·  GET site path
Platform · one Go service
Upload API
X-API-Key → authenticated owner
2 · validate ↓
Path-safety check
jailed to one site's current/
safe path only ↓
Validation gate
size caps · extension allowlist
path-traversal blocked
Static file serve
bytes + content type only
no SSR · no exec · no autoindex
.sh .php .env … → rejected at upload · nothing written to disk
3 · extract vN · flip current  ·  read-only bytes from current/
Storage · versioned disk
Versioned site tree
v1 … vN · immutable per upload
current → vN symlink · atomic flip
Postgres
users · sites · versions
per-site state & collections

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.

02.

Trust model

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.

T1Deploy & manage sitesPer-user API key, issued by email magic-link registration. The key authenticates the owner of every upload, rollback, and delete.
T2AdminA single admin key held in the service's environment file (/etc/simple-host.env, root-owned) — never in code, config, or the repo.
T3Viewing sitesPublic 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.
T4Per-site stateOrigin-checked — only browser calls originating from that deployed site's own subdomain are accepted. Attribution-grade, not a secret store.
T5Create with AISign-in-gated and rate-limited, per user and per IP. Runs off-box; the platform stores no model credentials on served pages.
T6DB & secretsPostgres 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.

03.

The little backend

Static hosting is commoditized. The difference here is that every site gets just enough backend — with no database to set up.

3.1

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.

3.2

Append-only collections. Growing lists (/collections/{name}) for signups, RSVPs, and form submissions — cheap appends, paginated reads.

3.3

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.

04.

Create with AI

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.

05.

Runtime & deployment

Where this actually runs: one small VM, one process, no orchestration.

R1

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.

R2

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.

R3

Small by design. 1 CPU, ~1 GB RAM, 25 GB disk. Static serving and pass-through proxying — no heavy compute in-process.

R4

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.

R5

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.

Fig 02 · Runtime — one box, one process Platform Traffic
Edge
nginx
TLS · wildcard *.simple-host.app
routes {site} → the Go service
HTTPS
One VM · systemd · non-root
simple-host · Go
static serve · REST API · admin UI
/healthz · /readyz · embedded skill
Probes & sandbox
ProtectSystem=strict · non-root
writes only /srv/simple-host/sites
reads / writes
Local state
Postgres
users · sites · versions
state · collections
Versioned disk
/srv/simple-host/sites
v1 … vN · current symlink
06.

What it deliberately is not

N1Not general computeNo user-code execution, by construction. No SSR, no backends, no PHP, no processes per site.
N2Static-only servingHTML/CSS/JS/images/fonts served as bytes. Framework output is welcome; framework servers are not.
N3Not a secret storePer-site state is readable by the site's audience and documented as such. Skills tell agents never to put credentials in uploads or state.
N4Not bypassable by scalePer-site size and state caps are enforced server-side; AI create is rate-limited per user and IP.