> Source: https://nexusatlas.dev/guides/least-privilege — Nexus Atlas developer documentation (Guides). Converted from the HTML page; the page is canonical.

# A least-privilege fleet shipped

Every node in a record fleet holds exactly the [capability flags](https://nexusatlas.dev/docs/concepts/identity#capabilities) 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

| Record | relay | classes | config_author | dashboard |
|--------|-------|---------|---------------|-----------|
| gs — ground station | true | all six | true | fleet |
| sensor — weather sensor | false | Telemetry, Position, Bulk | false | none |
| observer — partner | false | Voice, Position | false | own |

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

| Try | What happens | Where you see it |
|-----|--------------|------------------|
| The sensor's path between gs and the observer is the cheapest | Routes go another way; nothing is routed through the sensor | routes[].next_hop on every node; the topology view |
| The sensor passes on a packet it received for someone else | Its neighbour drops it | caps.relay_refused on the neighbour |
| The sensor marks its telemetry Control | It is sent as Position | caps.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 Bulk | It is sent as Position, the lowest class it holds | caps.class_clamped |
| A config epoch signed with the observer's key | Refused by every node | epoch.rejected in each node's journal |
| The observer's node token reads /api/stats on the ground station, which protects its reads | Refused like an anonymous request; it reads only the observer itself | The response to the request |
| The sensor mints a node token | It reads no node that protects its reads, not even the sensor | The 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](https://nexusatlas.dev/guides/key-rotation#list); each node applies the new flags when it adopts the list. The flags follow the record, so [rotating a node's key](https://nexusatlas.dev/guides/key-rotation) never touches them.
