This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Reproxy is a simple edge HTTP(s) reverse proxy supporting multiple providers (docker, static, file, consul catalog). It handles automatic SSL termination with Let's Encrypt, load balancing, health checks, and optional plugin support via RPC.
# run all tests
cd app && go test ./...
# run tests with race detection
cd app && go test -race -timeout=60s -count 1 ./...
# run specific test
cd app && go test -run TestName ./...
# run tests with coverage
cd app && go test -cover ./...
# build binary (from repo root)
make build
# build docker image
make docker
# run linter (from repo root)
golangci-lint run-
app/main.go - CLI entry point with all configuration options (flags, environment variables). Wires together providers, proxy server, and management services.
-
app/discovery/ - Service discovery layer
discovery.go-Serviceaggregates multiple providers, merges URL mappers, handles health checksprovider/- Provider implementations (docker, file, static, consul-catalog)URLMapper- Core routing rule struct containing server, source regex, destination, health ping URL, match type, and per-route overrides (auth, IP allowlist, timeout, throttle)
-
app/proxy/ - HTTP/HTTPS proxy server
proxy.go-Httpstruct is the main proxy server, handles both http and https modeshandlers.go- Middleware handlers (throttling, auth, logging, headers)ssl.go- SSL/TLS configuration and ACME (Let's Encrypt) autocert managementhealth.go- Health check endpoint handlerslb_selector.go- Load balancer strategies (random, failover, roundrobin)only_from.go- IP-based access control middleware
-
app/mgmt/ - Management API
server.go- Exposes/routesand/metricsendpointsmetrics.go- Prometheus metrics middleware
-
app/plugin/ - Plugin system
conductor.go- RPC server for plugin registration and middleware chain
-
lib/ - Plugin development library
- Used by external plugins to implement custom middleware via RPC
- Request hits
proxy.Http.Run()which sets up middleware chain matchHandlermiddleware matches request toURLMapperviadiscovery.Service.Match()- Match result stored in request context
proxyHandlerroutes to either:httputil.ReverseProxyfor proxy matches (MTProxy)- File server for static matches (MTStatic)
- Assets handler for default static files
Providers are processed in order: static → file → docker → consul-catalog. Earlier providers take precedence for conflicting rules.
SSLNone- HTTP onlySSLStatic- User-provided certificatesSSLAuto- ACME/Let's Encrypt automatic certificates with DNS-01 or HTTP-01 challenges
- Port allocation: Use
net.Listen("tcp", "127.0.0.1:0")to get OS-allocated ports instead of random range - avoids port collisions when previous test's port is in TIME_WAIT state - httptest cleanup: Always close
httptest.Serverwithdefer ds.Close()to prevent goroutine leaks between tests ("Fail in goroutine after TestX has completed" errors) - Context timeouts: Context timeouts must be longer than
waitForServertimeouts to allow proper server startup and cleanup
- Configuration via
github.com/umputun/go-flagswith struct tags for CLI/env options - Logging via
github.com/go-pkgz/lgr - Middleware chain built with
github.com/go-pkgz/rest.Wrap() - Mocks generated with moq, stored in same package with
_mock.gosuffix - Tests use testify assertions
Reproxy auto-detects docker environment via REPROXY_IN_DOCKER env var and adjusts default ports:
- Docker:
0.0.0.0:8080(http) /0.0.0.0:8443(https) - Non-docker:
127.0.0.1:80(http) /127.0.0.1:443(https)
DNS providers for ACME DNS-01 challenges use the libdns ecosystem (github.com/libdns). To add a new provider:
- Import the libdns provider package in
app/main.go - Add choice to
DNS.Typefield tag:choice:"newprovider" - Add config struct inside
DNS structwith provider-specific fields (usually justAPIToken) - Add switch case in
makeSSLConfig()creating the provider instance - Add test in
main_test.gofollowing existing pattern (set opts, call makeSSLConfig, assert NotNil) - Update README DNS provider section
Important considerations:
- libdns providers themselves are tiny (~20-70KB), but some pull in large cloud SDKs (AWS ~5MB, Azure/Google similar)
- "Lightweight" providers (DigitalOcean, Hetzner, Linode) use HTTP APIs with minimal deps
- Check libdns version compatibility - some providers lag behind API changes (e.g., vultr v1.0.0 incompatible with libdns v1.1.x)