@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.
Files changed (363) hide show
  1. package/.mise/locks/aws-cdk/2.1146.1/aube-lock.yaml +24 -0
  2. package/.mise/locks/aws-cdk/2.1146.1/package.json +7 -0
  3. package/.mise/locks/azure-cli/2.91.0~a837e014/pyproject.toml +5 -0
  4. package/.mise/locks/azure-cli/2.91.0~a837e014/uv.lock +2794 -0
  5. package/.mise/locks/gemini/0.66.0~84aac8e6/aube-lock.yaml +123 -0
  6. package/.mise/locks/gemini/0.66.0~84aac8e6/package.json +12 -0
  7. package/.mise/locks/npm-devcontainers-cli/0.90.0/aube-lock.yaml +24 -0
  8. package/.mise/locks/npm-devcontainers-cli/0.90.0/package.json +7 -0
  9. package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/aube-lock.yaml +1432 -0
  10. package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/package.json +7 -0
  11. package/.mise/locks/npm-vercel/65.8.0/aube-lock.yaml +3595 -0
  12. package/.mise/locks/npm-vercel/65.8.0/package.json +7 -0
  13. package/.mise/locks/npm-wrangler/4.156.0/aube-lock.yaml +895 -0
  14. package/.mise/locks/npm-wrangler/4.156.0/package.json +7 -0
  15. package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/aube-lock.yaml +24 -0
  16. package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/package.json +7 -0
  17. package/LICENSE +21 -0
  18. package/README.md +357 -2
  19. package/agents/antigravity-proxy.ts +3 -0
  20. package/agents/audit.md +89 -0
  21. package/agents/claude-proxy +5 -0
  22. package/agents/claude-proxy.ts +3 -0
  23. package/agents/codex-proxy.ts +3 -0
  24. package/agents/grok-proxy.ts +3 -0
  25. package/agents/managed-proxy.ts +55 -0
  26. package/agents/opencode-proxy.ts +3 -0
  27. package/agents/registry.json +209 -0
  28. package/agents/registry.schema.json +149 -0
  29. package/agents/setup.md +158 -0
  30. package/agents/t3.json +40 -0
  31. package/ansible/macos/manager.yml +30 -0
  32. package/ansible/macos/policy.json +9 -0
  33. package/ansible/monitoring/README.md +215 -0
  34. package/ansible/monitoring/agent-darwin.yml +198 -0
  35. package/ansible/monitoring/agent.yml +172 -0
  36. package/ansible/monitoring/beszel-agent.service +42 -0
  37. package/ansible/monitoring/compose.yml +74 -0
  38. package/ansible/monitoring/hub-darwin.yml +102 -0
  39. package/ansible/monitoring/hub-route-darwin.yml +51 -0
  40. package/ansible/monitoring/lock.json +16 -0
  41. package/ansible/net/remote-probe.sh +13 -0
  42. package/ansible/pyproject.toml +12 -0
  43. package/ansible/ubuntu/authorize-sudo.sh +51 -0
  44. package/ansible/ubuntu/baseline.yml +89 -0
  45. package/ansible/ubuntu/disk-gc.py +336 -0
  46. package/ansible/ubuntu/disk-guard.py +104 -0
  47. package/ansible/ubuntu/policy.json +52 -0
  48. package/ansible/ubuntu/recovery.yml +15 -0
  49. package/ansible/ubuntu/t3/policy.json +7 -0
  50. package/ansible/ubuntu/t3-desktop.yml +220 -0
  51. package/ansible/ubuntu/t3-health.sh +33 -0
  52. package/ansible/ubuntu/t3-policy.yml +76 -0
  53. package/ansible/ubuntu/t3-route.yml +28 -0
  54. package/ansible/ubuntu/tailscale.yml +78 -0
  55. package/ansible/ubuntu/tasks/credentials.yml +47 -0
  56. package/ansible/ubuntu/tasks/docker.yml +31 -0
  57. package/ansible/ubuntu/tasks/packages.yml +56 -0
  58. package/ansible/ubuntu/tasks/power.yml +51 -0
  59. package/ansible/ubuntu/tasks/recovery.yml +39 -0
  60. package/ansible/ubuntu/tasks/resources.yml +312 -0
  61. package/ansible/ubuntu/tasks/services.yml +31 -0
  62. package/ansible/ubuntu/tasks/ssh.yml +71 -0
  63. package/ansible/ubuntu/tasks/user.yml +120 -0
  64. package/ansible/uv.lock +190 -0
  65. package/bin/devfleet.mjs +36 -0
  66. package/bootstrap/bootstrap.ps1 +50 -0
  67. package/bootstrap/bootstrap.sh +80 -0
  68. package/bootstrap/devfleet +7 -0
  69. package/bootstrap/docker-desktop.sh +33 -0
  70. package/bootstrap/doctor.ts +234 -0
  71. package/bootstrap/fleet-manager-run.sh +103 -0
  72. package/bootstrap/fleet-remote-bootstrap.sh +258 -0
  73. package/bootstrap/global-config.ts +171 -0
  74. package/bootstrap/install-launcher.sh +10 -0
  75. package/bootstrap/native-aws.ts +86 -0
  76. package/bootstrap/native-t3.ts +154 -0
  77. package/bootstrap/npm.ts +358 -0
  78. package/bootstrap/release-version.ts +157 -0
  79. package/bootstrap/setup-status.ts +51 -0
  80. package/bootstrap/setup.ts +88 -0
  81. package/bootstrap/shell-path.ts +105 -0
  82. package/bootstrap/verify-t3-archive.py +49 -0
  83. package/bootstrap/worker-auth.ts +65 -0
  84. package/bootstrap/worker-github.ts +47 -0
  85. package/bootstrap/worker-tools.ts +24 -0
  86. package/cliproxy/AGENTS.md +11 -0
  87. package/cliproxy/README.md +269 -0
  88. package/cliproxy/clients/bootstrap.py +143 -0
  89. package/cliproxy/clients/launcher.py +57 -0
  90. package/cliproxy/compose.yaml +90 -0
  91. package/cliproxy/config.template.yaml +70 -0
  92. package/cliproxy/docs/cloud-adoption.md +16 -0
  93. package/cliproxy/docs/deployment.png +0 -0
  94. package/cliproxy/docs/deployment.svg +109 -0
  95. package/cliproxy/docs/health-workflow.example.yml +185 -0
  96. package/cliproxy/docs/migration.md +131 -0
  97. package/cliproxy/docs/model-health-report.schema.json +364 -0
  98. package/cliproxy/docs/model-health.md +393 -0
  99. package/cliproxy/image.lock.json +5 -0
  100. package/cliproxy/launchd/dev.devfleet.cliproxy-backup-check.plist +38 -0
  101. package/cliproxy/launchd/dev.devfleet.cliproxy-backup.plist +48 -0
  102. package/cliproxy/launchd/dev.devfleet.cliproxy-fleet.plist +32 -0
  103. package/cliproxy/launchd/dev.devfleet.cliproxy-gc.plist +38 -0
  104. package/cliproxy/launchd/dev.devfleet.cliproxy-monitor.plist +29 -0
  105. package/cliproxy/launchd/dev.devfleet.cliproxy-vault-renew.plist +31 -0
  106. package/cliproxy/models.json +1618 -0
  107. package/cliproxy/requirements.lock +78 -0
  108. package/cliproxy/scripts/accounts.py +38 -0
  109. package/cliproxy/scripts/audit.py +123 -0
  110. package/cliproxy/scripts/backup +234 -0
  111. package/cliproxy/scripts/check.py +34 -0
  112. package/cliproxy/scripts/checkpoint.py +226 -0
  113. package/cliproxy/scripts/cli.py +64 -0
  114. package/cliproxy/scripts/cliproxy_host.py +257 -0
  115. package/cliproxy/scripts/cloud_state.py +150 -0
  116. package/cliproxy/scripts/container-health +6 -0
  117. package/cliproxy/scripts/docker +24 -0
  118. package/cliproxy/scripts/external-health.py +79 -0
  119. package/cliproxy/scripts/fleet_config.py +43 -0
  120. package/cliproxy/scripts/fleet_sync.py +274 -0
  121. package/cliproxy/scripts/initialize +84 -0
  122. package/cliproxy/scripts/install-dependencies +48 -0
  123. package/cliproxy/scripts/install.py +406 -0
  124. package/cliproxy/scripts/model_health.py +16 -0
  125. package/cliproxy/scripts/modelhealth/__init__.py +5 -0
  126. package/cliproxy/scripts/modelhealth/checks.py +429 -0
  127. package/cliproxy/scripts/modelhealth/cli.py +117 -0
  128. package/cliproxy/scripts/modelhealth/config.py +186 -0
  129. package/cliproxy/scripts/modelhealth/emulator.py +536 -0
  130. package/cliproxy/scripts/modelhealth/expect.py +88 -0
  131. package/cliproxy/scripts/modelhealth/findings.py +183 -0
  132. package/cliproxy/scripts/modelhealth/protocol.py +330 -0
  133. package/cliproxy/scripts/modelhealth/redact.py +28 -0
  134. package/cliproxy/scripts/modelhealth/report.py +150 -0
  135. package/cliproxy/scripts/modelhealth/runner.py +390 -0
  136. package/cliproxy/scripts/modelhealth/scenario.py +131 -0
  137. package/cliproxy/scripts/modelhealth/schema.py +63 -0
  138. package/cliproxy/scripts/modelhealth/simulate.py +111 -0
  139. package/cliproxy/scripts/modelhealth/transport.py +213 -0
  140. package/cliproxy/scripts/monitor +68 -0
  141. package/cliproxy/scripts/ops +527 -0
  142. package/cliproxy/scripts/public-endpoint +127 -0
  143. package/cliproxy/scripts/recover_node.py +177 -0
  144. package/cliproxy/scripts/restore-verify +60 -0
  145. package/cliproxy/scripts/retention.py +144 -0
  146. package/cliproxy/scripts/service-start +7 -0
  147. package/cliproxy/scripts/validate-config +56 -0
  148. package/cliproxy/scripts/vault-access.py +57 -0
  149. package/cliproxy/scripts/vault-backup-login +55 -0
  150. package/cliproxy/scripts/vault-recovery +56 -0
  151. package/cliproxy/scripts/vault-renew +30 -0
  152. package/cliproxy/systemd/cliproxy-backup-check.service +10 -0
  153. package/cliproxy/systemd/cliproxy-backup-check.timer +8 -0
  154. package/cliproxy/systemd/cliproxy-backup.service +13 -0
  155. package/cliproxy/systemd/cliproxy-backup.timer +8 -0
  156. package/cliproxy/systemd/cliproxy-fleet.service +17 -0
  157. package/cliproxy/systemd/cliproxy-gc.service +13 -0
  158. package/cliproxy/systemd/cliproxy-gc.timer +8 -0
  159. package/cliproxy/systemd/cliproxy-monitor.service +8 -0
  160. package/cliproxy/systemd/cliproxy-monitor.timer +7 -0
  161. package/cliproxy/systemd/cliproxy-vault-renew.service +8 -0
  162. package/cliproxy/systemd/cliproxy-vault-renew.timer +7 -0
  163. package/cliproxy/vendor/LICENSE +22 -0
  164. package/cliproxy/vendor/config.upstream.yaml +1349 -0
  165. package/docs/consumer-setup.md +408 -0
  166. package/docs/declared.md +89 -0
  167. package/docs/machine-setup.md +399 -0
  168. package/docs/operations.md +276 -0
  169. package/docs/releasing.md +103 -0
  170. package/docs/state.md +254 -0
  171. package/docs/topology.md +67 -0
  172. package/docs/ubuntu-sync.md +351 -0
  173. package/docs/worker-contract.md +67 -0
  174. package/examples/fleet.json +100 -0
  175. package/examples/roster.json +13 -0
  176. package/llms.txt +35 -0
  177. package/mise.lock +1521 -0
  178. package/mise.toml +110 -0
  179. package/package.json +74 -4
  180. package/schema/fleet.schema.json +420 -0
  181. package/schema/roster.schema.json +77 -0
  182. package/src/agents/account-relay.ts +111 -0
  183. package/src/agents/accounts.ts +85 -0
  184. package/src/agents/audit.ts +77 -0
  185. package/src/agents/cli.ts +13 -0
  186. package/src/agents/command.ts +204 -0
  187. package/src/agents/converge.ts +123 -0
  188. package/src/agents/doctor.ts +107 -0
  189. package/src/agents/errors.ts +2 -0
  190. package/src/agents/files.ts +73 -0
  191. package/src/agents/fleet-verify.ts +165 -0
  192. package/src/agents/launch.ts +290 -0
  193. package/src/agents/native-verify.ts +371 -0
  194. package/src/agents/provider-errors.ts +57 -0
  195. package/src/agents/registry.ts +61 -0
  196. package/src/agents/render.ts +110 -0
  197. package/src/agents/round-trip.ts +192 -0
  198. package/src/agents/secrets.ts +13 -0
  199. package/src/agents/toolchain.ts +34 -0
  200. package/src/agents/verify-cache.ts +103 -0
  201. package/src/agents/verify.ts +127 -0
  202. package/src/auth-checks.ts +163 -0
  203. package/src/cli/agent-guide.ts +129 -0
  204. package/src/cli/app.ts +122 -0
  205. package/src/cli/cli.ts +28 -0
  206. package/src/cli/command-index.ts +57 -0
  207. package/src/cli/contracts.ts +25 -0
  208. package/src/cli/fleet-reference.ts +17 -0
  209. package/src/cli/fleet-state.ts +499 -0
  210. package/src/cli/fleet-sync-checkout.ts +130 -0
  211. package/src/cli/fleet-sync-enrollment.ts +140 -0
  212. package/src/cli/fleet-sync-forward.ts +134 -0
  213. package/src/cli/fleet-sync-manager.ts +249 -0
  214. package/src/cli/fleet-sync-monitoring.ts +185 -0
  215. package/src/cli/fleet-sync-output.ts +317 -0
  216. package/src/cli/fleet-sync-privilege.ts +27 -0
  217. package/src/cli/fleet-sync-progress.ts +109 -0
  218. package/src/cli/fleet-sync-remote.ts +242 -0
  219. package/src/cli/fleet-sync-report.ts +26 -0
  220. package/src/cli/fleet-sync-schedule.ts +138 -0
  221. package/src/cli/fleet-sync-state.ts +86 -0
  222. package/src/cli/fleet-sync-vault.ts +166 -0
  223. package/src/cli/fleet-sync.ts +375 -0
  224. package/src/cli/fleet.ts +165 -0
  225. package/src/cli/managed-health.ts +219 -0
  226. package/src/cli/node-deployment.ts +34 -0
  227. package/src/cli/node-sync.ts +121 -0
  228. package/src/cli/node-t3.ts +31 -0
  229. package/src/cli/options.ts +141 -0
  230. package/src/cli/probe-source.ts +7 -0
  231. package/src/cli/program.ts +185 -0
  232. package/src/cli/project.ts +51 -0
  233. package/src/cli/register-fleet.ts +296 -0
  234. package/src/cli/registry.ts +185 -0
  235. package/src/cli/repair-apply.ts +67 -0
  236. package/src/cli/repair-guard.ts +11 -0
  237. package/src/cli/repair-inspect.ts +150 -0
  238. package/src/cli/repair-script.ts +76 -0
  239. package/src/cli/repair.ts +209 -0
  240. package/src/cli/roster.ts +105 -0
  241. package/src/cli/search.ts +35 -0
  242. package/src/cli/settings.ts +67 -0
  243. package/src/cli/source-operations.ts +102 -0
  244. package/src/cli/t3/command.ts +90 -0
  245. package/src/cli/t3/register.ts +55 -0
  246. package/src/cli/tailnet.ts +127 -0
  247. package/src/cli/ui-actions.ts +133 -0
  248. package/src/cli/ui-controller.ts +113 -0
  249. package/src/cli/ui-data.ts +106 -0
  250. package/src/cli/ui-execute.ts +163 -0
  251. package/src/cli/ui-repairs.ts +34 -0
  252. package/src/cli/ui-reports.ts +211 -0
  253. package/src/cli/ui-targets.ts +100 -0
  254. package/src/cli/ui-terminal.ts +109 -0
  255. package/src/cli/ui-types.ts +49 -0
  256. package/src/cli/ui-wizard.ts +101 -0
  257. package/src/cli/ui.ts +9 -0
  258. package/src/cli/worker-health-output.ts +37 -0
  259. package/src/config.ts +186 -0
  260. package/src/configure.ts +255 -0
  261. package/src/definition.ts +290 -0
  262. package/src/diagnostics.ts +92 -0
  263. package/src/doctor-check.ts +10 -0
  264. package/src/errors.ts +1 -0
  265. package/src/fingerprint.ts +120 -0
  266. package/src/fleet-config.ts +174 -0
  267. package/src/input-hashes.ts +73 -0
  268. package/src/machine/local-source.ts +52 -0
  269. package/src/machine/lock-error.ts +2 -0
  270. package/src/machine/platform.ts +44 -0
  271. package/src/machine/source.ts +68 -0
  272. package/src/machine/state.ts +173 -0
  273. package/src/machine-health.ts +95 -0
  274. package/src/managed-source.ts +47 -0
  275. package/src/manager-failure.ts +48 -0
  276. package/src/manager-sync.ts +405 -0
  277. package/src/manager.ts +62 -0
  278. package/src/monitoring/agent-evidence.ts +146 -0
  279. package/src/monitoring/apply.ts +183 -0
  280. package/src/monitoring/beszel.ts +198 -0
  281. package/src/monitoring/enroll.ts +111 -0
  282. package/src/monitoring/hub-access.ts +114 -0
  283. package/src/monitoring/hub-backup.ts +121 -0
  284. package/src/monitoring/hub-viewer.ts +142 -0
  285. package/src/monitoring/hub.ts +93 -0
  286. package/src/monitoring/manager-agent.ts +73 -0
  287. package/src/monitoring/manager-hub.ts +248 -0
  288. package/src/monitoring/remote.ts +65 -0
  289. package/src/network-definition.ts +83 -0
  290. package/src/node-converge-linux.ts +374 -0
  291. package/src/node-converge-shared.ts +83 -0
  292. package/src/node-converge.ts +40 -0
  293. package/src/onboard.ts +35 -0
  294. package/src/package-info.ts +45 -0
  295. package/src/platform/ansible-receipt.ts +128 -0
  296. package/src/platform/ansible.ts +66 -0
  297. package/src/platform/darwin.ts +203 -0
  298. package/src/platform/detect.ts +11 -0
  299. package/src/platform/fallback.ts +110 -0
  300. package/src/platform/index.ts +38 -0
  301. package/src/platform/launchd.ts +98 -0
  302. package/src/platform/linux.ts +139 -0
  303. package/src/platform/ps.ts +45 -0
  304. package/src/platform/resource-verify.ts +132 -0
  305. package/src/platform/systemd-user.ts +95 -0
  306. package/src/platform/t3-policy.ts +137 -0
  307. package/src/platform/t3-route.ts +68 -0
  308. package/src/platform/t3-service.ts +75 -0
  309. package/src/platform/types.ts +123 -0
  310. package/src/platform/ubuntu-audit.ts +66 -0
  311. package/src/platform/ubuntu-verify.ts +105 -0
  312. package/src/platform/windows.ts +148 -0
  313. package/src/power-recovery.ts +55 -0
  314. package/src/probe.ts +51 -0
  315. package/src/protocol.ts +119 -0
  316. package/src/proxy-converge.ts +85 -0
  317. package/src/remote-bootstrap.ts +210 -0
  318. package/src/remote-entry.ts +89 -0
  319. package/src/remote-lock.ts +12 -0
  320. package/src/repair-catalog.ts +206 -0
  321. package/src/repair-plan.ts +78 -0
  322. package/src/repair-runner.ts +84 -0
  323. package/src/report.ts +71 -0
  324. package/src/runner.ts +104 -0
  325. package/src/secrets.ts +169 -0
  326. package/src/service-auth.ts +115 -0
  327. package/src/ssh.ts +135 -0
  328. package/src/state/bucket.ts +208 -0
  329. package/src/state/check.ts +74 -0
  330. package/src/state/connection.ts +92 -0
  331. package/src/state/enrollment.ts +45 -0
  332. package/src/state/legacy.ts +89 -0
  333. package/src/state/litestream.ts +143 -0
  334. package/src/state/operator.ts +20 -0
  335. package/src/state/paths.ts +40 -0
  336. package/src/state/roster.ts +62 -0
  337. package/src/state/store.ts +169 -0
  338. package/src/state/sync.ts +352 -0
  339. package/src/sync.ts +104 -0
  340. package/src/t3/accounts.ts +81 -0
  341. package/src/t3/desired.ts +165 -0
  342. package/src/t3/files.ts +81 -0
  343. package/src/t3/names-check.ts +53 -0
  344. package/src/t3/names.ts +155 -0
  345. package/src/t3/plan.ts +82 -0
  346. package/src/t3/providers.ts +74 -0
  347. package/src/t3/reconcile.ts +123 -0
  348. package/src/t3-endpoint.ts +104 -0
  349. package/src/t3-linked-check.ts +123 -0
  350. package/src/tailnet-health.ts +231 -0
  351. package/src/tailnet-matching.ts +37 -0
  352. package/src/tailscale.ts +141 -0
  353. package/src/toolchain-checks.ts +108 -0
  354. package/src/topology.ts +24 -0
  355. package/src/transport.ts +122 -0
  356. package/src/types.ts +99 -0
  357. package/src/vault-config.ts +38 -0
  358. package/src/worker-access-checks.ts +168 -0
  359. package/src/worker-capacity.ts +34 -0
  360. package/src/worker-host-checks.ts +181 -0
  361. package/src/worker-policy.ts +64 -0
  362. package/src/worker-readiness.ts +64 -0
  363. package/src/workstation-checks.ts +29 -0
@@ -0,0 +1,74 @@
1
+ # Run on the manager only, through hub-darwin.yml from the verified checkout.
2
+ # Never copy the hub data to a worker or run a second copy of its identity.
3
+ name: personal-beszel
4
+ services:
5
+ hub:
6
+ image: henrygd/beszel:0.21.1@sha256:8514a69d9bbbcb74f371a3205561ee231fc0289739cea64e8131f341d695b770
7
+ restart: unless-stopped
8
+ ports:
9
+ - '127.0.0.1:8090:8090'
10
+ environment:
11
+ APP_URL: '${BESZEL_APP_URL:-${BESZEL_HUB_URL:?private HTTPS hub URL required}}'
12
+ DISABLE_SSH: 'true'
13
+ CHECK_UPDATES: 'false'
14
+ # Single-operator private hub: fleet-registered systems are visible to every login.
15
+ SHARE_ALL_SYSTEMS: 'true'
16
+ # Visitors are signed in automatically as this read-only viewer account
17
+ # (never the fleet admin), so the dashboard needs no login. Empty keeps
18
+ # the normal login page.
19
+ AUTO_LOGIN: '${BESZEL_AUTO_LOGIN:-}'
20
+ # First start only (empty data directory): Beszel creates the fleet login
21
+ # as a verified user and superuser. hub-darwin.yml sets these just once.
22
+ USER_EMAIL: '${BESZEL_BOOTSTRAP_EMAIL:-}'
23
+ USER_PASSWORD: '${BESZEL_BOOTSTRAP_PASSWORD:-}'
24
+ volumes:
25
+ - type: bind
26
+ source: '${BESZEL_DATA_DIR:?per-user hub data directory required}'
27
+ target: /beszel_data
28
+ bind:
29
+ create_host_path: false
30
+ logging:
31
+ driver: local
32
+ options:
33
+ max-size: '10m'
34
+ max-file: '3'
35
+ mem_limit: 512m
36
+ cpus: 1.0
37
+ pids_limit: 128
38
+ stop_grace_period: 30s
39
+ # Outbound-only Cloudflare Tunnel for the public dashboard hostname. The
40
+ # connector token is delivered from the manager's Vault session to a 0600
41
+ # per-user file by hub-darwin.yml.
42
+ cloudflared:
43
+ profiles: [public-endpoint]
44
+ image: cloudflare/cloudflared:2026.9.3@sha256:072c067d25ccbe61d46e18f0d0723255f2bb5304f7317caa95b27031520ff92c
45
+ user: '1000:1000'
46
+ restart: unless-stopped
47
+ command:
48
+ [
49
+ 'tunnel',
50
+ '--no-autoupdate',
51
+ 'run',
52
+ '--token-file',
53
+ '/run/secrets/tunnel-token',
54
+ ]
55
+ volumes:
56
+ - type: bind
57
+ source: '${BESZEL_TUNNEL_TOKEN_FILE:?per-user connector token file required}'
58
+ target: /run/secrets/tunnel-token
59
+ read_only: true
60
+ bind:
61
+ create_host_path: false
62
+ depends_on:
63
+ - hub
64
+ read_only: true
65
+ cap_drop: [ALL]
66
+ security_opt: ['no-new-privileges:true']
67
+ mem_limit: 256m
68
+ cpus: 0.5
69
+ pids_limit: 128
70
+ logging:
71
+ driver: local
72
+ options:
73
+ max-size: '5m'
74
+ max-file: '2'
@@ -0,0 +1,102 @@
1
+ ---
2
+ # The Beszel hub runs only on the manager (the human's macOS machine) under
3
+ # Docker Desktop, as that user: no become, per-user data and credential paths.
4
+ # Runtime variables arrive through a private 0600 variables file, never argv.
5
+ - name: Converge the Beszel hub on the manager
6
+ hosts: localhost
7
+ connection: local
8
+ become: false
9
+ gather_facts: true
10
+ vars:
11
+ beszel_lock: "{{ lookup('ansible.builtin.file', 'lock.json') | from_json }}"
12
+ beszel_compose:
13
+ - docker
14
+ - compose
15
+ - '-f'
16
+ - '{{ playbook_dir }}/compose.yml'
17
+ beszel_environment:
18
+ BESZEL_HUB_URL: '{{ beszel_hub_url }}'
19
+ BESZEL_APP_URL: '{{ beszel_public_url | default(beszel_hub_url) }}'
20
+ BESZEL_DATA_DIR: '{{ beszel_data_dir }}'
21
+ BESZEL_AUTO_LOGIN: '{{ beszel_auto_login | default("") }}'
22
+ BESZEL_TUNNEL_TOKEN_FILE: '{{ beszel_tunnel_dir }}/token'
23
+ tasks:
24
+ - name: Require the macOS manager and private dashboard policy
25
+ ansible.builtin.assert:
26
+ that:
27
+ - ansible_facts.os_family == 'Darwin'
28
+ - beszel_hub_url is match('^https://[a-z0-9.-]+\.ts\.net:8443$')
29
+ - beszel_data_dir is match('^/Users/[^/]+/Library/Application Support/personal-beszel$')
30
+ - beszel_tunnel_dir is match('^/Users/[^/]+/Library/Application Support/personal-beszel-tunnel$')
31
+ fail_msg: BESZEL_NOT_MANAGER; the hub runs only on the macOS manager with per-user data.
32
+ - name: Require a running Docker Desktop engine with Compose
33
+ ansible.builtin.command:
34
+ argv: [docker, compose, version]
35
+ changed_when: false
36
+ - name: Inspect an existing hub image before any upgrade
37
+ ansible.builtin.command:
38
+ argv:
39
+ [
40
+ docker,
41
+ inspect,
42
+ '--format',
43
+ '{{ "{{.Config.Image}}" }}',
44
+ personal-beszel-hub-1,
45
+ ]
46
+ changed_when: false
47
+ failed_when: false
48
+ register: existing_hub
49
+ - name: Require explicit maintenance for a different existing image
50
+ ansible.builtin.assert:
51
+ that: existing_hub.rc != 0 or existing_hub.stdout == beszel_lock.image
52
+ fail_msg: BESZEL_HUB_UPGRADE_PENDING; take and verify a backup before an explicit maintenance upgrade.
53
+ - name: Create per-user hub data and connector directories without replacing data
54
+ ansible.builtin.file:
55
+ path: '{{ item }}'
56
+ state: directory
57
+ mode: '0700'
58
+ loop:
59
+ - '{{ beszel_data_dir }}'
60
+ - '{{ beszel_tunnel_dir }}'
61
+ - name: Write the public dashboard connector token for the current user only
62
+ ansible.builtin.copy:
63
+ dest: '{{ beszel_tunnel_dir }}/token'
64
+ content: '{{ beszel_tunnel_token }}'
65
+ mode: '0600'
66
+ when: beszel_tunnel_token | default('') | length > 0
67
+ no_log: true
68
+ - name: Detect a hub that has never started (no database yet)
69
+ ansible.builtin.stat:
70
+ path: '{{ beszel_data_dir }}/data.db'
71
+ register: hub_database
72
+ - name: Start pinned stock Compose service
73
+ ansible.builtin.command:
74
+ argv: "{{ beszel_compose + ['up', '-d', '--pull', 'missing', 'hub'] }}"
75
+ # A fresh hub creates the fleet login itself on first start, so no one
76
+ # has to create the account by hand. Existing data is never changed.
77
+ environment: >-
78
+ {{ beszel_environment | combine({
79
+ 'BESZEL_BOOTSTRAP_EMAIL': beszel_fleet_identity,
80
+ 'BESZEL_BOOTSTRAP_PASSWORD': beszel_fleet_password
81
+ } if (not hub_database.stat.exists
82
+ and beszel_fleet_password | default('') | length > 0)
83
+ else {}) }}
84
+ no_log: true
85
+ register: hub_apply
86
+ changed_when: "'Started' in hub_apply.stderr or 'Created' in hub_apply.stderr or 'Recreated' in hub_apply.stderr"
87
+ - name: Start the public connector when its credential is present
88
+ ansible.builtin.command:
89
+ argv: "{{ beszel_compose + ['--profile', 'public-endpoint', 'up', '-d', '--pull', 'missing'] }}"
90
+ environment: '{{ beszel_environment }}'
91
+ when: beszel_tunnel_token | default('') | length > 0
92
+ register: tunnel_apply
93
+ changed_when: "'Started' in tunnel_apply.stderr or 'Created' in tunnel_apply.stderr or 'Recreated' in tunnel_apply.stderr"
94
+ - name: Independently verify the local hub health endpoint
95
+ ansible.builtin.uri:
96
+ url: http://127.0.0.1:8090/api/health
97
+ status_code: 200
98
+ return_content: false
99
+ retries: 10
100
+ delay: 2
101
+ register: hub_health
102
+ until: hub_health.status == 200
@@ -0,0 +1,51 @@
1
+ ---
2
+ # The manager's hub is reached over the tailnet only: Tailscale Serve
3
+ # terminates HTTPS on the declared port and proxies to the loopback hub. Never
4
+ # Funnel. A port that already has any owner is left alone: fleet-sync refuses
5
+ # a conflicting owner before this runs and reads the route back afterwards.
6
+ - name: Serve the manager hub privately over the tailnet
7
+ hosts: localhost
8
+ connection: local
9
+ gather_facts: false
10
+ vars:
11
+ beszel_serve:
12
+ [
13
+ '{{ beszel_tailscale_cli }}',
14
+ serve,
15
+ --bg,
16
+ '--https={{ beszel_route_port }}',
17
+ '{{ beszel_route_target }}',
18
+ ]
19
+ tasks:
20
+ - name: Require the declared loopback target and port
21
+ ansible.builtin.assert:
22
+ that:
23
+ - "beszel_route_target is match('^http://127\\.0\\.0\\.1:[0-9]+$')"
24
+ - "beszel_route_port is match('^[0-9]+$')"
25
+ - "beszel_tailscale_cli is match('^/')"
26
+
27
+ - name: Read this node's Serve configuration
28
+ ansible.builtin.command:
29
+ argv: ['{{ beszel_tailscale_cli }}', serve, status, --json]
30
+ register: beszel_serve_status
31
+ changed_when: false
32
+ - name: Publish the hub on the tailnet HTTPS port as the signed-in user
33
+ ansible.builtin.command:
34
+ argv: '{{ beszel_serve }}'
35
+ vars:
36
+ serve: "{{ beszel_serve_status.stdout | default('{}', true) | from_json }}"
37
+ when:
38
+ - beszel_route_port not in (serve.TCP | default({}))
39
+ - "(serve.Web | default({})) | select('search', ':' ~ beszel_route_port ~ '$') | list | length == 0"
40
+ register: beszel_served
41
+ changed_when: beszel_served.rc == 0
42
+ failed_when: false
43
+ # The standalone daemon accepts Serve changes from an administrator only.
44
+ - name: Publish the hub with administrator rights where the daemon requires them
45
+ ansible.builtin.command:
46
+ argv: '{{ beszel_serve }}'
47
+ become: true
48
+ when:
49
+ - beszel_served is not skipped
50
+ - beszel_served.rc != 0
51
+ changed_when: true
@@ -0,0 +1,16 @@
1
+ {
2
+ "version": "0.21.1",
3
+ "image": "henrygd/beszel:0.21.1@sha256:8514a69d9bbbcb74f371a3205561ee231fc0289739cea64e8131f341d695b770",
4
+ "tunnelImage": "cloudflare/cloudflared:2026.9.3@sha256:072c067d25ccbe61d46e18f0d0723255f2bb5304f7317caa95b27031520ff92c",
5
+ "agents": {
6
+ "amd64": "3b6ee592632ec03b9a43312e99a40203a7b3d83fe40dd0c25acda1eb915ea7c0",
7
+ "arm64": "a6ea544976b9a74398ac88d5da5bb96813d9a2e353a37bbfdb6a903fa8ba1838",
8
+ "darwin-arm64": "9246065f680cdb0af2885b7bf5876dc45b754019f2c90eb28550cf45db978eb8",
9
+ "darwin-amd64": "21f4a03fd90ae46869cfcbb69e21e8312f0f05deff49669d4602a3dee4866981"
10
+ },
11
+ "agentBinaries": {
12
+ "amd64": "5c84416b63028f7ad86ebafa4c8a7a27977d485a838de357216ded3ab844b2b5",
13
+ "arm64": "eb4e7ac9340d380b19ab18261301b18813c1e46ebab5f0e4bf8cb6c86b4d5c20"
14
+ },
15
+ "source": "https://github.com/henrygd/beszel/releases/tag/v0.21.1"
16
+ }
@@ -0,0 +1,13 @@
1
+ # Read-only health probe that `dev-fleet net check` pipes to each host over SSH (`sh -s`).
2
+ # Prints key=value lines; works on macOS and Linux (including WSL).
3
+ if [ "$(uname)" = Darwin ]; then
4
+ echo "ncpu=$(sysctl -n hw.ncpu)"
5
+ echo "load=$(sysctl -n vm.loadavg | awk '{print $2}')"
6
+ echo "memfree=$(memory_pressure -Q 2>/dev/null | awk -F': ' '/percentage/{gsub("%","",$2); print $2}')"
7
+ sysctl -n vm.swapusage | awk '{t=$3; u=$6; gsub("M","",t); gsub("M","",u); if (t>0) print "swap=" int(u*100/t); else print "swap=0"}'
8
+ else
9
+ echo "ncpu=$(nproc)"
10
+ echo "load=$(cut -d' ' -f1 /proc/loadavg)"
11
+ awk '/MemTotal/{t=$2} /MemAvailable/{a=$2} /SwapTotal/{st=$2} /SwapFree/{sf=$2} END{print "memfree=" int(a*100/t); if (st>0) print "swap=" int((st-sf)*100/st); else print "swap=0"}' /proc/meminfo
12
+ fi
13
+ echo "diskmb=$(df -Pk "$HOME" | awk 'NR==2{print int($4/1024)}')"
@@ -0,0 +1,12 @@
1
+ # The Ansible the manager runs these playbooks with: one release, on one
2
+ # Python, with every dependency hash-locked in uv.lock. A worker runs the
3
+ # exact ansible-core package ubuntu/policy.json names instead. Change the
4
+ # version here, then run `uv lock` in this directory.
5
+ [project]
6
+ name = "dev-fleet-ansible"
7
+ version = "0"
8
+ requires-python = "==3.13.16"
9
+ dependencies = ["ansible-core==2.19.9"]
10
+
11
+ [tool.uv]
12
+ package = false
@@ -0,0 +1,51 @@
1
+ #!/bin/sh
2
+ # Manager-led, explicitly approved initial privilege step. Run through a real
3
+ # SSH terminal so sudo reads the operator's password itself; never capture it.
4
+ set -eu
5
+ umask 077
6
+ case "$(uname -s)" in
7
+ Linux)
8
+ . /etc/os-release
9
+ [ "$ID" = ubuntu ] && [ "$VERSION_ID" = 26.04 ] || exit 1 ;;
10
+ Darwin)
11
+ [ "$(sw_vers -productVersion | cut -d. -f1)" -ge 14 ] || exit 1 ;;
12
+ *) exit 1 ;;
13
+ esac
14
+ user=$(id -un)
15
+ case "$user" in root|''|*[!a-z0-9_-]*) exit 1;; esac
16
+ command -v sudo >/dev/null 2>&1 || exit 1
17
+ # The same operation lock used by remote bootstrap excludes another installer.
18
+ root="$HOME/.local/share/personal/fleet"
19
+ for path in "$HOME/.local" "$HOME/.local/share" "$HOME/.local/share/personal" "$root"; do
20
+ [ ! -L "$path" ] || exit 1
21
+ done
22
+ mkdir -p "$root"
23
+ chmod 700 "$root"
24
+ [ ! -L "$root/remote-operation.lock" ] || exit 1
25
+ exec 9>"$root/remote-operation.lock"
26
+ if command -v flock >/dev/null 2>&1; then
27
+ flock -n 9 || exit 1
28
+ else
29
+ python3 -c 'import fcntl; fcntl.flock(9, fcntl.LOCK_EX | fcntl.LOCK_NB)' 9>&9 || exit 1
30
+ fi
31
+ sudo -v
32
+ file=$(mktemp)
33
+ trap 'rm -f "$file"' EXIT
34
+ trap 'exit 1' HUP INT TERM
35
+ printf '# Managed by personal Fleet: designated development user\n%s ALL=(ALL:ALL) NOPASSWD: ALL\n' "$user" >"$file"
36
+ sudo -n visudo -cf "$file" >/dev/null
37
+ # This dedicated path is owned by the same Workstation baseline. Preserve any
38
+ # existing fragment for recovery before installing the validated declaration.
39
+ target="/etc/sudoers.d/personal-fleet-$user"
40
+ if sudo -n test -e "$target"; then
41
+ sudo -n test ! -L "$target" || exit 1
42
+ sudo -n grep -qxF '# Managed by personal Fleet: designated development user' "$target" || {
43
+ printf '%s\n' 'SUDOERS_OWNERSHIP_CONFLICT: preserve the existing fragment and review its source before migration.' >&2
44
+ exit 1
45
+ }
46
+ sudo -n cp -p -- "$target" "$target.before-fleet-enrollment"
47
+ fi
48
+ group=root
49
+ [ "$(uname -s)" != Darwin ] || group=wheel
50
+ sudo -n install -o root -g "$group" -m 0440 -- "$file" "$target"
51
+ sudo -n true
@@ -0,0 +1,89 @@
1
+ ---
2
+ - name: Apply reviewed Ubuntu development-server policy
3
+ hosts: localhost
4
+ connection: local
5
+ become: true
6
+ gather_facts: true
7
+ vars_files:
8
+ - policy.json
9
+ pre_tasks:
10
+ - name: Require the supported common release and an explicit non-root user
11
+ ansible.builtin.assert:
12
+ that:
13
+ - ansible_facts.distribution == 'Ubuntu'
14
+ - ansible_facts.distribution_version == release
15
+ - ansible_facts.architecture in ['x86_64', 'aarch64']
16
+ - development_user is match('^[a-z_][a-z0-9_-]{0,31}$')
17
+ - development_user != 'root'
18
+ fail_msg: Unsupported platform or user; no distribution upgrade is permitted.
19
+ - name: Read the designated existing account
20
+ ansible.builtin.getent:
21
+ database: passwd
22
+ key: '{{ development_user }}'
23
+ - name: Refuse a different user identity or home
24
+ ansible.builtin.assert:
25
+ that:
26
+ - ansible_facts.getent_passwd[development_user][1] | int == development_uid | int
27
+ - ansible_facts.getent_passwd[development_user][4] == development_home
28
+ # Memory and disk safety must land even when the disk is nearly full.
29
+ - ansible.builtin.import_tasks: tasks/resources.yml
30
+ - name: Inspect capacity before package and service changes
31
+ ansible.builtin.command:
32
+ argv:
33
+ - /usr/bin/python3
34
+ - '-c'
35
+ - |
36
+ import json, os
37
+ print(json.dumps([{"path": p, "freeMiB": (v.f_bavail * v.f_frsize) // (1024 * 1024), "freeInodes": v.f_favail} for p in ['/', '/var', os.environ['FLEET_HOME']] for v in [os.statvfs(p)]]))
38
+ environment:
39
+ FLEET_HOME: '{{ development_home }}'
40
+ changed_when: false
41
+ register: capacity
42
+ - name: Preserve management capacity instead of installing into an exhausted disk
43
+ ansible.builtin.assert:
44
+ that:
45
+ - item.freeMiB >= minimumFreeDiskMiB
46
+ - item.freeInodes >= minimumFreeInodes
47
+ fail_msg: RESOURCE_CAPACITY_REQUIRED; inspect disk space and inodes on the reported filesystem. Preserve developer work; the disk GC removes only build output and caches, so what remains needs a person.
48
+ loop: '{{ capacity.stdout | from_json }}'
49
+ - name: Inspect installed packages
50
+ ansible.builtin.package_facts:
51
+ manager: apt
52
+ - name: Preserve Docker installations owned by a different distribution
53
+ ansible.builtin.assert:
54
+ that: "'docker-ce' not in ansible_facts.packages and 'containerd.io' not in ansible_facts.packages"
55
+ fail_msg: DOCKER_OWNER_CONFLICT; migrate the existing Docker owner explicitly before applying the Ubuntu-native baseline.
56
+ - name: Inspect existing service ownership
57
+ ansible.builtin.service_facts:
58
+ - name: Wait for legitimate package transactions before inspecting dpkg
59
+ ansible.builtin.command:
60
+ argv:
61
+ - /usr/bin/python3
62
+ - '-c'
63
+ - |
64
+ import fcntl
65
+ files = [open(path, 'a') for path in ['/var/lib/dpkg/lock-frontend', '/var/lib/dpkg/lock']]
66
+ for handle in files:
67
+ fcntl.lockf(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
68
+ register: apt_idle
69
+ changed_when: false
70
+ retries: 24
71
+ delay: 5
72
+ until: apt_idle.rc == 0
73
+ - name: Inspect package database consistency
74
+ ansible.builtin.command: dpkg --audit
75
+ changed_when: false
76
+ register: dpkg_audit
77
+ - name: Require package database recovery before modifying packages
78
+ ansible.builtin.assert:
79
+ that: dpkg_audit.stdout | trim == ''
80
+ fail_msg: APT_STATE_BROKEN; inspect dpkg --audit and use reviewed package recovery. Never remove package lock files.
81
+ tasks:
82
+ - ansible.builtin.import_tasks: tasks/packages.yml
83
+ - ansible.builtin.import_tasks: tasks/docker.yml
84
+ - ansible.builtin.import_tasks: tasks/services.yml
85
+ - ansible.builtin.import_tasks: tasks/power.yml
86
+ - ansible.builtin.import_tasks: tasks/user.yml
87
+ - ansible.builtin.import_tasks: tasks/ssh.yml
88
+ - ansible.builtin.import_tasks: tasks/credentials.yml
89
+ when: development_source is defined