# 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 Layer 4 TLS SNI proxy and router for Metacircular Dynamics services. It reads the SNI hostname from incoming TLS ClientHello messages and proxies the raw TCP stream to the matched backend. It does not terminate TLS. A global firewall (IP, CIDR, GeoIP country blocking) is evaluated before routing. 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
- **gRPC admin API** — manages routes and firewall rules at runtime; TLS with optional mTLS; 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); thread-safe mutations and GeoIP reload
- `internal/proxy/` — bidirectional TCP relay with half-close propagation and idle timeout
- `internal/server/` — orchestrates listeners → firewall → SNI → route → proxy pipeline; per-listener state with connection tracking
- `internal/grpcserver/` — gRPC admin API: route/firewall CRUD, status, write-through to DB
- `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

- mc-proxy never terminates TLS and never modifies the byte stream.
- Firewall rules are always evaluated before any routing decision.
- 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).
