@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,220 @@
1
+ ---
2
+ # The worker's only T3 process: the pinned desktop app, which embeds the server
3
+ # on 127.0.0.1:3773. GDM logs the designated user in automatically and the user
4
+ # unit starts the app with the graphical session, so the UI is on screen and the
5
+ # server is up after every boot. Starting or migrating is left to fleet-sync.
6
+ #
7
+ # The upstream .deb declares libxss1, which Ubuntu 26.04 no longer ships and the
8
+ # binary does not link. Instead of patching the package or leaving apt with an
9
+ # unmet dependency, the verified package payload is unpacked to its own path and
10
+ # its real runtime libraries and bundled AppArmor profile are installed.
11
+ - name: Install the pinned T3 Code desktop app and start it with the session
12
+ hosts: localhost
13
+ connection: local
14
+ become: true
15
+ gather_facts: false
16
+ vars:
17
+ t3_desktop_dir: /opt/T3 Code (Nightly)
18
+ # The package's Depends without libxss1 (see above).
19
+ t3_desktop_libraries:
20
+ - libasound2t64
21
+ - libatspi2.0-0t64
22
+ - libgbm1
23
+ - libgtk-3-0t64
24
+ - libnotify4
25
+ - libnss3
26
+ - libsecret-1-0
27
+ - libuuid1
28
+ - libxtst6
29
+ - xdg-utils
30
+ tasks:
31
+ - name: Create the package cache
32
+ ansible.builtin.file:
33
+ path: /var/cache/personal-fleet
34
+ state: directory
35
+ owner: root
36
+ group: root
37
+ mode: '0755'
38
+ - name: Download the pinned desktop package
39
+ ansible.builtin.get_url:
40
+ url: '{{ t3_desktop_url }}'
41
+ dest: '/var/cache/personal-fleet/{{ t3_desktop_name }}'
42
+ checksum: 'sha256:{{ t3_desktop_sha256 }}'
43
+ mode: '0644'
44
+ timeout: 300
45
+ - name: Install the desktop runtime libraries
46
+ ansible.builtin.apt:
47
+ name: '{{ t3_desktop_libraries }}'
48
+ state: present
49
+ lock_timeout: 120
50
+ - name: Read the unpacked app version
51
+ ansible.builtin.command:
52
+ argv: [cat, '{{ t3_desktop_dir }}/.personal-fleet-version']
53
+ register: t3_desktop_installed
54
+ changed_when: false
55
+ failed_when: false
56
+ - name: Unpack the pinned app when its version differs
57
+ when: t3_desktop_installed.stdout | default('') != t3_desktop_name
58
+ block:
59
+ - name: Clear the staging directory
60
+ ansible.builtin.file:
61
+ path: /opt/.t3code-staging
62
+ state: absent
63
+ - name: Unpack the verified package payload
64
+ ansible.builtin.command:
65
+ argv:
66
+ - dpkg-deb
67
+ - -x
68
+ - '/var/cache/personal-fleet/{{ t3_desktop_name }}'
69
+ - /opt/.t3code-staging
70
+ changed_when: true
71
+ - name: Record the unpacked package
72
+ ansible.builtin.copy:
73
+ dest: '/opt/.t3code-staging{{ t3_desktop_dir }}/.personal-fleet-version'
74
+ content: '{{ t3_desktop_name }}'
75
+ mode: '0644'
76
+ - name: Remove the copy kept from the previous update
77
+ ansible.builtin.file:
78
+ path: '{{ t3_desktop_dir }}.previous'
79
+ state: absent
80
+ - name: Inspect the current app directory
81
+ ansible.builtin.stat:
82
+ path: '{{ t3_desktop_dir }}'
83
+ register: t3_desktop_current
84
+ - name: Keep the current app until the next update (a running app keeps its files)
85
+ ansible.builtin.command:
86
+ argv:
87
+ [mv, -T, '{{ t3_desktop_dir }}', '{{ t3_desktop_dir }}.previous']
88
+ when: t3_desktop_current.stat.exists
89
+ changed_when: true
90
+ - name: Move the pinned app into place
91
+ ansible.builtin.command:
92
+ argv:
93
+ - mv
94
+ - -T
95
+ - '/opt/.t3code-staging{{ t3_desktop_dir }}'
96
+ - '{{ t3_desktop_dir }}'
97
+ changed_when: true
98
+ - name: Add the launcher entry and icon
99
+ ansible.builtin.copy:
100
+ src: '/opt/.t3code-staging/usr/share/{{ item }}'
101
+ dest: '/usr/share/{{ item }}'
102
+ remote_src: true
103
+ mode: '0644'
104
+ loop:
105
+ - applications/t3code.desktop
106
+ - icons/hicolor/512x512/apps/t3code.png
107
+ - name: Remove the staging directory
108
+ ansible.builtin.file:
109
+ path: /opt/.t3code-staging
110
+ state: absent
111
+ - name: Install the AppArmor profile the package ships
112
+ ansible.builtin.copy:
113
+ src: '{{ t3_desktop_dir }}/resources/apparmor-profile'
114
+ dest: /etc/apparmor.d/t3code
115
+ remote_src: true
116
+ mode: '0644'
117
+ register: t3_desktop_apparmor
118
+ - name: Load the AppArmor profile so the app sandbox can start
119
+ ansible.builtin.command:
120
+ argv:
121
+ [
122
+ apparmor_parser,
123
+ --replace,
124
+ --write-cache,
125
+ --skip-read-cache,
126
+ /etc/apparmor.d/t3code,
127
+ ]
128
+ when: t3_desktop_apparmor.changed
129
+ changed_when: true
130
+ - name: Inspect the GDM configuration
131
+ ansible.builtin.stat:
132
+ path: /etc/gdm3/custom.conf
133
+ register: gdm_config
134
+ - name: Log the designated user in automatically
135
+ ansible.builtin.lineinfile:
136
+ path: /etc/gdm3/custom.conf
137
+ regexp: '^#?\s*{{ item.key }}\s*='
138
+ line: '{{ item.key }} = {{ item.value }}'
139
+ insertafter: '^\[daemon\]'
140
+ loop:
141
+ - { key: AutomaticLoginEnable, value: 'true' }
142
+ - { key: AutomaticLogin, value: '{{ development_user }}' }
143
+ when: gdm_config.stat.exists
144
+ - name: Create the user unit directory
145
+ ansible.builtin.file:
146
+ path: '{{ development_home }}/.config/systemd/user'
147
+ state: directory
148
+ owner: '{{ development_user }}'
149
+ mode: '0700'
150
+ - name: Declare the desktop app as a session service
151
+ ansible.builtin.copy:
152
+ dest: '{{ development_home }}/.config/systemd/user/t3code-desktop.service'
153
+ owner: '{{ development_user }}'
154
+ mode: '0600'
155
+ content: |
156
+ # Managed by personal fleet-sync: ansible/ubuntu/t3-desktop.yml
157
+ [Unit]
158
+ Description=T3 Code desktop (UI and server)
159
+ PartOf=graphical-session.target
160
+ After=graphical-session.target
161
+ StartLimitIntervalSec=300
162
+ StartLimitBurst=5
163
+ [Service]
164
+ Type=simple
165
+ WorkingDirectory=%h
166
+ Environment=T3CODE_HOME=%h/.t3
167
+ # /tmp is RAM with a per-user quota that agent and build caches
168
+ # exhaust; agents use disk-backed /var/tmp (aged by tasks/resources.yml).
169
+ Environment=TMPDIR=/var/tmp
170
+ # Native Wayland hangs before the first window on Asahi (16K pages);
171
+ # XWayland works on every GNOME worker, so use it everywhere.
172
+ ExecStart="{{ t3_desktop_dir }}/t3code" --ozone-platform=x11
173
+ KillMode=mixed
174
+ Restart=always
175
+ RestartSec=5
176
+ CPUWeight=1000
177
+ IOWeight=1000
178
+ MemoryLow=1536M
179
+ OOMScoreAdjust=100
180
+ ManagedOOMPreference=avoid
181
+ [Install]
182
+ WantedBy=graphical-session.target
183
+ # Repoint recovery now: a timer still running an older revision's script
184
+ # would restart the headless server right after its approved migration.
185
+ - ansible.builtin.import_tasks: tasks/recovery.yml
186
+ - name: Enable the desktop app for every graphical login
187
+ become: true
188
+ become_user: '{{ development_user }}'
189
+ environment:
190
+ XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
191
+ ansible.builtin.systemd_service:
192
+ scope: user
193
+ name: t3code-desktop.service
194
+ daemon_reload: true
195
+ enabled: true
196
+ # One T3 process per worker: the desktop app. The former headless server
197
+ # (`t3 service install`) is retired; absent on machines that never had it.
198
+ - name: Retire the former headless T3 server
199
+ become: true
200
+ become_user: '{{ development_user }}'
201
+ environment:
202
+ XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
203
+ ansible.builtin.systemd_service:
204
+ scope: user
205
+ name: t3code.service
206
+ state: stopped
207
+ enabled: false
208
+ failed_when: false
209
+ # Starting needs the auto-login graphical session, which a machine has only
210
+ # after its first reboot; the running state is measured after this play.
211
+ - name: Start the desktop app without restarting a running one
212
+ become: true
213
+ become_user: '{{ development_user }}'
214
+ environment:
215
+ XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
216
+ ansible.builtin.systemd_service:
217
+ scope: user
218
+ name: t3code-desktop.service
219
+ state: started
220
+ failed_when: false
@@ -0,0 +1,33 @@
1
+ #!/bin/sh
2
+ # Keep the worker T3 desktop app (UI and server) up and responsive. Run every minute by
3
+ # personal-worker-recovery.timer (managed by fleet-sync, tasks/recovery.yml).
4
+ # Stopped or failed: start it. Running but its local API has not answered for
5
+ # three checks in a row: restart it, since a hung server serves no turns.
6
+ set -u
7
+ url=http://127.0.0.1:3773/.well-known/t3/environment
8
+ state="${XDG_RUNTIME_DIR:?}/personal-t3-health-failures"
9
+ limit=3
10
+
11
+ # The former headless server still runs until its approved migration; never
12
+ # start a second T3 next to it.
13
+ if systemctl --user is-active --quiet t3code.service; then
14
+ echo "Headless T3 still runs; desktop migration pending."
15
+ exit 0
16
+ fi
17
+ if ! systemctl --user is-active --quiet t3code-desktop.service; then
18
+ rm -f "$state"
19
+ echo "T3 is not running; starting it."
20
+ exec systemctl --user start t3code-desktop.service
21
+ fi
22
+ if curl --fail --silent --max-time 10 --output /dev/null "$url"; then
23
+ rm -f "$state"
24
+ exit 0
25
+ fi
26
+ failures=$(($(cat "$state" 2>/dev/null || echo 0) + 1))
27
+ echo "$failures" > "$state"
28
+ echo "T3 local API did not answer ($failures/$limit)."
29
+ if [ "$failures" -ge "$limit" ]; then
30
+ rm -f "$state"
31
+ echo "T3 is unresponsive; restarting it."
32
+ exec systemctl --user restart t3code-desktop.service
33
+ fi
@@ -0,0 +1,76 @@
1
+ ---
2
+ - name: Stage the native T3 policy and make the running app load it
3
+ hosts: localhost
4
+ connection: local
5
+ become: true
6
+ gather_facts: false
7
+ tasks:
8
+ - name: Inspect existing administrator policy
9
+ ansible.builtin.stat:
10
+ path: /etc/t3code/policy.json
11
+ register: policy_file
12
+ - name: Inspect Fleet ownership marker
13
+ ansible.builtin.stat:
14
+ path: /etc/t3code/.personal-fleet-policy
15
+ register: policy_owner
16
+ - name: Refuse to overwrite an unrelated administrator policy
17
+ ansible.builtin.assert:
18
+ that: not policy_file.stat.exists or policy_owner.stat.exists
19
+ fail_msg: T3_POLICY_OWNER_CONFLICT; review and merge the existing administrator policy in Git before adoption.
20
+ - name: Create administrator-owned policy directory
21
+ ansible.builtin.file:
22
+ path: /etc/t3code
23
+ state: directory
24
+ owner: root
25
+ group: root
26
+ mode: '0755'
27
+ - name: Record policy ownership before resumable installation
28
+ ansible.builtin.copy:
29
+ dest: /etc/t3code/.personal-fleet-policy
30
+ content: "ansible/ubuntu/t3/policy.json\n"
31
+ owner: root
32
+ group: root
33
+ mode: '0644'
34
+ - name: Stage validated policy with backup
35
+ ansible.builtin.copy:
36
+ content: "{{ t3_policy_document | default(lookup('ansible.builtin.file', 't3/policy.json') | from_json) | to_nice_json }}"
37
+ validate: '{{ t3_executable | quote }} policy check %s'
38
+ dest: /etc/t3code/policy.json
39
+ owner: root
40
+ group: root
41
+ mode: '0644'
42
+ backup: true
43
+ # T3 reads its policy once, at startup. A running app that has not loaded
44
+ # the staged policy is restarted (this ends its running turns) so every
45
+ # node offers the same providers after every sync.
46
+ - name: Check whether the desktop app is running
47
+ become: true
48
+ become_user: '{{ development_user }}'
49
+ environment:
50
+ XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
51
+ ansible.builtin.command:
52
+ argv: [systemctl, --user, is-active, --quiet, t3code-desktop.service]
53
+ register: t3_desktop_active
54
+ changed_when: false
55
+ failed_when: false
56
+ - name: Ask the running T3 which policy it loaded
57
+ become: true
58
+ become_user: '{{ development_user }}'
59
+ ansible.builtin.command:
60
+ argv: ['{{ t3_executable }}', policy, show]
61
+ register: t3_policy_loaded
62
+ changed_when: false
63
+ failed_when: false
64
+ when: t3_desktop_active.rc == 0
65
+ - name: Restart T3 so it loads the staged policy
66
+ become: true
67
+ become_user: '{{ development_user }}'
68
+ environment:
69
+ XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
70
+ ansible.builtin.systemd_service:
71
+ scope: user
72
+ name: t3code-desktop.service
73
+ state: restarted
74
+ when:
75
+ - t3_desktop_active.rc == 0
76
+ - t3_policy_loaded.stdout is not search('loaded this policy\.\s*$')
@@ -0,0 +1,28 @@
1
+ ---
2
+ # The worker's T3 is reached over the tailnet only: Tailscale Serve terminates
3
+ # HTTPS on port 443 and proxies to the loopback T3 server. Never Funnel. A
4
+ # port 443 that already has any owner is left alone (reported, never reset).
5
+ - name: Serve the worker T3 privately over the tailnet
6
+ hosts: localhost
7
+ connection: local
8
+ become: true
9
+ gather_facts: false
10
+ tasks:
11
+ - name: Require the declared loopback target
12
+ ansible.builtin.assert:
13
+ that: "t3_route_target is match('^http://127\\.0\\.0\\.1:[0-9]+$')"
14
+
15
+ - name: Read this node's Serve configuration
16
+ ansible.builtin.command:
17
+ argv: [tailscale, serve, status, --json]
18
+ register: t3_serve_status
19
+ changed_when: false
20
+ - name: Publish T3 on tailnet HTTPS port 443
21
+ ansible.builtin.command:
22
+ argv: [tailscale, serve, --bg, --https=443, '{{ t3_route_target }}']
23
+ vars:
24
+ serve: "{{ t3_serve_status.stdout | default('{}', true) | from_json }}"
25
+ when:
26
+ - "'443' not in (serve.TCP | default({}))"
27
+ - "(serve.Web | default({})) | select('search', ':443$') | list | length == 0"
28
+ changed_when: true
@@ -0,0 +1,78 @@
1
+ ---
2
+ - name: Converge native Tailscale without replacing its device identity
3
+ hosts: localhost
4
+ connection: local
5
+ become: true
6
+ gather_facts: true
7
+ # The approved version and the signing key's digest: `tailscale` in policy.json.
8
+ vars_files:
9
+ - policy.json
10
+ tasks:
11
+ - name: Require supported Ubuntu
12
+ ansible.builtin.assert:
13
+ that:
14
+ - ansible_facts.distribution == 'Ubuntu'
15
+ - ansible_facts.distribution_version == release
16
+ - name: Inspect installed packages before possible network interruption
17
+ ansible.builtin.package_facts:
18
+ manager: apt
19
+ - name: Require maintenance for an installed version change
20
+ ansible.builtin.assert:
21
+ that: "'tailscale' not in ansible_facts.packages or ansible_facts.packages.tailscale[0].version == tailscale.version"
22
+ fail_msg: TAILSCALE_UPGRADE_PENDING; upgrading an active VPN can disconnect SSH. Review an explicit maintenance operation.
23
+ - name: Inspect existing package-source ownership before writing
24
+ ansible.builtin.find:
25
+ paths: /etc/apt/sources.list.d
26
+ patterns: ['*.list', '*.sources']
27
+ contains: '.*pkgs.tailscale.com.*'
28
+ read_whole_file: true
29
+ register: existing_sources
30
+ - name: Refuse duplicate or custom sources instead of breaking APT
31
+ ansible.builtin.assert:
32
+ that:
33
+ - existing_sources.matched <= 1
34
+ - existing_sources.matched == 0 or existing_sources.files[0].path == '/etc/apt/sources.list.d/tailscale.list'
35
+ fail_msg: TAILSCALE_SOURCE_OWNER_CONFLICT; review the existing signed source and its migration before creating another repository entry.
36
+ - name: Install pinned upstream signing key
37
+ ansible.builtin.get_url:
38
+ url: https://pkgs.tailscale.com/stable/ubuntu/resolute.noarmor.gpg
39
+ dest: /usr/share/keyrings/tailscale-archive-keyring.gpg
40
+ checksum: 'sha256:{{ tailscale.keySha256 }}'
41
+ owner: root
42
+ group: root
43
+ mode: '0644'
44
+ timeout: 30
45
+ - name: Declare the signed upstream package source
46
+ ansible.builtin.apt_repository:
47
+ repo: deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/ubuntu resolute main
48
+ filename: tailscale
49
+ state: present
50
+ update_cache: false
51
+ register: tailscale_source
52
+ - name: Install the approved version only when absent
53
+ ansible.builtin.apt:
54
+ name: 'tailscale={{ tailscale.version }}'
55
+ state: present
56
+ update_cache: true
57
+ cache_valid_time: 0
58
+ lock_timeout: '{{ aptLockTimeoutSeconds }}'
59
+ allow_downgrade: false
60
+ allow_unauthenticated: false
61
+ when: "'tailscale' not in ansible_facts.packages"
62
+ - name: Enable and start the native daemon, preserving existing identity
63
+ ansible.builtin.systemd_service:
64
+ name: tailscaled.service
65
+ enabled: true
66
+ state: started
67
+ - name: Inspect native authentication without prompting or opening a browser
68
+ ansible.builtin.command:
69
+ argv: [tailscale, status, '--json']
70
+ changed_when: false
71
+ failed_when: false
72
+ register: tailscale_status
73
+ no_log: true
74
+ - name: Require genuine device authentication before claiming connectivity
75
+ ansible.builtin.assert:
76
+ that: (tailscale_status.stdout | from_json).BackendState == 'Running'
77
+ fail_msg: TAILSCALE_LOGIN_REQUIRED; authorize this device through its native login. Existing state is never copied or reset.
78
+ no_log: true
@@ -0,0 +1,47 @@
1
+ ---
2
+ - name: Create persistent worker credential renewal units
3
+ ansible.builtin.file:
4
+ path: '{{ development_home }}/.config/systemd/user'
5
+ state: directory
6
+ owner: '{{ development_user }}'
7
+ mode: '0700'
8
+ - name: Declare native self-token renewal without copying credentials
9
+ ansible.builtin.copy:
10
+ dest: '{{ development_home }}/.config/systemd/user/personal-worker-auth.service'
11
+ owner: '{{ development_user }}'
12
+ mode: '0600'
13
+ content: |
14
+ [Unit]
15
+ Description=Renew this worker's own Vault authorization
16
+ [Service]
17
+ Type=oneshot
18
+ WorkingDirectory={{ development_source }}
19
+ ExecStart={{ development_home }}/.local/bin/mise exec -- bun bootstrap/worker-auth.ts
20
+ Environment="PATH={{ development_home }}/.local/share/mise/shims:{{ development_home }}/.local/bin:/usr/local/bin:/usr/bin:/bin"
21
+ UMask=0077
22
+ TimeoutStartSec=45
23
+ - name: Declare bounded persistent renewal schedule
24
+ ansible.builtin.copy:
25
+ dest: '{{ development_home }}/.config/systemd/user/personal-worker-auth.timer'
26
+ owner: '{{ development_user }}'
27
+ mode: '0600'
28
+ content: |
29
+ [Unit]
30
+ Description=Keep enrolled worker Vault authorization renewed
31
+ [Timer]
32
+ OnBootSec=2min
33
+ OnUnitActiveSec=10min
34
+ Persistent=true
35
+ [Install]
36
+ WantedBy=timers.target
37
+ - name: Enable this user's credential renewal timer
38
+ become: true
39
+ become_user: '{{ development_user }}'
40
+ environment:
41
+ XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
42
+ ansible.builtin.systemd_service:
43
+ scope: user
44
+ name: personal-worker-auth.timer
45
+ daemon_reload: true
46
+ enabled: true
47
+ state: started
@@ -0,0 +1,31 @@
1
+ ---
2
+ - name: Rotate container logs by default (new containers, after the next Docker restart)
3
+ ansible.builtin.copy:
4
+ dest: /etc/docker/daemon.json
5
+ force: false
6
+ owner: root
7
+ group: root
8
+ mode: '0644'
9
+ content: |
10
+ {
11
+ "log-driver": "json-file",
12
+ "log-opts": { "max-size": "{{ resourceSafety.dockerLogMaxSize }}", "max-file": "{{ resourceSafety.dockerLogMaxFiles }}" }
13
+ }
14
+ validate: dockerd --validate --config-file %s
15
+ - name: Start the native Docker engine without restarting existing workloads
16
+ ansible.builtin.systemd_service:
17
+ name: docker.service
18
+ enabled: true
19
+ state: started
20
+ - name: Grant the designated developer access to the local Docker engine
21
+ ansible.builtin.user:
22
+ name: '{{ development_user }}'
23
+ groups: docker
24
+ append: true
25
+ - name: Verify native Docker Compose and Buildx installation
26
+ ansible.builtin.command:
27
+ argv: '{{ item }}'
28
+ loop:
29
+ - [docker, compose, version]
30
+ - [docker, buildx, version]
31
+ changed_when: false
@@ -0,0 +1,56 @@
1
+ ---
2
+ - name: Create owned APT configuration directory
3
+ ansible.builtin.file:
4
+ path: /etc/personal-fleet
5
+ state: directory
6
+ owner: root
7
+ group: root
8
+ mode: '0755'
9
+ - name: Declare snapshot sources used only for managed package transactions
10
+ ansible.builtin.copy:
11
+ dest: /etc/personal-fleet/ubuntu.sources
12
+ owner: root
13
+ group: root
14
+ mode: '0644'
15
+ backup: true
16
+ content: |
17
+ Types: deb
18
+ URIs: https://snapshot.ubuntu.com/ubuntu/{{ snapshot }}/
19
+ Suites: resolute resolute-updates resolute-security
20
+ Components: main universe restricted multiverse
21
+ Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
22
+ register: snapshot_sources
23
+ - name: Isolate snapshot selection from unrelated APT operations
24
+ ansible.builtin.copy:
25
+ dest: /etc/personal-fleet/apt.conf
26
+ owner: root
27
+ group: root
28
+ mode: '0644'
29
+ content: |
30
+ Dir::Etc::sourcelist "/etc/personal-fleet/ubuntu.sources";
31
+ Dir::Etc::sourceparts "-";
32
+ DPkg::Lock::Timeout "{{ aptLockTimeoutSeconds }}";
33
+ - name: Inspect immutable snapshot package index
34
+ ansible.builtin.stat:
35
+ path: '/var/lib/apt/lists/snapshot.ubuntu.com_ubuntu_{{ snapshot }}_dists_resolute_InRelease'
36
+ register: snapshot_index
37
+ - name: Fetch signed snapshot indexes after source change or interrupted setup
38
+ ansible.builtin.apt:
39
+ update_cache: true
40
+ cache_valid_time: 0
41
+ lock_timeout: '{{ aptLockTimeoutSeconds }}'
42
+ environment:
43
+ APT_CONFIG: /etc/personal-fleet/apt.conf
44
+ when: snapshot_sources.changed or not snapshot_index.stat.exists
45
+ - name: Install exact managed packages without downgrading or restarting services
46
+ ansible.builtin.apt:
47
+ name: '{{ item.key }}={{ item.value }}'
48
+ state: present
49
+ lock_timeout: '{{ aptLockTimeoutSeconds }}'
50
+ allow_downgrade: false
51
+ allow_unauthenticated: false
52
+ fail_on_autoremove: true
53
+ policy_rc_d: 101
54
+ environment:
55
+ APT_CONFIG: /etc/personal-fleet/apt.conf
56
+ loop: '{{ packages | dict2items }}'
@@ -0,0 +1,51 @@
1
+ ---
2
+ - name: Ensure systemd policy directories exist
3
+ ansible.builtin.file:
4
+ path: '{{ item }}'
5
+ state: directory
6
+ owner: root
7
+ group: root
8
+ mode: '0755'
9
+ loop:
10
+ - /etc/systemd/logind.conf.d
11
+ - /etc/systemd/sleep.conf.d
12
+ - name: Declare idle and lid policy without restarting login sessions
13
+ ansible.builtin.copy:
14
+ dest: /etc/systemd/logind.conf.d/60-personal-fleet.conf
15
+ owner: root
16
+ group: root
17
+ mode: '0644'
18
+ backup: true
19
+ content: |
20
+ # Managed by personal fleet-sync. Effective after logind reload/reboot.
21
+ [Login]
22
+ HandleLidSwitch=ignore
23
+ HandleLidSwitchExternalPower=ignore
24
+ HandleLidSwitchDocked=ignore
25
+ IdleAction=ignore
26
+ KillUserProcesses=no
27
+ StopIdleSessionSec=infinity
28
+ - name: Declare supported sleep policy
29
+ ansible.builtin.copy:
30
+ dest: /etc/systemd/sleep.conf.d/60-personal-fleet.conf
31
+ owner: root
32
+ group: root
33
+ mode: '0644'
34
+ backup: true
35
+ content: |
36
+ [Sleep]
37
+ AllowSuspend=no
38
+ AllowHibernation=no
39
+ AllowSuspendThenHibernate=no
40
+ AllowHybridSleep=no
41
+ - name: Mask sleep transitions without stopping running user sessions
42
+ ansible.builtin.systemd_service:
43
+ name: '{{ item }}'
44
+ masked: true
45
+ daemon_reload: true
46
+ loop:
47
+ - sleep.target
48
+ - suspend.target
49
+ - hibernate.target
50
+ - hybrid-sleep.target
51
+ - suspend-then-hibernate.target
@@ -0,0 +1,39 @@
1
+ ---
2
+ - name: Declare bounded recovery of the worker T3 desktop app
3
+ ansible.builtin.copy:
4
+ dest: '{{ development_home }}/.config/systemd/user/personal-worker-recovery.service'
5
+ owner: '{{ development_user }}'
6
+ mode: '0600'
7
+ content: |
8
+ # Managed by personal fleet-sync: ansible/ubuntu/t3-health.sh
9
+ [Unit]
10
+ Description=Keep the worker T3 desktop app running and responsive
11
+ [Service]
12
+ Type=oneshot
13
+ ExecStart=/bin/sh {{ development_source }}/ansible/ubuntu/t3-health.sh
14
+ TimeoutStartSec=90
15
+ - name: Declare persistent service recovery checks
16
+ ansible.builtin.copy:
17
+ dest: '{{ development_home }}/.config/systemd/user/personal-worker-recovery.timer'
18
+ owner: '{{ development_user }}'
19
+ mode: '0600'
20
+ content: |
21
+ [Unit]
22
+ Description=Check the worker T3 desktop app every minute
23
+ [Timer]
24
+ OnBootSec=1min
25
+ OnUnitActiveSec=1min
26
+ Persistent=true
27
+ [Install]
28
+ WantedBy=timers.target
29
+ - name: Enable bounded T3 recovery without restarting active sessions
30
+ become: true
31
+ become_user: '{{ development_user }}'
32
+ environment:
33
+ XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
34
+ ansible.builtin.systemd_service:
35
+ scope: user
36
+ name: personal-worker-recovery.timer
37
+ daemon_reload: true
38
+ enabled: true
39
+ state: started