Relay and AutoNAT Infrastructure
lattice infrastructure run operates a combined Circuit Relay v2 and AutoNAT
v2 service. It improves connectivity for online Lattice clients but stores no
vault data, membership data, reservations, circuits, or probe results.
The service intentionally does not provide rendezvous, mailbox, object storage,
DHT, or gossipsub. Deploy those as separate services when a product mode calls
for them. The optionally allowlisted rendezvous service is documented in
docs/services/rendezvous.md; mailbox and object storage also retain their own process,
identity, state, config, and service account.
Quickstart
Section titled “Quickstart”lattice infrastructure init --host relay.example.net --print-firewallinit writes a validated config to the default service path, creates the stable
identity under lock, and prints the client [network] entry. Use
--external-ip for a raw address and --out/--state-dir to place the config
and identity explicitly. The installer and containers wrap this; see
Self-Hosting the Edge Services.
Configuration
Section titled “Configuration”Start from packaging/lattice-infrastructure.toml. Replace every
relay.example.net address with the public DNS name or IP address that routes to
this host, then validate the file:
lattice infrastructure config-check --config ./lattice-infrastructure.tomllattice infrastructure peer-idClients configure the printed Peer ID together with one advertised address:
[network]relay_nodes = ["/dns4/relay.example.net/tcp/4001/p2p/12D3Koo..."]The default peer-id command uses the current user’s infrastructure state
directory. For a system service, query the explicit system identity path after
the service has created it, as shown below.
The production config requires nonzero TCP and QUIC listeners and at least one matching routable external address. Unknown fields, duplicate addresses, unbounded values, and per-peer limits above global limits are rejected.
The default policy permits 128 reservations, 64 active circuits, 256 MiB and 10 minutes per circuit, 64 pending AutoNAT probes, 8 concurrent probes per source IP, 16 probe starts per source IP per 30 seconds, 512 established connections, and at most 8 established connections per peer. The packaged file is the authoritative list of defaults and can be tightened for smaller deployments.
Runtime State
Section titled “Runtime State”The stable libp2p identity key is the only durable service state. Clients pin its Peer ID, so deleting the key changes the service identity and requires updating every client configuration. Reservations, circuits, probes, and metrics reset on restart.
Without systemd, paths follow the platform’s per-user config, data, and runtime
directories under lattice-infrastructure. --config overrides only the config
file path. The state and lock paths remain in the dedicated infrastructure data
directory.
Network Exposure
Section titled “Network Exposure”The sample listens on TCP 4001 and UDP 4001 for QUIC. Expose both through the
host firewall and any upstream NAT. Lattice does not alter firewall rules. The
metrics listener defaults to 127.0.0.1:9474 and should remain private.
Endpoints:
/healthz: the metrics HTTP task is alive./readyz: both libp2p listeners reached readiness./metrics: OpenMetrics relay, AutoNAT, connection, swarm, and bandwidth data.
Metrics do not use Peer IDs, IP addresses, multiaddresses, vault IDs, or rendezvous namespaces as labels.
systemd System Service
Section titled “systemd System Service”Install the release binary, sample config, service user declaration, and unit:
sudo install -Dm0755 target/release/lattice /usr/bin/latticesudo install -Dm0644 packaging/systemd/lattice.sysusers.conf /usr/lib/sysusers.d/lattice.confsudo systemd-sysusers /usr/lib/sysusers.d/lattice.confsudo install -Dm0644 packaging/systemd/lattice-infrastructure.service /etc/systemd/system/lattice-infrastructure.servicesudo install -Dm0644 packaging/lattice-infrastructure.toml /etc/lattice-infrastructure/config.tomlsudo systemctl daemon-reloadsudo systemctl enable --now lattice-infrastructure.serviceThe infrastructure config contains public listener addresses and bounded
policy, not secrets. It is root-owned and world-readable because systemd keeps
ConfigurationDirectory root-owned. The public service runs as the dedicated
lattice-infrastructure account and cannot read the password-vault daemon’s
private /var/lib/lattice state.
Verify startup and scrape locally:
systemctl status lattice-infrastructure.servicejournalctl -u lattice-infrastructure.service -fsudo -u lattice-infrastructure /usr/bin/lattice infrastructure peer-id \ --identity /var/lib/lattice-infrastructure/infrastructure-identity.keycurl --fail http://127.0.0.1:9474/readyzcurl --fail http://127.0.0.1:9474/metricsThe unit uses systemd readiness, watchdog, restart-on-failure, dedicated configuration/state/runtime directories, a restrictive umask, and an empty capability bounding set. A listener or runtime failure after readiness exits the process so systemd can restart it.
systemd User Service
Section titled “systemd User Service”Install the binary, user unit, and config without root access:
install -Dm0755 target/release/lattice ~/.local/bin/latticeinstall -Dm0644 packaging/systemd/user/lattice-infrastructure.service ~/.config/systemd/user/lattice-infrastructure.serviceinstall -Dm0600 packaging/lattice-infrastructure.toml ~/.config/lattice-infrastructure/config.tomlsystemctl --user daemon-reloadsystemctl --user enable --now lattice-infrastructure.serviceEnable user lingering separately when the service must survive logout:
loginctl enable-linger "$USER"Foreground Operation
Section titled “Foreground Operation”Other Unix supervisors should run the process in the foreground:
lattice infrastructure run --config /path/to/lattice-infrastructure.tomlSend SIGTERM or Ctrl-C for a bounded graceful shutdown. Do not wrap this command in classic double-fork daemonization when a supervisor owns the process.
Cross-NAT Application Verification
Section titled “Cross-NAT Application Verification”The release-only Linux topology gate uses isolated network namespaces and does not modify the host firewall ruleset:
bash xtask/cross-nat-e2e.shIt requires ip, nft, sysctl, and unshare. Compilation runs as the
invoking user. By default the already-built test runs as root only inside a new
unprivileged user, mount, and network namespace. Its nested namespaces and
nftables rules therefore cannot modify the host network or firewall. If the
kernel disables unprivileged user namespaces, the wrapper prints an explicit
sudo command for operator review and exits; it never invokes sudo itself.
The test creates a temporary public segment, two independent stateful NAT routers, two private client networks, and a namespace running the real infrastructure command. It uses deterministic source-port translation without static destination NAT, so an inbound direct connection is available only after the clients perform the DCUtR simultaneous-open exchange. Two complete Lattice daemons then sync an attachment over the direct path, restart, converge concurrent edits to one conflict, propagate a signed resolution, lose all node-to-node reachability, and propagate a deletion over the retained relay. All nested namespaces and nftables state disappear with the disposable user namespace. A directly root-run fallback also removes its exact per-run namespace prefix on normal exit, failure, SIGINT, or SIGTERM.