@crvouga/dev-fleet 0.7.0 → 0.9.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 (206) hide show
  1. package/README.md +88 -7
  2. package/agents/audit.md +18 -18
  3. package/ansible/agents/agents.yml +134 -0
  4. package/ansible/agents/templates/antigravity.settings.json.j2 +3 -0
  5. package/ansible/agents/templates/claude.settings.json.j2 +3 -0
  6. package/ansible/agents/templates/codex.config.toml.j2 +11 -0
  7. package/ansible/agents/templates/gemini.settings.json.j2 +14 -0
  8. package/ansible/agents/templates/grok.config.toml.j2 +19 -0
  9. package/ansible/agents/templates/opencode.json.j2 +24 -0
  10. package/ansible/agents/templates/receipt.json.j2 +10 -0
  11. package/ansible/ansible.cfg +14 -0
  12. package/ansible/github/git.yml +82 -0
  13. package/ansible/macos/manager-privilege.yml +83 -0
  14. package/ansible/macos/manager.yml +164 -9
  15. package/ansible/macos/policy.json +13 -0
  16. package/ansible/macos/t3-policy.yml +5 -3
  17. package/ansible/macos/vault-renew.yml +85 -0
  18. package/ansible/macos/worker-host-key.yml +33 -0
  19. package/ansible/monitoring/agent-darwin.yml +3 -0
  20. package/ansible/monitoring/agent.yml +2 -0
  21. package/ansible/monitoring/hub-darwin.yml +3 -0
  22. package/ansible/monitoring/hub-route-darwin.yml +1 -0
  23. package/ansible/net/remote-probe.sh +4 -1
  24. package/ansible/platforms.json +2 -2
  25. package/ansible/pyproject.toml +5 -0
  26. package/ansible/ubuntu/baseline.yml +39 -13
  27. package/ansible/ubuntu/disk-gc.py +6 -2
  28. package/ansible/ubuntu/firewall.nft.j2 +50 -0
  29. package/ansible/ubuntu/policy.json +125 -6
  30. package/ansible/ubuntu/reboot.yml +89 -0
  31. package/ansible/ubuntu/recovery.yml +13 -3
  32. package/ansible/ubuntu/t3/policy.json +3 -0
  33. package/ansible/ubuntu/t3-desktop.yml +23 -2
  34. package/ansible/ubuntu/t3-headless.yml +128 -0
  35. package/ansible/ubuntu/t3-health.sh +24 -10
  36. package/ansible/ubuntu/t3-idle.py +37 -2
  37. package/ansible/ubuntu/t3-policy.yml +4 -1
  38. package/ansible/ubuntu/t3-route.yml +1 -0
  39. package/ansible/ubuntu/tailscale.yml +54 -8
  40. package/ansible/ubuntu/tasks/browser.yml +3 -0
  41. package/ansible/ubuntu/tasks/containers-off.yml +43 -0
  42. package/ansible/ubuntu/tasks/docker.yml +53 -6
  43. package/ansible/ubuntu/tasks/dpkg.yml +4 -0
  44. package/ansible/ubuntu/tasks/features.yml +22 -0
  45. package/ansible/ubuntu/tasks/firewall.yml +172 -0
  46. package/ansible/ubuntu/tasks/install.yml +2 -1
  47. package/ansible/ubuntu/tasks/packages.yml +4 -2
  48. package/ansible/ubuntu/tasks/release.yml +6 -0
  49. package/ansible/ubuntu/tasks/resources.yml +6 -4
  50. package/ansible/ubuntu/tasks/services.yml +2 -2
  51. package/ansible/ubuntu/tasks/sizing.yml +49 -0
  52. package/ansible/ubuntu/tasks/snapshot.yml +14 -3
  53. package/ansible/ubuntu/tasks/ssh.yml +61 -4
  54. package/ansible/ubuntu/tasks/t3-restart.yml +3 -1
  55. package/ansible/ubuntu/tasks/user.yml +1 -0
  56. package/ansible/ubuntu/worker-account.yml +117 -0
  57. package/ansible/uv.lock +265 -0
  58. package/bin/devfleet.mjs +42 -4
  59. package/bootstrap/fleet-remote-bootstrap.sh +91 -25
  60. package/bootstrap/global-config.ts +29 -3
  61. package/bootstrap/package-smoke.ts +127 -0
  62. package/bootstrap/pins.ts +189 -0
  63. package/bootstrap/release-notes.ts +88 -0
  64. package/bootstrap/release-version.ts +41 -7
  65. package/bootstrap/worker-features.ts +114 -0
  66. package/bootstrap/worker-seed.yaml +65 -0
  67. package/bootstrap/worker-tools.ts +39 -9
  68. package/cliproxy/README.md +1 -1
  69. package/cliproxy/docs/health-workflow.example.yml +20 -143
  70. package/docs/authentication.md +21 -11
  71. package/docs/consumer-setup.md +74 -58
  72. package/docs/convergence.md +67 -0
  73. package/docs/declared.md +61 -34
  74. package/docs/machine-setup.md +73 -7
  75. package/docs/manager-setup.md +34 -0
  76. package/docs/operations.md +64 -9
  77. package/docs/releasing.md +49 -1
  78. package/docs/runbooks.md +112 -7
  79. package/docs/state.md +23 -11
  80. package/docs/support-matrix.md +40 -4
  81. package/docs/topology.md +82 -12
  82. package/docs/ubuntu-sync.md +98 -42
  83. package/docs/worker-contract.md +122 -19
  84. package/docs/worker-requirements.md +107 -0
  85. package/examples/fleet.json +1 -0
  86. package/examples/workers-from-infrastructure/main.tf +132 -0
  87. package/examples/workers-from-infrastructure/worker-records.json +14 -0
  88. package/llms.txt +3 -1
  89. package/mise.lock +29 -0
  90. package/mise.toml +5 -3
  91. package/package.json +2 -2
  92. package/schema/check-ids.json +1991 -162
  93. package/schema/check-ids.schema.json +34 -0
  94. package/schema/fleet.schema.json +349 -10
  95. package/schema/output/check.schema.json +1 -1
  96. package/schema/output/fleet-state-definition.schema.json +14 -0
  97. package/schema/output/fleet-sync-schedule.schema.json +26 -0
  98. package/schema/output/fleet-sync.schema.json +18 -0
  99. package/schema/output/report.schema.json +3 -2
  100. package/schema/worker-record.schema.json +76 -0
  101. package/src/agents/converge.ts +140 -24
  102. package/src/agents/files.ts +2 -32
  103. package/src/agents/native-verify.ts +34 -3
  104. package/src/agents/secrets.ts +39 -4
  105. package/src/check-owners.ts +62 -2
  106. package/src/check-registry.ts +630 -0
  107. package/src/cli/agent-guide.ts +3 -3
  108. package/src/cli/cli.ts +4 -0
  109. package/src/cli/fleet-manifest.ts +108 -0
  110. package/src/cli/fleet-node-lifecycle.ts +704 -0
  111. package/src/cli/fleet-node-park.ts +238 -0
  112. package/src/cli/fleet-node-seed.ts +167 -0
  113. package/src/cli/fleet-state.ts +2 -1
  114. package/src/cli/fleet-sync-checkout.ts +7 -0
  115. package/src/cli/fleet-sync-enrollment.ts +193 -32
  116. package/src/cli/fleet-sync-forward.ts +6 -0
  117. package/src/cli/fleet-sync-manager.ts +123 -8
  118. package/src/cli/fleet-sync-output.ts +31 -11
  119. package/src/cli/fleet-sync-plan.ts +65 -9
  120. package/src/cli/fleet-sync-records.ts +113 -0
  121. package/src/cli/fleet-sync-remote.ts +172 -15
  122. package/src/cli/fleet-sync-schedule.ts +14 -4
  123. package/src/cli/fleet-sync-state.ts +31 -10
  124. package/src/cli/fleet-sync-vault.ts +152 -26
  125. package/src/cli/fleet-sync-worker.ts +214 -0
  126. package/src/cli/fleet-sync.ts +172 -21
  127. package/src/cli/fleet-validate.ts +277 -0
  128. package/src/cli/fleet-watch.ts +7 -1
  129. package/src/cli/fleet.ts +87 -5
  130. package/src/cli/managed-health.ts +25 -1
  131. package/src/cli/register-fleet.ts +251 -4
  132. package/src/cli/registry.ts +47 -0
  133. package/src/cli/tailnet.ts +43 -10
  134. package/src/cli/ui-data.ts +3 -1
  135. package/src/cli/worker-usage-report.ts +63 -0
  136. package/src/clock.ts +42 -0
  137. package/src/definition.ts +762 -232
  138. package/src/diagnostics.ts +5 -2
  139. package/src/exposure.ts +102 -0
  140. package/src/fleet-config.ts +3 -1
  141. package/src/git-identity.ts +76 -3
  142. package/src/ide/ide.ts +5 -0
  143. package/src/ide/t3.ts +12 -2
  144. package/src/input-hashes.ts +6 -0
  145. package/src/machine/machine-class.ts +59 -0
  146. package/src/machine/platform.ts +90 -11
  147. package/src/machine/state.ts +70 -16
  148. package/src/machine/support-matrix.ts +11 -9
  149. package/src/machine-health.ts +28 -5
  150. package/src/manager-health.ts +148 -2
  151. package/src/manager-sync.ts +89 -8
  152. package/src/monitoring/beszel.ts +8 -8
  153. package/src/monitoring/enroll.ts +8 -1
  154. package/src/monitoring/hub-access.ts +10 -4
  155. package/src/monitoring/systems.ts +138 -0
  156. package/src/network-definition.ts +13 -4
  157. package/src/node-converge-linux.ts +136 -27
  158. package/src/node-converge.ts +13 -3
  159. package/src/platform/ansible-receipt.ts +17 -2
  160. package/src/platform/ansible.ts +40 -4
  161. package/src/platform/manager-host.ts +25 -8
  162. package/src/platform/resource-verify.ts +18 -7
  163. package/src/platform/t3-policy.ts +24 -10
  164. package/src/platform/t3-service.ts +109 -34
  165. package/src/platform/ubuntu-release.ts +24 -0
  166. package/src/platform/ubuntu-verify.ts +31 -3
  167. package/src/platform/ubuntu-worker.ts +58 -0
  168. package/src/platform/worker-platform.ts +72 -0
  169. package/src/protocol.ts +1 -1
  170. package/src/public-reachability.ts +186 -0
  171. package/src/remote-bootstrap.ts +50 -12
  172. package/src/remote-operation.ts +18 -4
  173. package/src/repair-plan.ts +23 -12
  174. package/src/report.ts +2 -1
  175. package/src/schedule-check.ts +21 -2
  176. package/src/secrets.ts +7 -0
  177. package/src/service-auth.ts +55 -48
  178. package/src/ssh-connection.ts +141 -0
  179. package/src/ssh.ts +63 -24
  180. package/src/state/store.ts +4 -2
  181. package/src/state/sync.ts +35 -6
  182. package/src/state/workers.ts +140 -0
  183. package/src/t3/pairing.ts +59 -0
  184. package/src/t3/peer-link.ts +333 -0
  185. package/src/t3/version.ts +1 -1
  186. package/src/tailnet-cleanup.ts +185 -0
  187. package/src/tailnet-control.ts +346 -0
  188. package/src/tailscale.ts +1 -1
  189. package/src/toolchain-checks.ts +11 -1
  190. package/src/types.ts +4 -2
  191. package/src/worker-account.ts +63 -0
  192. package/src/worker-activity.ts +303 -0
  193. package/src/worker-capacity.ts +31 -0
  194. package/src/worker-credentials.ts +201 -0
  195. package/src/worker-exposure.ts +388 -0
  196. package/src/worker-features.ts +201 -0
  197. package/src/worker-host-checks.ts +71 -20
  198. package/src/worker-manifest.ts +224 -0
  199. package/src/worker-parking.ts +70 -0
  200. package/src/worker-policy.ts +38 -36
  201. package/src/worker-readiness.ts +15 -3
  202. package/src/worker-reboot.ts +129 -0
  203. package/src/worker-records.ts +305 -0
  204. package/src/worker-seed.ts +143 -0
  205. package/src/worker-sizing.ts +76 -0
  206. package/src/cli/schedule-logs.ts +0 -36
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "check-ids.schema.json",
4
+ "title": "The check-ID registry, version 1",
5
+ "description": "schema/check-ids.json: every check ID dev-fleet reports, generated from the code.",
6
+ "type": "object",
7
+ "required": ["version", "checks"],
8
+ "properties": {
9
+ "version": { "const": 1 },
10
+ "checks": {
11
+ "type": "array",
12
+ "items": {
13
+ "type": "object",
14
+ "required": ["id", "meaning", "severity", "repair"],
15
+ "properties": {
16
+ "id": {
17
+ "type": "string",
18
+ "pattern": "^[a-z0-9-]+(:[A-Za-z0-9:.*_-]+)?$"
19
+ },
20
+ "meaning": { "type": "string" },
21
+ "severity": { "enum": ["required", "advisory"] },
22
+ "requirement": { "const": true },
23
+ "repair": {
24
+ "type": "object",
25
+ "properties": {
26
+ "recipe": { "type": "string" },
27
+ "manual": { "type": "string" }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
@@ -53,27 +53,45 @@
53
53
  "neverSleepOnPower": {
54
54
  "const": true,
55
55
  "description": "Workers depend on the manager's singleton services, so it never sleeps on AC power."
56
+ },
57
+ "schedule": {
58
+ "type": "object",
59
+ "additionalProperties": false,
60
+ "description": "How often the manager's scheduled jobs run. Each defaults to the interval in ansible/macos/policy.json.",
61
+ "properties": {
62
+ "syncMinutes": {
63
+ "type": "integer",
64
+ "minimum": 5,
65
+ "maximum": 1440,
66
+ "description": "fleet-sync --schedule converges the fleet this often (default 30)."
67
+ },
68
+ "watchMinutes": {
69
+ "type": "integer",
70
+ "minimum": 5,
71
+ "maximum": 1440,
72
+ "description": "fleet-watch checks the fleet and stores its heartbeat this often (default 15). Keep it well under the max-age-minutes of the heartbeat workflow."
73
+ }
74
+ }
56
75
  }
57
76
  }
58
77
  },
59
78
  "workers": {
60
79
  "type": "array",
61
- "minItems": 1,
62
80
  "maxItems": 64,
63
81
  "items": {
64
82
  "type": "object",
65
83
  "additionalProperties": false,
66
- "required": ["id", "ssh", "os"],
84
+ "required": ["id", "os"],
67
85
  "properties": {
68
86
  "id": {
69
87
  "type": "string",
70
88
  "pattern": "^[a-z][a-z0-9-]{0,62}$",
71
- "description": "A short stable id for the worker. The first worker listed is the canary: a revision new to the fleet must pass there before the others receive it."
89
+ "description": "A short stable id for the worker. A revision new to the fleet must pass on one worker, the canary, before the others receive it: the one named under canary, or else the first reachable worker in this order."
72
90
  },
73
91
  "ssh": {
74
92
  "type": "string",
75
93
  "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,252}$",
76
- "description": "The worker's Host alias in the manager's ~/.ssh/config. It must work with BatchMode=yes and a host key already in known_hosts."
94
+ "description": "Optional: a Host alias in the operator's own ~/.ssh/config, checked against their own known_hosts. Without one the worker is reached from its network block (user, sshPort, tailscaleName) with a host key pinned in the fleet state or given as network.hostKey, against a known_hosts file dev-fleet owns; the operator's ~/.ssh is neither read for it nor changed."
77
95
  },
78
96
  "os": {
79
97
  "const": "linux",
@@ -105,9 +123,38 @@
105
123
  "minimum": 1,
106
124
  "maximum": 65535,
107
125
  "description": "The port the worker T3 server listens on, normally 3773."
126
+ },
127
+ "hostKey": {
128
+ "type": "string",
129
+ "pattern": "^(ssh-ed25519|ecdsa-sha2-nistp256|ssh-rsa) [A-Za-z0-9+/=]{16,}$",
130
+ "description": "The SSH host public key, \"TYPE BASE64\", given as data (a worker record writes it). It is the pinned key; a worker without one is pinned in the fleet state (fleet-node-replace --host-key, fleet-node-seed --apply)."
108
131
  }
109
132
  }
110
133
  },
134
+ "features": {
135
+ "type": "object",
136
+ "additionalProperties": false,
137
+ "properties": {
138
+ "preset": {
139
+ "enum": ["editing"],
140
+ "description": "A named set of features, applied before the fields beside it. editing: no containers, a headless session and only the base tools (it adds no tool profile)."
141
+ },
142
+ "containers": {
143
+ "type": "boolean",
144
+ "description": "Docker on the worker: its packages, service, docker group membership, the docker-cli tool and the Docker step of the disk GC. Default true. Turning it off on an existing worker removes what dev-fleet installed for it; fleet-sync --dry-run lists what."
145
+ },
146
+ "session": {
147
+ "enum": ["desktop", "headless"],
148
+ "description": "How the worker runs T3. desktop (default): the pinned desktop app in an auto-login graphical session. headless: the pinned T3 server as a lingering user service, with no display manager. ~/.t3 is kept when this changes."
149
+ }
150
+ },
151
+ "description": "This worker's features, over the fleet-wide worker.features."
152
+ },
153
+ "powerRecoveryNote": {
154
+ "type": "string",
155
+ "pattern": "^[^\\n\\r]{1,120}$",
156
+ "description": "On a virtual machine, who restarts it after a power loss (for example: provider restarts instances). Shown with worker:power-loss-recovery, which is not applicable to a virtual machine."
157
+ },
111
158
  "toolProfiles": {
112
159
  "type": "array",
113
160
  "uniqueItems": true,
@@ -115,10 +162,152 @@
115
162
  "enum": ["web", "go", "rust", "jvm"]
116
163
  },
117
164
  "description": "Tool profiles this worker installs on top of the fleet-wide ones: web (pnpm, Deno, Yarn), go, rust (with CMake and Ninja), jvm (Temurin). Each is a locked tool set dev-fleet ships."
165
+ },
166
+ "exposure": {
167
+ "type": "object",
168
+ "additionalProperties": false,
169
+ "description": "What this worker opens beyond loopback and the tailnet. A field named here replaces the one of worker.exposure.",
170
+ "properties": {
171
+ "publicSsh": {
172
+ "type": "boolean",
173
+ "description": "Break-glass: keep the SSH port open beyond the tailnet. Default false: after enrolment SSH is reached only over the tailnet."
174
+ },
175
+ "publicPorts": {
176
+ "type": "array",
177
+ "maxItems": 32,
178
+ "uniqueItems": true,
179
+ "items": {
180
+ "type": "object",
181
+ "additionalProperties": false,
182
+ "required": ["protocol", "port"],
183
+ "properties": {
184
+ "protocol": {
185
+ "enum": ["tcp", "udp"]
186
+ },
187
+ "port": {
188
+ "type": "integer",
189
+ "minimum": 1,
190
+ "maximum": 65535
191
+ }
192
+ }
193
+ },
194
+ "description": "Ports opened on every interface; a listener on one passes worker:exposure. Default none."
195
+ },
196
+ "otherListeners": {
197
+ "enum": ["warn", "fail"],
198
+ "description": "How worker:exposure reports a listener beyond loopback and the tailnet that no fleet service owns, such as an agent's development server on 0.0.0.0. The firewall closes it either way. Default warn; a fleet service there always fails."
199
+ }
200
+ }
201
+ },
202
+ "labels": {
203
+ "type": "object",
204
+ "additionalProperties": {
205
+ "type": "string",
206
+ "pattern": "^[^\\u0000-\\u001f\\u007f]{0,128}$"
207
+ },
208
+ "description": "Free-form labels, from a worker record; only displayed. Names are lowercase, at most 32 of them."
209
+ },
210
+ "billing": {
211
+ "type": "object",
212
+ "additionalProperties": false,
213
+ "required": ["hourlyRate"],
214
+ "properties": {
215
+ "hourlyRate": {
216
+ "type": "number",
217
+ "minimum": 0,
218
+ "description": "What one hour of running costs, as you declare it. dev-fleet asks no pricing API; it only displays and sums it."
219
+ },
220
+ "currency": {
221
+ "type": "string",
222
+ "pattern": "^[A-Z]{3}$"
223
+ },
224
+ "note": {
225
+ "type": "string",
226
+ "pattern": "^[^\\u0000-\\u001f\\u007f]{1,200}$"
227
+ }
228
+ },
229
+ "description": "What the worker costs while it runs: fleet-status sums it for idle workers."
230
+ },
231
+ "origin": {
232
+ "const": "records",
233
+ "description": "Written by dev-fleet for a worker read from workerRecords, in the copy it installs and stores; do not write it."
118
234
  }
119
235
  }
120
236
  }
121
237
  },
238
+ "canary": {
239
+ "type": "string",
240
+ "pattern": "^[a-z][a-z0-9-]{0,62}$",
241
+ "description": "The id of the worker that proves a revision new to the fleet first. When it is unreachable, the next reachable worker proves it instead. Default: the first reachable worker."
242
+ },
243
+ "workerRecords": {
244
+ "type": "array",
245
+ "maxItems": 8,
246
+ "items": {
247
+ "oneOf": [
248
+ {
249
+ "type": "object",
250
+ "additionalProperties": false,
251
+ "required": ["file"],
252
+ "properties": {
253
+ "file": {
254
+ "type": "string",
255
+ "description": "A JSON file of worker records (schema/worker-record.schema.json), relative to fleet.json."
256
+ }
257
+ }
258
+ },
259
+ {
260
+ "type": "object",
261
+ "additionalProperties": false,
262
+ "required": ["command"],
263
+ "properties": {
264
+ "command": {
265
+ "type": "array",
266
+ "minItems": 1,
267
+ "items": {
268
+ "type": "string"
269
+ },
270
+ "description": "A command run where fleet.json is that prints worker records, such as an infrastructure tool printing its outputs."
271
+ }
272
+ }
273
+ }
274
+ ]
275
+ },
276
+ "description": "Where generated worker records are read: their workers join the list above. An infrastructure tool writes them; dev-fleet only reads them and never creates a machine. A worker whose record disappears is retired by the next fleet-sync."
277
+ },
278
+ "parking": {
279
+ "type": "object",
280
+ "additionalProperties": false,
281
+ "properties": {
282
+ "idleAfterHours": {
283
+ "type": "number",
284
+ "minimum": 1,
285
+ "maximum": 8760,
286
+ "description": "A worker that has run no agent turn for longer is reported as worker:idle."
287
+ },
288
+ "autoPark": {
289
+ "type": "boolean",
290
+ "description": "fleet-watch parks a worker idle longer than idleAfterHours. Off unless true."
291
+ },
292
+ "stop": {
293
+ "type": "array",
294
+ "minItems": 1,
295
+ "items": {
296
+ "type": "string"
297
+ },
298
+ "description": "Run with the worker ID as its last argument when the worker is parked."
299
+ },
300
+ "start": {
301
+ "type": "array",
302
+ "minItems": 1,
303
+ "items": {
304
+ "type": "string"
305
+ },
306
+ "description": "Run with the worker ID as its last argument when the worker is resumed."
307
+ }
308
+ },
309
+ "description": "Workers that are stopped on purpose (fleet-node-park) and when an idle one is reported."
310
+ },
122
311
  "services": {
123
312
  "type": "object",
124
313
  "additionalProperties": false,
@@ -162,7 +351,9 @@
162
351
  },
163
352
  "order": {
164
353
  "type": "array",
165
- "items": { "type": "string" },
354
+ "items": {
355
+ "type": "string"
356
+ },
166
357
  "description": "Account labels (sign-in emails) first, in this order; the others follow in the order of their routing prefix."
167
358
  }
168
359
  }
@@ -259,7 +450,9 @@
259
450
  },
260
451
  "architectures": {
261
452
  "type": "array",
262
- "items": { "enum": ["amd64", "arm64"] },
453
+ "items": {
454
+ "enum": ["amd64", "arm64"]
455
+ },
263
456
  "description": "Exactly the architectures the policy names for that release."
264
457
  }
265
458
  },
@@ -319,6 +512,25 @@
319
512
  "neverSleep": {
320
513
  "const": true
321
514
  },
515
+ "features": {
516
+ "type": "object",
517
+ "additionalProperties": false,
518
+ "properties": {
519
+ "preset": {
520
+ "enum": ["editing"],
521
+ "description": "A named set of features, applied before the fields beside it. editing: no containers, a headless session and only the base tools (it adds no tool profile)."
522
+ },
523
+ "containers": {
524
+ "type": "boolean",
525
+ "description": "Docker on the worker: its packages, service, docker group membership, the docker-cli tool and the Docker step of the disk GC. Default true. Turning it off on an existing worker removes what dev-fleet installed for it; fleet-sync --dry-run lists what."
526
+ },
527
+ "session": {
528
+ "enum": ["desktop", "headless"],
529
+ "description": "How the worker runs T3. desktop (default): the pinned desktop app in an auto-login graphical session. headless: the pinned T3 server as a lingering user service, with no display manager. ~/.t3 is kept when this changes."
530
+ }
531
+ },
532
+ "description": "Features every worker has unless its own features say otherwise. Absent: containers and a desktop session, as before features existed."
533
+ },
322
534
  "toolProfiles": {
323
535
  "type": "array",
324
536
  "uniqueItems": true,
@@ -335,6 +547,42 @@
335
547
  "pattern": "^[A-Za-z0-9@:/._-]{1,75}$"
336
548
  },
337
549
  "description": "Single tools every worker installs: names of tools dev-fleet's mise.toml pins (for example fd or ripgrep). A tool it does not pin is refused; ask for it in an issue."
550
+ },
551
+ "exposure": {
552
+ "type": "object",
553
+ "additionalProperties": false,
554
+ "description": "What every worker opens beyond loopback and the tailnet. Without it inbound traffic arrives only over loopback and the tailnet, sshd takes keys only and no root login, and published container ports are closed beyond the tailnet.",
555
+ "properties": {
556
+ "publicSsh": {
557
+ "type": "boolean",
558
+ "description": "Break-glass: keep the SSH port open beyond the tailnet. Default false: after enrolment SSH is reached only over the tailnet."
559
+ },
560
+ "publicPorts": {
561
+ "type": "array",
562
+ "maxItems": 32,
563
+ "uniqueItems": true,
564
+ "items": {
565
+ "type": "object",
566
+ "additionalProperties": false,
567
+ "required": ["protocol", "port"],
568
+ "properties": {
569
+ "protocol": {
570
+ "enum": ["tcp", "udp"]
571
+ },
572
+ "port": {
573
+ "type": "integer",
574
+ "minimum": 1,
575
+ "maximum": 65535
576
+ }
577
+ }
578
+ },
579
+ "description": "Ports opened on every interface; a listener on one passes worker:exposure. Default none."
580
+ },
581
+ "otherListeners": {
582
+ "enum": ["warn", "fail"],
583
+ "description": "How worker:exposure reports a listener beyond loopback and the tailnet that no fleet service owns, such as an agent's development server on 0.0.0.0. The firewall closes it either way. Default warn; a fleet service there always fails."
584
+ }
585
+ }
338
586
  }
339
587
  },
340
588
  "description": "Policy every worker must meet. All but the two logins, the capabilities and the tools are fixed."
@@ -355,7 +603,9 @@
355
603
  "type": "array",
356
604
  "minItems": 1,
357
605
  "uniqueItems": true,
358
- "items": { "enum": ["t3"] },
606
+ "items": {
607
+ "enum": ["t3"]
608
+ },
359
609
  "description": "The IDEs every machine runs. Defaults to [\"t3\"]; the README's support matrix lists the supported ones."
360
610
  },
361
611
  "t3": {
@@ -368,16 +618,23 @@
368
618
  "description": "Top-level T3 settings every machine holds the same (tool servers, projects, selected preferences). Keys not named here stay local; a key edited on a machine since dev-fleet last wrote it is reported as a conflict, never overwritten.",
369
619
  "propertyNames": {
370
620
  "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$",
371
- "not": { "const": "providerInstances" }
621
+ "not": {
622
+ "const": "providerInstances"
623
+ }
372
624
  }
373
625
  },
374
626
  "requiredModels": {
375
627
  "type": "object",
376
628
  "description": "Models T3 must offer, and the proxy must serve, for every account of a tool, by tool (claude, codex, opencode).",
377
- "propertyNames": { "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$" },
629
+ "propertyNames": {
630
+ "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$"
631
+ },
378
632
  "additionalProperties": {
379
633
  "type": "array",
380
- "items": { "type": "string", "pattern": "^[A-Za-z0-9._:-]{1,128}$" }
634
+ "items": {
635
+ "type": "string",
636
+ "pattern": "^[A-Za-z0-9._:-]{1,128}$"
637
+ }
381
638
  }
382
639
  }
383
640
  }
@@ -626,6 +883,38 @@
626
883
  "pattern": "^[A-Za-z0-9_]+$"
627
884
  }
628
885
  }
886
+ },
887
+ "tailnet-auth-key": {
888
+ "type": "object",
889
+ "additionalProperties": false,
890
+ "required": ["path", "field"],
891
+ "properties": {
892
+ "path": {
893
+ "type": "string",
894
+ "pattern": "^[A-Za-z0-9._/-]+$"
895
+ },
896
+ "field": {
897
+ "type": "string",
898
+ "pattern": "^[A-Za-z0-9_]+$"
899
+ }
900
+ },
901
+ "description": "Optional: a short-lived, single-use, tagged Tailscale auth key a new worker joins the tailnet with (fleet-node-seed)."
902
+ },
903
+ "tailnet-admin": {
904
+ "type": "object",
905
+ "additionalProperties": false,
906
+ "required": ["path", "field"],
907
+ "properties": {
908
+ "path": {
909
+ "type": "string",
910
+ "pattern": "^[A-Za-z0-9._/-]+$"
911
+ },
912
+ "field": {
913
+ "type": "string",
914
+ "pattern": "^[A-Za-z0-9_]+$"
915
+ }
916
+ },
917
+ "description": "Only with tailnet: an API credential of the tailnet's control plane that creates single-use auth keys for new workers. It stays on the manager."
629
918
  }
630
919
  },
631
920
  "description": "Required for every secret except github-token, which defaults to the field GITHUB_TOKEN beside proxy-client-key."
@@ -654,6 +943,10 @@
654
943
  "type": "string",
655
944
  "pattern": "^https://github\\.com/[A-Za-z0-9_.-]{1,100}/[A-Za-z0-9_.-]{1,100}$",
656
945
  "description": "A GitHub repository the fleet's account can push to, ideally private. Each node lists it through git with its own credentials."
946
+ },
947
+ "signing": {
948
+ "enum": ["ssh"],
949
+ "description": "Sign every commit and tag. ssh: each node makes an SSH key of its own (~/.ssh/dev-fleet-git-signing), which never leaves it, and signs with it; measured as worker:git-signing. GitHub shows the commits as verified once each node's public key is added as a signing key to the account."
657
950
  }
658
951
  }
659
952
  },
@@ -740,6 +1033,52 @@
740
1033
  "description": "owner/name of a repository fleet-github-verify branches, pushes to and opens pull requests on, then cleans up."
741
1034
  }
742
1035
  }
1036
+ },
1037
+ "tailnet": {
1038
+ "description": "Optional: the tailnet control plane fleet-sync and fleet-node-seed enrol new workers with, without a login on the machine. Its credential is the secret tailnet-admin.",
1039
+ "oneOf": [
1040
+ {
1041
+ "type": "object",
1042
+ "additionalProperties": false,
1043
+ "required": ["api", "tag"],
1044
+ "properties": {
1045
+ "api": { "const": "tailscale" },
1046
+ "url": {
1047
+ "type": "string",
1048
+ "pattern": "^https://[A-Za-z0-9.-]{1,253}(:[0-9]{1,5})?$",
1049
+ "description": "The API origin. Default: https://api.tailscale.com."
1050
+ },
1051
+ "tag": {
1052
+ "type": "string",
1053
+ "pattern": "^tag:[a-z0-9][a-z0-9-]{0,62}$",
1054
+ "description": "The tag every enrolled worker carries, so access rules and key expiry apply to workers as a group."
1055
+ }
1056
+ }
1057
+ },
1058
+ {
1059
+ "type": "object",
1060
+ "additionalProperties": false,
1061
+ "required": ["api", "url", "tag", "user"],
1062
+ "properties": {
1063
+ "api": { "const": "headscale" },
1064
+ "url": {
1065
+ "type": "string",
1066
+ "pattern": "^https://[A-Za-z0-9.-]{1,253}(:[0-9]{1,5})?$",
1067
+ "description": "The Headscale server: its API, and the login server workers join."
1068
+ },
1069
+ "tag": {
1070
+ "type": "string",
1071
+ "pattern": "^tag:[a-z0-9][a-z0-9-]{0,62}$",
1072
+ "description": "The tag every enrolled worker carries, so access rules apply to workers as a group."
1073
+ },
1074
+ "user": {
1075
+ "type": "string",
1076
+ "pattern": "^[A-Za-z0-9._@-]{1,64}$",
1077
+ "description": "The Headscale user the keys belong to (its ID from Headscale 0.26)."
1078
+ }
1079
+ }
1080
+ }
1081
+ ]
743
1082
  }
744
1083
  }
745
1084
  }
@@ -7,7 +7,7 @@
7
7
  "required": ["id", "status", "summary"],
8
8
  "properties": {
9
9
  "id": { "type": "string", "pattern": "^[a-z0-9-]+(:[A-Za-z0-9:._-]+)?$" },
10
- "status": { "enum": ["pass", "warn", "fail", "skip"] },
10
+ "status": { "enum": ["pass", "warn", "fail", "skip", "not-applicable"] },
11
11
  "summary": { "type": "string" },
12
12
  "required": { "type": "boolean" },
13
13
  "phaseDurationMs": { "type": "number" },
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "fleet-state-definition.schema.json",
4
+ "title": "A fleet configuration printed by fleet-state, version 1",
5
+ "description": "What devfleet fleet-state --example and fleet-state --export print with --json: the configuration itself is definition, and follows schema/fleet.schema.json.",
6
+ "type": "object",
7
+ "required": ["version", "command", "ok", "definition"],
8
+ "properties": {
9
+ "version": { "const": 1 },
10
+ "command": { "const": "fleet-state" },
11
+ "ok": { "const": true },
12
+ "definition": { "type": "object" }
13
+ }
14
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "fleet-sync-schedule.schema.json",
4
+ "title": "The fleet-sync --schedule report, version 2",
5
+ "description": "What devfleet fleet-sync --schedule ACTION prints with --json. status names whether the schedule is installed, its interval, its last three runs and the manager:schedule check; enable, pause, resume and disable say whether the change was made.",
6
+ "type": "object",
7
+ "required": ["version", "command", "scheduling"],
8
+ "properties": {
9
+ "version": { "const": 2 },
10
+ "command": { "const": "fleet-sync" },
11
+ "execution": { "const": "manager-push" },
12
+ "ok": { "type": "boolean" },
13
+ "scheduling": {
14
+ "type": ["string", "object"],
15
+ "properties": {
16
+ "installed": { "type": "boolean" },
17
+ "supported": { "type": "boolean" }
18
+ }
19
+ },
20
+ "intervalSeconds": { "type": "integer" },
21
+ "revision": { "type": "string" },
22
+ "summary": { "type": "string" },
23
+ "runs": { "type": "array", "items": { "type": "object" } },
24
+ "checks": { "type": "array", "items": { "$ref": "check.schema.json" } }
25
+ }
26
+ }
@@ -27,6 +27,12 @@
27
27
  "candidates": { "type": "integer" },
28
28
  "dashboardUrl": { "type": ["string", "null"] },
29
29
  "planFingerprint": { "type": "string" },
30
+ "canary": { "type": "string" },
31
+ "enrolled": {
32
+ "description": "The workers this run enrolled: added to the roster, or bound to their tailnet device.",
33
+ "type": "array",
34
+ "items": { "type": "string" }
35
+ },
30
36
  "nodes": {
31
37
  "type": "array",
32
38
  "items": {
@@ -105,6 +111,18 @@
105
111
  "changes": { "type": "boolean" }
106
112
  }
107
113
  }
114
+ },
115
+ "removals": {
116
+ "type": "array",
117
+ "description": "What the sync removes from a worker whose features the fleet configuration turned off since it was last applied.",
118
+ "items": {
119
+ "type": "object",
120
+ "required": ["node", "removes"],
121
+ "properties": {
122
+ "node": { "type": "string" },
123
+ "removes": { "type": "array", "items": { "type": "string" } }
124
+ }
125
+ }
108
126
  }
109
127
  }
110
128
  }
@@ -26,12 +26,13 @@
26
26
  },
27
27
  "summary": {
28
28
  "type": "object",
29
- "required": ["pass", "warn", "fail", "skip"],
29
+ "required": ["pass", "warn", "fail", "skip", "not-applicable"],
30
30
  "properties": {
31
31
  "pass": { "type": "integer" },
32
32
  "warn": { "type": "integer" },
33
33
  "fail": { "type": "integer" },
34
- "skip": { "type": "integer" }
34
+ "skip": { "type": "integer" },
35
+ "not-applicable": { "type": "integer" }
35
36
  }
36
37
  }
37
38
  }