8.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build
go build ./...
go vet ./...
There are no tests in this module. go build ./... is the primary verification.
Module Purpose
github.com/go-gost/core defines every interface and shared type used across the GOST project. It contains no implementations — all concrete types live in the x/ module. Go stdlib interfaces (net.Conn, net.Listener, net.Addr) are the only external dependencies (plus crypto/tls for TLS config references).
The go.mod has zero third-party dependencies (only go 1.22 / toolchain go1.22.2).
Key Interfaces & Their Relationships
GOST tunneling follows a layered request path. Each layer is defined here as an interface:
Service → Listener → Handler → chain.Router → chain.Route → chain.Transporter → (dialer.Dialer → connector.Connector)
Top-level: Service
service/service.go — Serve() error / Addr() net.Addr / Close() error. Wraps one listener.Listener + one handler.Handler.
Accept side: Listener and Handler
- listener.Listener —
Init(metadata) / Accept() (net.Conn, error) / Addr() / Close(). Accepts inbound connections. ItsOptionscarry achain.Routerfor upstream routing. - handler.Handler —
Init(metadata) / Handle(ctx, conn) error. Processes an inbound connection: authenticates, selects a route viaForwarder.Forward(hop), and proxies traffic. ItsOptionscarry achain.Router, bypass rules, auth, rate/traffic limiters, TLS config, and recorders.
Routing: Chain, Router, Hop, Node, Transporter
- chain.Router —
Options() / Dial(ctx, network, address) (net.Conn, error) / Bind(ctx, network, address) (net.Listener, error). The top-level router that bothListenerandHandleruse. Combines aChainer,resolver.Resolver,hosts.HostMapper, recorders, retries, and timeouts. - chain.Chainer —
Route(ctx, network, address) Route. Selects aRoutefor the target address. - chain.Route —
Dial(ctx, network, address) (net.Conn, error) / Bind(ctx, network, address) (net.Listener, error) / Nodes(). Represents one path through the proxy chain.Dialconnects outward;Bindis for reverse tunnels. - hop.Hop —
Select(ctx, SelectOptions) *chain.Node. Represents a group of nodes that serve the same role. Uses aselector.Selectorto pick one node (load balancing: round-robin, random, etc.). TheSelectOptionscarry HTTP-level context (host, method, path, headers, client IP) for routing decisions. - chain.Node — A single proxy hop with name, address, transporter, bypass rules, resolver, host mapper, TLS/HTTP settings, and a
failMarkerfor health tracking. Nodes areselector.Markable— failed nodes get marked so selectors can skip them. - chain.Transporter —
Dial / Handshake / Connect / Bind / Multiplex. Combines adialer.Dialer+connector.Connectorfor one node in the chain. Dial reaches the next proxy hop, handshake negotiates the transport protocol, connect reaches the destination through the hop, bind sets up reverse listeners.
Outbound: Dialer and Connector
- dialer.Dialer —
Init / Dial(ctx, addr) (net.Conn, error). Dials from GOST to the next-hop proxy server. Also definesHandshaker(transport-level handshake, e.g. TLS) andMultiplexer(can this connection be multiplexed?). - connector.Connector —
Init / Connect(ctx, conn, network, address) (net.Conn, error). Connects from the final GOST node to the actual destination. Also definesHandshaker(application-level, e.g. SOCKS handshake) andBinder(for reverse tunnels / BIND).
Cross-cutting: Registry, Metadata, Selector
- registry.Registry[T] — Generic
Register / Unregister / IsRegistered / Get / GetAll. Every component type inx/has its own registry instance. Registration happens viainit()side-effects and blank imports. - metadata.Metadata —
IsExists / Set / Getkey-value map. Every component has anInit(Metadata)method. Config fields that don't map to explicit struct fields go into metadata. - selector.Selector[T] — Generic
Select(ctx, ...T) T, plusStrategy[T],Filter[T],Markable/Markerfor health tracking. Used byHopfor node selection.
Support interfaces
| Interface | File | Purpose |
|---|---|---|
auth.Authenticator |
auth/auth.go | Authenticate(ctx, user, password) (id, ok) |
admission.Admission |
admission/admission.go | Admit(ctx, addr) bool — allow/deny connection |
bypass.Bypass |
bypass/bypass.go | IsWhitelist() / Contains(ctx, network, addr) bool — should this address bypass the proxy chain? |
resolver.Resolver |
resolver/resolver.go | Resolve(ctx, network, host) ([]net.IP, error) |
hosts.HostMapper |
hosts/hosts.go | Lookup(ctx, network, host) ([]net.IP, bool) — static host→IP mapping |
recorder.Recorder |
recorder/recorder.go | Record(ctx, b []byte) error — traffic recording (hex dump, HTTP body capture) |
observer.Observer |
observer/observer.go | Observe(ctx, events) error — observability events |
ingress.Ingress |
ingress/ingress.go | SetRule / GetRule(host) — hostname→tunnel endpoint mapping |
sd.SD |
sd/sd.go | Register / Deregister / Renew / Get — service discovery |
router.Router |
router/router.go | GetRoute(ctx, dst) — OS-level route table queries (TUN mode) |
routing.Matcher |
routing/matcher.go | Match(*Request) bool — request-based routing rules |
logger.Logger |
logger/logger.go | Structured logging with levels (trace→fatal). LoggerGroup fans out to multiple loggers. Global default via SetDefault/Default. |
metrics.Metrics |
metrics/metrics.go | Counter / Gauge / Observer — Prometheus-style metrics abstraction |
conn.Limiter |
limiter/conn/limiter.go | Allow(n int) bool — connection count limiting |
rate.Limiter |
limiter/rate/limiter.go | Allow(n int) bool — rate limiting |
traffic.Limiter |
limiter/traffic/limiter.go | Wait(ctx, n) int — bandwidth/traffic limiting |
limiter (scopes) |
limiter/limiter.go | Shared Options struct and scope constants (ScopeService, ScopeConn, ScopeClient) |
observer/stats.Stats |
observer/stats/stats.go | Connection/bytes/errors counters for observability |
bufpool |
common/bufpool/pool.go | Tiered sync.Pool for byte buffers (128B → 64KB). Get(size) / Put(b). |
xnet.Dialer |
common/net/dialer.go | Minimal Dial(ctx, network, addr) (net.Conn, error) — used as a shared dialer interface in connector/dialer options |
Patterns
Functional Options
Every interface uses the functional options pattern. Each package typically has two option layers:
- Init-time options (e.g.
handler.Option,listener.Option) — set inx/config/parsing/service/parse.gowhen constructing components from config. - Call-time options (e.g.
handler.HandleOption,dialer.DialOption) — passed at invocation time with per-request context.
Both follow the same pattern: an Options struct, and option functions type FooOption func(*FooOptions).
No Implementations Rule
This module must never contain implementations of any interface it defines. Implementations belong in x/. Exceptions are small utility types that are self-contained and used by multiple x/ packages: logger.LoggerGroup, selector.failMarker, listener.AcceptError/BindError, and bufpool.
Adding a New Interface
- Define the interface in a new or existing package under
core/ - Add any init-time/call-time option types in the same package
- Register implementations in
x/registry/using the registration pattern - Keep
go.moddependency-free (third-party types belong inx/, not here)
Relationship to Workspace
This is one of ~12 modules in the go-gost workspace. See ../../CLAUDE.md for the full project architecture, build commands, and the implementation module (x/). The workspace go.work ties all modules together — go build ./... from the workspace root resolves across all modules.