@crvouga/dev-fleet 0.1.0 → 0.3.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.
Files changed (159) hide show
  1. package/README.md +265 -37
  2. package/agents/setup.md +42 -12
  3. package/ansible/macos/manager.yml +10 -7
  4. package/ansible/macos/policy.json +2 -10
  5. package/ansible/macos/t3-policy.yml +49 -0
  6. package/ansible/monitoring/README.md +86 -25
  7. package/ansible/monitoring/agent.yml +40 -2
  8. package/ansible/monitoring/hub-route-darwin.yml +51 -0
  9. package/ansible/monitoring/lock.json +5 -0
  10. package/ansible/pyproject.toml +12 -0
  11. package/ansible/ubuntu/baseline.yml +1 -1
  12. package/ansible/ubuntu/disk-gc.py +336 -0
  13. package/ansible/ubuntu/policy.json +13 -3
  14. package/ansible/ubuntu/t3-desktop.yml +19 -0
  15. package/ansible/ubuntu/t3-health.sh +93 -22
  16. package/ansible/ubuntu/t3-idle.py +42 -0
  17. package/ansible/ubuntu/t3-policy.yml +4 -11
  18. package/ansible/ubuntu/tailscale.yml +8 -5
  19. package/ansible/ubuntu/tasks/recovery.yml +60 -1
  20. package/ansible/ubuntu/tasks/resources.yml +61 -1
  21. package/ansible/ubuntu/tasks/t3-restart.yml +37 -0
  22. package/ansible/ubuntu/wip-guard.py +121 -0
  23. package/ansible/uv.lock +190 -0
  24. package/bootstrap/bootstrap.sh +25 -18
  25. package/bootstrap/doctor.ts +46 -1
  26. package/bootstrap/fleet-manager-run.sh +42 -15
  27. package/bootstrap/fleet-remote-bootstrap.sh +85 -12
  28. package/bootstrap/global-config.ts +64 -6
  29. package/bootstrap/install-launcher.sh +3 -0
  30. package/bootstrap/native-t3.ts +22 -11
  31. package/bootstrap/npm.ts +10 -2
  32. package/bootstrap/release-version.ts +157 -0
  33. package/cliproxy/README.md +3 -3
  34. package/cliproxy/scripts/accounts.py +216 -0
  35. package/cliproxy/scripts/checkpoint.py +29 -1
  36. package/cliproxy/scripts/cli.py +1 -1
  37. package/cliproxy/scripts/clientkeys.py +99 -0
  38. package/cliproxy/scripts/lease.py +80 -0
  39. package/cliproxy/scripts/ops +137 -25
  40. package/cliproxy/scripts/restore-verify +15 -0
  41. package/docs/accounts.md +154 -0
  42. package/docs/consumer-setup.md +408 -0
  43. package/docs/declared.md +91 -0
  44. package/docs/durability.md +26 -0
  45. package/docs/machine-setup.md +400 -0
  46. package/docs/operations.md +276 -0
  47. package/docs/releasing.md +103 -0
  48. package/docs/state.md +254 -0
  49. package/docs/topology.md +69 -0
  50. package/docs/ubuntu-sync.md +357 -0
  51. package/docs/worker-contract.md +67 -0
  52. package/examples/fleet.json +5 -0
  53. package/llms.txt +36 -0
  54. package/mise.lock +29 -0
  55. package/mise.toml +15 -2
  56. package/package.json +11 -2
  57. package/schema/fleet.schema.json +137 -15
  58. package/src/accounts/checks.ts +220 -0
  59. package/src/accounts/context.ts +76 -0
  60. package/src/accounts/propagate.ts +62 -0
  61. package/src/accounts/roster.ts +85 -0
  62. package/src/accounts/selection.ts +67 -0
  63. package/src/accounts/sessions.ts +120 -0
  64. package/src/agents/account-relay.ts +176 -26
  65. package/src/agents/fleet-verify.ts +9 -0
  66. package/src/agents/launch.ts +84 -5
  67. package/src/agents/native-verify.ts +293 -22
  68. package/src/agents/provider-errors.ts +9 -2
  69. package/src/agents/registry.ts +7 -1
  70. package/src/cli/agent-guide.ts +39 -5
  71. package/src/cli/app.ts +11 -4
  72. package/src/cli/cli.ts +7 -0
  73. package/src/cli/command-index.ts +7 -0
  74. package/src/cli/fleet-accounts.ts +253 -0
  75. package/src/cli/fleet-state.ts +499 -0
  76. package/src/cli/fleet-sync-accounts.ts +58 -0
  77. package/src/cli/fleet-sync-checkout.ts +56 -17
  78. package/src/cli/fleet-sync-enrollment.ts +19 -23
  79. package/src/cli/fleet-sync-forward.ts +1 -1
  80. package/src/cli/fleet-sync-manager.ts +84 -12
  81. package/src/cli/fleet-sync-monitoring.ts +5 -5
  82. package/src/cli/fleet-sync-output.ts +7 -3
  83. package/src/cli/fleet-sync-remote.ts +31 -18
  84. package/src/cli/fleet-sync-state.ts +86 -0
  85. package/src/cli/fleet-sync.ts +159 -57
  86. package/src/cli/fleet.ts +4 -2
  87. package/src/cli/managed-health.ts +4 -4
  88. package/src/cli/node-sync.ts +7 -0
  89. package/src/cli/program.ts +20 -4
  90. package/src/cli/project.ts +51 -0
  91. package/src/cli/register-fleet.ts +86 -1
  92. package/src/cli/registry.ts +41 -1
  93. package/src/cli/roster.ts +46 -30
  94. package/src/cli/settings.ts +15 -12
  95. package/src/cli/source-operations.ts +27 -3
  96. package/src/cli/tailnet.ts +2 -2
  97. package/src/definition.ts +116 -4
  98. package/src/diagnostics.ts +1 -1
  99. package/src/fleet-config.ts +105 -19
  100. package/src/ide/definition.ts +86 -0
  101. package/src/ide/ide.ts +43 -0
  102. package/src/ide/index.ts +5 -0
  103. package/src/ide/orchestrate.ts +50 -0
  104. package/src/ide/t3.ts +111 -0
  105. package/src/machine/source.ts +3 -3
  106. package/src/machine/state.ts +15 -27
  107. package/src/manager-sync.ts +9 -18
  108. package/src/monitoring/agent-evidence.ts +146 -0
  109. package/src/monitoring/apply.ts +15 -3
  110. package/src/monitoring/beszel.ts +11 -4
  111. package/src/monitoring/enroll.ts +2 -1
  112. package/src/monitoring/hub-access.ts +4 -3
  113. package/src/monitoring/hub-backup.ts +121 -0
  114. package/src/monitoring/hub-viewer.ts +8 -8
  115. package/src/monitoring/hub.ts +3 -3
  116. package/src/monitoring/manager-hub.ts +30 -14
  117. package/src/node-converge-linux.ts +30 -7
  118. package/src/package-info.ts +45 -0
  119. package/src/platform/ansible.ts +19 -15
  120. package/src/platform/darwin.ts +0 -13
  121. package/src/platform/fallback.ts +0 -8
  122. package/src/platform/linux.ts +0 -10
  123. package/src/platform/resource-verify.ts +8 -4
  124. package/src/platform/t3-policy.ts +33 -6
  125. package/src/platform/t3-service.ts +104 -0
  126. package/src/platform/types.ts +0 -4
  127. package/src/platform/ubuntu-audit.ts +5 -2
  128. package/src/platform/windows.ts +0 -14
  129. package/src/remote-bootstrap.ts +17 -19
  130. package/src/remote-entry.ts +15 -1
  131. package/src/repair-catalog.ts +1 -1
  132. package/src/runner.ts +1 -1
  133. package/src/service-auth.ts +15 -5
  134. package/src/state/bucket.ts +208 -0
  135. package/src/state/check.ts +74 -0
  136. package/src/state/connection.ts +92 -0
  137. package/src/state/enrollment.ts +45 -0
  138. package/src/state/legacy.ts +89 -0
  139. package/src/state/litestream.ts +143 -0
  140. package/src/state/operator.ts +20 -0
  141. package/src/state/paths.ts +40 -0
  142. package/src/state/roster.ts +62 -0
  143. package/src/state/store.ts +169 -0
  144. package/src/state/sync.ts +352 -0
  145. package/src/t3/accounts.ts +7 -1
  146. package/src/t3/models.ts +133 -0
  147. package/src/t3/pairing.ts +31 -0
  148. package/src/t3/profile.ts +203 -0
  149. package/src/t3/version.ts +119 -0
  150. package/src/t3-endpoint.ts +64 -8
  151. package/src/t3-linked-check.ts +24 -2
  152. package/src/toolchain-checks.ts +25 -0
  153. package/src/transport.ts +3 -2
  154. package/src/types.ts +4 -0
  155. package/src/worker-access-checks.ts +56 -3
  156. package/src/worker-host-checks.ts +41 -13
  157. package/src/workstation-checks.ts +1 -1
  158. package/src/index.ts +0 -6
  159. package/src/test-fixtures.ts +0 -36
@@ -0,0 +1,69 @@
1
+ > This document was written for the fleet dev-fleet was extracted from. Paths and
2
+ > commands were updated; some statements still describe that fleet and are being
3
+ > revised. The README is authoritative.
4
+
5
+ # Fleet topology: one manager, many workers
6
+
7
+ The fleet has exactly two roles, declared in
8
+ your project's `fleet.json` (version 3, checked by [the schema](../schema/fleet.schema.json)) and validated by
9
+ [`src/definition.ts`](../src/definition.ts):
10
+
11
+ | Role | How many | Machine | Holds state | Runs |
12
+ | ----------- | ----------- | ------------------------------------- | ----------- | ---------------------------------------------------------------------------------------- |
13
+ | **manager** | exactly one | the human's macOS machine (`manager`) | yes | `fleet-sync`, the T3 desktop that links to every worker, and **every singleton service** |
14
+ | **workers** | any | Linux boxes (`workers`, Ubuntu 26.04) | no | the T3 desktop app (UI plus server), agent tools, the Beszel agent; clients only |
15
+
16
+ ## Manager
17
+
18
+ - A singleton. All control happens from it, by the human: `devfleet fleet-sync`
19
+ converges the manager **locally** first, then each worker over SSH.
20
+ Started on a worker, `fleet-sync` hands the same request to the manager over
21
+ host-key-verified SSH (no agent forwarding, no credentials copied) and prints
22
+ the manager's result; with no SSH route it changes nothing and reports
23
+ `FLEET_NOT_MANAGER` or `FLEET_MANAGER_UNREACHABLE`. The manager runs that
24
+ request from a clean, fleet-owned checkout of the exact revision
25
+ (`bootstrap/fleet-manager-run.sh`, under
26
+ `~/.local/share/personal/fleet/manager-runs`, three most recent kept), so
27
+ fixes in the revision reach the manager without touching the human's
28
+ checkout. The revision must be on the manager or on `origin`.
29
+ - It hosts every singleton service. Today that is the CLI proxy
30
+ (`services.proxy`, [`cliproxy`](../cliproxy/README.md))
31
+ and the Beszel hub (`services.monitoring`,
32
+ [`ansible/monitoring`](../ansible/monitoring/README.md)).
33
+ The manager also runs its own Beszel agent, so it is always a system on that
34
+ hub.
35
+ A new singleton service is declared under `services` and placed on the
36
+ manager; it never names a host.
37
+ - Workers depend on it, so it never sleeps on AC power
38
+ (`pmset` in `ansible/macos/policy.json`, checked as `manager:power`, applied
39
+ once by an administrator with `ansible/macos/manager.yml`; the failed check
40
+ prints the command). While the
41
+ manager is asleep, closed or offline, workers have no CLI proxy and no
42
+ dashboard; nothing fails over, because a singleton never runs twice.
43
+ - Its state (proxy OAuth/runtime state, the hub database) is backed up off the
44
+ machine as each service documents, so a replacement manager restores it.
45
+
46
+ ## Workers
47
+
48
+ - Stateless cattle: turn one off, re-image it or add another at will. A worker
49
+ is declared in `workers`, enrolled once (SSH trust, sudo, its own Vault
50
+ token), and then fully rebuilt by `fleet-sync` from Git and Vault.
51
+ - Workers never host a singleton service. The proxy reports any local install
52
+ on a worker as a duplicate owner, and the hub refuses to install anywhere but
53
+ the manager.
54
+ - Anything on a worker is disposable: T3 sessions and worktrees, caches,
55
+ checkouts. Durable work lives in Git remotes; credentials come from Vault.
56
+ Work in progress in agent worktrees is pushed to a recovery ref every 15
57
+ minutes; [durability](durability.md) says what a lost machine takes with it.
58
+ - The first declared worker is the rollout canary for a revision new to the
59
+ fleet: a failed canary stops promotion to the other workers. Repeating a
60
+ revision that already converged everywhere runs all workers at once.
61
+
62
+ ## Changing the topology
63
+
64
+ - **Add or replace a worker:** add it to `workers`, enroll it with
65
+ `fleet-sync`, done. Remove it by deleting the entry.
66
+ - **Replace the manager:** edit `manager`, then move each singleton service with
67
+ its documented fence-and-restore runbook (never two copies at once):
68
+ [CLI proxy](../cliproxy/docs/migration.md),
69
+ [Beszel hub](../ansible/monitoring/README.md).
@@ -0,0 +1,357 @@
1
+ > This document was written for the fleet dev-fleet was extracted from. Paths and
2
+ > commands were updated; some statements still describe that fleet and are being
3
+ > revised. The README is authoritative.
4
+
5
+ # Developer fleet: one manager (macOS) and stateless workers (Ubuntu)
6
+
7
+ ## Release status
8
+
9
+ What is proven, and where:
10
+
11
+ | Evidence | Where |
12
+ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | A bare Ubuntu 26.04 machine bootstrapped over real SSH, then repeated | `features/fresh-box/fresh-box.feature`, on every pull request (amd64 and arm64) |
14
+ | One run over several machines, one versioned report, no partial receipt | `features/fresh-box/fleet.feature` |
15
+ | Interrupted checkout, package installation and toolchain setup resumed | `features/fresh-box/fleet.feature` |
16
+ | Overlapping runs, a dropped connection, a held APT lock, no DNS, no disk | `features/fresh-box/fleet.feature` |
17
+ | A failed canary stops promotion; a changed host key is never touched | `features/fresh-box/fleet.feature`, `features/orchestration/fleet-sync.feature` |
18
+ | Offline, unapproved and replaced devices | `features/orchestration/fleet-sync.feature` (the tailnet is injected) |
19
+ | T3 route, origin policy, pairing redaction, service environment | `features/core/t3-access.feature`, `features/agents/native-agents.feature` |
20
+ | Vault renewal and policy outcomes, provider incompatibilities | `features/core/worker-checks.feature`, `features/agents/provider-failures.feature` |
21
+ | Beszel agent privileges, evidence, backups and the stock API | `features/monitoring/`, and `ansible/monitoring/tests/upstream.py` against the pinned binaries (backup restored into a separate instance) |
22
+
23
+ A container has no systemd, so the fresh-box runs prove bootstrap,
24
+ interruption and fault handling, not a booted desktop session. What only a
25
+ live fleet can show is checked by `fleet-sync` itself on every run and stays a
26
+ failed or skipped required check until it is true; nothing advances a success
27
+ receipt before then:
28
+
29
+ - the private T3 route reached from a separate tailnet client, and a paired
30
+ conversation with a tool action (`t3:manager-endpoint`, `t3:paired-access`,
31
+ `t3:end-to-end`);
32
+ - each provider route signed in on its real account, under the T3 service's
33
+ environment (`provider:*`, `agent:service-environment`);
34
+ - unattended Vault renewal under the fleet's real policy
35
+ (`worker:vault-renewal`, `worker:proxy-authentication`);
36
+ - private Beszel metrics from every worker and a scheduled off-host hub backup
37
+ (`beszel:*`, `beszel:backup`);
38
+ - reboot, logout and crash recovery of a real GNOME session
39
+ (`worker:t3-recovery`, `ubuntu:boot-policy`).
40
+
41
+ Reproduce the disposable runs with
42
+ `DEVFLEET_TEST_FRESH_BOX=1 bun test features/fresh-box`; the VM procedure for
43
+ a full systemd guest is in [`ansible/ubuntu/tests/README.md`](../ansible/ubuntu/tests/README.md).
44
+
45
+ ## One command, from the manager
46
+
47
+ ```sh
48
+ devfleet fleet-sync
49
+ ```
50
+
51
+ `devfleet fleet-sync` executes the manager-push implementation:
52
+
53
+ ```text
54
+ resolve enrolled fleet
55
+ → inspect Tailscale and verified SSH
56
+ → bootstrap missing remote prerequisites
57
+ → apply one approved exact revision
58
+ → establish fresh SSH connections and check readiness
59
+ → report every target, including offline and blocked nodes
60
+ ```
61
+
62
+ Bare invocation selects every canonical Linux and macOS node. It does not mean
63
+ “this computer,” and the manager is not implicitly a deployment target. Its own
64
+ node is targeted only if explicitly declared in Fleet membership. Ubuntu 26.04 is
65
+ the accepted worker platform; a node declared `"os": "macOS"` runs the same phases
66
+ best effort through `ansible/macos` (see the platform matrix below).
67
+ Windows machines stay declared for diagnostics and are never converged.
68
+ `--node ID` filters troubleshooting/rollout. `--all` is an explicit spelling of the
69
+ default. Neither flag authorizes managing arbitrary Tailscale peers.
70
+
71
+ Roster-level workspace convergence lives in `devfleet fleet-repair --repair workspace`, which previews until `--apply --yes`.
72
+ The new manager report is version **2**, deliberately replacing the unshipped
73
+ local-first fleet-sync version 1 contract.
74
+
75
+ ## Prerequisites and first enrollment
76
+
77
+ The manager needs Node/npm (Node `^22.13.0 || >=23.5.0`), an OpenSSH client,
78
+ a working Tailscale daemon, and Git for approved source preparation. npx cannot
79
+ execute without Node/npm. Existing shared setup remains
80
+ [`bootstrap/bootstrap.sh`](../bootstrap/bootstrap.sh); run that tracked
81
+ bootstrap on a manager without developer tools, then invoke the same command.
82
+ Do not install an unrelated fleet installer. The npm command’s help, catalog and
83
+ structured missing-enrollment result require neither Bun nor a checkout.
84
+
85
+ On first interactive use the command resolves only canonical machines through
86
+ existing SSH aliases, tests strict SSH access, and offers one enrollment
87
+ confirmation for the verified connections. The apply approval binds a fingerprint
88
+ of the selected revision, policy and connection records; changed bindings fail
89
+ with `FLEET_PLAN_STALE` before source preparation or remote mutation. It stores private overrides mode 0600. Unknown host keys are never accepted automatically. Compare fingerprints
90
+ out of band before enrolling trust. Unreachable machines remain listed; first
91
+ reachability requires an existing trusted LAN SSH route or physical setup of
92
+ SSH/Tailscale. No software on the manager can bootstrap a disconnected box.
93
+
94
+ A previously configured manager uses saved connection bindings and its approved
95
+ revision. A changed Tailscale device ID is a replacement gate, even if the old
96
+ DNS name and SSH alias still resolve. Enrollment never copies host keys,
97
+ Tailscale state, provider OAuth, or another machine’s T3 environment.
98
+
99
+ Remote developer-tool prerequisites are not required: the initial SSH probe uses
100
+ POSIX facilities. Approved application sends a Git bundle of that one commit
101
+ (its files, no history; the worker checkout is shallow) and the bootstrap
102
+ script extracted from that exact commit over verified SSH. A worker that
103
+ already has the revision receives nothing, and the toolchain setup is skipped
104
+ while its inputs (`mise.toml`, the locks, `scripts/dev`) are unchanged since a
105
+ successful setup within the last day. It installs missing
106
+ Ubuntu bootstrap packages through signed APT, then uses the existing mise and
107
+ frozen dependency installer. Bootstrap APT packages use the installed Ubuntu
108
+ archive configuration, because a new machine cannot reach the HTTPS snapshot
109
+ before it has CA certificates; the baseline then pins each of them, Ansible
110
+ included, to the exact version in `ansible/ubuntu/policy.json`. The interactive manager flow can request native sudo authorization over a verified SSH terminal after the consolidated apply confirmation. It validates and installs the designated-user sudo policy; passwords are never captured or stored. Missing administrator permission, an unmanaged conflicting sudoers fragment, or unavailable terminal remains action-required. JSON and unattended runs never prompt and require existing noninteractive sudo.
111
+
112
+ ## Policy, ownership and private state
113
+
114
+ - `fleet.json` and `definition.schema.json`: portable machine
115
+ IDs, aliases, OS, singleton roles and Ubuntu release policy.
116
+ - Private manager overrides: `--config PATH`, `PERSONAL_FLEET_CONFIG`, or the
117
+ roster in the [fleet state](state.md), using the existing roster schema.
118
+ Preserve workspace/runtime paths, capabilities and explicit `tailscaleId`
119
+ bindings.
120
+ - Workstation network consumers derive their inventory from Fleet. The old file
121
+ is retained only as `packages/fleet/migrations/workstation-network-v1.json` for
122
+ lossless identity-path migration; conflicts fail rather than silently winning.
123
+ - Manager approval: the `enrollment` document in the [fleet state](state.md).
124
+ Manager deployment lock: `~/.local/share/personal/fleet/manager/sync.lock`.
125
+ - Remote source: `~/.local/share/personal/fleet/revisions/FULL_SHA`.
126
+ Remote lock file: `~/.local/share/personal/fleet/remote-operation.lock`, held
127
+ with `flock` on Linux and the same descriptor through Python `fcntl` on macOS.
128
+ Never repurpose a development checkout or reset dirty worktrees.
129
+ - Fleet owns `ubuntu/`, `macos/` and `monitoring/`; mise manifests, locks and
130
+ `scripts/dev` own developer tools. CLIProxy deployment/auth/recovery stays with
131
+ `cliproxy`. Cloud resources, DNS, Vault policy and tailnet access
132
+ policy stay with OpenTofu and are not modified by this command.
133
+
134
+ The repository is public. Existing portable host metadata was retained during
135
+ migration; do not publish device IDs, private keys, manager-specific secrets,
136
+ credentials, backups or runtime databases. Intentional persistent policy changes
137
+ must be reviewed in Git. Emergency service restarts do not authorize permanent
138
+ untracked configuration edits.
139
+
140
+ ## Preview, approval and results
141
+
142
+ ```sh
143
+ devfleet fleet-sync --dry-run --json
144
+ devfleet fleet-sync --revision FULL_40_CHARACTER_SHA --dry-run --json
145
+ devfleet fleet-sync --revision FULL_40_CHARACTER_SHA --non-interactive --json
146
+ ```
147
+
148
+ Preview can query Tailscale and establish read-only SSH sessions. It never enrolls,
149
+ authenticates to a provider, installs, creates pairing material, schedules work or
150
+ writes configuration. JSON/noninteractive modes never prompt or open a browser.
151
+ `fleet-sync` applies on invocation with no confirmation prompt; use `--dry-run`
152
+ to preview. `--apply --yes` is still accepted for existing scripts. Initial
153
+ trust/enrollment of new connections still asks separately.
154
+
155
+ Select one full lowercase commit SHA. Approval is saved on the manager and
156
+ retained throughout the run. New package versions do not silently replace saved
157
+ approval. Promote a reviewed release with the explicit `--revision SHA` invocation.
158
+ Use `devfleet@VERSION fleet-sync ...` for automation; unversioned npx
159
+ is convenience, not a reproducibility guarantee. Dirty review artifacts have no
160
+ automatically approved source SHA.
161
+
162
+ Version 2 JSON contains `execution: manager-push`, desired revision, per-node
163
+ reachability, applied revision (null when not verified), checks, durations,
164
+ candidate count and the private dashboard URL when the enrolled hub resolves.
165
+ An absent applied revision is not “unchanged.” Every required skip/failure keeps
166
+ readiness incomplete. Exit 0 means the requested read-only inspection or required
167
+ checks passed; 1 means incomplete/action-required/failed; 2 means invalid usage or
168
+ refused configuration. JSON is one document on stdout, with no child process logs.
169
+
170
+ These are distinct facts: configuration applied; local services healthy; fresh
171
+ manager connection verified; authenticated IDE/T3 tool execution verified.
172
+ Neither an HTTP 200 nor a successful installer establishes the last fact.
173
+
174
+ ## Machine policy by platform
175
+
176
+ Every machine-level setting is declared in Ansible using only `ansible.builtin`
177
+ modules, applied by `node-converge` after source bootstrap, and then measured
178
+ independently. Bespoke TypeScript remains only where a native CLI or API has no
179
+ declarative owner: T3 service lifecycle, Beszel enrollment tokens, provider
180
+ verification and the CLIProxy singleton.
181
+
182
+ | Policy | Workers (Ubuntu 26.04, stateless) | Manager (macOS, singleton) |
183
+ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
184
+ | Never sleep | systemd sleep targets masked; logind lid/idle policy; GNOME power plugin set to `nothing` | never sleeps on AC power (`pmset -c sleep 0`, `macos/manager.yml`, verified as `manager:power`); battery sleep is left to the human |
185
+ | No automatic logout or lock | GNOME `lock-enabled false`, `idle-delay 0`; designated-user lingering | not managed (the human uses it) |
186
+ | Always-on services | drop-ins `Restart=always`, `StartLimitIntervalSec=0` for ssh, tailscaled, docker, beszel-agent | singleton services under launchd and Docker Desktop (see the last row) |
187
+ | T3 health and connection | `fleet-sync` and `fleet-status --workers` require `worker:t3-health` (local API answers), `t3:manager-endpoint` (manager HTTPS over Tailscale) and `t3:paired-access` (manager T3 link reachable) | the human's T3 desktop holds the links to every worker (`t3:paired-access`) |
188
+ | Memory safety | `systemd-oomd` (swap 90%, pressure 60%); `user.slice` `MemoryMax=90%`; each `t3code-*.scope` agent `MemoryMax=30%`, `OOMPolicy=continue`; swap file when none | not managed |
189
+ | Disk safety | `personal-fleet-disk-guard` kills only the top user-cgroup writer below 3 GiB free, never deletes files; hourly `personal-fleet-disk-gc.timer` deletes git-ignored build output (`node_modules`, `.cache`, `dist`, ...) in checkouts idle 7d (12h under 15 GiB free) with no process inside, plus unused Docker images and build cache, never source, worktrees or volumes; journal 1G; `/tmp` 25% of RAM, aged 2d hourly, a measured write probe (`TMP_QUOTA_EXHAUSTED`), and T3 agents use disk-backed `/var/tmp` (`TMPDIR`, aged 14d); Docker log rotation | not managed |
190
+ | Tailscale | pinned APT package, `tailscaled` enabled, native login required | Tailscale app, human login |
191
+ | Dashboard agent | pinned native binary, hardened `beszel-agent` systemd unit as user `beszel` | not installed |
192
+ | Credential renewal | user timer `personal-worker-auth` renews the node's own Vault token and GitHub login every 10min | the human's own Vault and GitHub sessions |
193
+ | Noninteractive sudo | validated `/etc/sudoers.d/personal-fleet-USER` | never; the human runs `manager.yml` once with `-K` |
194
+ | T3 worker app | pinned desktop `.deb` is the only T3 process (UI plus embedded server); GDM auto-login, user unit `t3code-desktop.service` bound to the graphical session with `Restart=always`; `personal-worker-recovery` runs a copy of `t3-health.sh` installed in `/usr/local/lib/personal-fleet` every minute: it stops and disables a still-running headless `t3code.service`, reports `T3_NO_GRAPHICAL_SESSION` and restarts the display manager (three times at most) when there is no session, starts a stopped app after `reset-failed`, restarts one whose local API missed three checks, and restarts one on another release than the installed one or with a deferred policy restart once it runs no agent turn; administrator policy | not a worker |
195
+ | T3 providers | the same managed instances (`fleet/agents/t3.json`) through locked T3 administrator policy | reconciled into the human's T3 settings by `fleet-sync` (`t3:providers`) with the ownership-safe `t3-apply` engine, so every T3 offers the identical set; unrelated and unmanaged instances are preserved. `fleet-sync` also applies the same proxy agent profiles (`configuration:apply`) and verifies the shared Vault gateway credential end to end with `provider:*:inference`, exactly as on workers |
196
+ | Singleton services | never; clients of the manager's services only | every singleton runs here: CLI proxy (`cliproxy`), Beszel hub (`workstation/monitoring`) |
197
+
198
+ Credential sync is deliberately indirect: no secret is copied between machines.
199
+ OpenTofu issues each enrolled worker its own renewable periodic token
200
+ (`vault.tokens` entries `fleet-worker-<node>`, policy `personal-read`, sunk into
201
+ `secret/personal/prd` as `FLEET_WORKER_TOKEN_<NODE>`). `devfleet fleet-sync`
202
+ delivers each node's token automatically over SSH stdin (never as an argument)
203
+ whenever that node has no working Vault session. Interactive runs first log the
204
+ manager in (`vault login -method=userpass`, password typed on your terminal)
205
+ and fall back to the same password login on a node whose token is not issued.
206
+ Without either, the node reports `VAULT_LOGIN_REQUIRED` and its T3 proxy
207
+ providers stay unavailable. The manual equivalent of the automatic delivery:
208
+
209
+ ```sh
210
+ vault kv get -field=FLEET_WORKER_TOKEN_WORKER_ONE secret/personal/prd \
211
+ | ssh worker-one 'vault login -address=https://vault.example.com -no-print -'
212
+ ```
213
+
214
+ From then on the node's `personal-worker-auth` timer renews that token and its
215
+ GitHub login, and renders provider configuration from Vault through Workstation.
216
+
217
+ ## Ubuntu and remote-work safety
218
+
219
+ The declared common baseline is Ubuntu **26.04 LTS**, amd64/arm64. Other releases
220
+ receive `UBUNTU_BASELINE_MISMATCH`; there is no automatic release upgrade or
221
+ package downgrade. Fleet audits release, packages, service state and dpkg
222
+ consistency, declares snapshot sources for managed transactions, and pins its
223
+ selected APT packages. Unrelated applications and package sources are preserved.
224
+
225
+ Power policy masks sleep/hibernate targets and declares logind sleep/lid defaults.
226
+ GNOME lock/idle changes apply to the designated user only. Effective running
227
+ session/logind behavior still needs a maintenance/reboot acceptance check; writing
228
+ a file does not prove logout/reboot survival. The sudoers fragment is validated
229
+ with visudo before installation; this user and its agents can administer the
230
+ machine. SSH authentication remains enabled.
231
+
232
+ SSH forwarding changes validate the complete configuration before reload and
233
+ restore the previous fragment on validation failure. Existing listeners,
234
+ authentication and firewall rules are preserved. Existing socket activation is preserved for forwarding-only changes; listener
235
+ migrations and unmanaged fragments require review. The manager makes a **new**
236
+ strict SSH connection after apply. Private-listener/firewall acceptance and
237
+ failure-after-reload recovery still require disposable integration testing.
238
+
239
+ The manager is the only configuration coordinator. No per-node pull timer is
240
+ installed. Manager schedule controls use the existing platform adapter:
241
+
242
+ ```sh
243
+ devfleet fleet-sync --schedule status --json
244
+ devfleet fleet-sync --schedule enable --revision FULL_40_CHARACTER_SHA --apply --yes
245
+ devfleet fleet-sync --schedule pause --apply --yes
246
+ devfleet fleet-sync --schedule resume --revision FULL_40_CHARACTER_SHA --apply --yes
247
+ devfleet fleet-sync --schedule disable --apply --yes
248
+ ```
249
+
250
+ These are manager setup/maintenance operations, not per-node commands. Enable
251
+ and resume install the exact published CLI version in the managed manager
252
+ runtime and schedule the same whole-fleet command every 30 minutes at the explicit
253
+ approved SHA. They refuse dirty artifacts. Pause/disable remove only the manager
254
+ schedule. Re-enable with the new approved SHA when promoting a release; the timer
255
+ does not resolve `latest`. Linux user scheduling requires lingering; macOS uses
256
+ the existing LaunchAgent and requires a user session. Unattended manager boot
257
+ acceptance and log-retention configuration remain gates. No node pull scheduler
258
+ is installed or necessary.
259
+
260
+ Sync never reboots a box and never ends a running agent turn. A worker's T3
261
+ that must restart for a new provider policy or release is restarted only while
262
+ it runs no agent turn; otherwise the restart is pending
263
+ (`T3_POLICY_RESTART_PENDING`, `T3_RESTART_PENDING`) and the recovery timer
264
+ applies it at the first idle check. Explicit
265
+ maintenance reboot/reconnect orchestration remains an acceptance gate. Kernel,
266
+ driver, firmware, hardware and power failures are outside any “never freezes”
267
+ promise. Inspect disk/inodes, swap/memory pressure, `systemctl --failed`, and prior
268
+ boot journal evidence locally; sanitize it before sharing. Do not enable automatic
269
+ reboot-on-pressure or kill active work to obtain a green report.
270
+
271
+ T3 0.1.7's supported administrator policy at `/etc/t3code/policy.json`
272
+ disables independent updates and declares the existing managed Codex instance
273
+ plus an OpenCode instance for all five gateway models. Fleet reuses the
274
+ existing `agents/t3.json` renderer; it does not edit the live settings database.
275
+ Native `t3 policy check` validates before installation, and `t3 policy show`
276
+ compares staged policy with the running server. A staged policy the running
277
+ server has not loaded restarts the T3 desktop app in the same sync;
278
+ `T3_POLICY_RESTART_FAILED` means it still did not load afterwards.
279
+ Unrelated administrator policy is a conflict requiring a reviewed merge.
280
+
281
+ ## Central provider access
282
+
283
+ The `https://cli-proxy.example.com` gateway is a singleton service, so it
284
+ runs on the manager (`definition.json.services.proxy`). Workers consume that
285
+ gateway; no worker gets a separate OAuth-owning proxy. Keep the existing
286
+ key/endpoint contract, encrypted recovery and runtime authentication. Moving it
287
+ requires fencing the old host before restoring state
288
+ ([migration](../cliproxy/docs/migration.md)). Fleet never copies identities.
289
+
290
+ ### Agents on every node
291
+
292
+ Every node (manager and workers) offers the same agents in T3, at the same
293
+ versions (pinned in `mise.toml`/`mise.lock`, including the vendor CLIs
294
+ `http:grok` and `http:antigravity`), signed in to the same multiplexed proxy
295
+ accounts. T3 launches most through a tracked launcher in
296
+ `agents/` that injects the shared proxy key from Vault at
297
+ launch; nothing is copied between nodes.
298
+
299
+ | T3 provider | T3 driver | Route |
300
+ | ----------------------------- | ------------- | --------------------------------------------------------------- |
301
+ | `Claude <account email>` | `claudeAgent` | One per Claude account on the proxy (`claude-proxy`) |
302
+ | `Codex <account email>` | `codex` | One per Codex account on the proxy (`codex-proxy.ts`) |
303
+ | `AntiGravity <account email>` | `opencode` | One per Antigravity account, via OpenCode (`opencode-proxy.ts`) |
304
+ | `Grok CLI Proxy` | `grok` | Pooled: Grok Build custom model, `env_key` (`grok-proxy.ts`) |
305
+ | `OpenCode CLI Proxy` | `opencode` | Pooled accounts and Muse via OpenCode (`opencode-proxy.ts`) |
306
+
307
+ **Ansible applies, TypeScript measures.** On a worker the T3 lifecycle is
308
+ declared in playbooks: `t3-policy.yml` stages the provider policy and restarts
309
+ a running T3 that has not loaded it once it runs no agent turn, `t3-desktop.yml` installs the app, retires
310
+ the former headless server and starts it, and `t3-route.yml` publishes the
311
+ private tailnet route when port 443 has no owner. The TypeScript around them
312
+ only refuses conflicts and reports what it observes afterwards. Each sync also
313
+ prunes old revision checkouts on the worker, keeping the new one, the one T3
314
+ launches from and the two most recent others.
315
+
316
+ **Every sync applies everything, except a T3 restart that would end a turn.**
317
+ A worker whose T3 has not loaded the staged provider policy, or runs an older
318
+ release than the one installed, is restarted only while it runs no agent turn;
319
+ otherwise sync reports the pending restart and the recovery timer applies it
320
+ at the first idle check, failing the check after
321
+ `t3RestartMaxDeferralHours` (`ansible/ubuntu/policy.json`). A pending proxy
322
+ update is installed even when it recreates the gateway. The manager finishes before the workers
323
+ start, so they are verified against the updated proxy. `--dry-run` previews.
324
+
325
+ **Accounts are never listed in this repository.** The proxy's credential files
326
+ are the only source of truth: every sync gives each Claude, Codex and
327
+ Antigravity credential the model prefix `<type>-<email>`
328
+ (`cliproxy/scripts/accounts.py`, applied through the proxy's own
329
+ API), so the proxy's model list names every signed-in account. Each node reads
330
+ that list with its own proxy key and offers one provider per account, so all
331
+ machines list the same providers. Signing an account in or out on the proxy
332
+ (`devfleet cliproxy add-account PROVIDER`, or the dashboard) changes T3 on the
333
+ next `fleet-sync`; an unreachable proxy fails the check and changes nothing.
334
+
335
+ A provider is pinned to its account: T3 sets `WS_AGENT_ACCOUNT` and the
336
+ launcher routes every request to `<type>-<email>/<model>`. Claude and Codex
337
+ send whichever model is picked, so their launcher runs a loopback relay
338
+ (`account-relay.ts`, alive only while that agent runs) that adds the prefix;
339
+ OpenCode is configured with exactly the account's models. Requests without a
340
+ prefix (Grok, the pooled OpenCode provider, health checks) still use the whole
341
+ pool, fill-first. T3's own Antigravity runtime cannot carry an account, so
342
+ Antigravity accounts run on the OpenCode harness. Cursor is not part of the
343
+ fleet (its backend cannot be proxied). Native Muse Code is not used: it only
344
+ talks to Meta's own catalog endpoint, so Muse models run through OpenCode.
345
+
346
+ ### Checkout project actions
347
+
348
+ `bash bootstrap/project-action.sh fleet-sync` warms the shared toolchain
349
+ and passes the current checkout HEAD as the exact revision to the existing approval
350
+ flow. Extra arguments such as `--dry-run` are forwarded. The manager can bundle
351
+ committed objects from this canonical local repository, including unpublished
352
+ commits; dirty working files are never transferred. Review and commit desired
353
+ changes before approving deployment. The Ubuntu baseline now owns the native
354
+ Docker engine, Compose and Buildx on every enrolled Linux workstation, preserving
355
+ third-party engine ownership conflicts and existing running workloads. Workstation
356
+ launcher/link convergence is part of the node apply, with credentials still checked
357
+ separately. A dependency receipt proves frozen installation inputs only.
@@ -0,0 +1,67 @@
1
+ > This document was written for the fleet dev-fleet was extracted from. Paths and
2
+ > commands were updated; some statements still describe that fleet and are being
3
+ > revised. The README is authoritative.
4
+
5
+ # Declarative remote workers
6
+
7
+ The public `devfleet` CLI owns two primary operations:
8
+
9
+ ```sh
10
+ devfleet fleet-sync
11
+ devfleet fleet-status --workers
12
+ ```
13
+
14
+ The manager needs Node/npm, Git, SSH, Tailscale, and its own authorized T3 link.
15
+ Initially each Ubuntu 26.04 worker (or best-effort macOS 14+ node) needs a
16
+ designated non-root account, trusted SSH access and administrator authorization. Standard Ubuntu cloud-image users
17
+ already commonly have noninteractive sudo; otherwise establish the repository's
18
+ `authorize-sudo.sh` policy once during administrator enrollment. Routine commands
19
+ never ask for or store an SSH/sudo password. Unknown host keys, a different device
20
+ identity, or missing privilege stop with a named action rather than bypassing trust.
21
+
22
+ Convergence binds one reviewed revision and worker membership. It transfers only
23
+ committed Git objects over SSH, bootstraps missing prerequisites, installs the
24
+ locked minimal tool profile and frozen dependencies, and applies Fleet's
25
+ Ansible policy. Developer checkouts and dirty work stay intact. A published CLI
26
+ uses its source provenance; a source project action explicitly selects checkout
27
+ HEAD. Private rosters contain connection paths and identities, not weaker policy.
28
+
29
+ | Requirement | Declaration and enforcement |
30
+ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | One consistent dependency set | `mise.toml` worker profile, `mise.lock`, Ubuntu snapshot/package pins; exact versions and deployed input hashes checked live |
32
+ | Common service access | Canonical required capabilities; declared GitHub account, worker's own renewable Vault session, authenticated singleton gateway catalog |
33
+ | One gateway host | The CLI proxy runs only on the manager; ownership/audit/recovery code rejects any install on a worker and missing singleton state |
34
+ | T3 connected and persistent | Pinned desktop app as the only T3 process, auto-login session unit enabled/active with restart policy, bounded recovery timer, private endpoint, manager link matched to SSH-observed environment identity |
35
+ | No idle sleep and session survival | systemd sleep masks, logind policy, designated-user lingering; running policy and boot timing checked, not inferred from files alone |
36
+ | Recovery after power loss | Required boot services and recovery timers; recognized firmware restore-after-AC settings inspected when exposed; macOS `autorestart` where supported |
37
+ | Always-on services | `Restart=always` drop-ins for ssh, tailscaled, docker and beszel-agent (launchd `KeepAlive` on macOS); health requires the running unit to report that policy |
38
+ | Central dashboard | Pinned Beszel hub on the manager, every worker enrolled with its own token; health verifies hub reachability, registration, connection and fresh samples |
39
+ | No routine password prompts | Verified BatchMode SSH, `sudo -n`, source-owned NOPASSWD policy; missing initial authorization is a failure |
40
+ | Keep service authorization alive | Native service credentials stay per-node; Vault self-renewal timer and GitHub recovery from authorized Vault credentials; expiring T3 links are reported before expiry |
41
+
42
+ Health does not mutate configuration, renew tokens, log in, install software, or
43
+ make billable inference requests. It audits the immutable desired worker deployment,
44
+ not an unrelated developer branch. Every declared worker must be enrolled and
45
+ reachable. Extra service hosts are listed as outside scope. Required failures,
46
+ warnings and unverified checks prevent a ready result; explicitly non-required
47
+ checks remain visible without inventing a pass.
48
+
49
+ The desired fleet contract is stronger than today's live acceptance. A real
50
+ power-loss test and firmware/VM-host automatic restart cannot be proved merely by
51
+ starting an OS service. Unsupported firmware inspection remains unverified. A
52
+ still-running headless `t3code.service` needs a deliberate migration to the
53
+ desktop app; convergence preserves active work instead of killing it. External providers can
54
+ revoke authorization; renewal is not a substitute for an initially granted,
55
+ renewable node identity. Required expired/missing credentials remain failures,
56
+ never interactive password prompts or successful receipts.
57
+
58
+ Cloud resources, Vault roles/policies and secret values remain exclusively owned
59
+ by the OpenTofu roots. Worker configuration belongs to Fleet's Ansible policy and
60
+ mise. Runtime logins/renewal do not provision cloud resources. Existing SSH keys,
61
+ T3 pairing data, provider identities and the gateway's encrypted recovery state
62
+ are never cloned from another node.
63
+
64
+ The normal manager apply flow reports configuration application separately
65
+ from readiness and advances the applied-revision receipt only after required
66
+ acceptance succeeds. Read the remaining release gates in [ubuntu-sync.md](ubuntu-sync.md)
67
+ before treating any release as complete fleet certification.
@@ -4,6 +4,11 @@
4
4
  "source": {
5
5
  "repository": "https://github.com/crvouga/dev-fleet.git"
6
6
  },
7
+ "state": {
8
+ "bucket": "my-fleet-state",
9
+ "endpoint": "https://s3.example.com",
10
+ "region": "auto"
11
+ },
7
12
  "secrets": {
8
13
  "provider": "vault",
9
14
  "address": "https://vault.example.com",
package/llms.txt ADDED
@@ -0,0 +1,36 @@
1
+ # dev-fleet
2
+
3
+ > dev-fleet keeps a set of development machines in sync: one macOS **manager** and any number of Ubuntu 26.04 **workers** reached over SSH and Tailscale. It is used as the npm package `@crvouga/dev-fleet` (CLI `devfleet`) from a project of your own that holds `fleet.json` and pins the package version; one command, `devfleet fleet-sync`, converges every machine to the commit that version was published from. The fleet state lives in an S3-compatible bucket you own.
4
+
5
+ Rules for an agent working in a project that uses dev-fleet:
6
+
7
+ - Never clone, vendor or patch dev-fleet, and never edit what it installs on a machine. A defect or missing feature is a public issue at https://github.com/crvouga/dev-fleet/issues (no credentials, hostnames, accounts, domains or `fleet.json` contents).
8
+ - Never put a secret in an argument, `fleet.json`, a commit or a log. Bucket credentials come from `DEVFLEET_BUCKET_ACCESS_KEY_ID` and `DEVFLEET_BUCKET_SECRET_ACCESS_KEY`; other secrets from the provider `fleet.json` names (`vault`, `sops`, `file` or `env`).
9
+ - `npx devfleet commands --json` prints the machine-readable contract: commands, exit codes, safety rules, how to report a bug. Read it first; pass `--json --non-interactive` to every diagnostic.
10
+ - `--dry-run`, `fleet-status` and `fleet-state` (without flags) are read-only. `fleet-sync` changes every machine, and its first run must be done by a human at a terminal (host enrollment, sudo, Vault).
11
+ - The installed package carries these documents under `node_modules/@crvouga/dev-fleet/`.
12
+
13
+ ## Set up a fleet
14
+
15
+ - [Set up a fleet from your own project](https://github.com/crvouga/dev-fleet/blob/main/docs/consumer-setup.md): the one-pass procedure for an agent: prerequisites and how to verify them, what to ask the human, every `fleet.json` field, secrets, bucket credentials, the first sync, verification and failure codes
16
+ - [README](https://github.com/crvouga/dev-fleet/blob/main/README.md): what dev-fleet assumes, quickstart, versions, adding a machine, known limits
17
+ - [Example fleet.json](https://github.com/crvouga/dev-fleet/blob/main/examples/fleet.json): a complete configuration with placeholders (`npx devfleet fleet-state --example` prints it)
18
+ - [fleet.json schema](https://github.com/crvouga/dev-fleet/blob/main/schema/fleet.schema.json): JSON Schema for the fleet configuration
19
+
20
+ ## Operate it
21
+
22
+ - [Fleet state](https://github.com/crvouga/dev-fleet/blob/main/docs/state.md): the bucket, replication, replacing the manager, what the bucket does not hold yet
23
+ - [Topology](https://github.com/crvouga/dev-fleet/blob/main/docs/topology.md): the manager and worker roles and the canary
24
+ - [What is declared, and where](https://github.com/crvouga/dev-fleet/blob/main/docs/declared.md): what is pinned on a machine and what is not
25
+ - [Provider accounts](https://github.com/crvouga/dev-fleet/blob/main/docs/accounts.md): the account roster, its states and checks, adding or removing an account, propagation to every node, account selection
26
+ - [CLI proxy](https://github.com/crvouga/dev-fleet/blob/main/cliproxy/README.md): the CLIProxyAPI service on the manager that every coding agent uses
27
+ - [Monitoring](https://github.com/crvouga/dev-fleet/blob/main/ansible/monitoring/README.md): the Beszel hub on the manager and the agent on every machine
28
+
29
+ ## Optional
30
+
31
+ - [Ubuntu fleet-sync reference](https://github.com/crvouga/dev-fleet/blob/main/docs/ubuntu-sync.md): enrollment, approval, machine policy by platform, remote-work safety
32
+ - [Worker contract](https://github.com/crvouga/dev-fleet/blob/main/docs/worker-contract.md): what a worker guarantees and how readiness is scoped
33
+ - [Operations](https://github.com/crvouga/dev-fleet/blob/main/docs/operations.md): roster, diagnostics and repairs
34
+ - [Coding agents on every node](https://github.com/crvouga/dev-fleet/blob/main/agents/setup.md): agent launchers and the T3 providers
35
+ - [Releasing](https://github.com/crvouga/dev-fleet/blob/main/docs/releasing.md): how a version is published from one commit (for dev-fleet developers)
36
+ - [Features](https://github.com/crvouga/dev-fleet/blob/main/features/README.md): the behaviour specified in Gherkin (for dev-fleet developers)
package/mise.lock CHANGED
@@ -924,6 +924,35 @@ url = "https://dl.k8s.io/v1.37.2/bin/darwin/amd64/kubectl"
924
924
  checksum = "sha256:1691938f7de0e931969f9968c3601cf32d6b0d573573afb4ce471f3d28f51984"
925
925
  url = "https://dl.k8s.io/v1.37.2/bin/windows/amd64/kubectl.exe"
926
926
 
927
+ [[tools.litestream]]
928
+ version = "0.5.18"
929
+ backend = "aqua:benbjohnson/litestream"
930
+ specifiers = ["0.5.18"]
931
+
932
+ [tools.litestream."platforms.linux-arm64"]
933
+ checksum = "sha256:922a09e6a7ac67f2095571894dc6382e94e1dd4cdd9674883ddf1a8e74ab982d"
934
+ checksum_source = "mise-versions"
935
+ url = "https://github.com/benbjohnson/litestream/releases/download/v0.5.18/litestream-0.5.18-linux-arm64.tar.gz"
936
+ url_api = "https://api.github.com/repos/benbjohnson/litestream/releases/assets/706493661"
937
+
938
+ [tools.litestream."platforms.linux-x64"]
939
+ checksum = "sha256:d3687b9fd0f2e9426bbd3da828429ff3f1dfc5c9b74d54fdf028e4ee510e7dea"
940
+ checksum_source = "mise-versions"
941
+ url = "https://github.com/benbjohnson/litestream/releases/download/v0.5.18/litestream-0.5.18-linux-x86_64.tar.gz"
942
+ url_api = "https://api.github.com/repos/benbjohnson/litestream/releases/assets/706493670"
943
+
944
+ [tools.litestream."platforms.macos-arm64"]
945
+ checksum = "sha256:6d3e0875e5416628ef3d274e8f7bb0ecc2a19cbbe76b9ba43efe9901d82a5c84"
946
+ checksum_source = "mise-versions"
947
+ url = "https://github.com/benbjohnson/litestream/releases/download/v0.5.18/litestream-0.5.18-darwin-arm64.tar.gz"
948
+ url_api = "https://api.github.com/repos/benbjohnson/litestream/releases/assets/706493582"
949
+
950
+ [tools.litestream."platforms.macos-x64"]
951
+ checksum = "sha256:25846be4fc4a4e0b4fd9ac5d0e019c10ec70a4dc12fe84255f5607b8b9a520e0"
952
+ checksum_source = "mise-versions"
953
+ url = "https://github.com/benbjohnson/litestream/releases/download/v0.5.18/litestream-0.5.18-darwin-x86_64.tar.gz"
954
+ url_api = "https://api.github.com/repos/benbjohnson/litestream/releases/assets/706493692"
955
+
927
956
  [[tools.ninja]]
928
957
  version = "1.13.2"
929
958
  backend = "aqua:ninja-build/ninja"