# 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/kyle/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).
