Remove nodes by area, set geofences, wipe a node remotely shipped

Documents the atlasd 3.0.0 release lineView as Markdown

Three tools for a fleet that loses ground or loses hardware. Removal by area turns a polygon into a reviewed list of the nodes last seen inside it and revokes exactly those. A geofence is a signed operating area in a node's record: outside it for longer than a grace time, the node isolates itself or zeroizes. A remote zeroize is a signed command in the fleet list that wipes one node and, by default, revokes it in the same list. The mechanism is described under Removal by area, geofences and remote zeroize.

What you need: a record fleet ([auth] provider = "fleet"), the operator host with the authoring copy of the list and the config-author key, and nodes that know their position — live telemetry, [interface] position or PUT /api/position. From the dashboard, the node you are on must hold a config-author key the fleet trusts. For the API, a token with write scope.

Draw the area

A polygon is a GeoJSON Polygon, a Feature or a FeatureCollection holding one, in GeoJSON's [lon, lat] order; the closing point may be repeated. At least 3 and at most 256 corners. A polygon may cross the antimeridian; one that winds around a pole is refused, so split it in two.

sofia.geojson
{ "type": "Polygon",
  "coordinates": [[[23.28, 42.66], [23.38, 42.66], [23.38, 42.72], [23.28, 42.72], [23.28, 42.66]]] }

Select and review

Ask a running node which records lie inside. The answer comes from that node's last known positions: the last position each node advertised, kept after its advert ages out.

$ atlasd fleet select-area --polygon sofia.geojson -c /etc/atlas/config.toml
fleet `field-1` seq 7 · 3 inside · 2 outside · 1 without a position
  inside  n3               4c1e..a902  (42.69770, 23.32190)  fix 1 s ago
  inside  n4               91b0..33fe  (42.70120, 23.33810)  fix 1 s ago
  inside  n5               07da..c4e1  (42.68840, 23.31570)  fix 4 min ago (advert aged out)
  no fix  n2               e5a3..1b7c

Read the list before acting on it. Every position is what that node reported about itself, so check the fix ages: advert aged out means the node has gone quiet and is listed where it was last heard from. A node under no fix never advertised a position and cannot be placed by any polygon; decide on it by name. --json prints the same as {inside, outside, no_position, fleet, seq}.

On the dashboard: Topology → map → area, click the corners (Undo, Clear, Done), then Select nodes. The panel lists the nodes inside with their fix age, marks the silent ones, greys out the node you are on and lists the unpositioned nodes apart; the map rings each selected node at its last fix.

Revoke the confirmed list

$ atlasd fleet select-area --polygon sofia.geojson -c /etc/atlas/config.toml \
    --revoke --reason "position overrun"
…
revoke these 3 node(s) for good? [y/N] y
  removed `n3` (4c1e..a902) — id and keys revoked
  removed `n4` (91b0..33fe) — id and keys revoked
  removed `n5` (07da..c4e1) — id and keys revoked
  mesh secret rotated (rot-1791801600-mesh_secret), sealed to 3 remaining record(s)
fleet `field-1` seq 8 signed by 3c24f045 (3 records, 9b0e7d21)

--revoke removes exactly the listed records from the authoring file, the way fleet revoke does — revoke, rotate the mesh secret, sign — and hands the list to the daemon of -c, which floods it. The node of -c is never on its own list; remove it from another operator node. --yes skips the question; --no-rotate keeps the mesh secret. The reason defaults to removed by area.

On the dashboard, Revoke selected opens Remove nodes… with the selection ticked: preview, sign and push as for any removal. From a backend, POST /api/fleet/select-area returns the review, and POST /api/fleet/revoke takes the node ids you confirmed.

Give a node a geofence

$ atlasd fleet set-geofence n1 --polygon burgas.geojson --grace-secs 300 --action isolate
`n1`: fence of 4 points, isolate after 300 s outside (unsigned — run `atlasd fleet sign`)
$ atlasd fleet sign
$ atlasd fleet push -c /etc/atlas/config.toml

The fence is part of the record — [node.geofence] polygon = [[lat, lon], …], grace_secs, action — and travels in the signed list. --grace-secs defaults to 600 and --action to isolate; --clear removes the fence. Choose the action by what the node carries:

ActionWhat the node does after grace_secs outsideBack inside
isolateStops admitting peers, announces isolated in its signed status entry and flushes it to every peer, then tears every session down. Every handshake in either direction is refused and counted (area.isolation_refused).The isolation lifts, the announcement is withdrawn and the sessions return.
zeroizeThe zeroize routine, cause geofence: announces zeroized, destroys its keys, empties every store and stops.Nothing to return to; the device comes back only as a new identity.

The node checks its own fix against its fence every 10 s. A node with no fix never fires its action and is flagged no_fix; a fix lost while outside keeps the timer's start. The timer is stored sealed at rest, so a restart outside the fence resumes it. Follow it on the node:

$ atlasd status
  atlas0  10.1.0.11/32  running
    geofence outside — isolate in 212 s unless back inside

The journal records security.geofence_outside, security.geofence_inside, security.geofence_isolated and security.geofence_released. The snapshot carries area.geofence {state, action, grace_secs, outside_for_secs, grace_left_secs, fired}, area.isolated, and area.fences[] with every record's fence as this node sees it from that record's advertised position. The dashboard draws each fence on the map, coloured by state, and the Fleet tab shows Outside · isolate in 12 s for this node and the state of the others.

Wipe a node remotely

$ atlasd fleet zeroize n2 --delay-secs 600 --push /etc/atlas/config.toml
  zeroize `n2` (e5a3..1b7c) not before 1791802200 (in 600 s)
fleet `field-1` seq 9 signed by 3c24f045 (2 records, 1 command(s), 4f7a2c10)

The command goes into the list as commands = [{ node_id, cmd = "zeroize", not_before, issued_at, author, sig }], signed on its own over the fleet id and its fields, and the list is signed as a whole. By default the same list also revokes the node — its id and keys, with a mesh-secret rotation, exactly as fleet revoke — so the identity is refused everywhere whether or not the device ever acts on the command. --no-revoke keeps the record. --not-before T or --delay-secs N holds the command; --cancel drops a pending one; without --push, push the list with fleet push.

On the dashboard: Fleet → Zeroize… beside Remove nodes, pick one node, optionally hold it for N minutes, Preview, Sign and push. Pending commands can be cancelled from the same card. From a backend, POST /api/fleet/zeroize. A node never zeroizes itself remotely; on the device, atlasd zeroize does that.

The addressed node checks every second. A command from an unknown signer is refused (security.command_rejected), one not yet due is held (security.command_pending), a verified, due one runs: the node announces zeroized, destroys its store key, its private and signing keys, the mesh secret and its web secrets, empties every store and exits. Its journal keeps one clear line:

security.zeroized  node zeroized by a remote command signed by ops (fleet `field-1` seq 9): keys destroyed, stores truncated

Every node that hears the announcement evicts it and purges its adverts and roster entry at once (security.peer_zeroized), and area.commands[] marks the command acknowledged. The command stays in later lists until cancelled.

Boundaries

  • Area selection and every fence state seen from another node use self-reported positions: a node that misreports, or moved since its last advert, is placed where it said it was.
  • A geofence is enforced by the node against its own fix. A device whose GPS is spoofed or whose software is altered does not enforce it; revoke it. A device that never takes the list never sees a command, which is why a zeroize revokes by default.
  • Last-known positions are held in memory (up to 4096): a restarted node knows only the positions advertised since.
  • Polygon edges are straight in latitude and longitude, not great circles — exact at operating-area scale, increasingly off for polygons thousands of kilometres wide.
  • The isolated and zeroized announcements travel by gossip; a peer that misses an isolation keeps a dead session until its handshake stalls.
  • A zeroized identity is never reused; re-provisioning is a new identity with atlasd keygen --identity.
  • API tokens with write scope may call /api/fleet/revoke, /api/fleet/select-area and /api/fleet/zeroize; mint them accordingly.
Esc
↑ ↓ navigateEnter — openEsc — close