# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview mc-proxy is a TLS proxy and router for Metacircular Dynamics services. It operates in two per-route modes: **L4 passthrough** (reads SNI, proxies raw TCP without terminating TLS) and **L7 terminating** (terminates TLS, reverse proxies HTTP/2 and HTTP/1.1 traffic including gRPC). A global firewall (IP, CIDR, GeoIP, rate limiting) is evaluated before routing. PROXY protocol support enables multi-hop deployments preserving real client IPs. See `ARCHITECTURE.md` for full design. ## Build Commands ```bash make all # vet → lint → test → build make mc-proxy # build the binary with version injection make build # compile all packages make test # run all tests make vet # go vet make lint # golangci-lint make proto # regenerate gRPC code from proto definitions make devserver # build and run locally with srv/mc-proxy.toml ``` Run a single test: ```bash go test ./internal/sni -run TestExtract ``` ## Architecture - **Module path**: `git.wntrmute.dev/mc/mc-proxy` - **Go with CGO_ENABLED=0**, statically linked, Alpine containers - **Dual mode, per-route** — L4 (passthrough) and L7 (TLS-terminating HTTP/2 reverse proxy) coexist on the same listener - **PROXY protocol** — listeners accept v1/v2; routes send v2. Enables edge→origin deployments over Tailscale - **gRPC admin API** — manages routes and firewall rules at runtime; Unix socket only; optional (disabled if `[grpc]` section omitted from config) - **No auth on proxy listeners** — this is pre-auth infrastructure; services behind it handle their own MCIAS auth - **SQLite database** — persists listeners, routes, and firewall rules; pure-Go driver (`modernc.org/sqlite`); seeded from TOML on first run, DB is source of truth thereafter - **Write-through pattern** — gRPC mutations write to DB first, then update in-memory state - **Config**: TOML via `go-toml/v2`, runtime data in `/srv/mc-proxy/` - **Testing**: stdlib `testing` only, `t.TempDir()` for isolation - **Linting**: golangci-lint v2 with `.golangci.yaml` ## Package Structure - `internal/config/` — TOML config loading and validation - `internal/db/` — SQLite database: migrations, CRUD for listeners/routes/firewall rules, seeding, snapshots - `internal/sni/` — TLS ClientHello parser; extracts SNI hostname without consuming bytes - `internal/firewall/` — global blocklist evaluation (IP, CIDR, GeoIP via MaxMind GeoLite2); rate limiting; thread-safe mutations and GeoIP reload - `internal/proxy/` — L4 bidirectional TCP relay with half-close propagation and idle timeout - `internal/proxyproto/` — PROXY protocol v1/v2 parser and v2 writer - `internal/l7/` — L7 TLS termination, `prefixConn`, HTTP/2 reverse proxy with h2c backend transport - `internal/server/` — orchestrates listeners → PROXY protocol → firewall → SNI → route → L4/L7 dispatch; per-listener state with connection tracking - `internal/grpcserver/` — gRPC admin API: route/firewall CRUD, status, write-through to DB - `internal/metrics/` — Prometheus metric definitions and HTTP server; optional `[metrics]` config section - `proto/mc_proxy/v1/` — protobuf definitions; `gen/mc_proxy/v1/` has generated code ## Signals - `SIGINT`/`SIGTERM` — graceful shutdown (drain in-flight connections up to `shutdown_timeout`) - `SIGHUP` — reload GeoIP database without restart ## Critical Rules - L4 routes never terminate TLS and never modify the byte stream. - L7 routes terminate TLS at the proxy and reverse proxy HTTP/2 (including gRPC) to backends. - Firewall rules are always evaluated before any routing decision. - PROXY protocol is only parsed on explicitly enabled listeners. - SNI matching is exact and case-insensitive. - Blocked connections get a TCP RST — no error messages, no TLS alerts. - Database writes must succeed before in-memory state is updated (write-through).