The node's only overload protection is a single global socket cap: the intake counter is capped at capacity*2 (default 50 -> 100 sockets) in node/internal/server/server.go:181 (s.intake.enter closes anything over the cap; newIntake at server.go:77, logic in node/internal/server/intake.go), plus a soft client cap that returns an "overloaded" welcome once roster.count() >= capacity (server.go:249). There is no per-IP rate limit, connection throttle, cryptographic cookie, or proof-of-work anywhere — node/internal/server/connections.go only counts /proc/net/tcp for telemetry and enforces nothing.
This is a real availability gap because every unauthenticated or foreign connection still gets a full TLS handshake and is then bridged to the real cover site (node/internal/server/gate.go:63, cover.go, proxy.go) — expensive CPU plus an outbound connection per bogus client. A flood of valid-looking ClientHellos from a censor's active prober therefore consumes handshake + cover-bridge work all the way up to the 100-socket ceiling and locks out real clients, at no cost to the attacker. WireGuard handles this with its cookie mechanism: it stays silent without a valid MAC1 and, under load, demands MAC2 = a cookie bound to the initiator's source IP (server_secret rotated every 120s, cookie AEAD-encrypted), which proves source-IP ownership cheaply and enables token-bucket per-IP rate limiting (wireguard-go device/cookie.go).
Add a per-source-IP token bucket in the pre-handshake gate (node/internal/server/gate.go, where reality.Inspect runs) that sheds or delays connections from an IP exceeding a rate, before the full TLS handshake and cover proxy are committed. We cannot go silent like WireGuard without breaking the HTTPS cover, but capping per-IP handshake+proxy spend stops one source from exhausting the global socket budget; keep the cover-bridge behavior for connections within the allowed rate. Filing per a WireGuard comparison review.
The node's only overload protection is a single global socket cap: the intake counter is capped at
capacity*2(default 50 -> 100 sockets) innode/internal/server/server.go:181(s.intake.entercloses anything over the cap;newIntakeatserver.go:77, logic innode/internal/server/intake.go), plus a soft client cap that returns an "overloaded" welcome onceroster.count() >= capacity(server.go:249). There is no per-IP rate limit, connection throttle, cryptographic cookie, or proof-of-work anywhere —node/internal/server/connections.goonly counts/proc/net/tcpfor telemetry and enforces nothing.This is a real availability gap because every unauthenticated or foreign connection still gets a full TLS handshake and is then bridged to the real cover site (
node/internal/server/gate.go:63,cover.go,proxy.go) — expensive CPU plus an outbound connection per bogus client. A flood of valid-looking ClientHellos from a censor's active prober therefore consumes handshake + cover-bridge work all the way up to the 100-socket ceiling and locks out real clients, at no cost to the attacker. WireGuard handles this with its cookie mechanism: it stays silent without a valid MAC1 and, under load, demands MAC2 = a cookie bound to the initiator's source IP (server_secretrotated every 120s, cookie AEAD-encrypted), which proves source-IP ownership cheaply and enables token-bucket per-IP rate limiting (wireguard-godevice/cookie.go).Add a per-source-IP token bucket in the pre-handshake gate (
node/internal/server/gate.go, wherereality.Inspectruns) that sheds or delays connections from an IP exceeding a rate, before the full TLS handshake and cover proxy are committed. We cannot go silent like WireGuard without breaking the HTTPS cover, but capping per-IP handshake+proxy spend stops one source from exhausting the global socket budget; keep the cover-bridge behavior for connections within the allowed rate. Filing per a WireGuard comparison review.