Overproxy
A reverse proxy in the spirit of nginx or Caddy, but radically simpler: std-only Rust, a one-page config format, and a console log worth staring at. Three short specs, one agent, one small binary with zero runtime dependencies.
dist/overproxy — 518 KB Build it
One config, three specs, one command. Overplane raises each spec into a formal IR, checks it with Z3, and hands the whole contract to Claude Code inside a project sandbox. Unchanged specs hit the build cache on re-runs.
mkdir overproxy && cd overproxy
overplane init
overplane sandbox build
overplane spec new # 001-proxy.md
overplane spec new # 002-hardening.md
overplane spec new # 003-tls-passthrough.md
overplane build all
The output directory is a complete Cargo project;
cargo build --release produces the single
dist/overproxy binary shown in the screencast above.
The specs
This is everything the agent was given. Spec one shapes the proxy and its console personality, spec two hardens it for foreground use, and spec three adds HTTPS via SNI passthrough — no certificates, no TLS termination, still no crates.
001-proxy.md
---
id: '#0001'
title: Reverse Proxy
status: active
tags: [core]
---
# Reverse Proxy
## Goal
Create a Rust Cargo project in the working directory that builds a single
statically-linked-feeling binary called `overproxy`: a tiny HTTP/1.1 reverse
proxy in the spirit of nginx or Caddy, but radically simpler. Use only the
Rust standard library — no external crates — so the result is one small binary
with zero runtime dependencies.
## Requirements
* CLI: `overproxy [CONFIG]` where `CONFIG` defaults to `./overproxy.conf`.
`overproxy --help` prints polished, colorized usage with a short example
config. `overproxy --check [CONFIG]` validates the config and exits.
* Config file format, one directive per line (`#` comments allowed):
- `listen <addr:port>` — the address to bind (exactly one).
- `route <host> <upstream-addr:port>` — proxy requests whose `Host` header
matches `<host>` to the upstream (one or more).
* Proxy behavior: HTTP/1.1 only. Match the request's `Host` header (port
stripped) against the route table, forward the request to the upstream,
stream the response back. Add `X-Forwarded-For`. Requests for unknown hosts
get a clean built-in 404 page; unreachable upstreams get a built-in 502 page.
One thread per connection is fine.
* Console output is the product's face — make it beautiful:
- Startup banner: name, version, listen address, and an aligned route table,
with tasteful ANSI colors.
- One log line per request: timestamp, method, path, matched host, upstream,
status code (color-coded), and duration.
## Acceptance
- `cargo build --release` succeeds with no external dependencies in Cargo.toml.
- With a config routing `localhost` to a local upstream, `curl` through the
proxy returns the upstream's response and logs one colorized request line.
- `overproxy --check` accepts the example config and rejects a malformed one
with a helpful message. 002-hardening.md
---
id: '#0002'
title: Hardening
status: active
tags: [polish]
---
# Hardening
## Goal
Make `overproxy` pleasant to run in the foreground for real work: bounded
waits, a graceful exit, and a closing summary.
## Requirements
* Timeouts: connect, read, and write timeouts on upstream connections
(a few seconds each). A timed-out upstream produces the 502 page and a
clearly marked log line, never a hang.
* Graceful shutdown on Ctrl-C (SIGINT): stop accepting, then print a short,
aligned summary — total requests, responses by status class (2xx/4xx/5xx),
and uptime — and exit 0.
* Log polish: align columns so mixed method/path lengths stay readable, and
dim the timestamp so the request fields stand out.
## Acceptance
- Ctrl-C during a run prints the summary block and exits cleanly.
- A route pointing at a closed port logs a 502 with a visible timeout marker. 003-tls-passthrough.md
---
id: '#0003'
title: TLS Passthrough
status: active
tags: [feature]
---
# TLS Passthrough
## Goal
Give `overproxy` its "S": route HTTPS connections without terminating TLS, by
reading the SNI hostname from the TLS ClientHello and tunneling bytes to the
matching upstream. Still Rust standard library only.
## Requirements
* New config directives:
- `listen_tls <addr:port>` — optional TLS listener (at most one).
- `route_tls <sni-host> <upstream-addr:port>` — SNI-based passthrough routes.
* On a TLS connection, peek the ClientHello, parse the SNI extension, match
the hostname against the `route_tls` table, then splice the connection
bidirectionally to the upstream (including the bytes already read). No
certificate handling — the upstream terminates TLS.
* Unknown SNI or a malformed ClientHello: close the connection and log a
clearly marked line. TLS routes appear in the startup banner's route table,
visually distinct from HTTP routes.
## Acceptance
- `--check` accepts configs with and without the TLS section.
- With a `route_tls` entry pointing at an HTTPS upstream,
`curl -sk --resolve <host>:<port>:127.0.0.1 https://<host>:<port>/` returns
the upstream response, and the log shows an SNI passthrough line. The result
A single static-feeling binary: startup banner with an aligned route table,
color-coded per-request log lines, timeouts that turn into clean 502 pages,
and a graceful Ctrl-C summary. Click the screencast above to watch a run: an
upstream on port 9090, a route table for
localhost, and a few requests flowing through.