NAT traversal.
- STATUS
- Shipped
- CRATE
- tenzro-network
- TRANSPORTS
- TCP + QUIC (port 9000)
- DEFAULT
- Permissionless
Universal listen-addr default
tenzro-node binds both /ip4/0.0.0.0/tcp/9000 and /ip4/0.0.0.0/udp/9000/quic-v1 by default when --listen-addr is omitted. This is the universal transport set: any device — cloud VM, residential wifi, mobile tether, Raspberry Pi, ESP32 — can reach the node via whichever transport NAT permits.
QUIC's structural port-reuse gives Identify observed_addr a stable listening UDP port that AutoNAT v2 can probe back, which is what makes hole punching reliable.
Four behaviours composed
The NAT stack is built into TenzroBehaviour via libp2p's SwarmBuilder::with_relay_client:
- Identify — peers exchange listen addresses + observed-address feedback.
- AutoNAT v2 — dial-back probes confirm public reachability.
- Circuit-Relay v2 — public validators serve as relay hops for joiners.
- DCUtR — direct connection upgrade through relay, i.e. hole punching.
Validators with a confirmed public address run the server halves (relay::Behaviour, autonat::v2::server::Behaviour). Joiner-class roles (LightClient, ModelProvider, TeeProvider) run the client halves. All five sub-behaviours are wrapped in Toggle<> so disabled halves are no-ops.
Config knobs
[network]
enable_relay = true # server-side; default-on for Validator
enable_hole_punching = true # client-side; default-on for all rolesPermissionless observed_addr
We never require --external-p2p-addr. The node uses Identify observed-address tally (with quorum N≥3) to learn its public address, AutoNAT v2 to confirm it, and falls back to Circuit-Relay v2 + DCUtR when direct reachability fails. This matches Substrate, Sui, and IPFS-class permissionless discovery.
Bootstrap pairs
On testnet, bootstrap peers are advertised in both /ip4/.../tcp/9000/p2p/... and /ip4/.../udp/9000/quic-v1/p2p/... forms. The dialer picks whichever its NAT permits. --boot-nodes accepts a comma-separated list.
Cloud firewalls are operator runbook
Cloud NAT/security-group/network-policy rules are not part of the protocol. Open TCP/9000 and UDP/9000 in your cloud firewall (GCE, EC2 SG, Azure NSG, K8s NetworkPolicy). On COS-based GKE, also append an ACCEPT rule in the host iptables via cloud-init — COS defaults to DROP for everything except lo, icmp, tcp:22, and ESTABLISHED.