socketduct
v0.1.16
Published
Resilient authenticated tunnel/proxy for keeping socket streams alive across flaky links.
Maintainers
Readme
Socketduct
See the wire protocol for multiplexed v3, rollback-compatible compact framing v2, replay, flow control, and scheduling semantics.
Resilient authenticated tunnel/proxy for keeping socket streams alive across flaky links.
Socketduct is intended for setups where the application machine and a local Socketduct edge are on a stable LAN, but the edge's internet connection occasionally drops for a few seconds. Applications connect to the local edge; Socketduct reconnects and resumes the upstream tunnel when the WAN returns.
App / client software
-> local Socketduct edge on stable LAN
-> flaky internet
-> Socketduct relay/server
-> target serviceCore idea
Normal TCP sockets cannot magically resume after the underlying internet path dies. Socketduct works by owning both sides of the unstable leg:
- the local edge keeps the application-facing socket alive
- the remote relay validates the requested target per session and keeps the target-facing side alive across brief edge disconnects when possible
- the edge and relay exchange framed stream data with sequence numbers and acknowledgements
- stream data payloads are compressed with zstd when both peers negotiate it, falling back to raw framing otherwise; sequences, acknowledgements and limits stay in uncompressed bytes (see Protocol sketch)
- on interruption, unacknowledged session data is persisted to a temporary spool file
- on reconnect, the client authenticates, resumes the session by identifier, sends the spool state/data, and continues from the last acknowledged sequence
- initial target acquisition is retried with bounded exponential backoff before readiness; established targets and application operations are never automatically replayed
- dedicated
socketduct/httpandsocketduct/httpsagents support HTTP and strict target HTTPS/mTLS without a local listener - protocol v3 exposes
MultiplexedSocketductConnection: one authenticated physical TCP/TLS transport carries many normalVirtualSocketDuplex streams, with fair flow control and one shared reconnect/resume owner - protocol v3 also exposes
MultiplexedSocketductTransportSet: one explicit warm member per gateway path, bounded readiness waiting for app-facing sockets, and deterministic per-socket round-robin across ready members - native reverse-gateway v3 lets authenticated private home members register outbound with a Server3 hub, so CGNAT paths need no inbound forwarding
- optional bounded JSONL lifecycle logs can be written to per-process files in a Docker bind-mounted directory without replacing existing stdout/stderr
MVP goals
- Node.js implementation.
- Multi-core server using Node's cluster/worker model.
- Username/password authentication before session operations.
- Session creation with a human-readable name and stable opaque identifier.
- Session-scoped target selection through an explicit allowlist policy.
- Session resume by identifier.
- Temporary spool files for interrupted sessions.
- Bounded buffering and cleanup.
- No unauthenticated open proxy behavior.
Multiplexed v3 API
import {MultiplexedSocketductConnection} from "socketduct/multiplexed"
const transport = new MultiplexedSocketductConnection({
relay: {host: "relay.example.net", port: 3100, token: process.env.SOCKETDUCT_TOKEN}
})
const socket = transport.createConnection({host: "service.internal", port: 443})Pass the pre-bound transport.createConnection directly to generic Node clients.
Every returned VirtualSocket is an independent Duplex; closing one does not
close the physical transport. Legacy v2 APIs and HTTP agents remain available
for rollback.
For process-scoped path diversity, configure a transport set with stable gateway IDs and distinct gateway-pinned relay endpoints or physical socket factories:
import {MultiplexedSocketductTransportSet} from "socketduct/multiplexed"
const transports = new MultiplexedSocketductTransportSet({
connectionId: "build_runner_01",
gateways: [
{gatewayId: "starlink", endpoint: {relay: starlinkRelay}},
{gatewayId: "zyxel", endpoint: {relay: zyxelRelay}},
{gatewayId: "o2gateway", endpoint: {relay: o2Relay}}
]
})
const starting = transports.start()
const socket = transports.createConnection({host: "service.internal", port: 443})
await startingThe default minimum readiness is one. Other configured paths continue warming
and automatically rejoin selection. A socket created before readiness is
returned immediately and waits inside Socketduct with normal callback, event,
timeout and backpressure behavior; assignment stays FIFO and occurs only on a
ready member. A virtual socket never changes members.
Durable process-replacement recovery uses recoverConnection() with the
original gateway ID before the replacement set starts.
For home Docker hosts behind O2, Zyxel, and Starlink, run
OutboundReverseGateway in each router-pinned namespace and
startReverseGatewayHub on Server3. TensorBuzz can keep this transport-set API
by configuring each member with endpoint.reverseHub; all three may share one
hub address because gatewayId selects the outbound member. Docker remains
docker-server:2375 on a private Compose network and is never published. See
Native reverse gateway.
Documentation
- Architecture
- Protocol sketch
- Session lifecycle
- Security model
- Running Socketduct
- Docker gateway setup
- Docker topology
- Node-compatible socket API design
- Native reverse gateway
- Persistent structured logging
- Initial implementation plan
- Socket + Docker integration plan
Contributing
Every user-visible change, bug fix, security fix, and protocol change must include a concise release-note fragment in changelog.d/YYYYMMDDHHMMSS-short-slug.md. Release tooling consumes these fragments; do not edit generated release notes directly.
TensorBuzz is the sole CI and private-container publication workflow owner for
this repository; tensorbuzz.yml defines dependency installation, tests,
typechecking, linting, and a real Dockerfile build against an isolated Docker
service. Pull-request builds never publish images. Private GHCR publication
remains disabled until TensorBuzz supplies trusted default-branch/tag/manual
trigger metadata and the Socketduct project has an established secret-backed
private-registry write credential input.
Status
Initial MVP skeleton. The project includes authenticated session create/resume control messages with per-session targets, bounded initial target recovery, a cluster-based server skeleton, local TCP edge/relay forwarding, target-side socket retention across relay reconnects, spool-backed replay, multiplexed multi-gateway transport sets, HTTP and target HTTPS/mTLS adapters, and tests for those flows.
