Documentation
¶
Overview ¶
Package rendr is a connection-migration framework. An application holds a stable net.Conn whose underlying network path rendr can swap (TCP socket, QUIC connection, opaque-UDP flow) without surfacing any reset, EOF, or read-zero to the application.
rendr does only this. It does not implement proxy protocols, peer discovery, configuration management, or path-quality probing policies beyond the built-in defaults; those belong to the embedder.
Quickstart ¶
Server:
ln, _ := rendr.ListenTCP("0.0.0.0:5555")
for {
c, err := ln.Accept(context.Background())
if err != nil { return }
go handle(c) // c implements net.Conn
}
Client:
d := &rendr.Dialer{
Mode: rendr.ModePrime,
Paths: []rendr.PathSpec{
{Transport: "tcp", Address: "h1:5555"},
{Transport: "quic", Address: "h2:5555"},
},
}
c, err := d.Dial(context.Background())
// c.Read / c.Write survive a path swap; c.FlowID() stays
// constant for the connection's lifetime.
Packet-boundary mode (PacketConn) ¶
For datagram-oriented applications (WireGuard, opaque UDP echo, any protocol that owns its own framing) use DialPacket / ListenUDPFlowPacket. The wire format is identical; only the receive side changes: each rendr DATA frame becomes one packet, boundaries are preserved 1-to-1 with WriteTo / ReadFrom.
ln, _ := rendr.ListenUDPFlowPacket("0.0.0.0:5555")
d := &rendr.Dialer{Mode: rendr.ModePrime,
Paths: []rendr.PathSpec{{Transport: "udpflow", Address: "h1:5555"}}}
pc, _ := d.DialPacket(context.Background())
_, _ = pc.WriteTo(packet, nil) // one packet -> one frame
Packet-mode negotiation happens in HELLO via proto.CapsPacketMode; a stream-mode peer connecting to a packet listener is routed to the regular Accept channel instead. The two modes can coexist on one listener port.
Operational modes ¶
Three modes share the same migration engine. Set on Dialer.Mode or change at runtime via Conn.SetMode (subject to the legality rules below):
- ModePrime: pick the lowest-scoring path; migrate to a better path only when it beats the current by Hysteresis for Dwell, with Cooldown between switches. Quality is scored as rtt + jitter + 10 ms per percent loss.
- ModeRace: every frame fans out to every attached path; the receiver dedupes by SEQ. Bandwidth equals the best single path; latency equals the minimum across paths. Tolerates loss on individual paths.
- ModeBond: frames are round-robined across paths with N-frame pinning to bound reorder windows under RTT skew. Bandwidth approximates the sum of paths.
Legal SetMode transitions:
prime <-> race allowed prime <-> bond allowed race <-> bond forbidden (race has no per-path order)
Acceptance contracts ¶
rendr is gated on five invariants (the implementation is not considered done until all five hold in chaos-scale tests):
G1 - large file (>= 1 GiB) with forced mid-stream migrations
finishes with identical SHA-256 and < 10% baseline
throughput regression
G2 - 30 min echo loop with 30+ migrations, zero loss, P99
RTT < baseline x 2
G3 - 100k pps QUIC datagrams + 10 ConnID migrations, zero
application loss, P95 RTT < baseline x 2
G4 - path A force-killed (network DROP) with path B intact;
app sees no error; failover <= 5 s; in-flight data
reaches the peer via path B
G5 - after G4, recover path A (via AdminConn.AddPath); it
re-joins the path set without spurious reorder
AdminConn observability and control ¶
The public Conn returned by Dial / Accept also implements AdminConn (assert if you need it). PacketConn returned by DialPacket / AcceptPacket implements AdminPacketConn with the same surface. Both expose:
- Path-set management: Migrate, ActivePath, AddPath, RemovePath
- Mode read/write: Mode, inherited SetMode
- Lifecycle: State
- Diagnostic counters: RecvQueueHWM (race-mode dedup window), RecvDups (race-mode dedup events), BondStuckSkips (bond stuck- path bypass count), MigrationCount (active-path changes)
- One-call monitoring snapshot: Stats (returns ConnStats with all the above plus per-path PathInfo and FlowID)
Hard rules ¶
- A migration in flight NEVER surfaces an error from the application's Read or Write. Read may block during the migration budget (default 90 s); after the budget elapses with no usable path, Read returns ErrMigrationBudgetExceeded.
- Transport errors and application EOF are NEVER conflated. A clean peer BYE surfaces as io.EOF; everything else triggers migration.
- Default has NO active-migration trigger. Migration fires only on path death or explicit ModePrime quality-scoring decisions; quality scoring itself is OFF unless armed by setting Mode=ModePrime on the Dialer.
- All wire formats are versioned (see package proto). Any change to the bytes on the wire requires bumping the version constant.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrMigrationBudgetExceeded = engine.ErrMigrationBudgetExceeded ErrZombie = engine.ErrZombie ErrPeerProtoVersion = engine.ErrPeerProtoVersion ErrLastPath = engine.ErrLastPath ErrPacketTooLarge = engine.ErrPacketTooLarge // ErrReadDeadlineExceeded is the sentinel returned by Read / // ReadFrom when a SetReadDeadline-set deadline elapses before // payload is ready. Implements net.Error with Timeout()==true so // idiomatic timeout checks via errors.As work transparently. ErrReadDeadlineExceeded net.Error = engine.ErrReadDeadlineExceeded // ErrModeSwitchIllegal is returned by Conn.SetMode for transitions // that the engine refuses (e.g. race -> bond). ErrModeSwitchIllegal = errors.New("rendr: illegal mode transition") // ErrNotImplemented is a build-stage placeholder used by API // surfaces whose implementations land in later milestones. ErrNotImplemented = errors.New("rendr: not implemented in this milestone") )
Sentinel errors surfaced by the rendr API. The set is small on purpose: the contract is that migrations do not surface errors at all (CLAUDE.md hard rule #1), so the only errors that ever escape the API are end-of-life errors.
The three end-of-life errors live in internal/engine so the engine can return them directly; rendr re-exports the same value so callers can errors.Is against rendr.ErrFoo and have it match what the engine returned.
Functions ¶
This section is empty.
Types ¶
type AdminConn ¶
type AdminConn interface {
Conn
Migrate(pathID uint32) error
ActivePath() uint32
// AddPath dials a new path matching spec and joins it to the
// existing engine via BRIDGE_TAG. Returns the new path id on
// success. This is the G5 "path recovery" primitive: after a
// path death, dial a fresh replacement (typically same spec)
// to bring the path set back to its original cardinality.
AddPath(spec PathSpec) (uint32, error)
// RemovePath gracefully detaches the named path. If it is the
// active path, the engine first failovers to another attached
// path. Returns ErrLastPath if id is the only attached path;
// in that case callers who want full teardown should call Close.
// AddPath/RemovePath are the symmetric primitives for runtime
// path-set management; the engine itself never calls RemovePath.
RemovePath(pathID uint32) error
// State returns the bridge lifecycle stage as a short string:
// "init", "handshaking", "active", "migrating", "closing",
// "dead". Production monitoring uses this for a liveness
// check that doesn't require sending traffic.
State() string
// RecvQueueHWM returns the high-water mark of the reorder
// buffer over this Conn's lifetime. A consistently-growing HWM
// in race mode indicates "dedup window overflow" - the receiver
// is buffering more out-of-order frames than the path RTT skew
// should produce, suggesting a path is dropping or stuck.
// Production dashboards consult this to spot the condition.
RecvQueueHWM() int
// RecvDups returns the cumulative count of frames whose SEQ
// had already been delivered (or was already buffered in the
// reorder window). For race mode this is the duplicate-frames-
// reaped counter; for any mode it surfaces accidental
// retransmits.
RecvDups() uint64
// BondStuckSkips returns the cumulative count of round-robin
// slots that bond dispatch bypassed because the candidate
// path's probe-measured RTT exceeded best_rtt *
// BondStuckRTTMultiplier. Always zero outside bond mode.
BondStuckSkips() uint64
// MigrationCount returns the cumulative number of active-path
// changes since this Conn was established (initial activation
// is not counted). Both explicit Migrate calls and death-
// driven failover contribute. Production dashboards use this
// to detect churn that may need human attention.
MigrationCount() uint64
// OnMigrate registers fn to fire (in its own goroutine) on every
// active-path change. cause is "explicit" for Migrate-driven
// transitions and "death" for failover via onPathDeath. The
// returned cancel function unsubscribes. Use this instead of
// polling MigrationCount when you want push-based notification
// (e.g. metrics, structured logs).
OnMigrate(fn func(oldID, newID uint32, cause string)) (cancel func())
// Mode returns the current operational mode (prime/race/bond).
// Symmetric counterpart to SetMode; lets callers verify a
// mode transition succeeded.
Mode() Mode
// Stats returns a coherent one-call snapshot of everything a
// monitoring layer wants to see: flow id, mode, lifecycle
// state, path list (with per-path counters + quality), active
// path id, and recv-queue high-water mark. The contents are
// also obtainable individually but Stats avoids torn reads
// across getters.
Stats() ConnStats
}
AdminConn extends Conn with operations that are not part of the normal application surface: explicit path migration, active-path introspection, and dynamic path attach. Tools that drive migration externally (runtime balancers, monitoring panels, integration tests) assert to this interface.
Application code should NOT depend on AdminConn; the engine reserves the right to migrate on its own and an external migrate can race with internal scheduling.
Example ¶
ExampleAdminConn shows the monitoring / control face. After Dial, the application can assert to rendr.AdminConn to inspect path state, drive migration, or attach a fresh path post-death.
package main
import (
"context"
"fmt"
"io"
"log"
"github.com/FrankoonG/rendr"
)
func main() {
ln, err := rendr.ListenTCP("127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
srvDone := make(chan struct{})
go func() {
defer close(srvDone)
c, err := ln.Accept(context.Background())
if err != nil {
return
}
defer c.Close()
buf := make([]byte, 8)
_, _ = io.ReadFull(c, buf)
}()
d := &rendr.Dialer{
Mode: rendr.ModePrime,
Paths: []rendr.PathSpec{{Transport: "tcp", Address: ln.Addr().String()}},
}
c, err := d.Dial(context.Background())
if err != nil {
log.Fatal(err)
}
defer c.Close()
_, _ = c.Write(make([]byte, 8))
if adm, ok := c.(rendr.AdminConn); ok {
s := adm.Stats()
fmt.Println("state:", s.State)
fmt.Println("mode:", s.Mode)
fmt.Println("paths:", len(s.Paths))
}
<-srvDone
}
Output: state: active mode: prime paths: 1
type AdminPacketConn ¶
type AdminPacketConn interface {
PacketConn
Migrate(pathID uint32) error
ActivePath() uint32
AddPath(spec PathSpec) (uint32, error)
RemovePath(pathID uint32) error
State() string
RecvQueueHWM() int
RecvDups() uint64
BondStuckSkips() uint64
MigrationCount() uint64
OnMigrate(fn func(oldID, newID uint32, cause string)) (cancel func())
Mode() Mode
Stats() ConnStats
}
AdminPacketConn is the AdminConn analogue for packet-mode. It extends PacketConn with the same migration/observability surface stream-mode AdminConn exposes.
type Conn ¶
type Conn interface {
net.Conn
// Paths returns a snapshot of the currently-attached path set.
Paths() []PathInfo
// SetMode atomically switches the operational mode.
// Some transitions are illegal at runtime: race → bond is rejected
// (race has no per-path sequencing; bond requires it). bond → prime
// and prime ↔ race are allowed. SetMode returns nil on success or
// an error describing the rejection cause.
SetMode(Mode) error
// FlowID returns the 16-byte flow identifier assigned at
// handshake. It is invariant for the Conn's lifetime and serves
// as the demux key on the server side across path migration.
FlowID() [16]byte
}
Conn is a stream-oriented rendr connection. It is a net.Conn that survives underlying path changes.
Hard contract (CLAUDE.md, hard rule #1): no Read/Write/Close on this interface returns an error caused by a migration. A migration in flight may pause individual Read/Write calls but never surfaces a reset / SO_ERROR / read-zero / write-error. If the migration budget (90s) elapses without a usable path, Read/Write will then return a real error and the Conn is dead.
type ConnStats ¶
type ConnStats struct {
FlowID [16]byte
State string
Mode Mode
ActivePath uint32
Paths []PathInfo
RecvQueueHWM int
RecvDups uint64
BondStuckSkips uint64
MigrationCount uint64
// CreatedAt is the wall-clock time at which this connection's
// engine was constructed. Use time.Since(s.CreatedAt) to compute
// connection age.
CreatedAt time.Time
}
ConnStats is the one-call snapshot returned by AdminConn.Stats. Layout is stable; fields are added to the end for forward compatibility.
type Dialer ¶
type Dialer struct {
// Mode is the initial operational mode.
Mode Mode
// Paths are the candidate paths for this connection.
Paths []PathSpec
// Hysteresis (prime only): the new path must beat the current by
// this fraction of the current score before a switch is allowed.
// Default 0.25.
Hysteresis float64
// Dwell (prime only): minimum time the engine must stay on a path
// after attaching to it. Default 5s.
Dwell time.Duration
// Cooldown (prime only): minimum gap between two successive
// migrations regardless of quality. Default 30s.
Cooldown time.Duration
// MigrationBudget caps how long the engine will hold a Conn open
// after all paths have died. Default and maximum 90s, per
// CLAUDE.md hard rule #4. Lower values are allowed; higher ones
// will be clamped to 90s.
MigrationBudget time.Duration
// ProbeInterval is how often each attached path issues a
// CtrlPathProbe to measure RTT. 0 = default 1s.
ProbeInterval time.Duration
// ZombieMaxMigrations: number of consecutive completed migrations
// with zero application payload between them before the engine
// declares the peer a zombie and tears down (CLAUDE.md hard rule
// #5). Default 2; lower values trip earlier.
ZombieMaxMigrations int
// ZombieCooldown: how long the engine waits between zombie-
// counter decrements. After ZombieCooldown elapsed since the
// last migration, the counter resets to ZombieMaxMigrations.
// Default 30s.
ZombieCooldown time.Duration
// BondStuckRTTMultiplier (bond only): a path whose latest probe
// RTT exceeds best_path_rtt * Multiplier is skipped on bond
// round-robin. Default 3.0. Set lower to be more aggressive
// about bypassing slow paths.
BondStuckRTTMultiplier float64
// contains filtered or unexported fields
}
Dialer is the entry point for constructing a rendr Conn.
Dialer is intentionally minimal: the path set is fixed at dial time. The engine will not discover paths on its own; the embedder feeds candidate PathSpecs. Use AddPath / RemovePath on the returned AdminConn to mutate the path set after dial.
func (*Dialer) AddPacketPathFactory ¶
func (d *Dialer) AddPacketPathFactory(name string, f PacketPathFactory) error
AddPacketPathFactory registers a packet factory under name. Any PathSpec in d.Paths whose Transport equals name is dialed via this factory instead of transport.Default, used only by DialPacket() (packet-mode sessions). The contract:
- The returned net.PacketConn MUST preserve datagram boundaries (one WriteTo == one peer ReadFrom).
- MTU MUST be sufficient for the rendr 8B flow-id header plus expected payload.
- PathSpec.Address (or PathSpec.Opts["peer_addr"] if you need to decouple "dial target" from "datagram peer") is resolved as net.ResolveUDPAddr and used as the WriteTo destination on every outgoing datagram.
- PathSpec.Opts["flow_id_hex"] (14 hex digits = 7 bytes) overrides the random flow_id; otherwise crypto/rand picks one.
Names are unique per Dialer across both stream and packet maps.
func (*Dialer) AddStreamPathFactory ¶
func (d *Dialer) AddStreamPathFactory(name string, f StreamPathFactory) error
AddStreamPathFactory registers a stream factory under name. Any PathSpec in d.Paths whose Transport equals name is dialed via this factory instead of transport.Default. Names must be unique per Dialer; a duplicate (or shadowing of a packet factory name) returns an error.
Factories are consulted before transport.Default, so they can also override a registered transport (e.g. a custom "tcp" implementation for one Dialer only). To reach the global registry name, just leave it unregistered on the Dialer.
func (*Dialer) Dial ¶
Dial establishes a rendr Conn using d's configuration. The engine performs the HELLO handshake on the first path; subsequent paths (attached during the same Dial call or later via the migration API) send BRIDGE_TAG carrying the same flow_id.
M1 limits: prime mode only; the first path in d.Paths becomes the active path, the remainder are attached but kept idle until a migration trigger fires.
Example ¶
ExampleDialer_Dial demonstrates the smallest useful rendr setup: one TCP path between two in-process endpoints. Real deployments supply two or more paths so migration has somewhere to go.
package main
import (
"context"
"fmt"
"io"
"log"
"github.com/FrankoonG/rendr"
)
func main() {
ln, err := rendr.ListenTCP("127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
srvDone := make(chan struct{})
go func() {
defer close(srvDone)
c, err := ln.Accept(context.Background())
if err != nil {
return
}
defer c.Close()
buf := make([]byte, 64)
n, _ := c.Read(buf)
fmt.Println("server got:", string(buf[:n]))
}()
d := &rendr.Dialer{
Mode: rendr.ModePrime,
Paths: []rendr.PathSpec{{Transport: "tcp", Address: ln.Addr().String()}},
}
c, err := d.Dial(context.Background())
if err != nil {
log.Fatal(err)
}
defer c.Close()
_, _ = io.WriteString(c, "hello rendr")
<-srvDone
}
Output: server got: hello rendr
func (*Dialer) DialPacket ¶
func (d *Dialer) DialPacket(ctx context.Context) (PacketConn, error)
DialPacket establishes a rendr PacketConn using d's configuration. Each application WriteTo becomes one wire frame; each ReadFrom returns the payload of one wire frame. The peer (server) is notified via proto.CapsPacketMode in the HELLO so its engine also runs the packet-boundary drainer.
All Dialer fields (Mode, Paths, prime knobs, MigrationBudget) apply identically to packet-mode connections; the underlying engine and path machinery are the same.
Example ¶
ExampleDialer_DialPacket demonstrates packet-boundary mode over opaque UDP. Each WriteTo becomes one frame; each ReadFrom returns one frame's payload. Use this for datagram-oriented protocols (WireGuard, custom UDP echo) where boundaries must be preserved.
package main
import (
"context"
"fmt"
"log"
"github.com/FrankoonG/rendr"
)
func main() {
ln, err := rendr.ListenUDPFlowPacket("127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
srvDone := make(chan struct{})
go func() {
defer close(srvDone)
c, err := ln.AcceptPacket(context.Background())
if err != nil {
return
}
defer c.Close()
buf := make([]byte, 64)
n, _, _ := c.ReadFrom(buf)
fmt.Println("server got:", string(buf[:n]))
}()
d := &rendr.Dialer{
Mode: rendr.ModePrime,
Paths: []rendr.PathSpec{{Transport: "udpflow", Address: ln.Addr().String()}},
}
c, err := d.DialPacket(context.Background())
if err != nil {
log.Fatal(err)
}
defer c.Close()
_, _ = c.WriteTo([]byte("hello packets"), nil)
<-srvDone
}
Output: server got: hello packets
type Listener ¶
type Listener interface {
Accept(ctx context.Context) (Conn, error)
Close() error
// Addr returns the listener's local network address, useful for
// tests that bind ":0" and need to discover the chosen port.
Addr() net.Addr
// FlowIDs returns the live flow_id set for diagnostics.
FlowIDs() [][16]byte
}
Listener accepts inbound rendr Conns. The set of acceptable transports is determined by registering transport adapters on the Listener (see transport.Registry).
func ListenQUIC ¶
ListenQUIC starts a QUIC-only rendr listener bound to addr.
tlsCfg may be nil during local development; production embedders MUST supply a real *tls.Config. Inbound QUIC connections are demultiplexed by their first frame (HELLO -> new flow_id / engine; BRIDGE_TAG -> attach to existing engine), exactly as ListenTCP does.
func ListenTCP ¶
ListenTCP starts a TCP-only rendr listener bound to addr. The returned Listener accepts inbound paths, demultiplexes them by flow_id, and yields one rendr.Conn per new flow.
M1: TCP is the only transport. M2 expands this to a generic Listener with a TransportSet.
func ListenUDPFlow ¶
ListenUDPFlow starts an opaque-UDP rendr listener bound to addr. It mirrors ListenTCP / ListenQUIC: inbound flows are demuxed by their first ctrl frame (HELLO -> new engine / BRIDGE_TAG -> attach path to existing engine), and the engine sees a transport.PathConn that hides the UDP demux machinery entirely.
Migration over opaque UDP is implicit at the transport layer: a datagram arriving from a fresh 4-tuple but carrying a known flow_id updates the corresponding ServerPathConn's RemoteAddr without firing any engine-level migration. Engine-driven migration (Engine.Migrate / prime / race) layers on top of that and remains transport-agnostic.
type Mode ¶
type Mode uint8
Mode selects how the engine uses the set of available paths.
const ( // ModePrime picks the single best-scoring path and migrates only // when another path beats the current one by the hysteresis margin // for at least dwell, with cooldown between switches. ModePrime Mode = 1 // ModeBond splits frames across paths to aggregate throughput. ModeBond Mode = 2 // ModeRace duplicates every frame across all paths and dedupes on // receive. Bandwidth equals the single best path; latency equals // the minimum across paths. ModeRace Mode = 3 )
type PacketConn ¶
type PacketConn interface {
net.PacketConn
Paths() []PathInfo
SetMode(Mode) error
FlowID() [16]byte
}
PacketConn is the datagram analogue of Conn.
type PacketListener ¶
type PacketListener interface {
AcceptPacket(ctx context.Context) (PacketConn, error)
Close() error
Addr() net.Addr
FlowIDs() [][16]byte
}
PacketListener accepts inbound rendr PacketConns. The udpflow listener implements both Listener and PacketListener: HELLO with CapsPacketMode routes to AcceptPacket, otherwise to Accept. A listener can therefore serve mixed packet- and stream-mode peers simultaneously without separate ports.
func ListenQUICDatagram ¶
func ListenQUICDatagram(addr string, tlsCfg *tls.Config) (PacketListener, error)
ListenQUICDatagram starts a QUIC listener that accepts incoming connections in DATAGRAM mode (RFC 9221). One DATAGRAM == one rendr frame; no bidi stream is opened. Pair with Dialer.DialPacket and PathSpec.Opts["mode"]="datagram" on the client side.
The server's engine is forced into packet mode regardless of the CapsPacketMode bit on HELLO - a DATAGRAM-mode peer cannot honour stream-style byte concatenation, so accepting one here implies packet-boundary semantics on both ends.
tlsCfg may be nil during local development; production embedders MUST supply a real *tls.Config.
func ListenUDPFlowPacket ¶
func ListenUDPFlowPacket(addr string) (PacketListener, error)
ListenUDPFlowPacket is a convenience constructor returning the same listener cast to PacketListener. Stream and packet acceptors share the same socket; HELLO caps decide which channel each connection lands on.
type PacketPathFactory ¶
PacketPathFactory mirrors StreamPathFactory for packet-mode paths. The returned net.PacketConn MUST preserve datagram boundaries (single WriteTo == single peer ReadFrom) and have MTU sufficient for the rendr 8-byte flow-id header plus expected payload.
rendr-side wraps the returned net.PacketConn with transport/udpflow.WrapFromSpec — the same flow-id framing layer that backs the built-in udpflow transport. The factory does NOT have to produce a "connected" socket: udpflow's WrapFromSpec resolves PathSpec.Address as the WriteTo peer and validates incoming flow_ids regardless of source-addr.
type PathQuality ¶
type PathQuality = transport.PathQuality
type StreamPathFactory ¶
StreamPathFactory constructs the underlying byte-stream net.Conn for one rendr stream-mode path. The factory's contract:
- The returned net.Conn MUST preserve byte order (Read sees Write's bytes in order, unbroken). rendr layers 2-byte length-prefix framing on top; a torn or reordered byte stream will fail HELLO and the path is rejected.
- The returned connection MUST terminate at the same rendr peer as every other path in the Dialer (docs/plan.md §"项目定位" C1). rendr verifies this in HELLO via the shared flow_id; a mismatch causes the path to be dropped at handshake.
Typical uses:
- Wrap an xray-core outbound chain (vless / trojan / ss / nested / reverse) so rendr migrates across xray-protected paths.
- Plug in a custom transport (e.g. a private overlay socket) that isn't worth a full transport.Transport adapter.
addr is passed straight from PathSpec.Address. ctx is honored for dial cancellation.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
engine
Package engine houses the rendr migration engine: bridge table, per-Conn state machine, cleanClose discrimination, zombie protection.
|
Package engine houses the rendr migration engine: bridge table, per-Conn state machine, cleanClose discrimination, zombie protection. |
|
Package mode defines the Scheduler contract between the rendr engine and a mode strategy (prime / bond / race).
|
Package mode defines the Scheduler contract between the rendr engine and a mode strategy (prime / bond / race). |
|
Package proto defines the rendr control-plane wire format v0.
|
Package proto defines the rendr control-plane wire format v0. |
|
Package transport defines the contract between the rendr engine and a concrete network transport (tcp, quic, udp_opaque, gvisor, ...).
|
Package transport defines the contract between the rendr engine and a concrete network transport (tcp, quic, udp_opaque, gvisor, ...). |
|
quic
Package quic is the QUIC transport adapter.
|
Package quic is the QUIC transport adapter. |
|
tcp
Package tcp is the TCP byte-stream transport adapter.
|
Package tcp is the TCP byte-stream transport adapter. |
|
udpflow
Package udpflow is the opaque-UDP transport adapter.
|
Package udpflow is the opaque-UDP transport adapter. |
|
Package xray provides the rendr-as-xray-transport binding.
|
Package xray provides the rendr-as-xray-transport binding. |