A_GUZMAN c-copper v0.03
← 02_WORK / SYSTEMS
[0x02] SYSTEMS // C_COPPER

A web framework in C, from the socket up.

The page you're reading was routed, rendered, and sent by ~1,600 lines of C99 with zero runtime dependencies. Not because C is the right tool for a portfolio — because owning every layer from accept() to template interpolation is the most honest way to show what "full stack" means.

ROLE
Author — every layer
STACK
C99, pthreads, Lua → WASM
PERIOD
2026 — present
DEPLOY
Cloud Run, multi-stage Docker
STATUS
Live — serving this page
01 // THE_PROBLEM

Every framework hides the interesting parts.

After years of shipping on .NET and Angular, the question was: can I build the whole request lifecycle myself — socket loop, thread dispatch, routing, middleware, templating, static files — small enough to hold in one head, robust enough to run in production on Cloud Run?

Constraints: C99 + pthreads only. No libevent, no framework, no runtime dependencies. Every byte of the HTTP response is assembled by code in this repo.

02 // ARCHITECTURE

One thread per request, one linked list per concern.

The socket loop accepts and dispatches to a pthread. The request walks a middleware chain — logging, static files, rate limiting, CSRF — then the router, then a controller ACTION, then a two-pass template render into the layout's body slot.

/static/app.css

middleware chain (linked list)

logging

static/*

rate-limit

csrf

client

accept()

pthread dispatch

request parser

public/ file

router — strcmp / {param} bind

controller ACTION

view engine — 2-pass render

layout.html body slot

response_flush() → write()

ACCEPT
server.c
socket loop, pthread dispatch, request parser.
CHAIN
middleware.c
Linked list; each layer can short-circuit.
MATCH
router.c
strtok_r pattern match, {param} bind, first-match-wins.
RENDER
view.c
Three directives, two-pass layout render.
03 // DECISIONS
D1

Thread-per-request over an event loop

Simpler invariants, no callback state machines. The throughput ceiling was accepted consciously and measured below — it is nowhere near this site's bottleneck.

D2

A template engine with three directives

Substitution, #if, #each, and a two-pass layout render. No partials, no filters, no expression language — enough for real pages, nothing speculative.

D3

Linked-list middleware chain

Logging → static → rate-limit → CSRF → router. Each layer can short-circuit, and the order is readable at the call site in main.c.

D4

Build-time everything

Tailwind compiled to a static file; articles are markdown compiled to HTML; diagrams are mermaid rendered to SVG. Browser interactivity is Lua compiled to WASM, loaded per route. The server's job stays brutally simple.

04 // NUMBERS
~1,900/s
Dynamic renders per second, full template.
~3,700/s
Static file responses per second.
23 / 65ms
p50 / p99 dynamic latency under load.
0
Runtime dependencies.
1,559
lines of C, framework core
15
http routes registered
~190KB
stripped server binary
40KB
total CSS shipped

Benchmark: hey, 50 concurrent connections, 10s runs (median of 3), Intel N100 (4 cores) under WSL2 — laptop-class hardware, not the Cloud Run deployment. Numbers are for honesty, not bragging: a thread-per-request C server on a small box outruns what this site will ever need.