@crvouga/dev-fleet 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mise/locks/aws-cdk/2.1146.1/aube-lock.yaml +24 -0
- package/.mise/locks/aws-cdk/2.1146.1/package.json +7 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/pyproject.toml +5 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/uv.lock +2794 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/aube-lock.yaml +123 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/package.json +12 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/aube-lock.yaml +24 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/package.json +7 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/aube-lock.yaml +1432 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/package.json +7 -0
- package/.mise/locks/npm-vercel/65.8.0/aube-lock.yaml +3595 -0
- package/.mise/locks/npm-vercel/65.8.0/package.json +7 -0
- package/.mise/locks/npm-wrangler/4.156.0/aube-lock.yaml +895 -0
- package/.mise/locks/npm-wrangler/4.156.0/package.json +7 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/aube-lock.yaml +24 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/package.json +7 -0
- package/LICENSE +21 -0
- package/README.md +357 -2
- package/agents/antigravity-proxy.ts +3 -0
- package/agents/audit.md +89 -0
- package/agents/claude-proxy +5 -0
- package/agents/claude-proxy.ts +3 -0
- package/agents/codex-proxy.ts +3 -0
- package/agents/grok-proxy.ts +3 -0
- package/agents/managed-proxy.ts +55 -0
- package/agents/opencode-proxy.ts +3 -0
- package/agents/registry.json +209 -0
- package/agents/registry.schema.json +149 -0
- package/agents/setup.md +158 -0
- package/agents/t3.json +40 -0
- package/ansible/macos/manager.yml +30 -0
- package/ansible/macos/policy.json +9 -0
- package/ansible/monitoring/README.md +215 -0
- package/ansible/monitoring/agent-darwin.yml +198 -0
- package/ansible/monitoring/agent.yml +172 -0
- package/ansible/monitoring/beszel-agent.service +42 -0
- package/ansible/monitoring/compose.yml +74 -0
- package/ansible/monitoring/hub-darwin.yml +102 -0
- package/ansible/monitoring/hub-route-darwin.yml +51 -0
- package/ansible/monitoring/lock.json +16 -0
- package/ansible/net/remote-probe.sh +13 -0
- package/ansible/pyproject.toml +12 -0
- package/ansible/ubuntu/authorize-sudo.sh +51 -0
- package/ansible/ubuntu/baseline.yml +89 -0
- package/ansible/ubuntu/disk-gc.py +336 -0
- package/ansible/ubuntu/disk-guard.py +104 -0
- package/ansible/ubuntu/policy.json +52 -0
- package/ansible/ubuntu/recovery.yml +15 -0
- package/ansible/ubuntu/t3/policy.json +7 -0
- package/ansible/ubuntu/t3-desktop.yml +220 -0
- package/ansible/ubuntu/t3-health.sh +33 -0
- package/ansible/ubuntu/t3-policy.yml +76 -0
- package/ansible/ubuntu/t3-route.yml +28 -0
- package/ansible/ubuntu/tailscale.yml +78 -0
- package/ansible/ubuntu/tasks/credentials.yml +47 -0
- package/ansible/ubuntu/tasks/docker.yml +31 -0
- package/ansible/ubuntu/tasks/packages.yml +56 -0
- package/ansible/ubuntu/tasks/power.yml +51 -0
- package/ansible/ubuntu/tasks/recovery.yml +39 -0
- package/ansible/ubuntu/tasks/resources.yml +312 -0
- package/ansible/ubuntu/tasks/services.yml +31 -0
- package/ansible/ubuntu/tasks/ssh.yml +71 -0
- package/ansible/ubuntu/tasks/user.yml +120 -0
- package/ansible/uv.lock +190 -0
- package/bin/devfleet.mjs +36 -0
- package/bootstrap/bootstrap.ps1 +50 -0
- package/bootstrap/bootstrap.sh +80 -0
- package/bootstrap/devfleet +7 -0
- package/bootstrap/docker-desktop.sh +33 -0
- package/bootstrap/doctor.ts +234 -0
- package/bootstrap/fleet-manager-run.sh +103 -0
- package/bootstrap/fleet-remote-bootstrap.sh +258 -0
- package/bootstrap/global-config.ts +171 -0
- package/bootstrap/install-launcher.sh +10 -0
- package/bootstrap/native-aws.ts +86 -0
- package/bootstrap/native-t3.ts +154 -0
- package/bootstrap/npm.ts +358 -0
- package/bootstrap/release-version.ts +157 -0
- package/bootstrap/setup-status.ts +51 -0
- package/bootstrap/setup.ts +88 -0
- package/bootstrap/shell-path.ts +105 -0
- package/bootstrap/verify-t3-archive.py +49 -0
- package/bootstrap/worker-auth.ts +65 -0
- package/bootstrap/worker-github.ts +47 -0
- package/bootstrap/worker-tools.ts +24 -0
- package/cliproxy/AGENTS.md +11 -0
- package/cliproxy/README.md +269 -0
- package/cliproxy/clients/bootstrap.py +143 -0
- package/cliproxy/clients/launcher.py +57 -0
- package/cliproxy/compose.yaml +90 -0
- package/cliproxy/config.template.yaml +70 -0
- package/cliproxy/docs/cloud-adoption.md +16 -0
- package/cliproxy/docs/deployment.png +0 -0
- package/cliproxy/docs/deployment.svg +109 -0
- package/cliproxy/docs/health-workflow.example.yml +185 -0
- package/cliproxy/docs/migration.md +131 -0
- package/cliproxy/docs/model-health-report.schema.json +364 -0
- package/cliproxy/docs/model-health.md +393 -0
- package/cliproxy/image.lock.json +5 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup-check.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup.plist +48 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-fleet.plist +32 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-gc.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-monitor.plist +29 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-vault-renew.plist +31 -0
- package/cliproxy/models.json +1618 -0
- package/cliproxy/requirements.lock +78 -0
- package/cliproxy/scripts/accounts.py +38 -0
- package/cliproxy/scripts/audit.py +123 -0
- package/cliproxy/scripts/backup +234 -0
- package/cliproxy/scripts/check.py +34 -0
- package/cliproxy/scripts/checkpoint.py +226 -0
- package/cliproxy/scripts/cli.py +64 -0
- package/cliproxy/scripts/cliproxy_host.py +257 -0
- package/cliproxy/scripts/cloud_state.py +150 -0
- package/cliproxy/scripts/container-health +6 -0
- package/cliproxy/scripts/docker +24 -0
- package/cliproxy/scripts/external-health.py +79 -0
- package/cliproxy/scripts/fleet_config.py +43 -0
- package/cliproxy/scripts/fleet_sync.py +274 -0
- package/cliproxy/scripts/initialize +84 -0
- package/cliproxy/scripts/install-dependencies +48 -0
- package/cliproxy/scripts/install.py +406 -0
- package/cliproxy/scripts/model_health.py +16 -0
- package/cliproxy/scripts/modelhealth/__init__.py +5 -0
- package/cliproxy/scripts/modelhealth/checks.py +429 -0
- package/cliproxy/scripts/modelhealth/cli.py +117 -0
- package/cliproxy/scripts/modelhealth/config.py +186 -0
- package/cliproxy/scripts/modelhealth/emulator.py +536 -0
- package/cliproxy/scripts/modelhealth/expect.py +88 -0
- package/cliproxy/scripts/modelhealth/findings.py +183 -0
- package/cliproxy/scripts/modelhealth/protocol.py +330 -0
- package/cliproxy/scripts/modelhealth/redact.py +28 -0
- package/cliproxy/scripts/modelhealth/report.py +150 -0
- package/cliproxy/scripts/modelhealth/runner.py +390 -0
- package/cliproxy/scripts/modelhealth/scenario.py +131 -0
- package/cliproxy/scripts/modelhealth/schema.py +63 -0
- package/cliproxy/scripts/modelhealth/simulate.py +111 -0
- package/cliproxy/scripts/modelhealth/transport.py +213 -0
- package/cliproxy/scripts/monitor +68 -0
- package/cliproxy/scripts/ops +527 -0
- package/cliproxy/scripts/public-endpoint +127 -0
- package/cliproxy/scripts/recover_node.py +177 -0
- package/cliproxy/scripts/restore-verify +60 -0
- package/cliproxy/scripts/retention.py +144 -0
- package/cliproxy/scripts/service-start +7 -0
- package/cliproxy/scripts/validate-config +56 -0
- package/cliproxy/scripts/vault-access.py +57 -0
- package/cliproxy/scripts/vault-backup-login +55 -0
- package/cliproxy/scripts/vault-recovery +56 -0
- package/cliproxy/scripts/vault-renew +30 -0
- package/cliproxy/systemd/cliproxy-backup-check.service +10 -0
- package/cliproxy/systemd/cliproxy-backup-check.timer +8 -0
- package/cliproxy/systemd/cliproxy-backup.service +13 -0
- package/cliproxy/systemd/cliproxy-backup.timer +8 -0
- package/cliproxy/systemd/cliproxy-fleet.service +17 -0
- package/cliproxy/systemd/cliproxy-gc.service +13 -0
- package/cliproxy/systemd/cliproxy-gc.timer +8 -0
- package/cliproxy/systemd/cliproxy-monitor.service +8 -0
- package/cliproxy/systemd/cliproxy-monitor.timer +7 -0
- package/cliproxy/systemd/cliproxy-vault-renew.service +8 -0
- package/cliproxy/systemd/cliproxy-vault-renew.timer +7 -0
- package/cliproxy/vendor/LICENSE +22 -0
- package/cliproxy/vendor/config.upstream.yaml +1349 -0
- package/docs/consumer-setup.md +408 -0
- package/docs/declared.md +89 -0
- package/docs/machine-setup.md +399 -0
- package/docs/operations.md +276 -0
- package/docs/releasing.md +103 -0
- package/docs/state.md +254 -0
- package/docs/topology.md +67 -0
- package/docs/ubuntu-sync.md +351 -0
- package/docs/worker-contract.md +67 -0
- package/examples/fleet.json +100 -0
- package/examples/roster.json +13 -0
- package/llms.txt +35 -0
- package/mise.lock +1521 -0
- package/mise.toml +110 -0
- package/package.json +74 -4
- package/schema/fleet.schema.json +420 -0
- package/schema/roster.schema.json +77 -0
- package/src/agents/account-relay.ts +111 -0
- package/src/agents/accounts.ts +85 -0
- package/src/agents/audit.ts +77 -0
- package/src/agents/cli.ts +13 -0
- package/src/agents/command.ts +204 -0
- package/src/agents/converge.ts +123 -0
- package/src/agents/doctor.ts +107 -0
- package/src/agents/errors.ts +2 -0
- package/src/agents/files.ts +73 -0
- package/src/agents/fleet-verify.ts +165 -0
- package/src/agents/launch.ts +290 -0
- package/src/agents/native-verify.ts +371 -0
- package/src/agents/provider-errors.ts +57 -0
- package/src/agents/registry.ts +61 -0
- package/src/agents/render.ts +110 -0
- package/src/agents/round-trip.ts +192 -0
- package/src/agents/secrets.ts +13 -0
- package/src/agents/toolchain.ts +34 -0
- package/src/agents/verify-cache.ts +103 -0
- package/src/agents/verify.ts +127 -0
- package/src/auth-checks.ts +163 -0
- package/src/cli/agent-guide.ts +129 -0
- package/src/cli/app.ts +122 -0
- package/src/cli/cli.ts +28 -0
- package/src/cli/command-index.ts +57 -0
- package/src/cli/contracts.ts +25 -0
- package/src/cli/fleet-reference.ts +17 -0
- package/src/cli/fleet-state.ts +499 -0
- package/src/cli/fleet-sync-checkout.ts +130 -0
- package/src/cli/fleet-sync-enrollment.ts +140 -0
- package/src/cli/fleet-sync-forward.ts +134 -0
- package/src/cli/fleet-sync-manager.ts +249 -0
- package/src/cli/fleet-sync-monitoring.ts +185 -0
- package/src/cli/fleet-sync-output.ts +317 -0
- package/src/cli/fleet-sync-privilege.ts +27 -0
- package/src/cli/fleet-sync-progress.ts +109 -0
- package/src/cli/fleet-sync-remote.ts +242 -0
- package/src/cli/fleet-sync-report.ts +26 -0
- package/src/cli/fleet-sync-schedule.ts +138 -0
- package/src/cli/fleet-sync-state.ts +86 -0
- package/src/cli/fleet-sync-vault.ts +166 -0
- package/src/cli/fleet-sync.ts +375 -0
- package/src/cli/fleet.ts +165 -0
- package/src/cli/managed-health.ts +219 -0
- package/src/cli/node-deployment.ts +34 -0
- package/src/cli/node-sync.ts +121 -0
- package/src/cli/node-t3.ts +31 -0
- package/src/cli/options.ts +141 -0
- package/src/cli/probe-source.ts +7 -0
- package/src/cli/program.ts +185 -0
- package/src/cli/project.ts +51 -0
- package/src/cli/register-fleet.ts +296 -0
- package/src/cli/registry.ts +185 -0
- package/src/cli/repair-apply.ts +67 -0
- package/src/cli/repair-guard.ts +11 -0
- package/src/cli/repair-inspect.ts +150 -0
- package/src/cli/repair-script.ts +76 -0
- package/src/cli/repair.ts +209 -0
- package/src/cli/roster.ts +105 -0
- package/src/cli/search.ts +35 -0
- package/src/cli/settings.ts +67 -0
- package/src/cli/source-operations.ts +102 -0
- package/src/cli/t3/command.ts +90 -0
- package/src/cli/t3/register.ts +55 -0
- package/src/cli/tailnet.ts +127 -0
- package/src/cli/ui-actions.ts +133 -0
- package/src/cli/ui-controller.ts +113 -0
- package/src/cli/ui-data.ts +106 -0
- package/src/cli/ui-execute.ts +163 -0
- package/src/cli/ui-repairs.ts +34 -0
- package/src/cli/ui-reports.ts +211 -0
- package/src/cli/ui-targets.ts +100 -0
- package/src/cli/ui-terminal.ts +109 -0
- package/src/cli/ui-types.ts +49 -0
- package/src/cli/ui-wizard.ts +101 -0
- package/src/cli/ui.ts +9 -0
- package/src/cli/worker-health-output.ts +37 -0
- package/src/config.ts +186 -0
- package/src/configure.ts +255 -0
- package/src/definition.ts +290 -0
- package/src/diagnostics.ts +92 -0
- package/src/doctor-check.ts +10 -0
- package/src/errors.ts +1 -0
- package/src/fingerprint.ts +120 -0
- package/src/fleet-config.ts +174 -0
- package/src/input-hashes.ts +73 -0
- package/src/machine/local-source.ts +52 -0
- package/src/machine/lock-error.ts +2 -0
- package/src/machine/platform.ts +44 -0
- package/src/machine/source.ts +68 -0
- package/src/machine/state.ts +173 -0
- package/src/machine-health.ts +95 -0
- package/src/managed-source.ts +47 -0
- package/src/manager-failure.ts +48 -0
- package/src/manager-sync.ts +405 -0
- package/src/manager.ts +62 -0
- package/src/monitoring/agent-evidence.ts +146 -0
- package/src/monitoring/apply.ts +183 -0
- package/src/monitoring/beszel.ts +198 -0
- package/src/monitoring/enroll.ts +111 -0
- package/src/monitoring/hub-access.ts +114 -0
- package/src/monitoring/hub-backup.ts +121 -0
- package/src/monitoring/hub-viewer.ts +142 -0
- package/src/monitoring/hub.ts +93 -0
- package/src/monitoring/manager-agent.ts +73 -0
- package/src/monitoring/manager-hub.ts +248 -0
- package/src/monitoring/remote.ts +65 -0
- package/src/network-definition.ts +83 -0
- package/src/node-converge-linux.ts +374 -0
- package/src/node-converge-shared.ts +83 -0
- package/src/node-converge.ts +40 -0
- package/src/onboard.ts +35 -0
- package/src/package-info.ts +45 -0
- package/src/platform/ansible-receipt.ts +128 -0
- package/src/platform/ansible.ts +66 -0
- package/src/platform/darwin.ts +203 -0
- package/src/platform/detect.ts +11 -0
- package/src/platform/fallback.ts +110 -0
- package/src/platform/index.ts +38 -0
- package/src/platform/launchd.ts +98 -0
- package/src/platform/linux.ts +139 -0
- package/src/platform/ps.ts +45 -0
- package/src/platform/resource-verify.ts +132 -0
- package/src/platform/systemd-user.ts +95 -0
- package/src/platform/t3-policy.ts +137 -0
- package/src/platform/t3-route.ts +68 -0
- package/src/platform/t3-service.ts +75 -0
- package/src/platform/types.ts +123 -0
- package/src/platform/ubuntu-audit.ts +66 -0
- package/src/platform/ubuntu-verify.ts +105 -0
- package/src/platform/windows.ts +148 -0
- package/src/power-recovery.ts +55 -0
- package/src/probe.ts +51 -0
- package/src/protocol.ts +119 -0
- package/src/proxy-converge.ts +85 -0
- package/src/remote-bootstrap.ts +210 -0
- package/src/remote-entry.ts +89 -0
- package/src/remote-lock.ts +12 -0
- package/src/repair-catalog.ts +206 -0
- package/src/repair-plan.ts +78 -0
- package/src/repair-runner.ts +84 -0
- package/src/report.ts +71 -0
- package/src/runner.ts +104 -0
- package/src/secrets.ts +169 -0
- package/src/service-auth.ts +115 -0
- package/src/ssh.ts +135 -0
- package/src/state/bucket.ts +208 -0
- package/src/state/check.ts +74 -0
- package/src/state/connection.ts +92 -0
- package/src/state/enrollment.ts +45 -0
- package/src/state/legacy.ts +89 -0
- package/src/state/litestream.ts +143 -0
- package/src/state/operator.ts +20 -0
- package/src/state/paths.ts +40 -0
- package/src/state/roster.ts +62 -0
- package/src/state/store.ts +169 -0
- package/src/state/sync.ts +352 -0
- package/src/sync.ts +104 -0
- package/src/t3/accounts.ts +81 -0
- package/src/t3/desired.ts +165 -0
- package/src/t3/files.ts +81 -0
- package/src/t3/names-check.ts +53 -0
- package/src/t3/names.ts +155 -0
- package/src/t3/plan.ts +82 -0
- package/src/t3/providers.ts +74 -0
- package/src/t3/reconcile.ts +123 -0
- package/src/t3-endpoint.ts +104 -0
- package/src/t3-linked-check.ts +123 -0
- package/src/tailnet-health.ts +231 -0
- package/src/tailnet-matching.ts +37 -0
- package/src/tailscale.ts +141 -0
- package/src/toolchain-checks.ts +108 -0
- package/src/topology.ts +24 -0
- package/src/transport.ts +122 -0
- package/src/types.ts +99 -0
- package/src/vault-config.ts +38 -0
- package/src/worker-access-checks.ts +168 -0
- package/src/worker-capacity.ts +34 -0
- package/src/worker-host-checks.ts +181 -0
- package/src/worker-policy.ts +64 -0
- package/src/worker-readiness.ts +64 -0
- package/src/workstation-checks.ts +29 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# Set up a fleet from your own project
|
|
2
|
+
|
|
3
|
+
This is the whole procedure for connecting dev-fleet to your own machines,
|
|
4
|
+
written so a coding agent can do it in one pass from the project that will hold
|
|
5
|
+
the fleet configuration. It assumes nothing about your accounts, hosts or
|
|
6
|
+
domains: every value below in `UPPER_CASE` or under `example.com` is a
|
|
7
|
+
placeholder you replace.
|
|
8
|
+
|
|
9
|
+
If you are an agent, read the [rules](#rules-for-agents) first, then follow
|
|
10
|
+
the steps in order. Stop and ask the human only at the points marked **Ask**.
|
|
11
|
+
|
|
12
|
+
## What you end up with
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
your-fleet/ any Git repository you own (it may hold other things too)
|
|
16
|
+
package.json pins @crvouga/dev-fleet to one exact version
|
|
17
|
+
package-lock.json or bun.lock, pnpm-lock.yaml: commit it
|
|
18
|
+
fleet.json your machines, services, secrets locations, state bucket
|
|
19
|
+
.gitignore keeps credentials and node_modules out of Git
|
|
20
|
+
AGENTS.md tells future agents how to use dev-fleet here
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Nothing else is needed. You never clone dev-fleet, copy its files or write a
|
|
24
|
+
wrapper script around a checkout of it: the npm package is the tool, and the
|
|
25
|
+
version in `package.json` is the commit every machine runs.
|
|
26
|
+
|
|
27
|
+
| Concept | Meaning |
|
|
28
|
+
| ----------- | ------------------------------------------------------------------------------------------------------ |
|
|
29
|
+
| manager | The one macOS machine you run `devfleet fleet-sync` on. Hosts the CLI proxy and the monitoring hub. |
|
|
30
|
+
| worker | An Ubuntu 26.04 machine the manager converges over SSH. Stateless: rebuilt from Git and your secrets. |
|
|
31
|
+
| fleet state | One SQLite database (roster, approved revision, configuration last applied) replicated to your bucket. |
|
|
32
|
+
| bucket | Any S3-compatible bucket you own, or a directory. The source of truth for the fleet state. |
|
|
33
|
+
| revision | The dev-fleet commit the installed package was published from (`npx devfleet --version`). |
|
|
34
|
+
|
|
35
|
+
## Rules for agents
|
|
36
|
+
|
|
37
|
+
- **Do not clone, vendor or patch dev-fleet.** Not `node_modules`, not a
|
|
38
|
+
checkout `fleet-sync` puts on a machine, not a file it manages there. The
|
|
39
|
+
next install or sync replaces them. A bug or a missing feature is an issue
|
|
40
|
+
at <https://github.com/crvouga/dev-fleet/issues>; tell the human what is
|
|
41
|
+
blocked and link it. Issues are public: no credentials, hostnames,
|
|
42
|
+
accounts, domains or `fleet.json` contents.
|
|
43
|
+
- **Never put a secret in an argument, in `fleet.json`, in a commit or in a
|
|
44
|
+
log.** Bucket credentials are read only from the environment; every other
|
|
45
|
+
secret from the provider named under `secrets`.
|
|
46
|
+
- **Do not bypass SSH host keys, forward SSH agents or copy login caches**
|
|
47
|
+
between machines. Host trust, sudo and sign-ins are the human's steps.
|
|
48
|
+
- **Previews are safe; applies are not.** `--dry-run`, `fleet-status`,
|
|
49
|
+
`fleet-state` (without flags) and `commands --json` change nothing.
|
|
50
|
+
`fleet-sync` without `--dry-run` changes every machine.
|
|
51
|
+
- **Use the command, never the code.** dev-fleet has no library API: do not
|
|
52
|
+
import anything from `node_modules/@crvouga/dev-fleet/`. The project's own
|
|
53
|
+
tools go in the project's `mise.toml`; the global mise configuration on a
|
|
54
|
+
fleet machine is dev-fleet's.
|
|
55
|
+
- **Use JSON.** Every diagnostic command takes `--json` and
|
|
56
|
+
`--non-interactive`. Read the check IDs and `remedy.action` from the
|
|
57
|
+
output; do not parse the human table.
|
|
58
|
+
- The machine-readable contract (commands, exit codes, safety rules, how to
|
|
59
|
+
report a bug) is `npx devfleet commands --json`. Read it before running
|
|
60
|
+
anything else.
|
|
61
|
+
|
|
62
|
+
## 1. Check what the human must already have
|
|
63
|
+
|
|
64
|
+
These cannot be installed or decided by dev-fleet. Verify each one; for any
|
|
65
|
+
that fails, **Ask** the human to do it and wait.
|
|
66
|
+
|
|
67
|
+
On the manager (the Mac this project is checked out on):
|
|
68
|
+
|
|
69
|
+
| Requirement | Verify |
|
|
70
|
+
| -------------------------------------------------------- | --------------------------------------------------------------- |
|
|
71
|
+
| macOS 14 or newer | `sw_vers -productVersion` |
|
|
72
|
+
| Git | `git --version` |
|
|
73
|
+
| Node 22 or newer (for `npx`) | `node --version` |
|
|
74
|
+
| Docker Desktop running (the proxy and the hub run in it) | `docker info --format '{{.ServerVersion}}'` |
|
|
75
|
+
| Tailscale signed in | `tailscale status --json` (look for `"BackendState":"Running"`) |
|
|
76
|
+
| An SSH alias per worker that works without a prompt | `ssh -o BatchMode=yes WORKER_ALIAS true` for each worker |
|
|
77
|
+
|
|
78
|
+
On every worker:
|
|
79
|
+
|
|
80
|
+
| Requirement | Verify, from the manager |
|
|
81
|
+
| ------------------------------------ | ------------------------------------------------------------------ |
|
|
82
|
+
| Ubuntu 26.04, amd64 or arm64 | `ssh WORKER_ALIAS '. /etc/os-release; echo $VERSION_ID; uname -m'` |
|
|
83
|
+
| A non-root account that can use sudo | `ssh WORKER_ALIAS 'id -u; sudo -n true && echo sudo-ok'` |
|
|
84
|
+
| Joined to the same tailnet | the worker appears in `tailscale status` on the manager |
|
|
85
|
+
|
|
86
|
+
A worker needs nothing else beforehand: no Git, curl, Python, Node, Bun or
|
|
87
|
+
mise. `fleet-sync` installs them. If `sudo -n true` fails, that is fine for
|
|
88
|
+
the first, interactive run, which asks for the password once and installs a
|
|
89
|
+
sudo rule for the fleet; it is not fine for unattended runs.
|
|
90
|
+
|
|
91
|
+
The `ssh` value of a worker in `fleet.json` is an alias from the manager's
|
|
92
|
+
`~/.ssh/config`. dev-fleet always connects with `BatchMode=yes` and
|
|
93
|
+
`StrictHostKeyChecking=yes`, so the host key must already be in
|
|
94
|
+
`known_hosts`. **Ask** the human to verify each fingerprint out of band and
|
|
95
|
+
connect once by hand; never add `StrictHostKeyChecking=no` or
|
|
96
|
+
`accept-new`. A typical alias:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Host worker-one
|
|
100
|
+
HostName worker-one.tail0a1b2c.ts.net
|
|
101
|
+
User dev
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Also **Ask** for these, which only the human has:
|
|
105
|
+
|
|
106
|
+
1. **A bucket for the fleet state.** An empty private bucket on any
|
|
107
|
+
S3-compatible service (AWS S3, Cloudflare R2, Backblaze B2, MinIO) and an
|
|
108
|
+
access key that can read and write it: bucket name, endpoint (omit for AWS
|
|
109
|
+
S3), region (`auto` for R2). For a trial, an absolute directory path works
|
|
110
|
+
instead and needs no credentials.
|
|
111
|
+
2. **Where secrets are kept**, one of `vault`, `sops`, `file` or `env` (see
|
|
112
|
+
[secrets](#secrets)). With `vault`, the address, the KV v2 mount and the
|
|
113
|
+
path and field of each secret.
|
|
114
|
+
3. **The public origins** of the CLI proxy and of the monitoring dashboard,
|
|
115
|
+
and the two monitoring account emails (`services` below). They are
|
|
116
|
+
placeholders until the human has the domains.
|
|
117
|
+
4. **The GitHub login** every worker signs in as, and the Vault user name.
|
|
118
|
+
|
|
119
|
+
## 2. Create the project
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
mkdir -p your-fleet && cd your-fleet # or an existing repository
|
|
123
|
+
git init # if it is not one yet
|
|
124
|
+
npm init -y # if there is no package.json yet
|
|
125
|
+
npm install --save-dev --save-exact @crvouga/dev-fleet
|
|
126
|
+
npx devfleet --version # 1.2.3 (commit 3f2a…): the revision every machine will run
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Pin the exact version (`--save-exact`); a range would let a reinstall move the
|
|
130
|
+
whole fleet. Any package manager works (`bun add -d --exact`,
|
|
131
|
+
`pnpm add -D --save-exact`). The CLI runs on Bun, which the package brings as
|
|
132
|
+
a dependency; if your package manager skips install scripts, allow Bun's or
|
|
133
|
+
install Bun yourself.
|
|
134
|
+
|
|
135
|
+
Add to `.gitignore`:
|
|
136
|
+
|
|
137
|
+
```gitignore
|
|
138
|
+
node_modules/
|
|
139
|
+
.env
|
|
140
|
+
.env.*
|
|
141
|
+
.fleet.local.json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 3. Write `fleet.json`
|
|
145
|
+
|
|
146
|
+
Start from the example the installed package prints, which points `$schema`
|
|
147
|
+
at the schema shipped in the package, so editors validate the file:
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
npx devfleet fleet-state --example > fleet.json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Then replace every placeholder. Each field, from
|
|
154
|
+
[`schema/fleet.schema.json`](../schema/fleet.schema.json):
|
|
155
|
+
|
|
156
|
+
| Field | Value |
|
|
157
|
+
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
158
|
+
| `version` | `3` |
|
|
159
|
+
| `source.repository` | `https://github.com/crvouga/dev-fleet.git`, or your fork if you publish one. Machines only accept code from it. |
|
|
160
|
+
| `state` | `{ "bucket", "endpoint"?, "region"?, "prefix"? }` or `{ "directory": "/absolute/path" }`. Never credentials. |
|
|
161
|
+
| `secrets` | See [secrets](#secrets). |
|
|
162
|
+
| `manager.id` | A short lowercase id, `^[a-z][a-z0-9-]{0,62}$`, e.g. `manager-mac`. |
|
|
163
|
+
| `manager.os` | `"macOS"` |
|
|
164
|
+
| `manager.network.tailscaleName` | The manager's MagicDNS name: `tailscale status --json` → `.Self.DNSName` without the trailing dot. |
|
|
165
|
+
| `manager.network.user` | The manager's login name (`id -un`). |
|
|
166
|
+
| `manager.neverSleepOnPower` | `true` (workers depend on the manager's services). |
|
|
167
|
+
| `workers[].id` | A short lowercase id. The **first** worker is the canary: a new revision must pass there before the others get it. |
|
|
168
|
+
| `workers[].ssh` | The worker's alias in the manager's `~/.ssh/config`. |
|
|
169
|
+
| `workers[].os` | `"linux"` |
|
|
170
|
+
| `workers[].network.tailscaleName` | The worker's MagicDNS name (from `tailscale status --json` → `.Peer[].DNSName` on the manager). |
|
|
171
|
+
| `workers[].network.sshPort` | `22` unless the worker's sshd listens elsewhere. |
|
|
172
|
+
| `workers[].network.user` | The worker account (`ssh WORKER_ALIAS id -un`). |
|
|
173
|
+
| `workers[].network.t3Port` | `3773` |
|
|
174
|
+
| `services.proxy.endpoint` | The public HTTPS origin of the CLI proxy, e.g. `https://cli-proxy.example.com`. |
|
|
175
|
+
| `services.monitoring.hubPort` | `8443` |
|
|
176
|
+
| `services.monitoring.publicUrl` | The public HTTPS origin of the monitoring dashboard. |
|
|
177
|
+
| `services.monitoring.managerAccount` | The email of the monitoring account the manager enrolls machines with. |
|
|
178
|
+
| `services.monitoring.viewerAccount` | The email of the read-only account visitors are signed in as. |
|
|
179
|
+
| `ubuntu` | `{ "release": "26.04", "architectures": ["amd64", "arm64"] }` (fixed). |
|
|
180
|
+
| `worker.requiredCapabilities` | Normally all of `github`, `vault`, `t3`, `toolchain`, `workstation`, `worker`. |
|
|
181
|
+
| `worker.githubLogin` | The GitHub login every worker must be signed in as. |
|
|
182
|
+
| `worker.vaultUser` | The Vault user each worker's token is issued for. |
|
|
183
|
+
| `worker.toolchain`, `agentProfile` | `"mise.lock"`, `"proxy"` (fixed). |
|
|
184
|
+
| `worker.unattendedSudo`, `persistentT3`, `neverSleep` | `true` (fixed policy). |
|
|
185
|
+
|
|
186
|
+
Use the same values in every place they repeat. Validate before going on:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
npx devfleet fleet-state --json # exits 2 with code "invalid_usage" when fleet.json is invalid
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The `state:definition` check names the file it read. A `fleet.json` without a
|
|
193
|
+
`manager` object is not taken for a fleet configuration at all, and the
|
|
194
|
+
command then reports that it found none.
|
|
195
|
+
|
|
196
|
+
### Secrets
|
|
197
|
+
|
|
198
|
+
dev-fleet asks for four secrets by purpose: `proxy-client-key`,
|
|
199
|
+
`monitoring-account-password`, `monitoring-tunnel-token` and
|
|
200
|
+
`proxy-tunnel-token`. `secrets.provider` says where they are:
|
|
201
|
+
|
|
202
|
+
| Provider | `fleet.json` | Where the values are |
|
|
203
|
+
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
204
|
+
| `vault` | `{ "provider": "vault", "address": "https://vault.example.com", "mount": "secret", "locations": { NAME: { "path", "field" } } }` with all four names | A Vault KV v2 mount. Each worker gets its own renewable token. |
|
|
205
|
+
| `sops` | `{ "provider": "sops", "file": "secrets.enc.yaml" }` | One encrypted document with one top-level key per name. |
|
|
206
|
+
| `file` | `{ "provider": "file" }` (optional `"directory"`) | One private file per name, default `~/.config/dev-fleet/secrets/NAME`. |
|
|
207
|
+
| `env` | `{ "provider": "env" }` | `DEVFLEET_SECRET_PROXY_CLIENT_KEY` and so on (upper case, `_` for `-`). |
|
|
208
|
+
|
|
209
|
+
Only `vault` gives each worker its own login and renewal and gives the CLI
|
|
210
|
+
proxy its encrypted backup; with the other three those two features are not
|
|
211
|
+
available yet. You never write a secret value into the project; the human puts
|
|
212
|
+
them in the provider. **Ask** for that step.
|
|
213
|
+
|
|
214
|
+
### Bucket credentials
|
|
215
|
+
|
|
216
|
+
Every command reads them from the environment, never from arguments:
|
|
217
|
+
|
|
218
|
+
```sh
|
|
219
|
+
export DEVFLEET_BUCKET_ACCESS_KEY_ID=…
|
|
220
|
+
export DEVFLEET_BUCKET_SECRET_ACCESS_KEY=…
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Pass them however the human keeps secrets: a password manager's `run`
|
|
224
|
+
command, `vault kv get` into the environment, or a git-ignored `.env` loaded
|
|
225
|
+
with `set -a; . ./.env; set +a`. With neither variable set, the standard AWS
|
|
226
|
+
environment or profile is used. A `"directory"` state needs none.
|
|
227
|
+
|
|
228
|
+
Runs the manager starts by itself (a worker handing `fleet-sync` to the
|
|
229
|
+
manager) get no environment, so save the credentials on the manager once:
|
|
230
|
+
|
|
231
|
+
```sh
|
|
232
|
+
npx devfleet fleet-state --init # takes the bucket from fleet.json, asks on the terminal if the variables are unset
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 4. Tell future agents how to use it
|
|
236
|
+
|
|
237
|
+
Put this in the project's `AGENTS.md` (or `CLAUDE.md`):
|
|
238
|
+
|
|
239
|
+
```md
|
|
240
|
+
The fleet is managed with dev-fleet (`npx devfleet`, pinned in package.json).
|
|
241
|
+
Read `npx devfleet commands --json` before using it. Never edit dev-fleet
|
|
242
|
+
itself: not node_modules, not a clone, not what it installs on a machine. A
|
|
243
|
+
bug in dev-fleet or a feature it lacks is a GitHub issue at
|
|
244
|
+
https://github.com/crvouga/dev-fleet/issues; tell me what is blocked and link
|
|
245
|
+
it. Issues are public: no credentials, hostnames, accounts, domains or
|
|
246
|
+
fleet.json contents.
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Optionally add scripts, so nobody types the package name:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
"scripts": {
|
|
253
|
+
"fleet": "devfleet",
|
|
254
|
+
"fleet:sync": "devfleet fleet-sync",
|
|
255
|
+
"fleet:status": "devfleet fleet-status --workers"
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Commit `package.json`, the lockfile, `fleet.json`, `.gitignore` and
|
|
260
|
+
`AGENTS.md`.
|
|
261
|
+
|
|
262
|
+
## 5. Preview
|
|
263
|
+
|
|
264
|
+
Run on the manager, from the project, with the bucket credentials in the
|
|
265
|
+
environment:
|
|
266
|
+
|
|
267
|
+
```sh
|
|
268
|
+
npx devfleet fleet-state --json
|
|
269
|
+
npx devfleet fleet-sync --dry-run --json
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`fleet-state` reports the `state:bucket` and `state:replica` checks. On a new
|
|
273
|
+
fleet the bucket is empty, which is a warning, not a failure: the first sync
|
|
274
|
+
seeds it.
|
|
275
|
+
|
|
276
|
+
Before the first sync, the dry run is expected to stop with
|
|
277
|
+
`FLEET_MANAGER_ENROLLMENT_REQUIRED`: no worker's SSH connection and tailnet
|
|
278
|
+
identity has been enrolled yet. Anything else is a problem to fix first (see
|
|
279
|
+
[failures](#when-something-fails)).
|
|
280
|
+
|
|
281
|
+
## 6. The first sync is the human's
|
|
282
|
+
|
|
283
|
+
The first `fleet-sync` must run in a terminal the human is at. It:
|
|
284
|
+
|
|
285
|
+
- enrolls each declared worker it reaches over host-key-verified SSH, binding
|
|
286
|
+
it to its tailnet device;
|
|
287
|
+
- asks once for each worker's sudo password and installs a sudo rule for the
|
|
288
|
+
fleet account (the password is never stored);
|
|
289
|
+
- with `vault`, asks for the Vault password once and issues each worker its own
|
|
290
|
+
token.
|
|
291
|
+
|
|
292
|
+
An agent cannot answer those prompts, and `--json`, `--non-interactive` or a
|
|
293
|
+
`CI` variable turn them off. **Ask** the human to run:
|
|
294
|
+
|
|
295
|
+
```sh
|
|
296
|
+
npx devfleet fleet-sync
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
It converges the manager first, then the canary worker, then the rest. It
|
|
300
|
+
prints one table and the path of the full JSON report
|
|
301
|
+
(`~/.local/share/personal/fleet/manager/last-sync.json`, readable by the human
|
|
302
|
+
only). The first run on a bare worker takes several minutes: it installs the
|
|
303
|
+
operating system packages, mise, the pinned toolchain, T3 and the monitoring
|
|
304
|
+
agent.
|
|
305
|
+
|
|
306
|
+
Then the human signs in, once, to what only they can sign in to:
|
|
307
|
+
|
|
308
|
+
```sh
|
|
309
|
+
npx devfleet cliproxy install # the CLI proxy on the manager, if the sync reported it missing
|
|
310
|
+
npx devfleet cliproxy add-account claude
|
|
311
|
+
npx devfleet cliproxy add-account codex
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
and pairs the T3 desktop app on the manager with each worker as its check
|
|
315
|
+
remedy says (`t3:paired-access`).
|
|
316
|
+
|
|
317
|
+
## 7. Verify
|
|
318
|
+
|
|
319
|
+
These are read-only and safe for an agent to run at any time:
|
|
320
|
+
|
|
321
|
+
```sh
|
|
322
|
+
npx devfleet fleet-status --workers --json # every worker against the declared policy
|
|
323
|
+
npx devfleet fleet-status --json # the manager and every enrolled worker
|
|
324
|
+
npx devfleet fleet-state --json # the bucket holds what this machine has
|
|
325
|
+
npx devfleet fleet-sync --dry-run --json # what the next sync would do
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Exit code `0` is success, `1` a failed check, `2` invalid usage or
|
|
329
|
+
configuration. Each check is `{ id, status, summary, remedy? }` with status
|
|
330
|
+
`pass`, `warn`, `fail` or `skip`. Some checks stay failing until a person
|
|
331
|
+
acts (a sign-in, a pairing, a maintenance reboot); they say so in
|
|
332
|
+
`remedy.action` and do not stop other workers from converging.
|
|
333
|
+
|
|
334
|
+
Later syncs need no prompt and may be run by an agent when the human asks:
|
|
335
|
+
`npx devfleet fleet-sync --json`.
|
|
336
|
+
|
|
337
|
+
## When something fails
|
|
338
|
+
|
|
339
|
+
Read `summary` and `remedy.action` of the failing check. The common ones:
|
|
340
|
+
|
|
341
|
+
| Code or check | Meaning and remedy |
|
|
342
|
+
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
343
|
+
| `FLEET_MANAGER_ENROLLMENT_REQUIRED` | No worker is enrolled yet. The human runs the first `fleet-sync` in a terminal. |
|
|
344
|
+
| `FLEET_NOT_MANAGER` | You are not on the machine `manager` names. Run there, or from a worker that can SSH to it. |
|
|
345
|
+
| `FLEET_REVISION_REQUIRED` | No revision to converge to. Run the installed package (`npx devfleet`), not a copy of its source. |
|
|
346
|
+
| `FLEET_SOURCE_UNAVAILABLE` | The revision could not be fetched from `source.repository`. Check the network and that the URL is right. |
|
|
347
|
+
| `FLEET_STATE_DIVERGED` | This machine and the bucket hold different fleet states. `fleet-state --json` explains; never pick one without the human. |
|
|
348
|
+
| `FLEET_MANAGER_LOCKED` | Another run holds the manager's lock. Wait for it; do not delete the lock while a run is alive. |
|
|
349
|
+
| `FLEET_PLAN_STALE` | The plan changed after it was approved. Preview again. |
|
|
350
|
+
| `ssh:host-key` | Host key unknown or changed. The human verifies the fingerprint out of band; never disable checking. |
|
|
351
|
+
| `ssh:auth`, `ssh:dns`, `ssh:network` | The alias does not work non-interactively. Fix `~/.ssh/config`, the key or Tailscale, then `ssh -o BatchMode=yes ALIAS true`. |
|
|
352
|
+
| `TAILSCALE_NODE_OFFLINE` | The worker is off or off the tailnet. It is listed and skipped; others continue. |
|
|
353
|
+
| `TAILSCALE_DEVICE_REPLACED` | The tailnet device behind a worker changed. Nothing is written to it until the human confirms the replacement. |
|
|
354
|
+
| `SUDO_AUTHORIZATION_REQUIRED` | The worker account cannot use sudo without a password. Run the first sync interactively. |
|
|
355
|
+
| `UBUNTU_BASELINE_MISMATCH` | The worker is not Ubuntu 26.04. |
|
|
356
|
+
| `ROOT_DEVELOPMENT_USER_REJECTED` | The worker's SSH user is root. Use a normal account with sudo. |
|
|
357
|
+
| `BOOTSTRAP_DISK_CAPACITY_REQUIRED` | Less than 2 GiB free (or too few inodes) where the fleet keeps its files on the worker. |
|
|
358
|
+
| `FLEET_REMOTE_LOCKED` | Another operation is running on that worker. Wait and run again. |
|
|
359
|
+
| `FLEET_PROMOTION_BLOCKED` | The canary (first worker) failed, so the new revision went no further. Fix the canary first. |
|
|
360
|
+
| `VAULT_LOGIN_REQUIRED` | The manager is not logged in to Vault. The human logs in; the next sync issues worker tokens. |
|
|
361
|
+
| `PROVIDER_UNATTENDED_CREDENTIAL_REQUIRED` | A coding agent has no credential a background service can use. The human signs in on the proxy. |
|
|
362
|
+
| `BESZEL_ACCOUNT_REQUIRED` | The monitoring hub has no account yet. Follow the remedy; other workers are not blocked. |
|
|
363
|
+
|
|
364
|
+
A failure the remedy does not explain, or one you believe is a defect, is an
|
|
365
|
+
issue at <https://github.com/crvouga/dev-fleet/issues>: include
|
|
366
|
+
`npx devfleet --version`, the command and flags, and the failing check IDs
|
|
367
|
+
and codes; leave out everything private.
|
|
368
|
+
|
|
369
|
+
## Day to day
|
|
370
|
+
|
|
371
|
+
- **Add a worker.** Set up its SSH alias and host key and join it to the
|
|
372
|
+
tailnet (step 1), add it under `workers`, commit, then the human runs
|
|
373
|
+
`npx devfleet fleet-sync` once in a terminal to enroll it.
|
|
374
|
+
- **Remove a worker.** Delete its entry, commit, run `npx devfleet fleet-sync`.
|
|
375
|
+
- **Upgrade the fleet.** `npm install --save-dev --save-exact @crvouga/dev-fleet@latest`,
|
|
376
|
+
commit, `npx devfleet fleet-sync`. The canary proves the new revision first.
|
|
377
|
+
Install an older version and sync to roll back.
|
|
378
|
+
- **Replace the manager.** On the new Mac: clone the project, `npm ci`, put
|
|
379
|
+
the bucket credentials in the environment, `npx devfleet fleet-sync`. The
|
|
380
|
+
roster and the approved revision come back from the bucket. See
|
|
381
|
+
[state](state.md) for what the bucket does not hold yet.
|
|
382
|
+
|
|
383
|
+
## Moving from a pinned commit and a wrapper script
|
|
384
|
+
|
|
385
|
+
Some fleets were set up before the package existed: a `fleet.revision` file
|
|
386
|
+
holding a commit, and a script that cloned dev-fleet and ran it from that
|
|
387
|
+
clone. To move such a project to the package:
|
|
388
|
+
|
|
389
|
+
1. `npm install --save-dev --save-exact @crvouga/dev-fleet@VERSION`, choosing
|
|
390
|
+
the release whose commit (`npx devfleet --version`) is the one in
|
|
391
|
+
`fleet.revision` or newer.
|
|
392
|
+
2. Add the `state` block to `fleet.json` if it has none, with the bucket the
|
|
393
|
+
manager already uses (`~/.config/dev-fleet/bucket.json` on the manager
|
|
394
|
+
names it).
|
|
395
|
+
3. Replace every call of the wrapper with `npx devfleet …`, then delete the
|
|
396
|
+
wrapper script and `fleet.revision`.
|
|
397
|
+
4. Run `npx devfleet fleet-sync --dry-run --json`, then `npx devfleet fleet-sync`.
|
|
398
|
+
5. The CLI proxy on the manager may still run from the old clone: run
|
|
399
|
+
`npx devfleet cliproxy install` once before deleting that clone
|
|
400
|
+
([state](state.md) explains why).
|
|
401
|
+
|
|
402
|
+
## Further reading
|
|
403
|
+
|
|
404
|
+
- [README](../README.md): what dev-fleet assumes and its known limits.
|
|
405
|
+
- [Fleet state](state.md): the bucket, replication and recovery.
|
|
406
|
+
- [What is declared](declared.md): what is pinned on a machine and what is not.
|
|
407
|
+
- [Topology](topology.md): the manager and worker roles.
|
|
408
|
+
- [CLI proxy](../cliproxy/README.md) and [monitoring](../ansible/monitoring/README.md).
|
package/docs/declared.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# What is declared, and where
|
|
2
|
+
|
|
3
|
+
Two machines given the same dev-fleet version and the same `fleet.json` should
|
|
4
|
+
end up the same. That holds only for what is written down in a tracked file,
|
|
5
|
+
pinned to an exact version or digest, applied by a tool that converges
|
|
6
|
+
(Ansible or mise) and measured afterwards. This page lists what is, and what
|
|
7
|
+
is not yet.
|
|
8
|
+
|
|
9
|
+
`features/rules/declared.feature` keeps the rules below true on every pull
|
|
10
|
+
request. Its tables of exceptions are the same as [not declared yet](#not-declared-yet), and
|
|
11
|
+
may only shrink.
|
|
12
|
+
|
|
13
|
+
## Declared
|
|
14
|
+
|
|
15
|
+
| What | Declared in | Pinned by | Applied by | Measured by |
|
|
16
|
+
| --------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------- |
|
|
17
|
+
| The fleet: machines, services, accounts | Your `fleet.json` | The dev-fleet version in your `package.json`, which names one commit | `fleet-sync` | `fleet-status` |
|
|
18
|
+
| Tools: languages, CLIs, coding agents | `mise.toml` | `mise.lock`: version and checksum for each platform | `mise install --locked` | Tool and `agent:verification` checks |
|
|
19
|
+
| mise itself | `min_version` and `mise_*_sha256` in `mise.toml` | The digest of its release archive, held here | `bootstrap/bootstrap.sh` | Its version, at every toolchain setup |
|
|
20
|
+
| JavaScript dependencies | `package.json` | `bun.lock` | `bun install --frozen-lockfile` | The install fails on a difference |
|
|
21
|
+
| T3 Code, server and desktop app | `t3_*` under `[vars]` in `mise.toml` | SHA-256 of each artifact | `bootstrap/native-t3.ts`; `ansible/ubuntu/t3-desktop.yml` | T3 checks |
|
|
22
|
+
| Ubuntu packages | `packages` and `snapshot` in `ansible/ubuntu/policy.json` | Exact versions from a dated archive snapshot | `ansible/ubuntu/tasks/packages.yml` | `package:*` |
|
|
23
|
+
| Tailscale on workers | `tailscale` in `ansible/ubuntu/policy.json` | Exact version; digest of the signing key | `ansible/ubuntu/tailscale.yml` | `package:tailscale` |
|
|
24
|
+
| Worker policy: power, SSH, sudo, memory, disk | `ansible/ubuntu/*.yml`, values shared with checks in `policy.json` | The commit | `fleet-sync` | Host and resource checks |
|
|
25
|
+
| Monitoring: hub, tunnel, agents | `ansible/monitoring/*.yml`, `compose.yml`, `lock.json` | Image digests; SHA-256 of each agent archive | `fleet-sync` | `beszel:*` |
|
|
26
|
+
| The manager's private route to the hub | `ansible/monitoring/hub-route-darwin.yml` | The commit | `fleet-sync` | The route, read back |
|
|
27
|
+
| The manager's power settings | `pmset` in `ansible/macos/policy.json` | The commit | `ansible/macos/manager.yml`, once, as an administrator | `manager:power` |
|
|
28
|
+
| Ansible on the manager | `ansible/pyproject.toml` | `ansible/uv.lock`: one release, one Python, every dependency hashed | `uv run --locked --isolated` | A hash that differs stops the run |
|
|
29
|
+
| Ansible on a worker | `ansible-core` in `ansible/ubuntu/policy.json` | Exact version | `ansible/ubuntu/tasks/packages.yml` | `package:ansible-core` |
|
|
30
|
+
| The CLI proxy | `cliproxy/compose.yaml`, `image.lock.json`, `requirements.lock`, `launchd/`, `config.template.yaml` | Image digests; hashed Python dependencies | `devfleet cliproxy install`, and `fleet-sync` after it | The proxy's audit |
|
|
31
|
+
| Agent profiles and T3 providers | `agents/registry.json`, `agents/t3.json` | The commit | `fleet-sync` | Profile receipts; the plan after every apply |
|
|
32
|
+
| Docker Desktop on the manager | `docker_desktop_*` under `[vars]` in `mise.toml` | Version and build | You | `manager:docker` (warns) |
|
|
33
|
+
| CI | `.github/workflows/` | Each action at a commit; tools from `mise.lock` | GitHub | `features/rules/` |
|
|
34
|
+
|
|
35
|
+
A value lives in a policy file when something besides the playbook reads it,
|
|
36
|
+
so the check and the playbook cannot disagree. A value only a playbook uses
|
|
37
|
+
stays in the playbook: the playbook is its declaration.
|
|
38
|
+
|
|
39
|
+
## Not declared yet
|
|
40
|
+
|
|
41
|
+
- **A schedule.** Nothing re-applies the fleet on a timer, so a machine drifts
|
|
42
|
+
until someone runs `fleet-sync`. `fleet-sync --schedule enable` writes a
|
|
43
|
+
launchd job from TypeScript and has never worked from the published package.
|
|
44
|
+
It should become a playbook.
|
|
45
|
+
- **The rest of a worker's packages.** Only the packages in `policy.json` are
|
|
46
|
+
exact. `systemd-oomd` and the T3 desktop libraries are installed at whatever
|
|
47
|
+
version the machine's own archive holds, and the machine's own updates move
|
|
48
|
+
everything else. Pinning them needs every worker on the snapshot with its
|
|
49
|
+
own updates off, so security fixes arrive only when the snapshot moves. That
|
|
50
|
+
is a policy choice, not a missing line.
|
|
51
|
+
- **What a worker needs before Ansible exists.** `fleet-remote-bootstrap.sh`
|
|
52
|
+
installs Git, curl, CA certificates, Python and `ansible-core` from the
|
|
53
|
+
machine's own archive (the snapshot is HTTPS, and a new machine has no CA
|
|
54
|
+
certificates yet). The baseline then pins each of them.
|
|
55
|
+
- **The manager's applications.** Docker Desktop, Tailscale and Apple's
|
|
56
|
+
Command Line Tools are installed by a person. Docker Desktop is measured;
|
|
57
|
+
the other two are only required.
|
|
58
|
+
- **The CLI proxy's lifecycle.** It is installed and started by its own Python
|
|
59
|
+
scripts from the declarative files above, not by a playbook.
|
|
60
|
+
- **Files TypeScript and shell still write.** The installed `fleet.json`, the
|
|
61
|
+
`current` and `devfleet` links, the `PATH` lines in shell profiles, the
|
|
62
|
+
global mise configuration, the T3 server archive, agent profiles and T3
|
|
63
|
+
settings. Each is derived
|
|
64
|
+
from a tracked file and rewritten on every run, but by code.
|
|
65
|
+
- **Logins.** Tailscale, Vault, GitHub, each provider account and T3 pairing
|
|
66
|
+
belong to a machine and a person. They are checked, never copied.
|
|
67
|
+
- **Windows.** `bootstrap/bootstrap.ps1` exists; Windows is not a fleet role
|
|
68
|
+
and nothing tests it.
|
|
69
|
+
|
|
70
|
+
## Why not Nix
|
|
71
|
+
|
|
72
|
+
Nix would give the same three things this has: pinned inputs, verified
|
|
73
|
+
artifacts and a declarative description of the machine. Here they come from
|
|
74
|
+
`mise.lock`, the Ubuntu snapshot and Ansible. Adding Nix would put a second
|
|
75
|
+
package universe on every machine beside the one that is already running, and
|
|
76
|
+
moving to it entirely means NixOS workers and nix-darwin on the manager, which
|
|
77
|
+
replaces every unit and path that exists today. The one thing above that Nix
|
|
78
|
+
would settle and the current tools do not is the rest of a worker's packages.
|
|
79
|
+
|
|
80
|
+
## Changing a pin
|
|
81
|
+
|
|
82
|
+
| To change | Edit | Then |
|
|
83
|
+
| ----------------------- | -------------------------------------------------------------------- | ------------------------------------------------ |
|
|
84
|
+
| A tool | Its version in `mise.toml` | Update `mise.lock` with `mise lock` |
|
|
85
|
+
| mise | `min_version` and the four `mise_*_sha256` in `mise.toml` | Copy the digests from the release's `SHASUMS256` |
|
|
86
|
+
| An Ubuntu package | `packages` in `ansible/ubuntu/policy.json`, and `snapshot` if needed | The VM test in `ansible/ubuntu/tests` |
|
|
87
|
+
| Ansible on the manager | `ansible/pyproject.toml` | `uv lock` in `ansible/` |
|
|
88
|
+
| Beszel | `ansible/monitoring/lock.json`, `compose.yml`, the agent unit | `bun test features/rules` |
|
|
89
|
+
| A manager power setting | `pmset` in `ansible/macos/policy.json` | Run `manager.yml` once on the manager |
|