# Fleet: machine security

# Fleet

Fleet watches the machines you run: servers, build boxes, laptops. Run one command on a machine and it inventories itself, works out what is **actually reachable** (not just which ports are open), joins your workspace with its own Ed25519 identity, and keeps reporting. The portal shows every machine, every exposed service, the dependency graph between machines, and a firewall plan for each risk that a person reviews and that rolls back on its own.

AI explains; deterministic code decides. Findings, scores and plans come from fixed rules, the same code on the machine and on the server. A model may explain them; it never writes or applies a rule.

## Add a machine

Run one command on the machine:

| OS | Command |
| --- | --- |
| macOS, Linux | `sudo npx -y @pendral/inline-fleet@latest join` |
| Windows (PowerShell, Run as administrator) | `npx.cmd -y @pendral/inline-fleet@latest join` |

It generates its own key and shows a short code (`BCDF-GHJK`), the link to **Fleet, Pair a machine** and a QR code of that link with the code filled in. Type the code in the portal (or from **Fleet, Add machine**), check what is asking to join (hostname, addresses, the key fingerprint, where the request came from and inline's assessment of what it reported), and approve. The terminal says it joined and keeps running the agent. Approving adds the machine **approved**: a person looked at it and said yes. Codes last 10 minutes and are single use; nothing is saved on the machine until you approve.

Only approve a code shown on a terminal you are using right now. A page opened from a link with the code already in it asks you to compare the key fingerprint with the terminal before it lets you approve.

Swap `join` for `install` to run the agent as a service (systemd, launchd or a scheduled task) so it survives reboots; it pairs first when the machine has not joined yet. The service runs `inline-fleet run`, which also puts a confirmed firewall plan back after a reboot.

**Many machines** (cloud-init, Ansible, an image): under **Fleet, Add machine, Many machines**, name a join token and run `join --token <token>` (or `install --token <token>`) on each machine, with no prompt. Join tokens are shown once (only a hash is stored), expire, and can be limited to a number of machines. A machine joined with a token lands **pending** unless the token was minted with auto-approve: it reports, so you can check it first, but receives no commands until someone approves it.

Without `sudo` (or Administrator) the agent still runs, but cannot see socket owners, the firewall ruleset or sshd's effective config. Those show as **Unknown** on the dashboard, never as fine.

## What leaves the machine

- **Sent:** configuration and socket metadata. Ports, bind addresses, process names, interfaces, firewall rules, SSH and hardening settings, neighbors on the LAN. Every request is signed with the machine's private key. Machine ids are hashed.
- **Never sent:** file contents, environment variables, traffic payloads, the private key. Command lines only when you opt in (`--include-cmdlines`), and redacted even then.

## Exposure

Each listening socket gets a **reach** from its bind address and the interfaces it lands on (this machine, containers, VPN, local network, internet), then the firewall narrows it: **Blocked**, **Allowed**, **Unknown** (unreadable), or **Docker bypass** (a container-published port on Linux, where Docker's NAT runs before ufw or firewalld rules). The agent cannot see upstream routers, so "local network" and "internet" mean "reachable as far as this machine decides".

## Topology

inline joins every machine's established connections to every machine's listeners, so "5432 is open" becomes "PostgreSQL on `db` listens everywhere; only `web` uses it". **Fleet, Topology** draws that graph left to right in the direction traffic flows, groups external destinations, shows unmanaged devices dashed, and marks the flows a pending plan would block.

## Firewall plans

A plan contains only two kinds of action on specific ports: **restrict** (allow these sources, drop the rest) and **block**. inline-fleet never changes the default policy and never touches rules it did not write. It uses its own nftables table, iptables chain, pf anchor or Windows firewall rule group, so rollback removes exactly what it added.

Every plan keeps the management session, established flows, loopback and inline itself reachable. Applying is provisional: unless it is confirmed within the rollback timer (120 seconds by default), the agent reverts it on its own.

Applying from the dashboard takes **two keys**: the workspace setting (**Fleet, Settings, Allow applying from the dashboard**) and the agent started with `--allow-remote-apply`. Without both, approve the plan in the portal and run `npx @pendral/inline-fleet apply` on the machine, which shows the same plan and asks before it acts.

## The portal

- **Overview:** machines online, fleet score, open findings, exposed services, pending approvals and unmanaged devices; a seven-day trend; the live activity feed; every machine, filterable.
- **Machine:** services and their reach, findings, connections in and out, the firewall, the plan (script, rollback script, history), and system facts with what each collector could not see.
- **Findings:** everything across the fleet, worst first, with dismiss and reopen (a reason is required).
- **Devices:** neighbors seen on LAN segments that are not enrolled, to label, mark known or ignore.
- **Settings:** scan interval, heartbeat, offline threshold, remote apply, rollback timer, AI explanations, snapshot retention.

Everything updates live over the workspace event stream; there is nothing to refresh.

## CLI

```
npx @pendral/inline-fleet               # scan: local, offline, read-only report
inline-fleet join [--token <inlf_...>]  # join (a code to approve, or a token), then run the agent
inline-fleet run                        # the agent loop for a machine that has joined
inline-fleet status | doctor            # fleet membership; what each collector can see
inline-fleet plan | apply | confirm | rollback
inline-fleet install | uninstall | leave
```

## Permissions and plan

Module `fleet`: `read`, `triage` (dismiss findings, label devices, rescan, ask the AI), `enroll` (pair machines, join tokens, approve, quarantine, rename, revoke), `enforce` (approve and send plans, roll back), `configure`. Workspace-wide. Fleet is on every plan; the number of machines is what changes: Free includes 3, Pro 50, Enterprise has no limit.
