@crvouga/dev-fleet 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -16
- package/ansible/github/git.yml +66 -0
- package/ansible/macos/manager.yml +3 -0
- package/ansible/macos/policy.json +11 -1
- package/ansible/macos/t3-policy.yml +49 -0
- package/ansible/monitoring/compose.yml +10 -0
- package/ansible/monitoring/container-watchdog.py +118 -0
- package/ansible/monitoring/hub-darwin.yml +146 -0
- package/ansible/net/remote-probe.sh +5 -0
- package/ansible/ubuntu/baseline.yml +2 -0
- package/ansible/ubuntu/disk-gc.py +25 -3
- package/ansible/ubuntu/disk-guard.py +24 -5
- package/ansible/ubuntu/policy.json +51 -1
- package/ansible/ubuntu/t3-desktop.yml +19 -0
- package/ansible/ubuntu/t3-health.sh +93 -22
- package/ansible/ubuntu/t3-idle.py +42 -0
- package/ansible/ubuntu/t3-policy.yml +4 -11
- package/ansible/ubuntu/tasks/browser.yml +56 -0
- package/ansible/ubuntu/tasks/docker.yml +52 -0
- package/ansible/ubuntu/tasks/recovery.yml +60 -1
- package/ansible/ubuntu/tasks/resources.yml +2 -2
- package/ansible/ubuntu/tasks/services.yml +13 -0
- package/ansible/ubuntu/tasks/t3-restart.yml +37 -0
- package/ansible/ubuntu/wip-guard.py +121 -0
- package/bootstrap/doctor.ts +32 -6
- package/bootstrap/fleet-remote-bootstrap.sh +31 -4
- package/bootstrap/native-t3.ts +22 -11
- package/bootstrap/setup.ts +23 -8
- package/bootstrap/worker-tools.ts +118 -0
- package/cliproxy/README.md +4 -4
- package/cliproxy/compose.yaml +4 -0
- package/cliproxy/scripts/accounts.py +216 -0
- package/cliproxy/scripts/checkpoint.py +30 -2
- package/cliproxy/scripts/cli.py +1 -1
- package/cliproxy/scripts/clientkeys.py +99 -0
- package/cliproxy/scripts/lease.py +80 -0
- package/cliproxy/scripts/ops +137 -25
- package/cliproxy/scripts/restore-verify +15 -0
- package/docs/accounts.md +154 -0
- package/docs/authentication.md +90 -0
- package/docs/declared.md +5 -1
- package/docs/durability.md +26 -0
- package/docs/machine-setup.md +26 -15
- package/docs/operations.md +87 -31
- package/docs/runbooks.md +380 -0
- package/docs/state.md +13 -7
- package/docs/topology.md +2 -0
- package/docs/ubuntu-sync.md +35 -28
- package/examples/fleet.json +19 -1
- package/llms.txt +3 -0
- package/mise.toml +10 -1
- package/package.json +2 -2
- package/schema/check-ids.json +522 -0
- package/schema/fleet.schema.json +244 -1
- package/schema/output/check.schema.json +23 -0
- package/schema/output/error.schema.json +14 -0
- package/schema/output/fleet-report.schema.json +79 -0
- package/schema/output/fleet-sync.schema.json +112 -0
- package/schema/output/report.schema.json +38 -0
- package/src/accounts/checks.ts +220 -0
- package/src/accounts/context.ts +76 -0
- package/src/accounts/propagate.ts +62 -0
- package/src/accounts/roster.ts +85 -0
- package/src/accounts/selection.ts +67 -0
- package/src/accounts/sessions.ts +120 -0
- package/src/agents/account-relay.ts +176 -26
- package/src/agents/launch.ts +84 -5
- package/src/agents/native-verify.ts +128 -2
- package/src/auth-checks.ts +3 -0
- package/src/cli/agent-guide.ts +5 -3
- package/src/cli/fleet-accounts.ts +253 -0
- package/src/cli/fleet-report.ts +126 -0
- package/src/cli/fleet-sync-accounts.ts +58 -0
- package/src/cli/fleet-sync-manager.ts +31 -4
- package/src/cli/fleet-sync-output.ts +3 -2
- package/src/cli/fleet-sync-plan.ts +200 -0
- package/src/cli/fleet-sync-remote.ts +23 -14
- package/src/cli/fleet-sync-schedule.ts +142 -35
- package/src/cli/fleet-sync.ts +126 -8
- package/src/cli/fleet-watch.ts +198 -0
- package/src/cli/fleet.ts +4 -0
- package/src/cli/register-fleet.ts +86 -0
- package/src/cli/registry.ts +26 -0
- package/src/cli/schedule-logs.ts +36 -0
- package/src/clock.ts +63 -0
- package/src/definition.ts +226 -7
- package/src/git-identity.ts +160 -0
- package/src/ide/definition.ts +86 -0
- package/src/ide/ide.ts +43 -0
- package/src/ide/index.ts +5 -0
- package/src/ide/orchestrate.ts +50 -0
- package/src/ide/t3.ts +111 -0
- package/src/machine-health.ts +51 -4
- package/src/manager-health.ts +205 -0
- package/src/manager-sync.ts +13 -17
- package/src/monitoring/hub-backup.ts +63 -17
- package/src/monitoring/manager-hub.ts +44 -2
- package/src/node-converge-linux.ts +44 -12
- package/src/notify.ts +239 -0
- package/src/platform/darwin.ts +0 -13
- package/src/platform/fallback.ts +0 -8
- package/src/platform/linux.ts +0 -10
- package/src/platform/t3-policy.ts +33 -6
- package/src/platform/t3-service.ts +104 -0
- package/src/platform/types.ts +0 -4
- package/src/platform/windows.ts +0 -14
- package/src/remote-bootstrap.ts +87 -25
- package/src/remote-entry.ts +15 -1
- package/src/remote-operation.ts +213 -0
- package/src/schedule-check.ts +40 -0
- package/src/secrets.ts +26 -8
- package/src/service-auth.ts +15 -5
- package/src/ssh.ts +10 -2
- package/src/state/history.ts +172 -0
- package/src/state/store.ts +11 -4
- package/src/t3/accounts.ts +7 -1
- package/src/t3/models.ts +133 -0
- package/src/t3/pairing.ts +31 -0
- package/src/t3/profile.ts +203 -0
- package/src/t3/version.ts +119 -0
- package/src/t3-linked-check.ts +1 -1
- package/src/tailscale.ts +51 -0
- package/src/toolchain-checks.ts +8 -3
- package/src/transport.ts +3 -2
- package/src/worker-capability-checks.ts +167 -0
- package/src/worker-capacity.ts +19 -7
- package/src/worker-host-checks.ts +68 -13
- package/src/worker-policy.ts +33 -0
- package/src/worker-readiness.ts +10 -1
package/docs/runbooks.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Runbooks
|
|
2
|
+
|
|
3
|
+
One entry for every failure code dev-fleet reports: in a check's summary, a
|
|
4
|
+
bootstrap phase or an Ansible assertion. Each family says what its codes
|
|
5
|
+
mean, how to confirm one, the fix (a command or a `fleet-sync` that applies
|
|
6
|
+
the playbook that owns it) and when to escalate. A code whose fix needs a
|
|
7
|
+
person (a login, an approval, a choice) is marked **needs a person**.
|
|
8
|
+
`features/rules/runbooks.feature` fails when a code in the source has no
|
|
9
|
+
entry here, or a `devfleet` command quoted in the documents does not exist.
|
|
10
|
+
|
|
11
|
+
There are 241 codes; 31 need a person.
|
|
12
|
+
|
|
13
|
+
## Bootstrap of a new or returning worker
|
|
14
|
+
|
|
15
|
+
- **Meaning:** The remote bootstrap (`bootstrap/fleet-remote-bootstrap.sh`) stopped before node convergence. It reports the phase and the code, never its output.
|
|
16
|
+
- **Confirm:** Run `devfleet fleet-sync --dry-run` and read the `bootstrap:*` check of the worker; `ssh WORKER` and look under `~/.local/share/personal/fleet`.
|
|
17
|
+
- **Fix:** Follow the remedy the check prints, then run `devfleet fleet-sync`. The bootstrap resumes where it stopped.
|
|
18
|
+
- **Escalate:** A platform or architecture code means the machine must be reinstalled: Ubuntu 26.04 on x86_64 or aarch64.
|
|
19
|
+
|
|
20
|
+
- `BOOTSTRAP_CAPACITY_UNVERIFIED`: bootstrap capacity unverified.
|
|
21
|
+
- `BOOTSTRAP_DISK_CAPACITY_REQUIRED`: bootstrap disk capacity required.
|
|
22
|
+
- `BOOTSTRAP_LOCK_TOOL_REQUIRED`: bootstrap lock tool required.
|
|
23
|
+
- `CONFIGURATION_INSTALL_FAILED`: configuration install failed.
|
|
24
|
+
- `FLEET_APPROVED_BOOTSTRAP_UNAVAILABLE`: fleet approved bootstrap unavailable.
|
|
25
|
+
- `FLEET_STATE_PATH_UNSAFE`: fleet state path unsafe.
|
|
26
|
+
- `MACOS_ARCHITECTURE_UNSUPPORTED`: macos architecture unsupported. **Needs a person.**
|
|
27
|
+
- `MACOS_BASELINE_MISMATCH`: macos baseline mismatch. **Needs a person.**
|
|
28
|
+
- `PLATFORM_UNSUPPORTED`: platform unsupported. **Needs a person.**
|
|
29
|
+
- `REMOTE_BOOTSTRAP_EVIDENCE_INVALID`: remote bootstrap evidence invalid.
|
|
30
|
+
- `REMOTE_BOOTSTRAP_EXIT_FAILED`: remote bootstrap exit failed.
|
|
31
|
+
- `ROOT_DEVELOPMENT_USER_REJECTED`: root development user rejected.
|
|
32
|
+
- `UBUNTU_ARCHITECTURE_UNSUPPORTED`: ubuntu architecture unsupported. **Needs a person.**
|
|
33
|
+
- `XCODE_COMMAND_LINE_TOOLS_REQUIRED`: xcode command line tools required. **Needs a person.**
|
|
34
|
+
|
|
35
|
+
## Ubuntu packages
|
|
36
|
+
|
|
37
|
+
- **Meaning:** A package transaction could not run or the package database is inconsistent.
|
|
38
|
+
- **Confirm:** `ssh WORKER sudo dpkg --audit` and `sudo apt-get -o Debug::NoLocking=1 check`.
|
|
39
|
+
- **Fix:** Let any running package process finish (it is never killed), free disk under `/var`, then run `devfleet fleet-sync`.
|
|
40
|
+
- **Escalate:** APT_STATE_BROKEN and DPKG_INTERRUPTED need `sudo dpkg --configure -a` by a person once no package process runs.
|
|
41
|
+
|
|
42
|
+
- `APT_DISK_FULL`: apt disk full.
|
|
43
|
+
- `APT_DNS_UNAVAILABLE`: apt dns unavailable.
|
|
44
|
+
- `APT_LOCK_CONTENDED`: apt lock contended.
|
|
45
|
+
- `APT_REPOSITORY_UNAVAILABLE`: apt repository unavailable.
|
|
46
|
+
- `APT_STATE_BROKEN`: apt state broken. **Needs a person.**
|
|
47
|
+
- `DPKG_INTERRUPTED`: dpkg interrupted. **Needs a person.**
|
|
48
|
+
|
|
49
|
+
## The fleet-owned checkout
|
|
50
|
+
|
|
51
|
+
- **Meaning:** The checkout of the revision on the machine holds changes fleet-sync did not make, or its bundle changed in transit.
|
|
52
|
+
- **Confirm:** `ssh WORKER git -C ~/.local/share/personal/fleet/revisions/REVISION status`.
|
|
53
|
+
- **Fix:** Remove that revisions directory and run `devfleet fleet-sync`; it is a cache.
|
|
54
|
+
- **Escalate:** A digest mismatch that repeats points at the SSH route.
|
|
55
|
+
|
|
56
|
+
- `SOURCE_CACHE_CHANGED`: source cache changed.
|
|
57
|
+
- `SOURCE_CHECKOUT_CONFLICT`: source checkout conflict.
|
|
58
|
+
- `SOURCE_DIGEST_MISMATCH`: source digest mismatch.
|
|
59
|
+
- `SOURCE_STATUS_FAILED`: source status failed.
|
|
60
|
+
|
|
61
|
+
## The pinned toolchain
|
|
62
|
+
|
|
63
|
+
- **Meaning:** mise, the pinned tools, the global mise configuration or the frozen dependencies did not converge.
|
|
64
|
+
- **Confirm:** Run `bun bootstrap/doctor.ts --json --worker` in the checkout on the machine, or read the `tool:*` checks of `devfleet fleet-status`.
|
|
65
|
+
- **Fix:** Run `devfleet fleet-sync`; it resumes. TOOLCHAIN_CONFIG_CONFLICT means another tool owns `~/.config/mise/config.toml`: move it away.
|
|
66
|
+
- **Escalate:** A tool that fails to download from its pinned artifact needs a new pin in `mise.toml` and `mise.lock` (an issue).
|
|
67
|
+
|
|
68
|
+
- `LAUNCHER_INSTALLATION_FAILED`: launcher installation failed.
|
|
69
|
+
- `TOOLCHAIN_AWS_FAILED`: toolchain aws failed.
|
|
70
|
+
- `TOOLCHAIN_CONFIG_CONFLICT`: toolchain config conflict.
|
|
71
|
+
- `TOOLCHAIN_CONVERGENCE_FAILED`: toolchain convergence failed.
|
|
72
|
+
- `TOOLCHAIN_FROZEN_DEPENDENCIES_FAILED`: toolchain frozen dependencies failed.
|
|
73
|
+
- `TOOLCHAIN_MISE_FAILED`: toolchain mise failed.
|
|
74
|
+
- `TOOLCHAIN_PATH_CONFIGURATION_FAILED`: toolchain path configuration failed.
|
|
75
|
+
- `TOOLCHAIN_T3_FAILED`: toolchain t3 failed.
|
|
76
|
+
|
|
77
|
+
## The Ubuntu baseline and its services
|
|
78
|
+
|
|
79
|
+
- **Meaning:** The Ansible baseline (`ansible/ubuntu/baseline.yml`) did not apply, or what it declares is not what the machine runs.
|
|
80
|
+
- **Confirm:** Read the `worker:*` and `ubuntu:*` checks of `devfleet fleet-status --workers`; on the worker, `systemctl status SERVICE`.
|
|
81
|
+
- **Fix:** Run `devfleet fleet-sync`, which applies the baseline in full when its effect no longer verifies.
|
|
82
|
+
- **Escalate:** Ownership conflicts (sudoers, SSH, Docker from another distribution) need a person to migrate the other owner first.
|
|
83
|
+
|
|
84
|
+
- `DISK_FREE_BELOW_FLOOR`: disk free below floor.
|
|
85
|
+
- `DISK_FREE_UNREADABLE`: disk free unreadable.
|
|
86
|
+
- `DOCKER_OWNER_CONFLICT`: docker owner conflict. **Needs a person.**
|
|
87
|
+
- `MEMORY_RESERVE_MISSING`: memory reserve missing.
|
|
88
|
+
- `RESOURCE_CAPACITY_REQUIRED`: resource capacity required.
|
|
89
|
+
- `RESOURCE_GUARD_INACTIVE`: resource guard inactive.
|
|
90
|
+
- `SERVICE_NOT_ENABLED`: service not enabled.
|
|
91
|
+
- `SERVICE_NOT_INSTALLED`: service not installed.
|
|
92
|
+
- `SERVICE_NOT_RUNNING`: service not running.
|
|
93
|
+
- `SERVICE_RESTART_POLICY`: service restart policy.
|
|
94
|
+
- `SSH_UNMANAGED_CONFIG_CONFLICT`: ssh unmanaged config conflict.
|
|
95
|
+
- `SUDOERS_OWNERSHIP_CONFLICT`: sudoers ownership conflict. **Needs a person.**
|
|
96
|
+
- `SUDO_AUTHORIZATION_REQUIRED`: sudo authorization required. **Needs a person.**
|
|
97
|
+
- `SWAP_CAPACITY_REQUIRED`: swap capacity required.
|
|
98
|
+
- `SWAP_MISSING`: swap missing.
|
|
99
|
+
- `TMP_QUOTA_EXHAUSTED`: tmp quota exhausted.
|
|
100
|
+
- `UBUNTU_BASELINE_INCOMPLETE`: ubuntu baseline incomplete.
|
|
101
|
+
- `UBUNTU_BASELINE_MISMATCH`: ubuntu baseline mismatch. **Needs a person.**
|
|
102
|
+
- `UBUNTU_LINGER_REQUIRED`: ubuntu linger required.
|
|
103
|
+
- `UBUNTU_MAINTENANCE_REBOOT_PENDING`: ubuntu maintenance reboot pending.
|
|
104
|
+
- `UBUNTU_SESSION_POLICY_PENDING`: ubuntu session policy pending.
|
|
105
|
+
- `UBUNTU_SLEEP_POLICY_DRIFT`: ubuntu sleep policy drift.
|
|
106
|
+
- `WORKER_RECOVERY_SCRIPT_MISSING`: worker recovery script missing.
|
|
107
|
+
- `WORKER_RECOVERY_SETUP_FAILED`: worker recovery setup failed.
|
|
108
|
+
- `WORKER_RECOVERY_TIMER_INACTIVE`: worker recovery timer inactive.
|
|
109
|
+
|
|
110
|
+
## Containers on a worker
|
|
111
|
+
|
|
112
|
+
- **Meaning:** `worker:docker-run` could not start the pinned container or build a one-line image as the worker user.
|
|
113
|
+
- **Confirm:** On the worker, as that user: `docker run --rm --network none --pull never IMAGE true`, with IMAGE from `dockerSmokeTest` in `ansible/ubuntu/policy.json`.
|
|
114
|
+
- **Fix:** Run `devfleet fleet-sync`: it starts the engine, adds the user to the docker group and caches the image. A session started earlier keeps its old groups: restart it.
|
|
115
|
+
- **Escalate:** A cgroup or runtime failure that survives a reboot is an issue.
|
|
116
|
+
|
|
117
|
+
- `DOCKER_BUILD_FAILED`: docker build failed.
|
|
118
|
+
- `DOCKER_DAEMON_UNREACHABLE`: docker daemon unreachable.
|
|
119
|
+
- `DOCKER_DISK_FULL`: docker disk full.
|
|
120
|
+
- `DOCKER_RUN_FAILED`: docker run failed.
|
|
121
|
+
- `DOCKER_RUN_TIMEOUT`: docker run timeout.
|
|
122
|
+
- `DOCKER_SMOKE_IMAGE_MISSING`: docker smoke image missing.
|
|
123
|
+
- `DOCKER_SOCKET_ACCESS_DENIED`: docker socket access denied.
|
|
124
|
+
|
|
125
|
+
## Headless browser on a worker
|
|
126
|
+
|
|
127
|
+
- **Meaning:** `worker:browser` could not launch the pinned headless Chromium of the web tool profile.
|
|
128
|
+
- **Confirm:** On the worker run the headless shell under `/opt/personal-fleet/browser/` with `--headless --no-sandbox --dump-dom about:blank`.
|
|
129
|
+
- **Fix:** Run `devfleet fleet-sync`, which installs the pinned libraries and build (`ansible/ubuntu/tasks/browser.yml`).
|
|
130
|
+
- **Escalate:** A missing library the policy does not list is an issue: the list needs it.
|
|
131
|
+
|
|
132
|
+
- `BROWSER_LAUNCH_FAILED`: browser launch failed.
|
|
133
|
+
- `BROWSER_LAUNCH_TIMEOUT`: browser launch timeout.
|
|
134
|
+
- `BROWSER_LIBRARY_MISSING`: browser library missing.
|
|
135
|
+
- `BROWSER_NOT_INSTALLED`: browser not installed.
|
|
136
|
+
|
|
137
|
+
## Git identity and pushing
|
|
138
|
+
|
|
139
|
+
- **Meaning:** Commits on the node would carry the wrong author, or git cannot authenticate to GitHub on its own.
|
|
140
|
+
- **Confirm:** On the node: `git config --global --get user.name`, and `printf "protocol=https\nhost=github.com\n\n" | GIT_TERMINAL_PROMPT=0 git credential fill >/dev/null`.
|
|
141
|
+
- **Fix:** Declare the `git` block in `fleet.json` and run `devfleet fleet-sync` (`ansible/github/git.yml`). Remove lines in `~/.gitconfig` that override it.
|
|
142
|
+
- **Escalate:** GIT_REMOTE_REFUSED means the fleet account lost access to the declared repository: a person grants it.
|
|
143
|
+
|
|
144
|
+
- `GIT_CONFIGURATION_INCOMPLETE`: git configuration incomplete.
|
|
145
|
+
- `GIT_CREDENTIAL_HELPER_MISSING`: git credential helper missing.
|
|
146
|
+
- `GIT_IDENTITY_MISMATCH`: git identity mismatch.
|
|
147
|
+
- `GIT_IDENTITY_MISSING`: git identity missing.
|
|
148
|
+
- `GIT_IDENTITY_OVERRIDDEN`: git identity overridden.
|
|
149
|
+
- `GIT_IDENTITY_UNDECLARED`: git identity undeclared.
|
|
150
|
+
- `GIT_REMOTE_REFUSED`: git remote refused. **Needs a person.**
|
|
151
|
+
- `GIT_REMOTE_UNREACHABLE`: git remote unreachable.
|
|
152
|
+
|
|
153
|
+
## Tailscale
|
|
154
|
+
|
|
155
|
+
- **Meaning:** A machine is not on the tailnet as the fleet declares it.
|
|
156
|
+
- **Confirm:** `tailscale status` on the machine and `devfleet fleet-discover`.
|
|
157
|
+
- **Fix:** Sign the machine in (`sudo tailscale up` on a worker, the app on the manager), then run `devfleet fleet-sync`.
|
|
158
|
+
- **Escalate:** Logins and device approvals need a person.
|
|
159
|
+
|
|
160
|
+
- `TAILSCALE_DEVICE_REPLACED`: tailscale device replaced. **Needs a person.**
|
|
161
|
+
- `TAILSCALE_ENROLLMENT_REQUIRED`: tailscale enrollment required. **Needs a person.**
|
|
162
|
+
- `TAILSCALE_IDENTITY_CONFLICT`: tailscale identity conflict. **Needs a person.**
|
|
163
|
+
- `TAILSCALE_INTERACTIVE_AUTHORIZATION_REQUIRED`: tailscale interactive authorization required. **Needs a person.**
|
|
164
|
+
- `TAILSCALE_KEY_EXPIRY_UNOBSERVED`: tailscale key expiry unobserved.
|
|
165
|
+
- `TAILSCALE_LOGIN_REQUIRED`: tailscale login required. **Needs a person.**
|
|
166
|
+
- `TAILSCALE_NODE_MISSING`: tailscale node missing.
|
|
167
|
+
- `TAILSCALE_NODE_OFFLINE`: tailscale node offline.
|
|
168
|
+
- `TAILSCALE_SETUP_OR_LOGIN_REQUIRED`: tailscale setup or login required. **Needs a person.**
|
|
169
|
+
- `TAILSCALE_SOURCE_OWNER_CONFLICT`: tailscale source owner conflict. **Needs a person.**
|
|
170
|
+
- `TAILSCALE_UPGRADE_PENDING`: tailscale upgrade pending. **Needs a person.**
|
|
171
|
+
|
|
172
|
+
## T3 Code
|
|
173
|
+
|
|
174
|
+
- **Meaning:** The native T3 server or desktop app, its policy, its private route or its providers did not converge or verify.
|
|
175
|
+
- **Confirm:** `devfleet t3-status` and the `t3:*` checks of `devfleet fleet-status`.
|
|
176
|
+
- **Fix:** Run `devfleet fleet-sync`, then `devfleet t3-apply` for providers. A MIGRATION or MAINTENANCE code names a one-time step in its remedy.
|
|
177
|
+
- **Escalate:** Pairing and relinking a peer needs a person in T3 Settings.
|
|
178
|
+
|
|
179
|
+
- `MANAGER_T3_LINK_UNVERIFIED`: manager t3 link unverified.
|
|
180
|
+
- `T3_BASELINE_PREREQUISITES_REQUIRED`: t3 baseline prerequisites required.
|
|
181
|
+
- `T3_CACHE_DRIFT`: t3 cache drift.
|
|
182
|
+
- `T3_CHECKSUM_MISMATCH`: t3 checksum mismatch.
|
|
183
|
+
- `T3_DESKTOP_INSTALL_FAILED`: t3 desktop install failed.
|
|
184
|
+
- `T3_DESKTOP_STOPPED`: t3 desktop stopped.
|
|
185
|
+
- `T3_DOWNLOAD_UNAVAILABLE`: t3 download unavailable.
|
|
186
|
+
- `T3_ENVIRONMENT_IDENTITY_CHANGED`: t3 environment identity changed.
|
|
187
|
+
- `T3_EXECUTABLE_FAILED`: t3 executable failed.
|
|
188
|
+
- `T3_EXECUTABLE_OWNER_CONFLICT`: t3 executable owner conflict. **Needs a person.**
|
|
189
|
+
- `T3_EXTRACTION_FAILED`: t3 extraction failed.
|
|
190
|
+
- `T3_INSTALLATION_DRIFT`: t3 installation drift.
|
|
191
|
+
- `T3_MANAGER_ENDPOINT_REJECTED`: t3 manager endpoint rejected.
|
|
192
|
+
- `T3_MANAGER_ENDPOINT_UNREACHABLE`: t3 manager endpoint unreachable.
|
|
193
|
+
- `T3_MANAGER_IDENTITY_UNVERIFIED`: t3 manager identity unverified.
|
|
194
|
+
- `T3_MANAGER_ORIGIN_UNRESTRICTED`: t3 manager origin unrestricted.
|
|
195
|
+
- `T3_MANAGER_ORIGIN_UNVERIFIED`: t3 manager origin unverified.
|
|
196
|
+
- `T3_MODELS_MISMATCH`: t3 models mismatch.
|
|
197
|
+
- `T3_MODELS_UNVERIFIED`: t3 models unverified.
|
|
198
|
+
- `T3_POLICY_OWNER_CONFLICT`: t3 policy owner conflict. **Needs a person.**
|
|
199
|
+
- `T3_POLICY_RESTART_FAILED`: t3 policy restart failed.
|
|
200
|
+
- `T3_POLICY_RESTART_PENDING`: t3 policy restart pending.
|
|
201
|
+
- `T3_POLICY_STAGE_FAILED`: t3 policy stage failed.
|
|
202
|
+
- `T3_PRIVATE_IDENTITY_REQUIRED`: t3 private identity required.
|
|
203
|
+
- `T3_PRIVATE_ROUTE_CONFLICT`: t3 private route conflict.
|
|
204
|
+
- `T3_PRIVATE_ROUTE_REQUIRED`: t3 private route required.
|
|
205
|
+
- `T3_PRIVATE_ROUTE_UNVERIFIED`: t3 private route unverified.
|
|
206
|
+
- `T3_PROFILE_CONFLICT`: t3 profile conflict.
|
|
207
|
+
- `T3_PROFILE_PENDING`: t3 profile pending.
|
|
208
|
+
- `T3_PROFILE_RECORD_INVALID`: t3 profile record invalid.
|
|
209
|
+
- `T3_PROFILE_UNVERIFIED`: t3 profile unverified.
|
|
210
|
+
- `T3_PROVIDERS_PENDING`: t3 providers pending.
|
|
211
|
+
- `T3_PROVIDERS_UNVERIFIED`: t3 providers unverified.
|
|
212
|
+
- `T3_PROVIDER_CONFLICT`: t3 provider conflict.
|
|
213
|
+
- `T3_PROVIDER_NAMES_PENDING`: t3 provider names pending.
|
|
214
|
+
- `T3_PROVIDER_NAMES_UNVERIFIED`: t3 provider names unverified.
|
|
215
|
+
- `T3_RESTART_PENDING`: t3 restart pending.
|
|
216
|
+
- `T3_SETTINGS_BUSY`: t3 settings busy.
|
|
217
|
+
|
|
218
|
+
## Monitoring (Beszel)
|
|
219
|
+
|
|
220
|
+
- **Meaning:** The hub on the manager, an agent on a machine, an enrollment or the hub backups do not match what the fleet declares.
|
|
221
|
+
- **Confirm:** The `beszel:*` checks of `devfleet fleet-status --workers`; on the manager `docker compose ls` and the hub at `/_/`.
|
|
222
|
+
- **Fix:** Run `devfleet fleet-sync`, which converges the hub, its accounts, its backups and every agent. Hub data is never deleted.
|
|
223
|
+
- **Escalate:** BESZEL_HUB_UPGRADE_PENDING needs a verified backup and a planned upgrade by a person.
|
|
224
|
+
|
|
225
|
+
- `BESZEL_ACCOUNT_INVALID`: beszel account invalid.
|
|
226
|
+
- `BESZEL_ACCOUNT_REQUIRED`: beszel account required. **Needs a person.**
|
|
227
|
+
- `BESZEL_AGENT_CONNECTION_PENDING`: beszel agent connection pending.
|
|
228
|
+
- `BESZEL_AGENT_DISCONNECTED`: beszel agent disconnected.
|
|
229
|
+
- `BESZEL_AGENT_KEY_REJECTED`: beszel agent key rejected.
|
|
230
|
+
- `BESZEL_AGENT_LOG_UNAVAILABLE`: beszel agent log unavailable.
|
|
231
|
+
- `BESZEL_AGENT_TOKEN_REJECTED`: beszel agent token rejected.
|
|
232
|
+
- `BESZEL_API_UNAVAILABLE`: beszel api unavailable.
|
|
233
|
+
- `BESZEL_API_UNVERIFIED`: beszel api unverified.
|
|
234
|
+
- `BESZEL_AUTH_REQUIRED`: beszel auth required.
|
|
235
|
+
- `BESZEL_BACKUP_CONVERGENCE_FAILED`: beszel backup convergence failed.
|
|
236
|
+
- `BESZEL_BACKUP_NOT_CONVERGED`: beszel backup not converged.
|
|
237
|
+
- `BESZEL_BACKUP_OFFHOST_REQUIRED`: beszel backup offhost required.
|
|
238
|
+
- `BESZEL_BACKUP_STALE`: beszel backup stale.
|
|
239
|
+
- `BESZEL_BACKUP_UNSCHEDULED`: beszel backup unscheduled.
|
|
240
|
+
- `BESZEL_BACKUP_UNVERIFIED`: beszel backup unverified.
|
|
241
|
+
- `BESZEL_CREDENTIALS_REQUIRED`: beszel credentials required. **Needs a person.**
|
|
242
|
+
- `BESZEL_DISK_FULL`: beszel disk full.
|
|
243
|
+
- `BESZEL_DISK_UNVERIFIED`: beszel disk unverified.
|
|
244
|
+
- `BESZEL_DOCKER_DESKTOP_REQUIRED`: beszel docker desktop required.
|
|
245
|
+
- `BESZEL_ENROLLMENT_INCOMPLETE`: beszel enrollment incomplete.
|
|
246
|
+
- `BESZEL_HOST_CONFLICT`: beszel host conflict.
|
|
247
|
+
- `BESZEL_HUB_INSTALLATION_INCOMPLETE`: beszel hub installation incomplete.
|
|
248
|
+
- `BESZEL_HUB_MISMATCH`: beszel hub mismatch.
|
|
249
|
+
- `BESZEL_HUB_SELECTION_REQUIRED`: beszel hub selection required.
|
|
250
|
+
- `BESZEL_HUB_UNHEALTHY`: beszel hub unhealthy.
|
|
251
|
+
- `BESZEL_HUB_UNREACHABLE`: beszel hub unreachable.
|
|
252
|
+
- `BESZEL_HUB_UPGRADE_PENDING`: beszel hub upgrade pending. **Needs a person.**
|
|
253
|
+
- `BESZEL_INPUT_INVALID`: beszel input invalid.
|
|
254
|
+
- `BESZEL_INPUT_LIMIT`: beszel input limit.
|
|
255
|
+
- `BESZEL_INSTALLATION_INCOMPLETE`: beszel installation incomplete.
|
|
256
|
+
- `BESZEL_LEGACY_DAEMON`: beszel legacy daemon.
|
|
257
|
+
- `BESZEL_METRICS_STALE`: beszel metrics stale.
|
|
258
|
+
- `BESZEL_NODE_INVALID`: beszel node invalid.
|
|
259
|
+
- `BESZEL_NODE_MISSING`: beszel node missing.
|
|
260
|
+
- `BESZEL_NODE_TAILNET_REQUIRED`: beszel node tailnet required.
|
|
261
|
+
- `BESZEL_NOT_ENROLLED`: beszel not enrolled.
|
|
262
|
+
- `BESZEL_NOT_HUB`: beszel not hub.
|
|
263
|
+
- `BESZEL_NOT_MANAGER`: beszel not manager.
|
|
264
|
+
- `BESZEL_OPERATION_INCOMPLETE`: beszel operation incomplete.
|
|
265
|
+
- `BESZEL_PRIVATE_ROUTE_CONFLICT`: beszel private route conflict.
|
|
266
|
+
- `BESZEL_PRIVATE_ROUTE_REQUIRED`: beszel private route required.
|
|
267
|
+
- `BESZEL_PRIVATE_ROUTE_UNVERIFIED`: beszel private route unverified.
|
|
268
|
+
- `BESZEL_PROTOCOL`: beszel protocol.
|
|
269
|
+
- `BESZEL_PUBLIC_ENDPOINT_OFFLINE`: beszel public endpoint offline.
|
|
270
|
+
- `BESZEL_REMOTE_EVIDENCE_INVALID`: beszel remote evidence invalid.
|
|
271
|
+
- `BESZEL_RESPONSE_LIMIT`: beszel response limit.
|
|
272
|
+
- `BESZEL_SERVICE_STOPPED`: beszel service stopped.
|
|
273
|
+
- `BESZEL_SOURCE_MISMATCH`: beszel source mismatch.
|
|
274
|
+
- `BESZEL_TAILSCALE_REQUIRED`: beszel tailscale required.
|
|
275
|
+
- `BESZEL_TOKEN_INVALID`: beszel token invalid.
|
|
276
|
+
- `BESZEL_TUNNEL_CREDENTIAL_UNAVAILABLE`: beszel tunnel credential unavailable. **Needs a person.**
|
|
277
|
+
- `BESZEL_VERSION_MISMATCH`: beszel version mismatch.
|
|
278
|
+
- `BESZEL_VIEWER_ROLE`: beszel viewer role.
|
|
279
|
+
- `BESZEL_VIEWER_UNPROVISIONED`: beszel viewer unprovisioned.
|
|
280
|
+
- `BESZEL_VIEWER_UNVERIFIED`: beszel viewer unverified.
|
|
281
|
+
|
|
282
|
+
## Coding agents and providers
|
|
283
|
+
|
|
284
|
+
- **Meaning:** An agent CLI, its profile or a provider behind the CLI proxy did not answer as expected.
|
|
285
|
+
- **Confirm:** The `agent:*` and `provider:*` checks of `devfleet fleet-status`, and `devfleet cliproxy` on the manager.
|
|
286
|
+
- **Fix:** Run `devfleet fleet-sync`; a rate limit or upstream outage clears on its own.
|
|
287
|
+
- **Escalate:** PROVIDER_AUTH_REJECTED needs a person to sign the account in again on the proxy.
|
|
288
|
+
|
|
289
|
+
- `AGENT_MEMORY_LIMIT_MISSING`: agent memory limit missing.
|
|
290
|
+
- `AGENT_MODEL_UNSERVED`: agent model unserved.
|
|
291
|
+
- `AGENT_PROFILE_STALE`: agent profile stale.
|
|
292
|
+
- `AGENT_SERVICE_ENVIRONMENT_INCOMPLETE`: agent service environment incomplete.
|
|
293
|
+
- `AGENT_SERVICE_ENVIRONMENT_UNVERIFIED`: agent service environment unverified.
|
|
294
|
+
- `AGENT_SESSION_UNVERIFIED`: agent session unverified.
|
|
295
|
+
- `AGENT_VAULT_UNAVAILABLE`: agent vault unavailable.
|
|
296
|
+
- `AGENT_VERSION_MISMATCH`: agent version mismatch.
|
|
297
|
+
- `PROVIDER_AUTH_REJECTED`: provider auth rejected. **Needs a person.**
|
|
298
|
+
- `PROVIDER_EMPTY_RESPONSE`: provider empty response.
|
|
299
|
+
- `PROVIDER_MODEL_UNAVAILABLE`: provider model unavailable.
|
|
300
|
+
- `PROVIDER_NETWORK_UNVERIFIED`: provider network unverified.
|
|
301
|
+
- `PROVIDER_OUTPUT_UNVERIFIED`: provider output unverified.
|
|
302
|
+
- `PROVIDER_PROTOCOL_REJECTED`: provider protocol rejected.
|
|
303
|
+
- `PROVIDER_RATE_LIMITED`: provider rate limited.
|
|
304
|
+
- `PROVIDER_RESPONSE_INVALID`: provider response invalid.
|
|
305
|
+
- `PROVIDER_RESPONSE_LIMIT`: provider response limit.
|
|
306
|
+
- `PROVIDER_ROUTE_UNVERIFIED`: provider route unverified.
|
|
307
|
+
- `PROVIDER_STREAM_INCOMPLETE`: provider stream incomplete.
|
|
308
|
+
- `PROVIDER_TIMEOUT`: provider timeout.
|
|
309
|
+
- `PROVIDER_TOOL_ARGUMENTS_INVALID`: provider tool arguments invalid.
|
|
310
|
+
- `PROVIDER_TOOL_INVALID`: provider tool invalid.
|
|
311
|
+
- `PROVIDER_TOOL_MISSING`: provider tool missing.
|
|
312
|
+
- `PROVIDER_TOOL_RESULT_UNVERIFIED`: provider tool result unverified.
|
|
313
|
+
- `PROVIDER_UNATTENDED_CREDENTIAL_REQUIRED`: provider unattended credential required.
|
|
314
|
+
- `PROVIDER_UPSTREAM_UNAVAILABLE`: provider upstream unavailable.
|
|
315
|
+
- `PROXY_RECONCILE_UNVERIFIED`: proxy reconcile unverified.
|
|
316
|
+
- `UNATTENDED_PROXY_AUTHENTICATION_REQUIRED`: unattended proxy authentication required.
|
|
317
|
+
|
|
318
|
+
## Provider accounts
|
|
319
|
+
|
|
320
|
+
- **Meaning:** A change to a provider account on the CLI proxy (sign-in, removal, cooldown) did not complete.
|
|
321
|
+
- **Confirm:** `devfleet fleet-accounts` lists every account and its state; `devfleet fleet-accounts --diff` compares this machine.
|
|
322
|
+
- **Fix:** Retry the change with `devfleet fleet-accounts`; see [Provider accounts](accounts.md).
|
|
323
|
+
- **Escalate:** Signing an account in again needs a person.
|
|
324
|
+
|
|
325
|
+
- `ACCOUNT_CHANGE_FAILED`: account change failed.
|
|
326
|
+
|
|
327
|
+
## Vault and credentials
|
|
328
|
+
|
|
329
|
+
- **Meaning:** A machine cannot read the secrets it needs with its own Vault session.
|
|
330
|
+
- **Confirm:** `vault token lookup` on the machine and the `worker:vault-renewal` check.
|
|
331
|
+
- **Fix:** Log the machine in once (`devfleet fleet-sync` asks on the manager); renewal is automatic afterwards.
|
|
332
|
+
- **Escalate:** A refused policy needs the Vault administrator.
|
|
333
|
+
|
|
334
|
+
- `UNATTENDED_VAULT_IDENTITY_REQUIRED`: unattended vault identity required.
|
|
335
|
+
- `VAULT_LOGIN_REQUIRED`: vault login required. **Needs a person.**
|
|
336
|
+
- `VAULT_POLICY_PERMISSION_REQUIRED`: vault policy permission required. **Needs a person.**
|
|
337
|
+
- `VAULT_RENEWAL_FAILING`: vault renewal failing.
|
|
338
|
+
|
|
339
|
+
## Fleet operations and the fleet state
|
|
340
|
+
|
|
341
|
+
- **Meaning:** fleet-sync could not plan, lock, reach a machine, promote a revision or keep the fleet state.
|
|
342
|
+
- **Confirm:** `devfleet fleet-state` for the state, `devfleet fleet-status` for machines, and the report `devfleet fleet-sync` writes.
|
|
343
|
+
- **Fix:** Follow the remedy; most clear by running `devfleet fleet-sync` again once the holder of a lock finishes.
|
|
344
|
+
- **Escalate:** FLEET_STATE_DIVERGED needs a person to choose which copy of the state to keep (docs/state.md).
|
|
345
|
+
|
|
346
|
+
- `ANSIBLE_VARIABLE_LIMIT`: ansible variable limit.
|
|
347
|
+
- `CONTAINER_WATCHDOG_UNOBSERVED`: container watchdog unobserved.
|
|
348
|
+
- `FLEET_CONNECTION_REQUIRED`: fleet connection required.
|
|
349
|
+
- `FLEET_MANAGER_ENROLLMENT_REQUIRED`: fleet manager enrollment required. **Needs a person.**
|
|
350
|
+
- `FLEET_MANAGER_LOCKED`: fleet manager locked.
|
|
351
|
+
- `FLEET_MANAGER_UNREACHABLE`: fleet manager unreachable.
|
|
352
|
+
- `FLEET_NOT_MANAGER`: fleet not manager.
|
|
353
|
+
- `FLEET_PLAN_STALE`: fleet plan stale.
|
|
354
|
+
- `FLEET_PROMOTION_BLOCKED`: fleet promotion blocked.
|
|
355
|
+
- `FLEET_REMOTE_LOCKED`: fleet remote locked.
|
|
356
|
+
- `FLEET_REMOTE_OPERATION_FAILED`: fleet remote operation failed.
|
|
357
|
+
- `FLEET_REVISION_REQUIRED`: fleet revision required.
|
|
358
|
+
- `FLEET_SCHEDULE_PREVIEW`: fleet schedule preview.
|
|
359
|
+
- `FLEET_SOURCE_UNAVAILABLE`: fleet source unavailable.
|
|
360
|
+
- `FLEET_STATE_DIVERGED`: fleet state diverged. **Needs a person.**
|
|
361
|
+
- `FLEET_VERIFICATION_MISSING`: fleet verification missing.
|
|
362
|
+
- `MANAGER_AGENT_PROFILES_FAILED`: manager agent profiles failed.
|
|
363
|
+
- `MANAGER_DISK_UNOBSERVED`: manager disk unobserved.
|
|
364
|
+
- `MANAGER_DOCKER_DESKTOP_DRIFT`: manager docker desktop drift.
|
|
365
|
+
- `MANAGER_DOCKER_DESKTOP_UNOBSERVED`: manager docker desktop unobserved.
|
|
366
|
+
- `MANAGER_RELEASE_UNOBSERVED`: manager release unobserved.
|
|
367
|
+
- `MANAGER_SLEEPS_ON_POWER`: manager sleeps on power.
|
|
368
|
+
- `MANAGER_UPDATES_UNOBSERVED`: manager updates unobserved.
|
|
369
|
+
- `REMOTE_CONFIGURATION_INCOMPLETE`: remote configuration incomplete.
|
|
370
|
+
- `REMOTE_PHASE_FAILED`: remote phase failed.
|
|
371
|
+
|
|
372
|
+
## Notifications (fleet-watch)
|
|
373
|
+
|
|
374
|
+
- **Meaning:** fleet-watch could not reach a notification target `notify` in `fleet.json` names, or its address is not stored as a secret.
|
|
375
|
+
- **Confirm:** Run `devfleet fleet-watch --test` and read the `notify:*` checks.
|
|
376
|
+
- **Fix:** Store the target's address as the secret the remedy names, or fix the endpoint, then run `devfleet fleet-watch --test` again.
|
|
377
|
+
- **Escalate:** A target that keeps refusing needs whoever runs that endpoint.
|
|
378
|
+
|
|
379
|
+
- `NOTIFY_DELIVERY_FAILED`: notify delivery failed.
|
|
380
|
+
- `NOTIFY_TARGET_UNAVAILABLE`: notify target unavailable.
|
package/docs/state.md
CHANGED
|
@@ -4,13 +4,18 @@ A fleet is described in two places you own. Your project in Git holds
|
|
|
4
4
|
`fleet.json` and pins the dev-fleet version. Everything else the manager
|
|
5
5
|
cannot rebuild from Git is the **fleet state**:
|
|
6
6
|
|
|
7
|
-
| Document | What it is
|
|
8
|
-
| ------------ |
|
|
9
|
-
| `roster` | The enrolled connection to each worker (SSH alias, Tailscale device)
|
|
10
|
-
| `enrollment` | The revision `fleet-sync` approved and the one it last applied
|
|
11
|
-
| `definition` | The fleet configuration `fleet-sync` last applied: your `fleet.json`
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
| Document | What it is |
|
|
8
|
+
| ------------ | --------------------------------------------------------------------- |
|
|
9
|
+
| `roster` | The enrolled connection to each worker (SSH alias, Tailscale device) |
|
|
10
|
+
| `enrollment` | The revision `fleet-sync` approved and the one it last applied |
|
|
11
|
+
| `definition` | The fleet configuration `fleet-sync` last applied: your `fleet.json` |
|
|
12
|
+
| `history` | The last 90 days of `fleet-sync` runs (at most 500), and the schedule |
|
|
13
|
+
|
|
14
|
+
`devfleet fleet-watch` also keeps `watch.json` in the bucket, beside the
|
|
15
|
+
database: the health of each worker when it last looked, and when that was
|
|
16
|
+
(the manager's heartbeat). Losing it costs at most one repeated notification.
|
|
17
|
+
|
|
18
|
+
The three documents live in one SQLite database, and [Litestream](https://litestream.io)
|
|
14
19
|
replicates that database to a storage bucket you own. The bucket is the source
|
|
15
20
|
of truth. The copy on the manager is disposable: delete it, or replace the
|
|
16
21
|
whole machine, and the next command brings the fleet back.
|
|
@@ -118,6 +123,7 @@ way, needs no credentials and is the quickest way to try this out.
|
|
|
118
123
|
```sh
|
|
119
124
|
devfleet fleet-state # read-only: where the state is, and how it stands with the bucket
|
|
120
125
|
devfleet fleet-state --example # print a fleet.json to start a project from
|
|
126
|
+
devfleet fleet-report --since 30d # read-only: how reliably fleet-sync ran, from the history
|
|
121
127
|
devfleet fleet-state --export # print the configuration fleet-sync last applied
|
|
122
128
|
devfleet fleet-state --push # upload changes made while the bucket was unreachable
|
|
123
129
|
devfleet fleet-state --restore # replace the local copy with the bucket's
|
package/docs/topology.md
CHANGED
|
@@ -53,6 +53,8 @@ your project's `fleet.json` (version 3, checked by [the schema](../schema/fleet.
|
|
|
53
53
|
the manager.
|
|
54
54
|
- Anything on a worker is disposable: T3 sessions and worktrees, caches,
|
|
55
55
|
checkouts. Durable work lives in Git remotes; credentials come from Vault.
|
|
56
|
+
Work in progress in agent worktrees is pushed to a recovery ref every 15
|
|
57
|
+
minutes; [durability](durability.md) says what a lost machine takes with it.
|
|
56
58
|
- The first declared worker is the rollout canary for a revision new to the
|
|
57
59
|
fleet: a failed canary stops promotion to the other workers. Repeating a
|
|
58
60
|
revision that already converged everywhere runs all workers at once.
|
package/docs/ubuntu-sync.md
CHANGED
|
@@ -117,9 +117,8 @@ included, to the exact version in `ansible/ubuntu/policy.json`. The interactive
|
|
|
117
117
|
roster in the [fleet state](state.md), using the existing roster schema.
|
|
118
118
|
Preserve workspace/runtime paths, capabilities and explicit `tailscaleId`
|
|
119
119
|
bindings.
|
|
120
|
-
- Workstation network consumers derive their inventory from
|
|
121
|
-
|
|
122
|
-
lossless identity-path migration; conflicts fail rather than silently winning.
|
|
120
|
+
- Workstation network consumers derive their inventory from the fleet
|
|
121
|
+
configuration; conflicts fail rather than silently winning.
|
|
123
122
|
- Manager approval: the `enrollment` document in the [fleet state](state.md).
|
|
124
123
|
Manager deployment lock: `~/.local/share/personal/fleet/manager/sync.lock`.
|
|
125
124
|
- Remote source: `~/.local/share/personal/fleet/revisions/FULL_SHA`.
|
|
@@ -179,21 +178,21 @@ independently. Bespoke TypeScript remains only where a native CLI or API has no
|
|
|
179
178
|
declarative owner: T3 service lifecycle, Beszel enrollment tokens, provider
|
|
180
179
|
verification and the CLIProxy singleton.
|
|
181
180
|
|
|
182
|
-
| Policy | Workers (Ubuntu 26.04, stateless)
|
|
183
|
-
| --------------------------- |
|
|
184
|
-
| Never sleep | systemd sleep targets masked; logind lid/idle policy; GNOME power plugin set to `nothing`
|
|
185
|
-
| No automatic logout or lock | GNOME `lock-enabled false`, `idle-delay 0`; designated-user lingering
|
|
186
|
-
| Always-on services | drop-ins `Restart=always`, `StartLimitIntervalSec=0` for ssh, tailscaled, docker, beszel-agent
|
|
187
|
-
| T3 health and connection | `fleet-sync` and `fleet-status --workers` require `worker:t3-health` (local API answers), `t3:manager-endpoint` (manager HTTPS over Tailscale) and `t3:paired-access` (manager T3 link reachable)
|
|
188
|
-
| Memory safety | `systemd-oomd` (swap 90%, pressure 60%); `user.slice` `MemoryMax=90%`; each `t3code-*.scope` agent `MemoryMax=30%`, `OOMPolicy=continue`; swap file when none
|
|
189
|
-
| Disk safety | `personal-fleet-disk-guard` kills only the top user-cgroup writer below 3 GiB free, never deletes files; hourly `personal-fleet-disk-gc.timer` deletes git-ignored build output (`node_modules`, `.cache`, `dist`, ...) in checkouts idle 7d (12h under 15 GiB free) with no process inside, plus unused Docker images and build cache, never source, worktrees or volumes; journal 1G; `/tmp` 25% of RAM, aged 2d hourly, a measured write probe (`TMP_QUOTA_EXHAUSTED`), and T3 agents use disk-backed `/var/tmp` (`TMPDIR`, aged 14d); Docker log rotation
|
|
190
|
-
| Tailscale | pinned APT package, `tailscaled` enabled, native login required
|
|
191
|
-
| Dashboard agent | pinned native binary, hardened `beszel-agent` systemd unit as user `beszel`
|
|
192
|
-
| Credential renewal | user timer `personal-worker-auth` renews the node's own Vault token and GitHub login every 10min
|
|
193
|
-
| Noninteractive sudo | validated `/etc/sudoers.d/personal-fleet-USER`
|
|
194
|
-
| T3 worker app | pinned desktop `.deb` is the only T3 process (UI plus embedded server); GDM auto-login, user unit `t3code-desktop.service` bound to the graphical session with `Restart=always`; `personal-worker-recovery`
|
|
195
|
-
| T3 providers | the same managed instances (`fleet/agents/t3.json`) through locked T3 administrator policy
|
|
196
|
-
| Singleton services | never; clients of the manager's services only
|
|
181
|
+
| Policy | Workers (Ubuntu 26.04, stateless) | Manager (macOS, singleton) |
|
|
182
|
+
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
183
|
+
| Never sleep | systemd sleep targets masked; logind lid/idle policy; GNOME power plugin set to `nothing` | never sleeps on AC power (`pmset -c sleep 0`, `macos/manager.yml`, verified as `manager:power`); battery sleep is left to the human |
|
|
184
|
+
| No automatic logout or lock | GNOME `lock-enabled false`, `idle-delay 0`; designated-user lingering | not managed (the human uses it) |
|
|
185
|
+
| Always-on services | drop-ins `Restart=always`, `StartLimitIntervalSec=0` for ssh, tailscaled, docker, beszel-agent | singleton services under launchd and Docker Desktop (see the last row) |
|
|
186
|
+
| T3 health and connection | `fleet-sync` and `fleet-status --workers` require `worker:t3-health` (local API answers), `t3:manager-endpoint` (manager HTTPS over Tailscale) and `t3:paired-access` (manager T3 link reachable) | the human's T3 desktop holds the links to every worker (`t3:paired-access`) |
|
|
187
|
+
| Memory safety | `systemd-oomd` (swap 90%, pressure 60%); `user.slice` `MemoryMax=90%`; each `t3code-*.scope` agent `MemoryMax=30%`, `OOMPolicy=continue`; swap file when none | not managed |
|
|
188
|
+
| Disk safety | `personal-fleet-disk-guard` kills only the top user-cgroup writer below 3 GiB free, never deletes files; hourly `personal-fleet-disk-gc.timer` deletes git-ignored build output (`node_modules`, `.cache`, `dist`, ...) in checkouts idle 7d (12h under 15 GiB free) with no process inside, plus unused Docker images and build cache, never source, worktrees or volumes; journal 1G; `/tmp` 25% of RAM, aged 2d hourly, a measured write probe (`TMP_QUOTA_EXHAUSTED`), and T3 agents use disk-backed `/var/tmp` (`TMPDIR`, aged 14d); Docker log rotation | not managed |
|
|
189
|
+
| Tailscale | pinned APT package, `tailscaled` enabled, native login required | Tailscale app, human login |
|
|
190
|
+
| Dashboard agent | pinned native binary, hardened `beszel-agent` systemd unit as user `beszel` | not installed |
|
|
191
|
+
| Credential renewal | user timer `personal-worker-auth` renews the node's own Vault token and GitHub login every 10min | the human's own Vault and GitHub sessions |
|
|
192
|
+
| Noninteractive sudo | validated `/etc/sudoers.d/personal-fleet-USER` | never; the human runs `manager.yml` once with `-K` |
|
|
193
|
+
| T3 worker app | pinned desktop `.deb` is the only T3 process (UI plus embedded server); GDM auto-login, user unit `t3code-desktop.service` bound to the graphical session with `Restart=always`; `personal-worker-recovery` runs a copy of `t3-health.sh` installed in `/usr/local/lib/personal-fleet` every minute: it stops and disables a still-running headless `t3code.service`, reports `T3_NO_GRAPHICAL_SESSION` and restarts the display manager (three times at most) when there is no session, starts a stopped app after `reset-failed`, restarts one whose local API missed three checks, and restarts one on another release than the installed one or with a deferred policy restart once it runs no agent turn; administrator policy | not a worker |
|
|
194
|
+
| T3 providers | the same managed instances (`fleet/agents/t3.json`) through locked T3 administrator policy | reconciled into the human's T3 settings by `fleet-sync` (`t3:providers`) with the ownership-safe `t3-apply` engine, so every T3 offers the identical set; unrelated and unmanaged instances are preserved. `fleet-sync` also applies the same proxy agent profiles (`configuration:apply`) and verifies the shared Vault gateway credential end to end with `provider:*:inference`, exactly as on workers |
|
|
195
|
+
| Singleton services | never; clients of the manager's services only | every singleton runs here: CLI proxy (`cliproxy`), Beszel hub (`workstation/monitoring`) |
|
|
197
196
|
|
|
198
197
|
Credential sync is deliberately indirect: no secret is copied between machines.
|
|
199
198
|
OpenTofu issues each enrolled worker its own renewable periodic token
|
|
@@ -211,8 +210,10 @@ vault kv get -field=FLEET_WORKER_TOKEN_WORKER_ONE secret/personal/prd \
|
|
|
211
210
|
| ssh worker-one 'vault login -address=https://vault.example.com -no-print -'
|
|
212
211
|
```
|
|
213
212
|
|
|
214
|
-
From then on the node's `personal-worker-auth` timer renews that token and
|
|
215
|
-
GitHub login
|
|
213
|
+
From then on the node's `personal-worker-auth` timer renews that token and
|
|
214
|
+
restores its GitHub login from it (`bootstrap/worker-auth.ts`). It renders no
|
|
215
|
+
provider configuration: agent profiles are applied by `fleet-sync`. See
|
|
216
|
+
[Authentication and accounts](authentication.md).
|
|
216
217
|
|
|
217
218
|
## Ubuntu and remote-work safety
|
|
218
219
|
|
|
@@ -257,15 +258,18 @@ the existing LaunchAgent and requires a user session. Unattended manager boot
|
|
|
257
258
|
acceptance and log-retention configuration remain gates. No node pull scheduler
|
|
258
259
|
is installed or necessary.
|
|
259
260
|
|
|
260
|
-
Sync never reboots a box
|
|
261
|
-
|
|
261
|
+
Sync never reboots a box and never ends a running agent turn. A worker's T3
|
|
262
|
+
that must restart for a new provider policy or release is restarted only while
|
|
263
|
+
it runs no agent turn; otherwise the restart is pending
|
|
264
|
+
(`T3_POLICY_RESTART_PENDING`, `T3_RESTART_PENDING`) and the recovery timer
|
|
265
|
+
applies it at the first idle check. Explicit
|
|
262
266
|
maintenance reboot/reconnect orchestration remains an acceptance gate. Kernel,
|
|
263
267
|
driver, firmware, hardware and power failures are outside any “never freezes”
|
|
264
268
|
promise. Inspect disk/inodes, swap/memory pressure, `systemctl --failed`, and prior
|
|
265
269
|
boot journal evidence locally; sanitize it before sharing. Do not enable automatic
|
|
266
270
|
reboot-on-pressure or kill active work to obtain a green report.
|
|
267
271
|
|
|
268
|
-
T3
|
|
272
|
+
The pinned T3's (`t3_version` in `mise.toml`) supported administrator policy at `/etc/t3code/policy.json`
|
|
269
273
|
disables independent updates and declares the existing managed Codex instance
|
|
270
274
|
plus an OpenCode instance for all five gateway models. Fleet reuses the
|
|
271
275
|
existing `agents/t3.json` renderer; it does not edit the live settings database.
|
|
@@ -303,17 +307,20 @@ launch; nothing is copied between nodes.
|
|
|
303
307
|
|
|
304
308
|
**Ansible applies, TypeScript measures.** On a worker the T3 lifecycle is
|
|
305
309
|
declared in playbooks: `t3-policy.yml` stages the provider policy and restarts
|
|
306
|
-
a running T3 that has not loaded it, `t3-desktop.yml` installs the app, retires
|
|
310
|
+
a running T3 that has not loaded it once it runs no agent turn, `t3-desktop.yml` installs the app, retires
|
|
307
311
|
the former headless server and starts it, and `t3-route.yml` publishes the
|
|
308
312
|
private tailnet route when port 443 has no owner. The TypeScript around them
|
|
309
313
|
only refuses conflicts and reports what it observes afterwards. Each sync also
|
|
310
314
|
prunes old revision checkouts on the worker, keeping the new one, the one T3
|
|
311
315
|
launches from and the two most recent others.
|
|
312
316
|
|
|
313
|
-
**Every sync applies everything
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
+
**Every sync applies everything, except a T3 restart that would end a turn.**
|
|
318
|
+
A worker whose T3 has not loaded the staged provider policy, or runs an older
|
|
319
|
+
release than the one installed, is restarted only while it runs no agent turn;
|
|
320
|
+
otherwise sync reports the pending restart and the recovery timer applies it
|
|
321
|
+
at the first idle check, failing the check after
|
|
322
|
+
`t3RestartMaxDeferralHours` (`ansible/ubuntu/policy.json`). A pending proxy
|
|
323
|
+
update is installed even when it recreates the gateway. The manager finishes before the workers
|
|
317
324
|
start, so they are verified against the updated proxy. `--dry-run` previews.
|
|
318
325
|
|
|
319
326
|
**Accounts are never listed in this repository.** The proxy's credential files
|
package/examples/fleet.json
CHANGED
|
@@ -29,6 +29,14 @@
|
|
|
29
29
|
"proxy-tunnel-token": {
|
|
30
30
|
"path": "cliproxy-fleet/tunnel",
|
|
31
31
|
"field": "TUNNEL_TOKEN"
|
|
32
|
+
},
|
|
33
|
+
"monitoring-backup-access-key": {
|
|
34
|
+
"path": "beszel/backups",
|
|
35
|
+
"field": "ACCESS_KEY_ID"
|
|
36
|
+
},
|
|
37
|
+
"monitoring-backup-secret-key": {
|
|
38
|
+
"path": "beszel/backups",
|
|
39
|
+
"field": "SECRET_ACCESS_KEY"
|
|
32
40
|
}
|
|
33
41
|
}
|
|
34
42
|
},
|
|
@@ -73,7 +81,16 @@
|
|
|
73
81
|
"hubPort": 8443,
|
|
74
82
|
"publicUrl": "https://beszel.example.com",
|
|
75
83
|
"managerAccount": "fleet-manager@example.com",
|
|
76
|
-
"viewerAccount": "public-viewer@example.com"
|
|
84
|
+
"viewerAccount": "public-viewer@example.com",
|
|
85
|
+
"backups": {
|
|
86
|
+
"schedule": "0 3 * * *",
|
|
87
|
+
"keep": 7,
|
|
88
|
+
"storage": {
|
|
89
|
+
"bucket": "my-fleet-hub-backups",
|
|
90
|
+
"endpoint": "https://s3.example.com",
|
|
91
|
+
"region": "auto"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
77
94
|
}
|
|
78
95
|
},
|
|
79
96
|
"ubuntu": {
|
|
@@ -92,6 +109,7 @@
|
|
|
92
109
|
"githubLogin": "example-user",
|
|
93
110
|
"vaultUser": "example-user",
|
|
94
111
|
"toolchain": "mise.lock",
|
|
112
|
+
"toolProfiles": ["go"],
|
|
95
113
|
"agentProfile": "proxy",
|
|
96
114
|
"unattendedSudo": true,
|
|
97
115
|
"persistentT3": true,
|