Webzero
Ultra-minimal web server for low-end hardware. Serves static sites from a single .web binary bundle with zero runtime dependencies, zero heap allocations, and zero config. Runs on Linux 2.6+ and Windows XP+. 5,000 req/sec on a Pentium III.
Install / Use
/learn @davitotty/WebzeroREADME
WebZero
A minimal web server built for old hardware.
Single binary. No dependencies. Runs on Linux 2.6+ and Windows XP+.
┌───────────────────────────────┐
│ .web bundle │
│ ┌─────┐ ┌──────┐ ┌────────┐ │
request │ │trie │ │assets│ │handlers│ │ response
──────────► accept() │ │(mmap│ │ (br) │ │(bytecod│ │ ──────────►
parse hdrs │ │ 'd) │ │ │ │ e) │ │ sendfile()
trie_lookup │ └──┬──┘ └──────┘ └────────┘ │
sendfile() │ └─── O(depth) lookup ─────┘
Why
Every web server assumes you have RAM, disk IOPS, and a modern CPU. WebZero assumes you don't. It is built for:
- Raspberry Pi 1 (700 MHz ARM, 256 MB RAM)
- Pentium III workstations and thin clients
- Windows XP machines still running in production
- Old netbooks, Atom-powered NAS boxes, embedded boards
The design constraint is simple: if memory can fragment, it will. If there are threads, they will deadlock. If there's a config parser, it will crash on edge cases. WebZero eliminates all of those.
How It Works
1. The .web Bundle
Your site is compiled into a single binary file:
node tools/wz.js build ./my-site
The bundle contains:
- Route trie — a binary trie built from your directory structure
- Assets — every file, Brotli-compressed at level 11 (never at runtime)
- Handlers — optional bytecode for contact forms / simple APIs
- Config — hostname, port, max connections
At startup, the server mmap()s this file. The entire site lives in virtual memory. The OS page cache does all the work. Zero file I/O during requests.
2. The Memory Model
static Arena arena; // lives in BSS — zero-initialized, 4MB
One flat arena, allocated once, used forever. After main() initialization:
- Zero calls to
malloc - Zero calls to
free - Zero threads, zero mutexes
The server cannot fragment, cannot leak, cannot race.
3. The Request Pipeline
accept() → read headers into arena.conn_bufs[slot]
→ trie_lookup(path) ← O(1-3 pointer chases)
→ platform_send_file(asset) ← zero-copy path to socket
→ or: vm_run(bytecode) ← for dynamic handlers
Response headers are pre-built byte arrays — no sprintf during serving:
static const char HDR_200_BR[] =
"HTTP/1.1 200 OK\r\n"
"Content-Encoding: br\r\n"
"Cache-Control: max-age=31536000, immutable\r\n"
"Vary: Accept-Encoding\r\n"
"Content-Length: ";
4. Backpressure
if (active_connections >= MAX_CONNS) {
send(new_fd, HDR_503, sizeof(HDR_503), 0);
close(new_fd);
return;
}
No queue. No waiting. Under overload, the server sheds load immediately instead of accumulating state and eventually crashing.
Benchmarks
Benchmarks on real hardware coming soon. Currently tested on modern hardware during development.
Quick Start
Option A — npm (recommended)
npm install -g @davitotty/webzero
This installs the wz command globally and automatically downloads the right prebuilt binary for your platform.
# Build your site into a .web bundle
wz build ./my-site
# Serve it
wz serve my-site.web --port 8080
# Inspect bundle contents
wz inspect my-site.web
# Update the server binary
wz update
Startup looks like this:
┌─────────────────────────────┐
│ WebZero v1.0.0 │
│ bundle : my-site.web │
│ port : 8080 │
│ routes : 12 │
│ memory : 4.0 MB reserved │
│ ready ✓ │
└─────────────────────────────┘
Option B — Build from source
Prerequisites
- Linux or Windows (XP SP3+)
- GCC (or
musl-gccfor static builds,i686-w64-mingw32-gccfor Windows) - Node.js 14+ (for
wz.jsbuild tool only — not needed at runtime)
git clone https://github.com/davitotty/webzero
cd webzero
# Linux native (dynamic libc)
make
# Linux fully static (requires musl-gcc)
make static
# Windows XP target (cross-compile from Linux)
make windows
Build and Serve a Site
# Build the example landing page into a .web bundle
node tools/wz.js build examples/landing-page
# Serve it on port 8080
./webzero examples/landing-page.web 8080
# Or use the JS server for development (no C binary needed)
node tools/wz.js serve examples/landing-page.web 3000
Image Optimization
WebZero supports responsive image generation at build time via wzimg:
# Build with responsive image variants
wz build ./my-site --responsive
# This generates size variants at 320, 640, 960, 1280, 1920px
# Use srcset in your HTML to serve the right size
If a .webp file exists alongside an image, WebZero will serve the WebP version automatically for better compression.
Inspect a Bundle
node tools/wz.js inspect examples/landing-page.web
Project Structure
webzero/
├── core/
│ ├── pool.c / pool.h ← static arena, scratch allocator
│ ├── bundle.c / bundle.h ← .web mmap loader and validator
│ ├── router.c / router.h ← binary trie: O(depth) path lookup
│ └── vm.c / vm.h ← 12-opcode bytecode interpreter
├── platform/
│ ├── platform.h ← thin HAL interface
│ ├── linux.c ← epoll + sendfile
│ └── windows.c ← IOCP + TransmitFile
├── third_party/
│ ├── stb_image.h ← image decoding (vendored)
│ └── stb_image_write.h ← image encoding (vendored)
├── tools/
│ ├── wz.js ← CLI entry point (zero npm deps)
│ ├── wzimg.c ← image resize utility
│ └── install.js ← postinstall binary downloader
├── examples/
│ ├── blog/
│ └── landing-page/
├── bench/
│ └── run.sh
├── BUNDLE_SPEC.md ← .web format specification
├── package.json
├── Makefile
└── main.c ← entry point + request pipeline
The .web Bundle Format
See BUNDLE_SPEC.md for the full specification.
Short version:
[28 bytes] header (magic, version, section offsets, total size)
[N bytes] route trie (64 bytes per node, packed binary)
[M bytes] asset table + brotli-compressed asset data
[P bytes] handler table + bytecode
[96 bytes] config (hostname, port, counts)
The entire file is validated at load time, then never touched again.
Constraints (Never Violated)
- ✅ No
malloc/freeaftermain()initialization - ✅ No threads, no mutexes, no condition variables
- ✅ No external libraries at runtime (only libc on Linux, kernel32+ws2_32 on Windows)
- ✅ No config file parsing at server startup
- ✅ C99 only — no C11, no GCC extensions, no compiler builtins except
__builtin_expect - ✅ Compiles clean with
-Wall -Wextra -Wpedantic -Werror
Stretch Goals (Future GitHub Issues)
- [ ] TLS via embedded mbedTLS (~60KB overhead)
- [ ] WASM handler support (replace bytecode VM with µWASM runtime)
- [x]
.webhot-reload without restart (inotify/ReadDirectoryChangesW) - [ ] ARM/RISC-V port for embedded targets
- [x]
wz.jsimage optimization: WebP support + responsive size generation
License
Apache 2.0 — free to use, modify, and distribute. See LICENSE for details.
"The best code is no code. The second best is code that does exactly one thing with zero waste."
