Configuration reference
Every key the daemon parses from /etc/atlas/config.toml, with types, defaults and behavior. This page is generated from the same schema the daemon generates from its own configuration structs (atlasd config schema), so it cannot drift from what a release actually accepts. Add #:schema pointing at that schema file to your config and a TOML-aware editor validates as you type.
atlasd config check -c /etc/atlas/config.toml runs the daemon’s exact load pipeline — exit 0 means loadable, exit 1 means the daemon would refuse to start. Sections marked opt-in leave the data path byte-identical to a build without the feature until enabled.[interface]
requiredUDP source port every link binds by default (WireGuard-style single port). Per-link listen_port overrides it.
TUN interface name (e.g. nexus0). Identifies this daemon instance in stats files, the web UI and the CLI.
Base64 Curve25519 private key (from atlasd keygen). Keep the file mode 0600 — this is the node's identity.
""Tunnel address of this node as IPv4 CIDR, e.g. 10.0.100.1/24. Bare addresses imply /24. May be left out on a node joining by a pairing code ([auth] invite_token): the code's i with the prefix length of its h, or — a pool code, which carries no i — the address the inviting node assigns at admission (the engine starts with the interface up and unaddressed until then).
unsetBrief note describing what this atlas instance is for, e.g. "office to vehicle" or "ground station to aircraft". Surfaced in the web UI header beside the interface name so an operator can tell multiple atlas instances apart at a glance. Local-only — not advertised in LSAs.
trueEndpoint roaming (WireGuard semantics, per link): when an AUTHENTICATED packet from a peer arrives from a new source address, adopt it as that peer's endpoint for the receiving link. Lets a mobile node (drone re-associating to a different ground radio / mesh relay) keep the tunnel alive without restarts. On by default; static fleets are unaffected because their traffic already arrives from the configured endpoints. Probes/keepalives are unauthenticated and never steer endpoints.
1416TUN device MTU. Default 1416: a full-size packet relayed through another node (56 B of Atlas headers) plus UDP/IPv4 (28 B) is exactly 1500 bytes.
unsetThe next transport private key during a rotation overlap. The node answers handshakes on either key and initiates with private_key; activating the rotation swaps the two.
unsetThis node's record id (a UUID minted by atlasd keygen --identity). Only meaningful in a record fleet ([auth] provider = "fleet"), where it must match the fleet-list record that holds this node's key; the LSDB then knows the node by this id, so rotating a key changes nothing anyone routes on. Absent = the transport key is the node's id, as in a fleet without records.
unsetHuman-readable label for this node, advertised in our LSAs so peers can show it in their topology view. Optional — when omitted the daemon falls back to the system hostname.
unsetWhat this node physically is, advertised in our LSAs beside the position so peers can tell a drone from a rover from a mast without guessing from the name. Purely descriptive — nothing in the data path reads it; the topology views (atlas-sim, dashboard) use it to pick a symbol. Well-known values are listed in KNOWN_NODE_ROLES (ground-station, uav, ugv, usv, dismount, relay); anything else is passed through unchanged with a config-check warning, so a fleet can coin its own.
unsetStatic position of this node as [lat_deg, lon_deg, alt_m] (WGS-84). Advertised in our LSAs for peers' topology prediction and relay planning. Fixed installations set it here; mobile nodes instead stream fixes through the telemetry socket (node_position JSON messages), which override this value.
unsetBase64 Ed25519 seed of this node's signing key (beside private_key, same 0600 file). Signs the node-bound dashboard read token. Its public half belongs in the record's [[node.keys.signing]].
[[link]]
requiredOne underlay link of the bond: a UDP socket pinned to a physical device. type = "relay" makes it a relay link (peer traffic wrapped in encrypted envelopes through a Nexus Atlas relay server); type = "serial" puts Atlas frames straight onto a serial radio (device, no socket, no PPP).
Link name, unique within this config. Referenced by scheduler priority_order, peer endpoint_per_link and telemetry events.
Serial link only: this node's address on a channel shared by several nodes (RFD900 multipoint firmware with the radios broadcasting). Set, every frame carries destination and source addresses (3 bytes); unset, the link is point to point and frames carry none.
Serial link only: the radio's air data rate (SiK ATS2), unit-tagged or a bare number in Mbps. With no capacity, the link's capacity is the lower of this and the line rate (baud × 0.8).
Capacity as a plain number, always in Mbps. Overridden by capacity when both are set.
57600Serial link only: line rate, 8N1. Must match the radio's serial speed (SiK ATS1). A mismatch shows as framing and CRC errors in the link's counters.
9600192003840057600115200230400460800500000921600Local IP the socket binds. 0.0.0.0 = any IPv4; a bare IPv6 address selects the v6 family.
Network device the link's UDP socket is bound to (SO_BINDTODEVICE), e.g. eth0, wlan1, ppp-sik.
trueSerial link only: write to the port only while the serial line and the radio's buffer hold less than [qos] low_band_target_ms at the link's rate, so Atlas's own queues decide what goes on air next (the high band first). False writes as fast as the port takes it, as pppd's path does.
Nominal capacity of the physical link — the reference ceiling the scheduler's cost functions divide by. Unit-tagged string ("64 kbps", "100 Mbps", "1.5 Gbps", case-insensitive) or a bare number in Mbps. Default 80 Mbps when neither this nor bandwidth_mbps is set.
Accepted alias for probe_interval_ms.
Serial link only (required there): the serial device of the radio. A /dev/serial/by-id/… path keeps naming the same radio across replugs. The link reopens it by itself after an unplug or a radio reset.
trueSet false to keep the link configured but unused. Default true.
"none"Serial link only: rtscts when the radio and the cable carry RTS/CTS (SiK RTSCTS=1); the radio then stops the line instead of dropping bytes when its buffer is full.
nonertsctsSerial link only: interval of the MAVLink heartbeat written to the radio so it reports RADIO_STATUS (17 bytes each, also sent on air). Default 250 for rfd900/sik, 0 (never) for generic.
Per-link UDP source port overriding [interface] listen_port. REQUIRED when two links share bind_device+bind_address (SO_REUSEPORT would otherwise load-balance them against each other).
20Longest one fragment may occupy this link, in ms (default 20, one voice frame): packets sent over it are fragmented at capacity × airtime, clamped to [256 B, the global payload]. 0 = the global payload.
Maximum usable radio range in meters. Only meaningful with [link_monitor] prediction_source = "position"/"both": scheduler-facing loss inflates as the projected peer distance approaches it. Default: the global range_full_m.
Serial link with address only: peer tunnel address → that node's address on the channel (1–254). A node not listed is learned from its first authenticated frame.
Serial link only, point to point: tunnel addresses of the node(s) at the other end of the radio. Empty = every configured peer may be reached over this link.
Per-link probe (health-check) interval in ms, overriding the global [link_monitor] probe_interval_ms for this link only.
Metered-link budget: wire bytes this link may carry per quota_period. Unit-tagged string (kB/MB/GB/TB are decimal, KiB/MiB/GiB/TiB binary) or a bare number of bytes. Omit for an unmetered link. An over-budget link is not promoted by the scheduler while another link is usable, but is still used when it is the last path.
"monthly"Window the quota applies to: monthly (default), weekly, daily, or a sliding rolling-<N>d window.
1Anchor for the calendar periods: day-of-month for monthly (1-31, clamped to the month's length), ISO day-of-week for weekly (1 = Monday … 7 = Sunday). Ignored by daily and rolling-<N>d.
80Percent of the budget at which the link is reported as warning (journal event link.quota_warn, warning in the stats snapshot). 0 disables the warning.
"generic"Serial link only: the radio on the port. rfd900 and sik write a MAVLink heartbeat so the radio injects RADIO_STATUS (needs MAVLink framing, ATS6=1), whose RSSI, noise and transmit-buffer fill feed the link score and the pacer. generic = any byte pipe.
rfd900sikgeneric2048Serial link only: the radio's transmit buffer in bytes, to turn RADIO_STATUS txbuf (percent free) into bytes for the pacer. Default 2048.
Distance in meters where this link's range penalty starts ramping. Default 60% of max_range_m when that is set, else the global range_warn_m.
Base64 static public key of that relay, pinned — authenticates the relay itself.
ip:port of the relay server (its STUN/relay port, conventionally 3478). Required for type = "relay" unless traversal discovery resolves it.
Link-state flooding interval for THIS link in seconds, overriding the global [mesh] route_advert_interval_secs. Lets a narrow UHF bearer flood slowly while a wideband one keeps the fast cadence. Clamped to 30 s so a peer can never age this node's LSA out.
"direct"direct (default), relay, local or serial. A serial link frames Atlas datagrams straight onto a serial port (device, baud) — no PPP, no IP — and takes no bind_device/bind_address. A relay link carries peer traffic through a Nexus Atlas relay server and needs relay_server + relay_pubkey (or traversal discovery). A local link binds to loopback only and joins this daemon to another Atlas daemon on the same host — the seam of an Atlas Interconnect (see [[peer]] interconnect); it never carries link-state adverts.
directrelaylocalserialRound-trip time in ms past which a returning probe on THIS link stops counting as evidence the link works. Overrides the global [link_monitor] unusable_rtt_ms; 0 disables the rule for this link.
1Relative weight for the weighted_round_robin strategy. Default 1.
[[link_group]]
[[link_group]]: a named set of links.
[[link]] names; a link belongs to at most one group.
[[peer]]
Base64 Curve25519 public key of the peer — its identity.
[]Tunnel IPv4 CIDRs routed to this peer (WireGuard semantics), e.g. ["10.0.100.2/32"]. Bare addresses imply /32. May be left out for the inviting node of [auth] invite_token: the code's h as a /32.
[]Known endpoints, positionally per link. **Optional**: a peer reached through a relay — or one that will only ever be reached by hole punching — has no endpoint at all. Its identity is its public key, and its address is discovered at runtime (from the relay envelope, or from a punched candidate). Requiring an endpoint here would make the whole traversal stack unusable, since "I do not know where this peer is" is precisely the problem it exists to solve.
Explicit link-name → endpoint mapping. When non-empty, atlasd uses *only* this map for outbound sends to this peer: a link whose name has no entry here is not used to reach the peer at all. Useful when each radio has its own point-to-point IP pair (e.g. a multi-modem setup) so a packet that's bound to one link's interface can't be routed to the *other* link's remote IP without bypassing kernel iface pinning.
When empty (default), endpoints are matched to links by position: endpoint[i] is used for link i, with fallback to endpoint[0] when the index is out of range.
[[peer]]
public_key = "..."
endpoint = [] # not used when endpoint_per_link is set
endpoint_per_link = { "sik-hb" = "10.0.1.2:51820",
"sik-rfd" = "10.0.2.2:51820" }
allowed_ips = ["10.0.100.2/32"]Atlas Interconnect: this peer is a daemon of ANOTHER Atlas network on the same host (reached over a type = "local" link), and map says how that network's hosts appear inside this one. Each local address is advertised into this network as a served prefix; traffic to it is translated to the real remote address and sent across the seam, and traffic from a remote address arrives with its local source. export (optional) limits which of this network's addresses take part — reachable from the other side, and allowed to send across. LSAs never cross the seam.
[peer.interconnect]
export = ["10.0.101.2/32"]
[[peer.interconnect.map]]
remote = "10.0.9.249/32"
local = "10.0.101.249/32"Keys of [peer.interconnect]:
exportarray of string — This network's addresses that take part in the interconnect: the other side may reach them, and only they may send across. Empty = every address in this network.maparray of tables — How the other network's hosts appear in this one.remoteis a real address/prefix in the other network (inside this peer'sallowed_ips);localis where it appears here (inside this network's subnet, same prefix length).localstring required — Where it appears in this network, e.g."10.0.101.249/32".remotestring required — Real address or prefix in the other network, e.g."10.0.9.249/32".
unsetSend a keepalive to this peer every N seconds so NAT/stateful firewall mappings stay open. Omit to disable (default).
[scheduler]
5000Minimum dwell (ms) before the adaptive strategies de-escalate to fewer links; escalation is immediate. Default 5000.
0.9Enter n=2 when primary_cost / next_cost ≥ this. Default 0.9.
0.5Drop back to n=1 only when primary_cost / next_cost ≤ this. Default 0.5 (Schmitt gap of ~2× prevents flapping).
0.5Enter n=3 when primary_cost / third_cost ≥ this. Default 0.5.
0.25Drop from n=3 to n=2/n=1 only when primary_cost / third_cost ≤ this. Default 0.25.
10Piggyback a loss-probe sequence on every Nth real fragment so the peer can measure data-fragment loss directly (instead of just probe loss, which underestimates burst-loss damage on full-size fragments). Set to 0 to disable. Only takes effect when strategy = "adaptive". Default 10.
40Engage n=2 when the best link's RTT ≥ this many ms. Default 40.
100Engage n=3 when the best link's RTT ≥ this many ms. Default 100.
20Engage n=2 when the best link's loss ≥ this percent. Default 20.
50Engage n=3 when the best link's loss ≥ this percent. Default 50.
[]Link names in priority order, highest-priority first. Only honoured when strategy = "priority". Names are matched against [[link]].name entries; unknown names are ignored with a warning at engine startup.
1Send every fragment over up to this many of the best alive links at once. 1 (default) = single path. The adaptive strategies manage their own redundancy and ignore this.
"lowest_delivery_time"Multipath scheduling strategy. Default lowest_delivery_time.
lowest_delivery_timeweighted_round_robinbroadcastpriorityshortest_pathadaptiveloss_adaptivelatency_adaptive[link_monitor]
5Consecutive missed probes before a link is declared dead. Default 5.
200How often the kernel egress queues below each link (socket send buffer, root qdisc backlog) are sampled, in ms. 0 disables sampling.
2Window over which each link's utilisation is measured for the load entry of the link-state advert (headroom-aware routing), in seconds: bytes sent over the window ÷ the link's capacity (its declared capacity, or the queue's measured drain rate when that is lower). 1–60.
25Distance in metres from the last advertised position that triggers an immediate position advert. Default 25 (under one 30 m elevation cell, and about a second of flight for a fixed-wing aircraft); 0 turns the trigger off.
Range prediction and the SPF's terrain weights use the positions other nodes advertise, so this is how stale they can be about a moving node.
10Longest a moving node goes between position adverts, in seconds. A node with a fix that has moved at all since its last link-state advert re-advertises its position at least this often. Default 10; 0 turns the period off; at most 30 is useful (the advert refresh already carries the position every 30 s).
A position advert is an ordinary flood under the bearer-aware pacing: a link with its own route_advert_interval_secs never carries it faster than that, and no link faster than position_advert_min_interval_secs.
1Fewest seconds between two position adverts, node-wide (a float). Default 1.0; at least 0.1. However fast a node moves, an unpaced link carries its position at most this often, and a link with its own route_advert_interval_secs at the longer of the two. Position adverts are not held to [mesh] route_advert_interval_secs. Longer than position_advert_interval_secs is warned: the period trigger would be held to it.
unsetWhat feeds predictive scheduling/topology decisions: "none" (default), "rssi", "position", or "both". - rssi: local RSSI trend inflates scheduler-facing loss (see rssi_floor_dbm/rssi_lookahead_secs), and LSA-advertised per-link RSSI weights SPF costs. - position: LSA-advertised node positions/velocities are projected ahead; stretched links get expensive in SPF and the local scheduler (range_warn_m/range_full_m). - both: max of the two risks per link. Prediction inflates costs/loss only — probes remain the sole authority on link death, and dashboards keep measured values.
nonerssipositionboth250Probe (health-check) interval per link, in milliseconds. Default 250. Per-link probe_interval_ms overrides it.
1000Projected inter-node distance (m) where the position-based penalty reaches its maximum.
600Projected inter-node distance (m) where the position-based penalty starts ramping.
-88RSSI (dBm) below which a link is considered effectively dead for prediction purposes.
2How far ahead (seconds) the RSSI trend is projected.
falseShorthand switch for RSSI prediction: true behaves as prediction_source = "rssi" when prediction_source is unset.
trueLet the scheduler use each link's queue delay (standing-queue estimator and kernel egress queues). The figures stay visible in the snapshot either way; this only decides whether delivery time and the escalation guard react to them.
10000Round-trip time past which a probe that DOES come back stops counting as evidence the link works, in milliseconds. Default 10000; 0 turns the rule off.
Without it, liveness means "a round trip completed eventually", which a deeply queued link satisfies forever: probes return tens of seconds late, probe loss reads 0%, the link reads up, and traffic through it measures 100% loss. Probes past this ceiling are counted as misses, so the ordinary dead_probe_count rule declares the link dead — no separate state machine, and one late probe still cannot take a link down. Per-link [[link]] unusable_rtt_ms overrides it.
[crypto]
falseWhen true, payloads are sent in plaintext (no AEAD). Useful for performance testing. Both peers must have this set identically.
falseEnd-to-end payload encryption (opt-in, default **off**). When true, unicast inner IP packets are sealed under a source-to-destination AEAD *before* fragmentation, so relaying nodes forward an opaque blob and cannot read the payload — while still routing on the cleartext relay header, so un-upgraded relays forward it unchanged. The per-hop AEAD is unaffected. Broadcast, multicast and Fec-class traffic are e2e-exempt in this version. See crate::e2e.
falseRequire inbound unicast to be e2e-sealed: drop any unicast packet that arrives un-sealed. A hard posture for a node that must never accept relay-injected plaintext. Default **off**. Implies opening is active even when this node does not itself seal egress.
1073741824Also rekey after this many payload bytes on a session. Default 1073741824 (1 GiB).
120Re-run the Noise handshake with every peer after this many seconds. Default 120.
[auth]
Pluggable peer authentication ([auth]).
Controls what happens when a HandshakeInit arrives from a static public key that is NOT in the [[peer]] list. Configured peers are always accepted (they carry explicit operator intent) — the provider only ever *extends* trust to unknown keys, so the default is exactly today's behaviour: drop them.
[auth]
provider = "keyfile"
keyfile = "/etc/atlas/authorized_peers.toml"Providers: - "static" (default): unknown keys are rejected. Identical to a config with no [auth] table. - "keyfile": unknown keys are looked up in keyfile, a TOML allowlist of [[peer]] public_key = "...", allowed_ips = [...] entries. The file is re-read when its mtime changes, so keys can be granted/revoked without restarting the daemon (revocation stops NEW sessions; established sessions live until rekey). - "command": unknown keys are passed to an external hook — command <base64-pubkey> <source-addr> — which decides. Exit 0 authorizes; each stdout line is an allowed-IPs CIDR for the peer. Any other exit status denies. This is the integration point for an external control plane (console enrollment DB, LDAP, …).
Library embedders can bypass all of this and inject their own implementation of auth::PeerAuthenticator via Engine::with_authenticator.
trueOn a config author: sign records that other nodes admitted and forwarded (invitations and approvals on nodes that cannot sign the list) into the next fleet list at once, after checking them — never config_author, no flag the forwarding node lacks, no collision, no revoked key. false holds them for an operator (atlasd fleet pending list|approve|deny, the Pending panel).
3600How long a denied key stays blocked (dropped without an answer).
unsetJoining side: this node's fleet certificate (atlasd fleet cert), presented in its handshakes so any node trusting the fleet authority admits it without a list entry.
unsetHook executable for provider = "command".
5000Hard cap on how long the command hook may run before the handshake is dropped (the hook keeps running; its verdict is simply too late).
30A denied key is not re-checked for this long — keeps a handshake flood from an unauthorized key from hammering the file/hook.
unsetBootstrap copy of the signed fleet list for provider = "fleet" (TOML authored with atlasd fleet …, or the JSON envelope). A newer list received over a session supersedes it by sequence number. Absent on a node joining by invitation or certificate: it learns the list from the node that admits it.
unsetSecret behind invitation tokens (atlasd fleet invite): 32 random bytes, base64, mode 0600; created on the first invitation. Default: <state dir>/<iface>.invite-secret. Replacing it voids every open invitation.
unsetJoining side: the whole v=3 pairing code this node was invited with (atlas:pair?v=3&k=…&a=…&h=…&t=…). Its handshakes present the token and the code's binding; the code's i fills a missing [interface] address (with h's prefix length), and k names the inviting node — its [[peer]] gets h/32 as allowed_ips when they are left out, and is added (endpoint from e, if any) when missing. The anchors (a) and authors (c) it carries are trusted for fleet lists once the inviting node confirms the code. Keep it after joining: it is where those anchors come from.
unsetAllowlist file for provider = "keyfile".
falsePending-approval queue: an unknown key that fails every other check is parked (key, source, claimed name, first/last seen, count) for an operator to approve — minting its record with chosen flags — or deny. Works with any provider.
200Most keys the pending queue holds; further knocks are dropped.
"static"static (default, unknown keys rejected), keyfile, command, or fleet (the signed fleet list: a key without a record gets no session, and every record's capability flags are enforced). Misconfiguration refuses to start.
statickeyfilecommandfleetunsetFleet-wide pre-shared key (base64 of 32 random bytes) — admission by key + PSK. Every handshake then carries a PSK tag and mixes the key into the session keys (Noise IKpsk2); an init without the right tag is dropped without an answer. Both ends of every pair need it.
unsetNamed pre-shared keys for records that carry a psk_id: a TOML file of id = "<base64 32 bytes>" lines (mode 0600). A pair whose record names a psk_id uses that key instead of psk.
[routing]
Routing knobs.
- path_redundancy drives the 4.2 "dual-SPF" scenario: when K = 1 (default) atlas behaves as before, SPF picks one shortest path per destination; when K ≥ 2 atlas computes top-K next-hops and fans every outgoing/forwarded fragment across all of them with a receive-side dedup window. Killing any single link in the diamond becomes packet-loss-free because the alternate path is already carrying live traffic.
- static_path drives the 4.3 "static multipath" scenario: when one or more entries are present, atlas stops consulting SPF for forwarding entirely and routes purely off the operator-declared path table. Each path is a full hop list of tunnel IPs and carries a stable path_id that is stamped into the relay header so every intermediate node can pick the right next-hop without a database lookup. SPF/probing still runs (for telemetry); only the forwarding decision is overridden.
The two knobs are mutually exclusive: when static_path is non-empty the static FIB wins and path_redundancy is ignored.
headroom_classes is independent of both: it names the QoS classes that route by the headroom map — a second shortest-path computation whose edge cost adds each link's advertised queue delay and a utilisation penalty to its RTT, so those classes steer around loaded hops while everything else keeps the default path. Applied at the origin and at every transit hop from the packet's class byte; traffic from a sender without QoS is unclassified and never takes it. With path_redundancy ≥ 2 the top-K paths are taken on whichever map the class selects.
["Control"]QoS classes routed by the headroom map (headroom-aware routing): class names as the QoS policy spells them (Control, Voice, Position, Telemetry, Video, Bulk), or "*" for every class. Empty disables the second shortest-path computation. Unclassified traffic (a sender without QoS) always keeps the default map.
8The cap on Atlas's own Layer-3 relay hops: the hop budget this node stamps into the relay header of every packet it originates, counted in Atlas links from origin to destination (8 = up to seven relays in between). Default 8. Eight is a chosen default, not a technical ceiling: the header field that carries the budget is one byte, so any value from 1 to 255 is accepted, and 0 or anything above 255 is refused. 1 leaves only direct peers reachable.
The count is Atlas's own hops between Atlas nodes. Hops inside an underlying Layer-2 mesh (a MANET radio, 802.11s) that one Atlas link rides are invisible to Atlas and are not counted.
The budget belongs to the packet: a transit node forwards on what the origin stamped, whatever its own setting, so nodes with different values still agree on every forwarding decision. Raise it on the nodes that originate traffic across long chains.
Restart-required, like the rest of [routing].
[routing]
max_hops = 121Top-K next-hops to fan out across. 1 = single-path (default, unchanged behaviour). 2+ = dual/multi-SPF.
[]Operator-declared static paths. Empty (default) = SPF / dual-SPF routing. Non-empty = static-multipath: forwarding uses these entries exclusively.
Each entry:
hopsarray of string required — Full path as a sequence of tunnel IPv4 addresses (origin first, final destination last). At least two entries required.namestring required — Human-readable label, surfaced in stats/logs.path_idinteger required — Stable u16 path identifier, stamped intoRelayHeader.path_id.
[multicast]
opt-in · default offSubscriber-aware multicast distribution trees ([multicast]). **Opt-in. Default OFF.** Off, inner multicast keeps the flat-mesh semantics: a packet to 224.0.0.0/4 is replicated at the source, once per peer. On, traffic to a group listed in groups is carried as a source-rooted SPF distribution tree instead — one copy per shared segment, forked only where subscriber paths diverge. Subscriptions are advertised in the LSA flood (additive, wire-compatible field); every node that sources or forwards a tree group should carry the same groups list.
[multicast]
enabled = true
groups = ["239.10.10.10"] # tree-distributed groups (fleet-wide)
subscribe = ["239.10.10.10"] # deliver these to THIS node's TUNfalseMaster switch.
[]Multicast groups (IPv4, 224.0.0.0/4) distributed as trees. Groups not listed here keep the flat per-peer fan-out.
1Tree parents to be fed by, per subscribed group: 1 (default) or 2. With 2, a node whose record says sink = true (a public feed gateway with links to two mesh nodes) also takes a copy from a second neighbour that carries the stream anyway, so losing the tree parent costs no re-root gap; the multipath dedup window drops the duplicate. The extra copy rides only that neighbour's link to the sink. Ignored on a node that is not a sink, and by every peer for such a node.
[]Groups this node subscribes to: advertised to the mesh and delivered to the local TUN (the kernel still filters by the application's own group joins, exactly as with flat fan-out).
[mesh]
opt-in · default offMesh control plane configuration.
[]Bootstrap peer endpoints for initial join.
trueWhether mesh-wide config changes propagate to all nodes.
falseEnable mesh mode (gossip-based CRDT peer discovery).
60Seconds after which a non-heartbeating node is considered dead.
60How long a gossip target may go without a full roster re-send. Between full syncs a target receives only members whose clock moved since we last spoke to it, so this is the bound on repairing a document that was lost in flight. Default 60 s.
5Gossip interval in seconds (how often to sync state with a random peer).
falseAccept AND emit v1 plaintext (truncated-hash) gossip bodies beside v2. Default false: v2 (encrypted NAG2) only, and inbound v1 is dropped with a warning.
10Heartbeat interval in seconds.
1330Ceiling on one gossip document, in serialized JSON bytes.
The document goes out as a single UDP datagram: oversize it and the IP layer fragments it, at which point ONE lost fragment discards the entire membership update. Capping it turns that into a page — the members that did not fit lead the next document to the same target. Default 3000 (~config block + 3 members). Set 0 to disable the cap and send the whole roster in one datagram (pre-paging behaviour).
""Shared secret for mesh authorization (base64-encoded).
trueLet mesh members read this node's /api/config and /api/stats with a token derived from mesh_secret (atlasd config node-token), even under [web] protect = "all". Read-only and window-limited; it never creates a session and never authorises a write. Default true — protect = "all" is a deliberate lockdown, so set this false to close machine reads as well (the fleet compare then shows this node as unauthorized, as before).
unsetThis node's role for signed config epochs: an epoch's [roles.<name>.<section>] overlay applies only to nodes whose role matches, and target_roles gates whether an epoch is applied here at all. Optional; unset = untargeted by role-scoped epochs.
2Route advertisement interval in seconds.
90Route expiry in seconds (advertised routes removed after this).
60How often to re-contact bootstrap_peers once every seed is in the roster, in seconds.
Joining is not a one-shot: two halves of a mesh that have each converged internally share no member, so nothing but the seed list spans the split and gossip alone can never merge them. Re-sending a MeshJoin to the seeds on a slow timer is what makes a partition heal. Default 60 s; 0 disables the refresh (join once, as before).
[terrain]
opt-in · default off[terrain]: elevation tiles and the radio parameters terrain line-of-sight is judged with. Off by default.
2Antenna height above each node's reported position, metres.
trueLift a position reported below the ground up to the ground before adding the antenna height. GPS altitude is often on the ellipsoid while tiles are on the geoid (tens of metres apart); without this a ground node would sit "underground" and every path would read blocked.
falseLoad the tiles and assess every positioned neighbour's path.
5180Radio frequency for the first Fresnel zone, MHz, on links with no entry in link_frequency_mhz and on paths between other nodes.
0.6Share of the first Fresnel zone that must stay clear for a path to count as healthy (risk 0). The classic rule is 0.6.
1000How often paths are re-assessed, ms (a moved position triggers an assessment sooner).
1.3333333333333333Effective-earth-radius factor for refraction: 4/3 is the standard atmosphere; lower is more pessimistic.
Per-link frequency, MHz, keyed by [[link]] name ({ "wifi" = 5180, "rfd900" = 915 }).
512Most paths assessed per pass: this node's neighbours first, then the links between other nodes.
256Memory the tiles may take, MB. Tiles past the budget are skipped.
trueLet terrain steer: inflate the scheduler's view of a shadowed link and raise its SPF cost. false keeps the analysis (snapshot, journal, 3D view) without steering anything. Applies live.
256Terrain samples along each path.
[]Baked .nadt tiles (nexus-atlas-terrain/tools/dem_bake.py), as files or directories (every *.nadt inside). A relative entry is read from <config dir>/terrain/ (/etc/atlas/terrain).
[dns]
opt-in · default offMesh DNS ([dns]) — a per-node local resolver answering mesh names straight from the LSDB.
**Opt-in. Default OFF.** With enabled = false no socket is opened and no task is spawned. When on, the daemon binds a small DNS server (UDP + TCP) to the tunnel address and answers A/AAAA/PTR for <node_name>.<domain> from its own link-state database — no server anywhere in the mesh, no coordination plane, and each partition keeps resolving its own half's names because each half has its own LSDB.
DNS is a tenant of the tunnel, never a prerequisite for it: peer endpoints remain IP literals and nothing in tunnel establishment ever performs a lookup.
[dns]
enabled = true
domain = "atlas"
mode = "split"
[[dns.override]]
name = "video.acme.com"
a = ["10.0.100.7"]
[[dns.forward]]
suffix = "corp.acme.com"
servers = ["10.0.100.53"]"atlas"Zone suffix; a node's FQDN is <node_name>.<domain>. Must already be DNS-safe (lowercase [a-z0-9-.]).
falseMaster switch.
[]Per-suffix forwards ([[dns.forward]]): a customer's internal resolver, typically reachable only over the tunnel.
Each entry:
serversarray of string required — Resolvers for the suffix, as IP orIP:port(port defaults to 53).suffixstring required — Domain suffix (e.g.corp.acme.com) — matches the suffix itself and everything under it.
[]Listen addresses as IP:port (a bare IP gets port 53). Empty (the default) means the [interface] tunnel address on port 53. config check HARD-REFUSES wildcards (0.0.0.0/[::]) — a mesh resolver on a public interface is an open-resolver / amplification hazard — and warns when an address is neither the tunnel address nor loopback.
"split"split (default): mesh zone answered locally, everything else relayed to upstream. mesh-only (EMCON posture): out-of-zone queries answered REFUSED — no query ever leaves on an uplink.
splitmesh-only[]Split-horizon overrides ([[dns.override]]): names answered authoritatively with tunnel-side addresses when queried over the tunnel, regardless of zone — outside, public DNS answers.
Each entry:
aarray of string — IPv4 answers.aaaaarray of string — IPv6 answers.cnamestring — CNAME target. Exclusive ofa/aaaa.namestring required — Exact name to answer for (e.g.video.acme.com).
trueServe PTR (reverse) zones over the mesh's tunnel prefixes.
["9.9.9.9","149.112.112.112"]Upstream resolvers for out-of-zone queries in split mode, as IP or IP:port (port defaults to 53). Default: Quad9.
800Hard deadline for an upstream answer, milliseconds. Expiry answers SERVFAIL — a dead upstream must never become a hang, and a contested uplink never blocks in-mesh resolution.
[traversal]
opt-in · default offNAT traversal (the ".net" connectivity stack). **Opt-in. Default OFF.** With enabled = false (or no [traversal] table at all) no STUN demux runs, no netcheck task is spawned, and the data path is byte-identical to a build without traversal.
Phase 0 scope: netcheck — per-link STUN probes against two-IP observation points (our relays double as STUN servers), NAT mapping classification, and a JSON report at /tmp/atlas/<iface>.netcheck.json for the web UI / status CLI.
falseAuto-election of the shared relay set. When enabled (traversal on), the daemon periodically STUN-probes every configured relay link, advertises that RTT vector to each peer over the relay control channel, and — from the deterministic joint ranking both ends independently compute — keeps exactly relay_paths relay sessions **active**, gating the rest. A pair thus auto-converges on the same shared relay(s) with no negotiation, and re-elects the same next-best relay on failure. Off (default) = every configured relay link stays active and none is gated.
unsetDirectory-assisted rendezvous. While this node holds a relay session it posts, for every static [[peer]], a sealed presence record to the control plane naming the relays it is on — readable only by that peer, stored under an id only the pair can compute, so the control plane never learns which key is on which relay. A peer absent from every relay we hold is looked up there and its relay added as a runtime relay link. Unset = the value of auto_relay.
2How many relay links are reserved at start for relays learned from the directory (each bound on the first relay link's device with a port of its own). A slot whose relay has shown no present peer for 10 minutes is released, and a lookup naming another relay re-arms an idle slot. Default 2; 0 turns lookups off while records are still published — unless federation is on, which looks peers up for its via hint.
unsetOverride the control-plane base URL presence records are posted to and fetched from ({base}/v1/presence). None → relay_map_url, else the baked-in default.
trueRuntime relay discovery. When enabled (and traversal is on), a relay link that omits relay_server/relay_pubkey resolves its relay from the signed relay map fetched from the control plane at startup. Static per-link config always wins when present. A no-op if every relay link is statically configured.
falseMaster switch.
falseOne-hop relay federation. When a peer is not on the relay we hold but its presence record names a relay we do not hold, frames for it are sent to our relay with a via hint naming the peer's relay, which our relay forwards one hop to (both relays must run [federation] enabled). The election also ranks such federated paths (cost: our leg + the relay-to-relay RTT from the signed map + the peer's leg) against relays both ends hold. A second relay is a second observer of relayed traffic, so a federated path is used only when [crypto] e2e seals egress, or with federation_allow_plain. Requires enabled and the directory. Default off.
falseUse federated paths even when [crypto] e2e is off (both relays then see the Atlas datagrams they forward — still Noise-encrypted between the peers, but metadata and timing are visible to two operators). Default off.
300Netcheck re-run cadence in seconds (it always runs once shortly after startup). 0 disables the periodic re-run.
1500Per-request STUN response timeout in milliseconds.
trueHole punching: exchange candidates with peers over the relay and try to bring up a DIRECT path alongside the relayed one. On by default when traversal is enabled — a failed punch costs nothing (the relay keeps carrying the traffic), and a successful one removes a hop and the relay's bandwidth bill.
[]Which (non-relay) links take part in hole punching, by name: offered as host candidates to peers and tried for direct paths. Empty (the default) = every non-relay link, the historical behaviour. Set it on a node whose other links can never reach a traversal peer — a ground station's point-to-point radios — so their private addresses are not advertised to a phone on cellular, which would only waste its trials.
unsetHome-region hint for relay selection, e.g. "eu-central". None → pick the lowest-RTT relay across all regions.
unsetOverride the control-plane base URL the signed relay map is fetched from ({base}/v1/relays). None → the baked-in default.
1How many relays to hold a session to **concurrently** for a peer, from the deterministically-ranked preference list (best-first). 1 (default) = one shared relay per pair, re-elected on failure. 2 = the best two relays simultaneously for redundancy, etc. A punched DIRECT path (if any) is always additional to these. Clamped to at least 1.
[]STUN observation points as IP:port. Two entries with DIFFERENT IPs are required for mapping classification (a single relay's address pair is exactly that). Later phases populate this from the signed relay map; static config always wins when set.
[qos]
opt-in · default offQoS / transport-class data-path configuration (features K1–K4 + K3 store-carry-forward).
**Opt-in. Default OFF.** When enabled = false (the default, and the state for any config that has no [qos] table at all), the data path behaves byte-identically to a build without QoS: classification, the class queue, per-class multipath and the bundle store are never constructed and packets take the plain outbound/inbound paths.
The active policy is loaded at startup from /tmp/atlas/<iface>.qos.json (the same file the web UI / daemon writes) and re-read every reload_secs so an operator's "Apply" takes effect live. An absent or corrupt file falls back to PolicyTable::tactical_default.
4000000ClassQueue AQM byte budget across all classes.
"memory"Bundle durability backend: "memory" | "file".
memoryfiletrueK3 store-carry-forward for the StoreCarryForward (Bulk) class.
600000Per-bundle lifetime in milliseconds before it expires unsent.
16000000Total bundle payload byte cap.
10000Total bundle count cap.
""Write-ahead-log path when bundle_backend = "file". Per-iface OK.
Per-class settings by class name ([qos.classes.Video]); the policy file's value wins when it sets one.
Keys of each [qos.classes.<name>]:
budget_by_level_msarray — Latency budget per precedence level in ms.companionstring — Ride with this class: its link plan, its stripe unit and, at the receiver, its release buffer — so the two arrive in step.redundancystring —none|previous_packet(each packet also carries the previous one; default for Voice).retainboolean — Journal every packet of this class so destinations can ask for what they missed (complete footage; bounds in[qos.retain]). Refused on a store-carry-forward class (Bulk). The policy file'sretainturns it on as well.
1000Upper clamp on the derived deadline budget, milliseconds.
80Lower clamp on the derived deadline budget, milliseconds.
3.5Deadline budget multiplier: budget = k * worst alive-link RTT.
When a class's late and shed drops are reported as an event (journal code qos.drop.burst, and syslog/CEF when export is on).
Keys of [qos.drop_event]:
cooldown_secsinteger default60—pctnumber default1—thresholdinteger default20—window_secsinteger default10—
falseMaster switch. false (default) = the plain data path: no classification, no class queue, the class byte is not acted on.
trueOffer a packet with a deadline only to links that can still deliver it in time (half the RTT + serialisation + queue delay ≤ time left). A packet no link can deliver in time is dropped at planning.
5Subtracted from the time left before the feasibility comparison.
8FEC block size k: number of data packets batched before parity shards are appended and the block is shipped.
200FEC RX expiry: give up on an incomplete block after this many milliseconds (deliver the data shards that arrived, drop the rest).
falseForward-error-correction master switch. Opt-in; false (default) = no FEC even for classes whose policy is Fec. When true, any class whose MultipathPolicy is Fec { overhead_pct } has its packets batched into blocks, parity shards generated, and lost packets reconstructed on the receiver with NO retransmission. Doubly gated: this switch AND the per-class Fec policy must both be set.
60FEC TX flush: emit a partial block after this many milliseconds of idle so latency stays bounded when fewer than k packets are in flight.
[]Operator flow rules ([[qos.flow]]), tried before the DSCP and port rules: an explicit rule wins.
Each entry:
classstring required — Service class name.dst_portinteger — Destination port (UDP or TCP).levelinteger — Precedence inside the class: 0 primary, 1 secondary, 2 tertiary.srcstring — IPv4 source prefix, e.g.10.0.9.20/32.
Per link group ([[link_group]] name, or default): per class name, whether the class may ride the group and the multipath policy used when planning on it. Everything else about a class stays node-wide.
Keys of each [qos.group.<name>.<class>]:
allowedboolean —falsekeeps the class off this group's links.multipathGroupMultipath | null — Multipath policy when the class is planned on this group.
truePace each link's low band (everything but Control, Voice and Position) to the measured drain rate of the kernel queue below it, so a voice frame waits behind at most low_band_target_ms of video there. Applies while QoS is enabled, on links whose device queue can be read (not a noqueue device such as veth or most Wi-Fi drivers, nor a netem root that emulates delay).
5Airtime the kernel queue below a link may hold before low-band datagrams wait in Atlas's queue, in ms at the measured drain rate (2–100).
2How often to re-read the <iface>.qos.json policy file, seconds. 0 = never reload (load once at startup).
20Ceiling on time-diversity repeats per class, as a percentage of the sole link's capacity; beyond it copies degrade to 2, then 1.
Retention and backfill ([qos.classes.<C>] retain = true): bounds of the sender's journal, the receiver's request pacing and the backfill pace. See RetainConfig.
Keys of [qos.retain]:
backfill_max_kbpsinteger default0— Ceiling on the backfill rate in kbit/s;0= no ceiling beyond the Bulk lane (backfill only uses capacity no live class is using).gap_request_delay_msinteger default2000— How long a hole stays open at the receiver before it is requested, so reordering and FEC repair get their chance first.gap_request_interval_msinteger default2000— At most one request per (origin, class) this often; doubled while the origin answers nothing.max_bytesinteger | string default2147483648— Journal size bound across every retained stream (a ring: the oldest segment is dropped first). Also the bound on the receiver's captures, which stop growing (counted) instead of deleting footage. Unit-tagged string ("2 GiB","500 MB") or a bare number of bytes.max_inflightinteger default1024— Backfill packets queued per destination at the origin (more requested packets wait for the next request).max_secsinteger default86400— Journal age bound in seconds: segments whose newest packet is older are dropped.atlasd retain clearempties the journal at the end of a flight.pathstring default""— Directory for journals (<iface>.retain/) and captures (<iface>.capture/); empty = the state directory.
120Relative arrival-based hold time for the RX release buffer, in milliseconds. Each Video packet is held ~this long from local arrival then released in sequence order. 0 falls back to the default (120); never unbounded.
1024Per-peer cap on buffered Video packets in the RX release buffer. On overflow the lowest sequence is force-released early.
falseReceiver-side release buffer (jitter / reorder buffer with late-drop). Opt-in master switch; false (default) = received frames are handed on as they arrive, with no release buffer. Only ever applied to the Video (DeadlineBounded) class — never to any other class or reliable traffic.
[telemetry]
opt-in · default offOut-of-band telemetry feed from per-radio adapter processes (e.g. atlas-sik). Adapters open a Unix domain socket here and push newline-delimited JSON LinkTelemetry events. Each event names a link (matching [[link]] name) and carries radio-native signals — RSSI, noise floor, TX buffer free space — that the OS can't see.
Strictly opt-in. Disabled by default; the scheduler doesn't depend on any of this for correctness.
trueReport link telemetry to the customer console (console.nexusatlas.net) every ~30 s. Opt-out — true by default, but a node that was never enrolled sends at most one token-mint request (the console answers 404 not_enrolled and the reporter stops for the daemon's lifetime). Independent of enabled, which gates only the local adapter socket.
unsetConsole base URL override. Resolution order (highest wins): NEXUS_ATLAS_CONSOLE_URL env var, this key, the built-in default (https://console.nexusatlas.net) — same chain the enroll flow and the web dashboard use.
falseAccept adapter connections on socket_path. Default false.
trueAlso answer to the pre-rename socket path (/run/nexus-atlas/telemetry.sock) by symlinking it onto whatever socket_path bound, so adapters whose units still name it keep connecting. Default true. Never clobbers a real socket already sitting there.
"/run/atlas/telemetry.sock"UDS path the adapter connects to. atlasd creates the parent directory and removes any stale socket file at startup.
[metrics]
on by defaultPrometheus / OpenMetrics exposition (GET /metrics on the web server).
**On by default.** "Does it feed our NMS?" is the first question a systems integrator asks, and the answer has to be yes without an operator finding a switch first. It costs nothing when nobody scrapes: the endpoint renders from the snapshot documents the daemon already writes, in the atlasd web process, so the forwarding daemon is not on the path at all.
The endpoint is a plain READ and the node's existing [web] protect posture governs it — open on a protect = "writes" node, session- or token-gated on a protect = "all" node, exactly like /api/stats.
trueMaster switch. false makes the endpoint answer 404.
"/metrics"Exposition path. Change it only when something else on the node already owns /metrics; scrapers assume the default.
[journal]
on by defaultPersistent event journal (/var/lib/atlas/journal/<iface>.jsonl).
**On by default.** Every other view of the node is a live value: once a link flaps back up, nothing on disk remembers that it went down. The journal is what makes "what happened at 14:32?" answerable after the fact, so it has to be on for the incident nobody planned for.
The two size knobs are a hard ceiling, not a target: max_files x max_file_bytes is the most disk one interface's journal can ever use (default 4 x 2 MiB = 8 MiB). max_age_days expires rotated files sooner on a quiet node. See crate::journal for the retention math.
trueMaster switch. false stops the writer thread from starting; the emit sites become a single atomic load and nothing is written.
30Delete rotated files older than this. 0 disables the age bound and leaves the size cap as the only limit.
2097152Rotate the live file once it would exceed this many bytes.
4Files kept INCLUDING the live one. Total disk = this x max_file_bytes.
[storage]
[storage]: encrypt every daemon-owned file at rest.
"off""off" (default: files exactly as before) or "on".
"node_key"Where the store key comes from: node_key (default; derived from the interface private key — protects deleted data and casual reads, not an attacker holding the config file), file:<path> (32 bytes on a separate volume), boot_secret ($NEXUS_ATLAS_STORE_SECRET or the systemd credential atlas-store), tpm (reserved).
[syslog]
opt-in · default offSyslog / CEF event export to a collector (the SIEM half of the integrator question).
**Opt-in. Default OFF.** With enabled = false no socket is opened, no thread is spawned and no event is built. When on, events go to a bounded queue drained by one writer thread — the daemon never blocks on a collector that is down, and the events it had to drop are reported into the stream once the collector returns.
falseMaster switch.
"local0"Syslog facility by name (local0…local7, daemon, …) or number.
"rfc5424"Wire format: rfc5424 (structured syslog) or cef (ArcSight Common Event Format).
rfc5424cefunsetHOSTNAME field override. None uses the kernel hostname.
"info"Least severe level that is exported: emerg, alert, crit, err, warning, notice, info, debug.
1000How often the interim snapshot watcher looks for state changes, milliseconds. Irrelevant once the event journal feeds the exporter.
1024Bounded queue depth. Deeper rides out a longer collector outage; beyond it, events are dropped and counted rather than blocking.
"atlas@32473"RFC 5424 SD-ID for the structured-data block. The default carries the enterprise number IANA reserves for documentation; set a real PEN if the collector validates it.
"unix:///dev/log"Collector: udp://host:port, tcp://host:port or unix:///dev/log. A bare host:port means UDP; the port defaults to 514.
[reporting]
on by defaultCrash reporting and the watchdog ([reporting], ).
**Capture is on; sending is not.** The product's positioning is no-phone-home , so the defaults here capture a crash locally, redact it, and stop. With auto_send = false (the default) and relay_peers = false the daemon starts no upload thread and opens no socket to hub_url; a report leaves only when an operator runs atlasd crash export or atlasd crash send <id>, one report at a time. send_mode decides whether such a send names this node.
coredump is off for a harder reason: a coredump of this daemon holds a Curve25519 private key, live Noise session keys and the mesh secret. It cannot be turned on without coredump_pubkey, because the only acceptable core is one encrypted to the hub at capture time.
falseopt-in: upload spooled reports in the background instead of waiting for atlasd crash send. Off by default — per-report consent is the default posture, and this is the switch that trades it away deliberately.
falseLet systemd-coredump capture a full core and attach it encrypted. Requires coredump_pubkey; see the struct docs.
Base64 X25519 hub key a core is encrypted to at capture time.
trueMaster switch for crash capture. false installs no panic hook and no signal handlers, and writes no markers.
falseCarry whole peer public keys instead of 8-character fingerprints.
"https://console.nexusatlas.net"Collector base URL. Sovereign customers self-host; nothing in the tree hardcodes a collector beyond this default.
200Journal entries attached to a report (the tail). Capped at the hub's 200.
8388608Total spool size. Whichever bound binds first evicts the oldest.
20Reports kept in the spool.
falseSame, for the copy kept for local export.
trueRemove addresses, endpoints, hostnames and node names from the form of the report that would be uploaded.
falsePull crash reports from mesh members that have no route to the hub and forward them. Requires [mesh]; off by default, because relaying another node's diagnostics is a decision.
"attributed""attributed" (default) or "anonymous" — two modes.
Attributed carries this node's public key and presents the ingest token, so the report lands in the owning account's console. Anonymous carries no identity and no credential: the fingerprint still groups the crash with everyone else's, but nothing says whose it was. Anonymous also **forces redact_uploads** — a report that still named an endpoint would not be anonymous.
trueFeed systemd's watchdog from the data-path liveness counters. Harmless when the unit has no WatchdogSec=: with no $WATCHDOG_USEC in the environment nothing is sent.
3Consecutive failed liveness checks before the watchdog stops feeding systemd. One missed check is a scheduling hiccup; three in a row is a wedged data path.
[logging]
"info"Log level / tracing filter: trace, debug, info (default), warn, error, or any RUST_LOG-style directive string.
[config_mgmt]
Fleet pushes ([config_mgmt]). Update classes live on the node records of the fleet list; these are the timings the pushing node and the left-behind nodes use.
[config_mgmt]
release_watch_secs = 600 # 0 = 3 x confirm_secs + 120
left_behind_grace_secs = 600600A left-behind node that is not pinned takes a new epoch this many seconds after it first saw it, so it catches up after the release watch rather than during it. A pinned node never takes it.
0How long a released rollout is watched before a required node that has not confirmed the epoch aborts it. 0 (the default) derives the window from the rollout: 3 x its confirm_secs + 120 s. A rollout started with an explicit release_watch_secs uses that instead.
[rotation]
Partition-safe rotation ([rotation]): node keys and the mesh secret change on a publish → activate → retire schedule carried in the signed fleet list. The windows here are the defaults atlasd fleet rotate plan and the dashboard use on the operator host; the lifeline window and the clock rule are read by every node.
[rotation]
default_publish_to_activate_secs = 3600
default_activate_to_retire_secs = 604800
lifeline_secs = 86400
require_trusted_clock_for_scheduled_activation = true604800Default gap between activation and the earliest retire the operator is offered. Retire is always a signed step of its own, gated on every required node reporting the rotation active.
3600Default gap between publishing a rotation (every node accepts the new key or secret) and activating it (the subject sends with it).
86400Nodes whose record says lifeline = true (bootstrap peers) keep accepting the retired key or secret this much longer, so a straggler still finds a node that speaks its material. A fleet list's own lifeline_secs overrides this.
trueAct on a rotation's activate_at only while the system clock is synchronised (NTP/PTP/GPS discipline, as the kernel reports it); without a synchronised clock the node activates on receipt and says so in its journal. false trusts the clock as it is.