fleetd #184: stop claiming memberHerdrSocket proves a different OS user #269

Closed
agent wants to merge 0 commits from worker/fleetd-184-uid-claim-8e1f31-4 into main
Member

fleetd #184, item 5 — stop claiming memberHerdrSocket proves a different OS user

Problem

HerdrPeerLauncher.java treated "memberHerdrSocket is configured" as proof that member
panes run under a DIFFERENT OS user than fleetd's own process. fleetd cannot see the uid
at the other end of a unix socket — that is an assumption, not a fact. An operator may
point memberHerdrSocket at a second herdr running as the SAME user (pane isolation
only), in which case members DO inherit fleetd's environment, and the WARN was telling
the operator to disregard exactly that gap.

Scope — wording only, no behaviour change

Kept the same conclusion (member credential gap is UNKNOWN, memberCredentials cannot
be verified from this daemon) and the same fallback code paths. Only the stated REASON
changed: from "members run as a different user" to "fleetd cannot confirm what OS user
that herdr runs as." Matches the framing Fleetd.reportMemberTrustModel already uses on
main.

Messages changed (before → after)

  1. hostEnvNames field javadoc (~line 117-124)

    • Before: "...member panes run on a second herdr owned by a different user — different
      $HOME, different secrets.sh, different environment entirely — so this field's
      data no longer describes what a member pane inherits."
    • After: "...member panes are routed to a second herdr, and fleetd has no channel to
      confirm what OS user that herdr runs as — it may be a different user with a
      different $HOME and secrets.sh, or the same one the daemon runs as. Either way
      this field's data can no longer be trusted to describe what a member pane inherits."
  2. WARN in warnUnknownMemberEnvironment (~line 1683)

    • Before: "memberCredentials gap: memberHerdrSocket is configured, so member panes run
      under a different OS user than fleetd's own process, with a different environment
      entirely — fleetd has no channel to read that user's environment. {} of the {}
      names..."
    • After: "memberCredentials gap: memberHerdrSocket is configured, so member panes are
      routed to a second herdr — fleetd has no channel to confirm what OS user that herdr
      runs as, so it cannot tell whether those panes inherit its own environment or a
      different one entirely. {} of the {} names..."
    • Same javadoc above it reworded to match.
  3. memberHerdrSocketConfigured() javadoc (~line 1633)

    • Before: "...i.e. member panes run on a second herdr owned by a different OS user
      than the daemon's own process."
    • After: "...i.e. member panes are routed to a second herdr. This tests only that the
      config key is set — fleetd has no channel to confirm what OS user that second herdr
      runs as, so a true result means 'member panes may run under a different OS user,'
      not that they do."
  4. warnCannotShareScrubDirectory (~line 1544, javadoc + WARN)

    • Before: "...java.io.tmpdir is fleetd's own 0700 temp dir, unreadable by another
      uid, which is the exact gap this ticket exists to close."
    • After: "...java.io.tmpdir is fleetd's own 0700 temp dir, which is unreadable if
      the member pane runs as a different OS user, and fleetd has no channel to confirm
      whether it does or not. Rather than gamble on that, this treats memberHerdrSocket as
      reason enough to require an explicitly shared location..."
    • No behaviour change — the fallback (falling back to the CB-596 sentinel overlay,
      requiring worktreeRoot+worktreeGroup) is unchanged; only the stated reason is
      softened from a claimed fact to an acknowledged unknown.

Left every other "different OS user" mention alone where it describes the INTENT of the
feature (design rationale for why a branch exists), per the ticket's scope — only fixed
places stating it as an established fact about the running system.

Test — mutation-proven

Added unknownEnvironmentWarnStatesUncertaintyNotAnAssertedDifferentUser to
HerdrPeerLauncherAllowListWiringTest, asserting the WARN names the real uncertainty
("no channel to confirm what OS user that herdr runs as") and does NOT contain the old
false-fact phrase ("member panes run under a different OS user than fleetd's own
process").

Mutation proof (broke the message, ran the test, it failed, restored, confirmed clean):

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
org.opentest4j.AssertionFailedError: expected the WARN to name the actual uncertainty
(no channel to confirm the herdr's uid), got: [... "MUTATION TEST BREAK" ...]

After restoring the real message: mvn -Dtest=HerdrPeerLauncherAllowListWiringTest test
→ Tests run: 19, Failures: 0, Errors: 0, Skipped: 0 (was 18 before this change; one
test added). git diff --exit-code on HerdrPeerLauncher.java after restoring showed
no leftover "MUTATION TEST BREAK" text — the file matched the intended final edit
exactly (confirmed via grep -n MUTATION returning no match).

Build

cd fleetd && mvn clean install — full, unpiped output:

Tests run: 1265, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

Also found (NOT fixed — out of this ticket's scope, reported for the lead)

Same "unverified uid asserted as fact" shape found elsewhere, all driven by the same
"memberHerdrSocket configured ⇒ different OS user" inference that HerdrPeerLauncher.java
no longer makes as fact:

  1. src/main/java/dev/ltms/fleet/config/FleetConfig.java:82 — worktreeGroup javadoc:
    "so a member running as a different OS user (see memberHerdrSocket) can write its own
    worktree..." — stated outright, no hedge.
  2. src/main/java/dev/ltms/fleet/config/FleetConfig.java:91-93 — memberLoginShell
    javadoc: "that mode spawns member panes under a different OS user than fleetd's own
    process..." — same inference as fact.
  3. src/main/java/dev/ltms/fleet/member/ClaudeCodeLauncher.java:741 —
    writeCharterFile javadoc: "Under memberHerdrSocket: the member pane runs as a
    DIFFERENT OS user, so that user cannot even traverse the directory..." — unverified
    claim driving file-placement logic.
  4. src/main/java/dev/ltms/fleet/member/OpenCodeLauncher.java:517 —
    configParentDir javadoc: "the member pane runs as a DIFFERENT OS user under this
    config key and cannot even traverse it" — same.
  5. src/main/java/dev/ltms/fleet/member/OpenCodeLauncher.java:495 — inline comment:
    // fleetd #219: the same "different OS user" gap fleetd #213 closed — restates the
    premise as settled fact.
  6. src/main/java/dev/ltms/fleet/session/GitWorktrees.java:798-799 —
    shareRootWithGroup javadoc: "under memberHerdrSocket: the member pane runs as a
    different OS user, which needs the execute bit..." — drives a real permission
    decision.
  7. src/main/java/dev/ltms/fleet/session/Worktrees.java:104 — shareWithGroup
    interface javadoc: same pattern in the interface contract itself.
  8. src/main/java/dev/ltms/fleet/member/EnvAllowListScrub.java:159-160 — "a
    fleetd-generated directory of flat files that a different-uid member process must
    read but never write" — assumes different-uid as fact to justify the chmod scheme.
  9. src/main/java/dev/ltms/fleet/session/GitWorktrees.java:763-765 and
    Worktrees.java:110 — "a different-uid member can write to it" — same premise
    repeated in the shareWithGroup rationale.
  10. src/main/java/dev/ltms/fleet/session/SessionManager.java:500 — inline comment:
    "leaves those overlay files operator-owned and read-only for a different-uid member"
    — same assumed-fact framing at a call site.

Fleetd.java:1140-1156 (reportMemberTrustModel) and the reworded parts of
HerdrPeerLauncher.java in this PR are the correct pattern to match if these get fixed
later.

Ticket: fleetd #184, item 5.

## fleetd #184, item 5 — stop claiming memberHerdrSocket proves a different OS user ### Problem `HerdrPeerLauncher.java` treated "memberHerdrSocket is configured" as proof that member panes run under a DIFFERENT OS user than fleetd's own process. fleetd cannot see the uid at the other end of a unix socket — that is an assumption, not a fact. An operator may point `memberHerdrSocket` at a second herdr running as the SAME user (pane isolation only), in which case members DO inherit fleetd's environment, and the WARN was telling the operator to disregard exactly that gap. ### Scope — wording only, no behaviour change Kept the same conclusion (member credential gap is UNKNOWN, `memberCredentials` cannot be verified from this daemon) and the same fallback code paths. Only the stated REASON changed: from "members run as a different user" to "fleetd cannot confirm what OS user that herdr runs as." Matches the framing `Fleetd.reportMemberTrustModel` already uses on `main`. ### Messages changed (before → after) 1. **`hostEnvNames` field javadoc** (~line 117-124) - Before: "...member panes run on a second herdr owned by a different user — different `$HOME`, different `secrets.sh`, different environment entirely — so this field's data no longer describes what a member pane inherits." - After: "...member panes are routed to a second herdr, and fleetd has no channel to confirm what OS user that herdr runs as — it may be a different user with a different `$HOME` and `secrets.sh`, or the same one the daemon runs as. Either way this field's data can no longer be trusted to describe what a member pane inherits." 2. **WARN in `warnUnknownMemberEnvironment`** (~line 1683) - Before: "memberCredentials gap: memberHerdrSocket is configured, so member panes run under a different OS user than fleetd's own process, with a different environment entirely — fleetd has no channel to read that user's environment. {} of the {} names..." - After: "memberCredentials gap: memberHerdrSocket is configured, so member panes are routed to a second herdr — fleetd has no channel to confirm what OS user that herdr runs as, so it cannot tell whether those panes inherit its own environment or a different one entirely. {} of the {} names..." - Same javadoc above it reworded to match. 3. **`memberHerdrSocketConfigured()` javadoc** (~line 1633) - Before: "...i.e. member panes run on a second herdr owned by a different OS user than the daemon's own process." - After: "...i.e. member panes are routed to a second herdr. This tests only that the config key is set — fleetd has no channel to confirm what OS user that second herdr runs as, so a `true` result means 'member panes may run under a different OS user,' not that they do." 4. **`warnCannotShareScrubDirectory`** (~line 1544, javadoc + WARN) - Before: "...`java.io.tmpdir` is fleetd's own 0700 temp dir, unreadable by another uid, which is the exact gap this ticket exists to close." - After: "...`java.io.tmpdir` is fleetd's own 0700 temp dir, which is unreadable if the member pane runs as a different OS user, and fleetd has no channel to confirm whether it does or not. Rather than gamble on that, this treats memberHerdrSocket as reason enough to require an explicitly shared location..." - No behaviour change — the fallback (falling back to the CB-596 sentinel overlay, requiring worktreeRoot+worktreeGroup) is unchanged; only the stated reason is softened from a claimed fact to an acknowledged unknown. Left every other "different OS user" mention alone where it describes the INTENT of the feature (design rationale for why a branch exists), per the ticket's scope — only fixed places stating it as an established fact about the running system. ### Test — mutation-proven Added `unknownEnvironmentWarnStatesUncertaintyNotAnAssertedDifferentUser` to `HerdrPeerLauncherAllowListWiringTest`, asserting the WARN names the real uncertainty ("no channel to confirm what OS user that herdr runs as") and does NOT contain the old false-fact phrase ("member panes run under a different OS user than fleetd's own process"). Mutation proof (broke the message, ran the test, it failed, restored, confirmed clean): ``` [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 org.opentest4j.AssertionFailedError: expected the WARN to name the actual uncertainty (no channel to confirm the herdr's uid), got: [... "MUTATION TEST BREAK" ...] ``` After restoring the real message: `mvn -Dtest=HerdrPeerLauncherAllowListWiringTest test` → `Tests run: 19, Failures: 0, Errors: 0, Skipped: 0` (was 18 before this change; one test added). `git diff --exit-code` on `HerdrPeerLauncher.java` after restoring showed no leftover "MUTATION TEST BREAK" text — the file matched the intended final edit exactly (confirmed via `grep -n MUTATION` returning no match). ### Build `cd fleetd && mvn clean install` — full, unpiped output: ``` Tests run: 1265, Failures: 0, Errors: 0, Skipped: 0 BUILD SUCCESS ``` ### Also found (NOT fixed — out of this ticket's scope, reported for the lead) Same "unverified uid asserted as fact" shape found elsewhere, all driven by the same "memberHerdrSocket configured ⇒ different OS user" inference that HerdrPeerLauncher.java no longer makes as fact: 1. `src/main/java/dev/ltms/fleet/config/FleetConfig.java:82` — `worktreeGroup` javadoc: "so a member running as a different OS user (see memberHerdrSocket) can write its own worktree..." — stated outright, no hedge. 2. `src/main/java/dev/ltms/fleet/config/FleetConfig.java:91-93` — `memberLoginShell` javadoc: "that mode spawns member panes under a different OS user than fleetd's own process..." — same inference as fact. 3. `src/main/java/dev/ltms/fleet/member/ClaudeCodeLauncher.java:741` — `writeCharterFile` javadoc: "Under memberHerdrSocket: the member pane runs as a DIFFERENT OS user, so that user cannot even traverse the directory..." — unverified claim driving file-placement logic. 4. `src/main/java/dev/ltms/fleet/member/OpenCodeLauncher.java:517` — `configParentDir` javadoc: "the member pane runs as a DIFFERENT OS user under this config key and cannot even traverse it" — same. 5. `src/main/java/dev/ltms/fleet/member/OpenCodeLauncher.java:495` — inline comment: `// fleetd #219: the same "different OS user" gap fleetd #213 closed` — restates the premise as settled fact. 6. `src/main/java/dev/ltms/fleet/session/GitWorktrees.java:798-799` — `shareRootWithGroup` javadoc: "under memberHerdrSocket: the member pane runs as a different OS user, which needs the execute bit..." — drives a real permission decision. 7. `src/main/java/dev/ltms/fleet/session/Worktrees.java:104` — `shareWithGroup` interface javadoc: same pattern in the interface contract itself. 8. `src/main/java/dev/ltms/fleet/member/EnvAllowListScrub.java:159-160` — "a fleetd-generated directory of flat files that a different-uid member process must read but never write" — assumes different-uid as fact to justify the chmod scheme. 9. `src/main/java/dev/ltms/fleet/session/GitWorktrees.java:763-765` and `Worktrees.java:110` — "a different-uid member can write to it" — same premise repeated in the `shareWithGroup` rationale. 10. `src/main/java/dev/ltms/fleet/session/SessionManager.java:500` — inline comment: "leaves those overlay files operator-owned and read-only for a different-uid member" — same assumed-fact framing at a call site. `Fleetd.java:1140-1156` (`reportMemberTrustModel`) and the reworded parts of `HerdrPeerLauncher.java` in this PR are the correct pattern to match if these get fixed later. Ticket: fleetd #184, item 5.
agent added 1 commit 2026-09-03 15:13:25 +02:00
fleetd #184: stop claiming memberHerdrSocket proves a different OS user
CI / contract (pull_request) Successful in 1m18s
CI / build (pull_request) Successful in 1m19s
3916adc372
HerdrPeerLauncher asserted, as established fact, that member panes run under
a different OS user whenever memberHerdrSocket is configured. fleetd has no
channel to see the uid at the other end of a herdr unix socket — an operator
may point memberHerdrSocket at a second herdr under the SAME user for pane
isolation, in which case members do inherit fleetd's environment and the
count this WARN told them to disregard is the real gap.

Reworded the class javadoc on hostEnvNames, the WARN in
warnUnknownMemberEnvironment, the javadoc on memberHerdrSocketConfigured(),
and warnCannotShareScrubDirectory's "unreadable by another uid" claim to say
what is actually true: fleetd cannot confirm what OS user the second herdr
runs as, so the member credential gap is UNKNOWN, not known-clean or
known-dirty. No behaviour change — the fallback paths and the honest
UNKNOWN conclusion stay the same, only the stated reason changes.

Matches the framing already used by Fleetd.reportMemberTrustModel on main.

Added unknownEnvironmentWarnStatesUncertaintyNotAnAssertedDifferentUser to
HerdrPeerLauncherAllowListWiringTest asserting the new WARN wording and that
it no longer claims a different OS user as fact.
ltms closed this pull request 2026-09-04 03:26:36 +02:00
Some checks are pending
CI / contract (pull_request) Successful in 1m18s
CI / build (pull_request) Successful in 1m19s

Pull request closed

Sign in to join this conversation.