A least-privilege fleet shipped

Documents the atlasd 3.0.0 release lineView as Markdown

Every node in a record fleet holds exactly the capability flags on its record. This guide writes three records — a ground station that runs the fleet, a weather sensor that only reports, and an observer node lent to a partner — and shows where each flag can be seen holding.

What you need: atlasd on the operator host, a config-author key (atlasd config keygen-author) whose public half is in every node's config-authors.toml, and an identity per node from atlasd keygen --identity, which prints the record UUID, the public keys and the matching fleet add-node line.

1 — Decide what each node is for

Recordrelayclassesconfig_authordashboard
gs — ground stationtrueall sixtruefleet
sensor — weather sensorfalseTelemetry, Position, Bulkfalsenone
observer — partnerfalseVoice, Positionfalseown

Start from the defaults — no relaying, Telemetry, Position and Bulk, no authorship, reads of its own node — and raise only what the job needs. A flag you leave out of a record takes its default.

2 — Write the three records

$ atlasd fleet init --fleet field-1 --file fleet-list.toml

$ atlasd fleet add-node --file fleet-list.toml --name gs \
    --node-id f09b1d95-0c96-440e-bd57-1296492bee9a \
    --transport-key f7eHos0bvrtSbHkkeB7K4kHiHYez9+qYPUnuslmdahc= \
    --signing-key RXwaUcrkWDhJ02lZMa1wYBa/pJXocKvlpYe1JIub1Lc= \
    --allowed-ip 10.0.9.1/32 --role operator \
    --relay --classes Control,Voice,Position,Telemetry,Video,Bulk \
    --config-author --dashboard fleet
record `gs` added to fleet-list.toml (unsigned — run `atlasd fleet sign`)

$ atlasd fleet add-node --file fleet-list.toml --name sensor \
    --node-id de8bae79-560d-43fb-b269-0e12b9c478a8 \
    --transport-key lZJk7MeEDtZbjfDhy7L56+C9tncbO4P+7lUjpnUt7k0= \
    --signing-key OyyVGXiJGjCfvPunLsHi5AH1szsD+Uav/zPUYLgXv6M= \
    --allowed-ip 10.0.9.3/32 --role sensor --dashboard none
record `sensor` added to fleet-list.toml (unsigned — run `atlasd fleet sign`)

$ atlasd fleet add-node --file fleet-list.toml --name observer \
    --node-id 9b4bd4f7-3f64-4270-911c-3a611a75678f \
    --transport-key Lhvl7ILoNTWhGG9zY4zPwn2zkRc//YifPHUNqIrkcW8= \
    --signing-key WrWhTU/EtsWWtruj6SikkvkOhLVz50/2zsi7utq8EtU= \
    --allowed-ip 10.0.9.4/32 --role partner --classes Voice,Position
record `observer` added to fleet-list.toml (unsigned — run `atlasd fleet sign`)

The sensor names only --dashboard none; everything else is its default. In the file, its record reads:

fleet-list.toml — the sensor
[[node]]
allowed_ips = ["10.0.9.3/32"]
name = "sensor"
node_id = "de8bae79-560d-43fb-b269-0e12b9c478a8"
roles = ["sensor"]

[node.capabilities]
classes = [
    "Telemetry",
    "Position",
    "Bulk",
]
config_author = false
dashboard = "none"
relay = false

[[node.keys.signing]]
key = "OyyVGXiJGjCfvPunLsHi5AH1szsD+Uav/zPUYLgXv6M="
valid_from = 0
valid_to = 0

[[node.keys.transport]]
key = "lZJk7MeEDtZbjfDhy7L56+C9tncbO4P+7lUjpnUt7k0="
valid_from = 0
valid_to = 0

3 — Check, sign and publish

$ atlasd fleet check --file fleet-list.toml
fleet `field-1` seq 0: 3 records, 0 error(s), 0 warning(s), signature unsigned
$ atlasd fleet sign --file fleet-list.toml
fleet `field-1` seq 1 signed by 3c24f045 (3 records, 33a84743)
$ atlasd fleet verify --file fleet-list.toml
fleet `field-1` seq 1: signature OK (anchor `operator`)

fleet check exits 1 on errors and warns on likely mistakes: overlapping allowed_ips, a record without a signing key, and config_author on a record whose roles do not include operator. Copy the signed list to each node as its bootstrap [auth] fleet_list for the first start; later lists travel over the mesh on their own.

4 — Watch each flag hold

TryWhat happensWhere you see it
The sensor's path between gs and the observer is the cheapestRoutes go another way; nothing is routed through the sensorroutes[].next_hop on every node; the topology view
The sensor passes on a packet it received for someone elseIts neighbour drops itcaps.relay_refused on the neighbour
The sensor marks its telemetry ControlIt is sent as Positioncaps.class_clamped on the sensor, and on its first neighbour if the sensor's own check was skipped; caps.class_clamped_transit on any later hop that had to lower it again
The observer sends BulkIt is sent as Position, the lowest class it holdscaps.class_clamped
A config epoch signed with the observer's keyRefused by every nodeepoch.rejected in each node's journal
The observer's node token reads /api/stats on the ground station, which protects its readsRefused like an anonymous request; it reads only the observer itselfThe response to the request
The sensor mints a node tokenIt reads no node that protects its reads, not even the sensorThe response to the request

On the ground station, the Fleet tab lists all three records with their flags (the sensor's "no relay" in yellow), and Peers shows each peer's flags beside its name. GET /api/identity on any node returns its own flags and the two caps counters.

5 — Change a flag later

fleet set-caps edits one record, refuses any change that would leave the list invalid, and clears the signature until the next sign:

$ atlasd fleet set-caps --file fleet-list.toml sensor --classes Telemetry,Position
`sensor`: relay=false classes=[Telemetry,Position] config_author=false dashboard=none (unsigned — run `atlasd fleet sign`)
$ atlasd fleet sign --file fleet-list.toml

Publish the signed list through any one node as in the rotation guide; each node applies the new flags when it adopts the list. The flags follow the record, so rotating a node's key never touches them.

Esc
↑ ↓ navigateEnter — openEsc — close