Interfaces
Role
Interfaces move Reticulum packets between the transport layer and a physical or virtual medium. All implementations live in pkg/interfaces and conform to the Interface contract defined in interface.go.
Factory entry point:
iface, err := interfaces.NewFromConfigWithContext(ctx, name, cfg)
Interface contract
Each interface implements:
StartandStopfor lifecycleSendfor outbound dataProcessIncomingandProcessOutgoingfor IFAC and framing- Statistics hooks used by the daemon and control API
Inbound path:
raw bytes -> ProcessIncoming -> packetCallback -> Transport.HandlePacket
Outbound path:
Transport.SendPacket -> Send -> ProcessOutgoing -> wire
Supported types
Config type |
Status | File |
|---|---|---|
UDPInterface |
Complete | udp.go |
TCPClientInterface |
Complete | tcp.go |
TCPServerInterface |
Complete | tcp.go |
AutoInterface |
Complete | auto.go, auto_rescan.go, auto_roam.go |
I2PInterface |
Complete | i2p.go (SAM in pkg/i2p) |
BackboneInterface |
Complete | backbone.go |
BackboneClientInterface |
Complete | backbone_client.go |
PipeInterface |
Complete | pipe.go |
SerialInterface |
Complete | serial.go |
LocalInterface / LocalServerInterface |
Complete | local.go, sharedinstance |
WebSocketInterface |
Go-only | websocket_native.go, websocket_wasm.go |
QUICClientInterface |
Go-only | quic.go, quic_tls.go |
QUICServerInterface |
Go-only | quic.go, quic_tls.go |
WebTransportClientInterface |
Go-only | webtransport.go |
WebTransportServerInterface |
Go-only | webtransport.go |
DNSRendezvousInterface |
Go-only | dns_rendezvous.go |
VSOCKClientInterface |
Go-only (Linux) | vsock.go |
VSOCKServerInterface |
Go-only (Linux) | vsock.go |
HTTPSClientInterface |
Go-only | https.go |
HTTPSServerInterface |
Go-only | https.go |
Not implemented
These Python interface types have no driver in Reticulum-Go:
- RNodeInterface, RNodeMultiInterface
- KISSInterface, AX25KISSInterface
- WeaveInterface
- Android-specific KISS, RNode, Serial variants
SerialInterface
HDLC-framed serial port matching Python SerialInterface wire framing (MTU 564, baud as bitrate, default IFAC size 8).
Go extensions beyond Python:
- Chunked reads instead of byte-at-a-time
- Configurable inter-byte frame idle drop (
frame_idle_ms, default 100) max_reconnect_triesand reconnect delay- Optional RTS/CTS DSR/DTR XON/XOFF keys
- IFAC and receive-only (
outgoing = no) - Live counters (
FramesRX/TX, framing errors, reconnects) - Injectable port opener for tests
[[Radio Serial]]
type = SerialInterface
enabled = yes
port = /dev/ttyUSB0
speed = 115200
databits = 8
parity = N
stopbits = 1
device = is an alias for the TTY path. Non-numeric port = values are treated as device paths so Python configs load.
Live Python framing interop: RUN_LIVE_INTEROP=1 with tests/interop/serial_live_test.go.
PipeInterface
Bridges Reticulum to any external program over stdin/stdout using HDLC framing, matching Python PipeInterface.
Configuration:
command(required): program and arguments, split like Pythonshlexrespawn_delay(optional): seconds before respawning after subprocess exit (default 5)
External interface plugins
Python loads .py modules from {config_dir}/interfaces/. Reticulum-Go keeps the same discovery path but uses process isolation and in-process factories instead of executing Python:
interfaces.RegisterExternalFactory(typeName, factory)for embedders{config_dir}/interfaces/{Type}.json(or.manifest) withdriverandcommand(pipe)- Executable
{config_dir}/interfaces/{Type}used as a PipeInterface command
Example manifest:
{"driver": "pipe", "command": "/usr/local/bin/my-rns-iface", "respawn_delay": 5}
Config:
[[Custom Radio]]
type = MyRadioIface
enabled = yes
LocalInterface
Local shared-instance access uses HDLC over TCP (127.0.0.1:port) or abstract Unix (@rns/<name>).
Two configuration paths:
- Automatic (Python-compatible):
share_instance = yesin[reticulum]viapkg/sharedinstance - Explicit interface block:
type = LocalInterfaceortype = LocalServerInterfacein[[...]]
Local clients set ConnectedToSharedInstance and skip path-request ingress limiting, matching Python behavior.
UDPInterface
Connects to a configured peer over UDP.
Requirements:
- Explicit
target_addressortarget_host(same policy as Pythonforward_ip) - Open binds do not adopt the source address of the first inbound packet
Optional reconnect when max_reconnect_tries > 0 is a Go extension. Python does not reconnect UDP by default.
TCP client and server
TCP uses HDLC framing with a maximum frame size cap (maxHDLC in tcp.go). Client interfaces support:
- Keepalives
- Reconnect via
reconnect.go - Tunnel re-synthesis on reconnect (
SetTunnelSynth/onConnected) - Optional I2P tunneling (
i2p_tunneled)
Server interfaces accept inbound connections and apply IFAC on each session.
AutoInterface
AutoInterface discovers peers on a local link using IPv6 link-local multicast.
Features:
- Peer aging and timeout
- Configurable discovery and data ports
- NIC binding via
interfacekey - Rescan when
watch_interfaces = yes(auto_rescan.go) - Listener replacement on Wi-Fi roam (
auto_roam.go, aligned with Python 1.3.5)
Interface modes
Modes match Python RNS wire values (full 0x01 through internal 0x07). Set with mode or interface_mode on an interface block.
| Mode | Effect (summary) |
|---|---|
full |
Default. Normal announce and path behavior |
access_point |
Does not rebroadcast announces |
gateway / roaming / internal |
Participate in unknown-path discovery (DISCOVER_PATHS_FOR) |
boundary / roaming / internal |
Extra announce forward filters vs next-hop mode (RNS 1.3.6+) |
recursive_prs = yes forces unknown-path discovery on any mode. announces_from_internal = no blocks rebroadcast of announces learned via an internal-mode next hop.
I2PInterface
Uses I2P SAM (pkg/i2p) for inbound and outbound tunnels.
Configuration keys: sam_address, peers, connectable, i2p_tunneled.
Live tests require RUN_LIVE_I2P=1 and a running SAM bridge.
Backbone
Backbone interfaces multiplex many TCP streams through pkg/backbone hubs. Select poller backend with backbone_io in [reticulum]:
| Value | Platform |
|---|---|
| auto | Best available |
| epoll | Linux |
| kqueue | BSD, macOS |
| io_uring | Linux (when available) |
| go | Portable fallback |
WebSocketInterface
Go-only transport for browser WASM clients. Native builds use websocket_native.go. WASM builds use websocket_wasm.go.
QUICClientInterface / QUICServerInterface
Go-only QUIC transport (not available on WASM). HDLC frames ride one bidirectional QUIC stream per connection, matching the TCP interface framing model.
TLS follows a Yggdrasil-style mesh model:
- Ephemeral self-signed ECDSA P-256 certificates by default
- X.509 CA verification is skipped (
InsecureSkipVerify) - Optional
peer_keypins the remote leaf SPKI SHA-256 (hex) - Optional
cert_file/key_filesupply persistent PEM material - Optional
snisets the client TLS ServerName - ALPN is fixed to
rns - IFAC (
network_name/passphrase) still applies above QUIC
[[QUIC Hub]]
type = QUICServerInterface
enabled = yes
listen_ip = 0.0.0.0
listen_port = 4242
[[QUIC Uplink]]
type = QUICClientInterface
enabled = yes
target_host = hub.example.com
target_port = 4242
peer_key = aabbccdd...
max_reconnect_tries = -1
Client reconnect uses reconnect.go like TCP. Server fan-out writes to all accepted sessions. Live Go-Go tests: RUN_LIVE_INTEROP=1 with tests/interop/quic_live_test.go.
WebTransportClientInterface / WebTransportServerInterface
Go-only HTTP/3 WebTransport transport (not on WASM). Prefer datagrams for RNS packets. Optional HDLC stream mode matches QUIC.
- Default path
/rns transport_mode = datagram(default),stream, ordual- Application protocol
rns - Same TLS options as QUIC (
cert_file,key_file,peer_key,sni) - IFAC and reconnect supported
[[WT Hub]]
type = WebTransportServerInterface
enabled = yes
listen_ip = 0.0.0.0
listen_port = 4433
path = /rns
transport_mode = datagram
[[WT Client]]
type = WebTransportClientInterface
enabled = yes
target_host = hub.example.com
target_port = 4433
path = /rns
transport_mode = datagram
peer_key = aabbccdd...
DNSRendezvousInterface
Go-only rendezvous underlay (not a DNS tunnel). Looks up DNS TXT for peer endpoints, then carries RNS packets over UDP to the discovered address.
TXT forms accepted:
rns=udp://1.2.3.4:4242rns proto=udp host=1.2.3.4 port=4242
Hosts must be a parseable IP or DNS name without whitespace. Only udp and tcp schemes are accepted (udp is used by this underlay). Invalid TXT records are ignored.
[[DNS Peer]]
type = DNSRendezvousInterface
enabled = yes
domain = peers.example.com
listen_ip = 0.0.0.0
listen_port = 0
resolve_interval = 60
resolve_interval is seconds between re-queries (default 60). Publish the TXT at domain (or a name your resolver returns for that query). Live Go-Go: RUN_LIVE_INTEROP=1 with tests/interop/dns_rendezvous_live_test.go.
VSOCKClientInterface / VSOCKServerInterface
Go-only Linux AF_VSOCK transport with HDLC framing (same pattern as TCP). Useful for host-guest and same-host Local CID paths.
[[VSOCK Hub]]
type = VSOCKServerInterface
enabled = yes
port = 4242
[[VSOCK Client]]
type = VSOCKClientInterface
enabled = yes
context_id = 3
port = 4242
max_reconnect_tries = -1
context_id / cid is the peer CID (1 is Local on Linux). Not available on non-Linux or WASM. Live Local CID: RUN_LIVE_INTEROP=1 with tests/interop/vsock_live_test.go.
HTTPSClientInterface / HTTPSServerInterface
Go-only TLS long-poll packet underlay for restrictive networks where only HTTPS egress works. Not WebTransport or HTTP/3.
- Default path
/rns - Client
POST {path}/sendand long-pollGET {path}/poll - Peer id header
X-RNS-Peer - Same TLS options as QUIC (
cert_file,key_file,peer_key,sni) long_poll_secdefault 25
[[HTTPS Hub]]
type = HTTPSServerInterface
enabled = yes
listen_ip = 0.0.0.0
listen_port = 8443
path = /rns
cert_file = /path/to/cert.pem
key_file = /path/to/key.pem
[[HTTPS Client]]
type = HTTPSClientInterface
enabled = yes
target_host = hub.example.com
target_port = 8443
path = /rns
long_poll_sec = 25
peer_key = aabbccdd...
Live Go-Go: RUN_LIVE_INTEROP=1 with tests/interop/https_live_test.go.
Interface Access Code (IFAC)
When network_name and passphrase are set on an interface, frames are masked on egress and verified on ingress.
Policy:
- Wrong or missing IFAC on a configured interface results in silent drop on ingress
- Applied on UDP, TCP, Auto, and other supported types via
pkg/common.ApplyIFACInboundandApplyIFACOutbound
Details: Cryptography.
Reconnect behavior
| Aspect | Python RNS | Reticulum-Go |
|---|---|---|
| TCP / backbone / QUIC / WebTransport / HTTPS / VSOCK client | Yes for TCP/backbone, 5 s wait | Yes via reconnect.go |
| Serial | Yes, 5 s wait | Yes with max_reconnect_tries |
| I2P | Yes, 15 s wait | Yes in i2p.go |
| UDP / DNS rendezvous | No | UDP when max_reconnect_tries > 0. DNS re-resolves on interval |
| Default max tries | Unlimited (None) |
Unlimited (-1 or omitted) |
| After exhaustion | Teardown | Teardown (Stop) |
ConnectivityNotifier hooks allow embedders to observe reconnect state (Go-only).
Hot reload
node.ReloadInterfaces swaps interface blocks without restarting the process. On Unix, SIGHUP triggers reload in the daemon.
On unregister, transport scrubs paths, discovery state, announce bookkeeping, relay rows, and link-table entries for the removed interface.
Equality checks in pkg/node/reload.go cover type, addresses, I2P settings, IFAC, Auto ports, MTU, bitrate, prefer_ipv6, announce-rate, ingress/egress control, mode, and outgoing.
Tests: interface_lifecycle_test.go, reload_e2e_test.go.
Rate and ingress settings
Per-interface keys announce_cap, announce_rate_*, ingress_control, and ic_* feed pkg/rate limiters consumed by transport ingress handlers.
Operational notes
MTU. Default Reticulum packet MTU is 500 bytes (pkg/packet.MTU). Interface mtu should be consistent with the physical path.
IPv6. prefer_ipv6 affects TCP and Auto binding and discovery.
Panic on error. panic_on_interface_error = yes can crash the daemon on fatal interface errors. Default is no.
Testing
| Test | Env / command |
|---|---|
| IFAC live | RUN_LIVE_INTEROP=1, tests/interop/ifac_live_test.go |
| Pipe live | RUN_LIVE_INTEROP=1, tests/interop/pipe_live_test.go |
| Serial live | RUN_LIVE_INTEROP=1, tests/interop/serial_live_test.go |
| DNS rendezvous live | RUN_LIVE_INTEROP=1, tests/interop/dns_rendezvous_live_test.go |
| VSOCK live | RUN_LIVE_INTEROP=1, tests/interop/vsock_live_test.go (Linux) |
| HTTPS live | RUN_LIVE_INTEROP=1, tests/interop/https_live_test.go |
| QUIC / WebTransport live | RUN_LIVE_INTEROP=1, tests/interop/quic_live_test.go |
| Shared RPC live | RUN_LIVE_INTEROP=1, tests/interop/shared_rpc_live_test.go |
| Auto live | tests/interop/auto_live_test.go |
| Backbone live | tests/interop/backbone_live_test.go |
| I2P live | RUN_LIVE_I2P=1 |
| Race (Stop vs Send) | go test -race ./pkg/interfaces/ -run Race plus DNS/VSOCK/HTTPS unit tests |
| Goroutine leak | go test ./pkg/interfaces/ -run NoGoroutineLeak |
| Fuzz (examples) | go test ./pkg/interfaces/ -run '^$' -fuzz=FuzzParseRNSTXT -fuzztime=20s |
Fuzz targets cover TXT parsing (FuzzParseRNSTXT), HTTPS path/long-poll normalization, WebTransport path/mode, VSOCK CID and HDLC decode, Serial HDLC, and peer-key pins.
Related documents
- Configuration for interface block keys
- Transport for registration and forwarding
- Compatibility for Python interface matrix