A compiler for small web apps. Point it at a directory and it produces a single self-contained native binary that is your app — embedded HTTP server, your content, SQLite, auth, forms, search and analytics, with zero runtime dependencies.
cwebify init mysite && cd mysite
cwebify dev # live from disk, <100ms reload, no toolchain needed
cwebify build # full native compile → dist/versions/v0001-a1b2c3/mysite
./dist/current/mysite # one file. that's the whole site.
Status: M0 rails + CLI surface.
cwebify initscaffolds a starter site;cwebify devserves it from disk on http://127.0.0.1:8080 (no toolchain).cwebify buildis not implemented yet. See docs/PLAN.md.
Two hard constraints, held as equals: lightweight and fast, and meaningfully secure by default — real, practical security that is on out of the box without the operator (or the AI agent) needing to be a security expert.
The organizing idea is that the compiler is the security tool. Because cwebify emits a purpose-built binary per site, decisions that are normally runtime configuration become compile-time facts, and whole vulnerability classes stop existing rather than being mitigated:
- No webroot — content is compiled in, so upload-to-webroot RCE and content path traversal do not exist.
- A closed set of SQL — every statement is fixed and verified at build time by SQLite's own authorizer; no code path accepts SQL from a request, and
sqlite3_execis not linked. - CSP hashed at build time — no nonces, no
unsafe-inline, no response rewriting. Inline event handlers are a build error. - seccomp generated per feature set — a static site's binary cannot
connect(). By BPF, not by policy. - Custom logic runs in a five-syscall worker process holding no keys, no database handle, and no ability to emit an unescaped byte.
Most of what people build needs no C at all. A link shortener, a waitlist with an admin dashboard, an internal CRUD tool, and a webhook receiver are each a couple of TOML files:
# routes/go.toml — the whole link shortener redirect
[route]
path = "/{slug}"
kind = "lookup_redirect"
[lookup]
query = "queries/link_by_slug.sql"
on_empty = { status = 404, template = "templates/404.html" }
[redirect]
to = "row.target" # a typed field reference, not string interpolation:
status = 302 # the compiler proves this can never be javascript:When declarative runs out, handlers/*.c is the escape hatch — contained by a capability
manifest that the linker, the object relocation audit, seccomp and Landlock all enforce.
- Handoff — state of play, what to build next
- Architecture and decisions of record
- Work is tracked in Plane (workspace
goldeneye, projectCWEB)
MIT