Fleet install for shared hosts and virtual desktops: per-user collector, Linux linger, Windows all-users logon task, Intune + Ansible recipes - #5950
Conversation
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Coordinator reviewReviewed head Verdict: fix before merge (1 blocking item). Blocking1. On a Windows I traced this in the code. It was not observed, and CI could not show it (see the last point).
Fix, any one of these, with a regression test:
Then make README "Pin, update, rollback" describe what the recipe actually does. On Linux, a root-owned Non-blocking (worth doing, not merge gates)
Checked and fine
|
…install Review of #5950: both fleet recipes install into an environment only an administrator can write, while auto-update is on by default. On Windows the respawn plan exited every signed-in user's collector, pip failed three times, and the in-memory backoff died with the process, so it repeated every few minutes while status still said registered. - routes/update_check: skip auto-update when the running venv's site-packages is not writable by this user (probed once per process with a real file create, since os.access ignores Windows ACLs). Non-venv interpreters are unchanged: pip falls back to a per-user install there. - service status reports auto_update off with basis install_not_writable and how to update (re-run the recipe with a new pin); README Pin section and the Intune script say the same. - Windows data_dir privacy is reported as not checked instead of yes. - Linux enables linger before registering the unit; loginctl --no-ask-password; plain-words CLI install text. - Intune script refuses a Python that lives in a user profile. - New criterion AC-OBS-FLEET-001.4 mirrored; baseline 109/179. - CI: Linux asserts the root-owned /opt venv reports install_not_writable as each user; Windows proves the probe honours a deny ACE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Visual diffComparing 49 of 70 comparison(s) flagged (>1% pixel diff).
Folder: dacf990beafa. Full PNGs also attached as a workflow artefact. Generated by visual-diff bot. Pixel diffs >1% flagged; eyeball the table before merging. This check is non-blocking — fail = bot bug, not a code problem. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
CI fix pushed (commit Root cause: Fix: one Generated by Claude Code |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
…install Review of #5950: both fleet recipes install into an environment only an administrator can write, while auto-update is on by default. On Windows the respawn plan exited every signed-in user's collector, pip failed three times, and the in-memory backoff died with the process, so it repeated every few minutes while status still said registered. - routes/update_check: skip auto-update when the running venv's site-packages is not writable by this user (probed once per process with a real file create, since os.access ignores Windows ACLs). Non-venv interpreters are unchanged: pip falls back to a per-user install there. - service status reports auto_update off with basis install_not_writable and how to update (re-run the recipe with a new pin); README Pin section and the Intune script say the same. - Windows data_dir privacy is reported as not checked instead of yes. - Linux enables linger before registering the unit; loginctl --no-ask-password; plain-words CLI install text. - Intune script refuses a Python that lives in a user profile. - New criterion AC-OBS-FLEET-001.4 mirrored; baseline 109/179. - CI: Linux asserts the root-owned /opt venv reports install_not_writable as each user; Windows proves the probe honours a deny ACE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
3177291 to
030b82e
Compare
|
Branch note: I rebased this branch onto
Please don't re-push the 🤖 Generated with Claude Code |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
Ready to merge. Head 63e602a: 47 checks pass, 1 skipped, 0 failing, including Blocking review item fixed. A collector running from an administrator-owned fleet install no longer tries to update itself. Before this, on Windows it exited every signed-in user's collector, pip failed three times, and the cycle repeated. The skip is Two more bugs the real runners found while verifying that fix, both fixed here:
Non-blocking review items also done:
Merge after: nothing. No dependency PR. Companion PRs: none. There is no new HTTP route, so no Post-merge / post-release verification:
Still not verified (listed on #5942): a real AVD or multi-session host with two interactive users, overhead under a real workload, identity for pooled clones (#5941), buffering (#5949) and scoped enrollment keys. 🤖 Generated with Claude Code |
…install Review of #5950: both fleet recipes install into an environment only an administrator can write, while auto-update is on by default. On Windows the respawn plan exited every signed-in user's collector, pip failed three times, and the in-memory backoff died with the process, so it repeated every few minutes while status still said registered. - routes/update_check: skip auto-update when the running venv's site-packages is not writable by this user (probed once per process with a real file create, since os.access ignores Windows ACLs). Non-venv interpreters are unchanged: pip falls back to a per-user install there. - service status reports auto_update off with basis install_not_writable and how to update (re-run the recipe with a new pin); README Pin section and the Intune script say the same. - Windows data_dir privacy is reported as not checked instead of yes. - Linux enables linger before registering the unit; loginctl --no-ask-password; plain-words CLI install text. - Intune script refuses a Python that lives in a user profile. - New criterion AC-OBS-FLEET-001.4 mirrored; baseline 109/179. - CI: Linux asserts the root-owned /opt venv reports install_not_writable as each user; Windows proves the probe honours a deny ACE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
63e602a to
94ee469
Compare
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
Automated CI triage — Syntax & Lint failure blocking all downstream jobs The Syntax & Lint job failed (job All other checks show as skipped/cancelled because they depend on Syntax & Lint passing. Once the lint issue is fixed and CI reruns, the full suite will execute. Please check the Syntax & Lint job logs for the specific error. Generated by Claude Code |
…install Review of #5950: both fleet recipes install into an environment only an administrator can write, while auto-update is on by default. On Windows the respawn plan exited every signed-in user's collector, pip failed three times, and the in-memory backoff died with the process, so it repeated every few minutes while status still said registered. - routes/update_check: skip auto-update when the running venv's site-packages is not writable by this user (probed once per process with a real file create, since os.access ignores Windows ACLs). Non-venv interpreters are unchanged: pip falls back to a per-user install there. - service status reports auto_update off with basis install_not_writable and how to update (re-run the recipe with a new pin); README Pin section and the Intune script say the same. - Windows data_dir privacy is reported as not checked instead of yes. - Linux enables linger before registering the unit; loginctl --no-ask-password; plain-words CLI install text. - Intune script refuses a Python that lives in a user profile. - New criterion AC-OBS-FLEET-001.4 mirrored; baseline 109/179. - CI: Linux asserts the root-owned /opt venv reports install_not_writable as each user; Windows proves the probe honours a deny ACE. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
872d553 to
84417d7
Compare
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
a2e227c to
77d3342
Compare
|
Branch note: rebased onto Please do not merge
🤖 Generated with Claude Code |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
`clawmetry service install | status | uninstall`: one collector per user,
running as that user. Linux requests linger (administrator command printed
when refused); Windows --all-users registers one least-privilege logon task
for the Users group. Intune and Ansible recipes install where desktop users
cannot write, and a collector in such an administrator-owned environment
skips unattended self-update (AC-OBS-FLEET-001.4) instead of exiting to
retry an upgrade that cannot succeed. Both recipes are exercised on their
OS in .github/workflows/fleet-install-test.yml.
Rebased onto main as one commit. Repairs from the previous merges:
docs/acceptance_criteria.json did not parse (a missing `}, {` before
AC-OBS-FLEET-001.1) and carried the eight FLEET criteria twice; both fixed,
AC traceability gate OK (153/223, ratchet holding). MODULE_MAP regenerated.
CHANGELOG entry removed from this PR; the [RELEASE] PR carries it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
0405259 to
dacf990
Compare
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
Generated by Claude Code |
|
Blocked on required review — skipping (auto-mergeability sweep). @vivekchand please approve when ready. Generated by Claude Code |
…y, cold-load fix, hosted Cost Optimizer data Carries #5950 (clawmetry service install/status/uninstall, per-user collectors on shared hosts), #5965 (LiteLLM gateway spend by team/person/key), #5957 (cold-load timeouts), #5996 (Cost Optimizer evidence slice), #5967 (CHANGELOG entries). This triggers PyPI 0.12.880. After it publishes: verify wheel contains clawmetry service and gateway_litellm; wait for cloud auto-pin PR; landing #836 can drop its DO NOT MERGE prefix; cloud #2450 and #2455 can merge after the pin. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jgaf95Zzshc3FUBzqNRxiT
Refs #5942
Product record: https://factory.8090.ai/project/b415065f-ab2f-4f53-8864-0c009fd098cb/requirements/d673cdaa-3bdb-4b10-8a2d-d40ccb5ae630
Design: https://factory.8090.ai/project/b415065f-ab2f-4f53-8864-0c009fd098cb/blueprints/ae944090-9c2b-4549-8ec2-f858736a747b (new "Fleet install on shared and virtual desktops" section)
Review fixes (head 63e602a, rebased on main)
Blocking: no unattended self-update from an administrator-owned install. Both recipes install where only an administrator can write (
C:\Program Files\ClawMetry\venv,/opt/clawmetry-fleet), and auto-update is on by default. On Windows the update plan is exit-and-respawn. Every signed-in user's collector exited, the helper ran pip three times against a directory it cannot write, then relaunched. The in-memory backoff died with the process, so the cycle repeated every few minutes.routes/update_check._maybe_auto_updatenow skips when the running virtual environment's site-packages is not writable by this user. The check is probed once per process with a real file create, becauseos.accessignores ACLs on Windows. A non-venv interpreter is unchanged, because pip falls back to a per-user install there. Nothing is set in the recipes; the collector detects it.service statusreportsauto_update: {state: off, basis: install_not_writable}with how to update (re-run the recipe with a new pin). The README "Pin, update, rollback" section and the Intune script header now say exactly that.test_auto_update_never_attempted_from_unwritable_fleet_installruns the Windowsrespawnplan three times and asserts no pip, no exit and no respawn, plus a control run showing the same harness does respawn on a writable install.test_self_update_blocked_only_for_unwritable_isolated_envuses a real 0500 directory./optvenv reportsinstall_not_writable. The Windows job puts a deny ACE for the runner account on a directory and asserts the probe reports it unwritable, and a writable directory not blocked.Found by the real runner: the shared environment was writable by every desktop user. The first Ubuntu run with the new assertion showed the Ansible-created
/opt/clawmetry-fleet/lib/python3.12/site-packagesat mode0o40777: root ran under a 0000 umask, and usercmalicecould create a file there. Any user could have planted code that every other user's collector imports. The self-update probe was right to call it writable, and this is worse than the review item.umask 022, then the playbook enforcesowner: rootandmode: u=rwX,go=rXrecursively withfollow: false. CI fails on any group-writable, world-writable or non-root path under the environment.-InstallDircannot inherit "Authenticated Users: Modify". CI fails if Users, Authenticated Users, Everyone or Interactive holds a write-type right.test_recipes_keep_the_shared_environment_unwritable_by_desktop_users. Proof that the checks catch the bug: run 34793069392 on 875adc2, before the recipe fix, printedmode 0o40777 ... CREATE SUCCEEDEDforcmalice.chmod -R go-wtask for the same finding (3177291). It is folded into the above; see the PR comment.Found by the Windows runner: the first write probe could hang. Under the deny-ACE check the step ran 24 minutes, until the job timeout. CPython's
tempfile.mkstempretries aPermissionErroron Windows wheneveros.access, which ignores ACLs, says the directory is writable, up toTMP_MAXnames. A collector's first update check would have hung the same way. The probe now makes one exclusiveos.openof a random name and treats a denied create as not writable.test_write_probe_is_one_attempt_never_a_mkstemp_retry_loopasserts one attempt and nomkstemp, and it fails against the previous implementation. The CI step has a 3-minute timeout so a regression fails fast.Non-blocking items also addressed:
private: null(windows_profile_acl_not_checked) instead of a claimed yes, and the Windows job asserts that.loginctl --no-ask-password.-PythonExethat lives in a user profile.Left as is: the respawn failure backoff is still in-memory for other failure causes (pre-existing, not fleet-specific).
The VDI profile this implements
One collector per user, running as that user, registered per machine where the OS allows it. The issue refinement asked us to validate a privileged all-user service before assuming it. We rejected it for this increment: the collector reads agent files in each user's profile and writes a store holding their session content, so one SYSTEM service would be able to read every user's content and would attribute everyone to one node.
--all-users, elevated)statussays so~/.clawmetryrestricted to 0700 by fleet install/optWhat changed
clawmetry service install | status | uninstall(clawmetry/fleet_install.py, wired inclawmetry/cli.pyincluding the_subcmdsrouting list,--jsonfor scripts). Exit 0 only when the requested state was actually reached. Linux linger refused: statedeniedplussudo loginctl enable-linger <user>, exit 1. Windows not elevated:needs_admin.clawmetry uninstallon Windows now also removes the per-user and all-users scheduled tasks (before, the per-user task survived uninstall).deploy/fleet/intune/Install-ClawMetry.ps1,deploy/fleet/ansible/clawmetry.yml,deploy/fleet/README.md(pin, update, rollback, uninstall, proxy/CA/offline reuse, enrollment guidance). No credential in either recipe.Verification
Unit tests
tests/test_fleet_install.py(27 tests after the review fixes, in the ci.yml unit list).fleet_install.pyand restoring it:tests/test_daemon_registration.py,tests/test_uninstall_windows.py,tests/test_cli_help_no_dashboard_import.py.service status(text and JSON),service install --all-users(unsupported, exit 1),service --help.Real runners (
.github/workflows/fleet-install-test.yml; first green on b00080b, rerun on 63e602a with the review assertions)Ubuntu, Ansible playbook, users cmalice and cmbob:
Both users show linger
enabledandsurvives_logoff: true, with no login session for either user.Each collector's pid is owned by its own user.
Neither user can list or read the other's
~/.clawmetry. Homes were deliberately left 0755, so this tests the directory mode, not the distro default.Uninstall removes the unit.
Idle overhead, runner reference only (not a VDI image, no agent workload). CPU is a lifetime average over about 65 s, so it includes startup:
Not exercised on the runner: the linger
deniedpath. The runner's polkit let a user with no session enable her own linger, and the job asserts only that the reported state matches logind, which it did. The denied path and its admin command are covered by unit tests.Windows, Intune script, elevated. The exported task XML contains:
<GroupId>S-1-5-32-545</GroupId>, noUserId<MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy><LogonTrigger />with no userpython.exe -m clawmetry.syncactionRunLevelis omitted because LeastPrivilege is the schema default, and the check fails on any explicit other value.statusreportssupervisor: task_all_usersandsurvives_logoff: false, and uninstall removes the task.AC-OBS-FLEET-001.1through003.2added todocs/acceptance_criteria.json, each declared by a test; baseline tightened (108/178).No real Azure Virtual Desktop or multi-session host was available. Everything above ran on GitHub-hosted runners. On Windows, CI cannot sign in two interactive users, so one instance per signed-in user follows from the task definition and was not observed.
Remaining on #5942 (not in this PR)
🤖 Generated with Claude Code
https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9