All import paths updated to git.wntrmute.dev/mc/. Bumps mcdsl to v1.2.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
69 lines
3.9 KiB
Markdown
69 lines
3.9 KiB
Markdown
# 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).
|