diff --git a/fleetd/src/main/java/dev/ltms/fleet/member/HerdrPeerLauncher.java b/fleetd/src/main/java/dev/ltms/fleet/member/HerdrPeerLauncher.java index 990b076..2ca305c 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/member/HerdrPeerLauncher.java +++ b/fleetd/src/main/java/dev/ltms/fleet/member/HerdrPeerLauncher.java @@ -117,9 +117,11 @@ public abstract class HerdrPeerLauncher implements PeerLauncher { * *
fleetd #185 stage 2: that mirroring assumption holds only while the member pane runs under
* the SAME OS user as the daemon. When {@code memberHerdrSocket:} is configured, member panes
- * run on a second herdr owned by a different user — different {@code $HOME}, different {@code
- * secrets.sh}, different environment entirely — so this field's data no longer describes what a
- * member pane inherits. See {@link #logCredentialGap} for how that mode is handled.
+ * 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 {@code $HOME} and {@code 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. See {@link #logCredentialGap} for how that mode is
+ * handled.
*/
private final Supplier {@link #config} is {@code null} on any call site that never threaded the full config
* through (every production {@code HerdrPeerLauncher} does; a handful of older tests do not) —
@@ -1652,14 +1659,15 @@ public abstract class HerdrPeerLauncher implements PeerLauncher {
* fleetd #185 stage 2: the single replacement WARN for {@link #logCredentialGap}'s usual
* conclusions when {@code memberHerdrSocket:} is configured. {@link #hostEnvNames} (and
* everything derived from it — {@code known}/{@code allow} coverage, the allow-list scrub's
- * derived set) describes the DAEMON's own environment; under this config key member panes run as
- * a different OS user with a different environment entirely, so neither "every member pane
- * inherits them UNBLOCKED" nor "the scrub blanks them" is evidence-backed here — both would be
- * reporting on the wrong process. Logged once, names the config key, and states the honest
- * conclusion: the gap for member panes is UNKNOWN, not clean, so {@code memberCredentials} cannot
- * be verified from this daemon. The one count it does report is scoped explicitly to fleetd's own
- * environment, never presented as if it said anything about the member's — see {@link
- * #logCredentialGap}'s javadoc for why this branch exists.
+ * derived set) describes the DAEMON's own environment; under this config key member panes are
+ * routed to a second herdr, and fleetd has no channel to confirm what OS user that herdr runs
+ * as or to read its environment, so neither "every member pane inherits them UNBLOCKED" nor
+ * "the scrub blanks them" is evidence-backed here — both would be reporting on the wrong
+ * process. Logged once, names the config key, and states the honest conclusion: the gap for
+ * member panes is UNKNOWN, not clean, so {@code memberCredentials} cannot be verified from this
+ * daemon. The one count it does report is scoped explicitly to fleetd's own environment, never
+ * presented as if it said anything about the member's — see {@link #logCredentialGap}'s javadoc
+ * for why this branch exists.
*/
private void warnUnknownMemberEnvironment(FleetConfig.MemberCredentials creds) {
if (!unknownMemberEnvironmentWarned.compareAndSet(false, true)) {
@@ -1672,13 +1680,13 @@ public abstract class HerdrPeerLauncher implements PeerLauncher {
.filter(name -> CREDENTIAL_SHAPED_NAME.matcher(name).matches())
.filter(name -> !covered.contains(name))
.count();
- log.warn("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 in fleetd's OWN environment are credential-shaped and not on "
- + "known:/allow:, but that count describes fleetd's process, not the member "
- + "herdr's. The credential gap for member panes is UNKNOWN, not clean, and "
- + "memberCredentials cannot be verified from here.",
+ log.warn("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 in fleetd's OWN environment are "
+ + "credential-shaped and not on known:/allow:, but that count describes fleetd's "
+ + "process, not the member herdr's. The credential gap for member panes is "
+ + "UNKNOWN, not clean, and memberCredentials cannot be verified from here.",
gapInFleetdsOwnEnv, hostNames.size());
}
diff --git a/fleetd/src/test/java/dev/ltms/fleet/member/HerdrPeerLauncherAllowListWiringTest.java b/fleetd/src/test/java/dev/ltms/fleet/member/HerdrPeerLauncherAllowListWiringTest.java
index 75f883c..82c8b32 100644
--- a/fleetd/src/test/java/dev/ltms/fleet/member/HerdrPeerLauncherAllowListWiringTest.java
+++ b/fleetd/src/test/java/dev/ltms/fleet/member/HerdrPeerLauncherAllowListWiringTest.java
@@ -387,6 +387,33 @@ class HerdrPeerLauncherAllowListWiringTest {
+ appender.list.stream().map(ILoggingEvent::getFormattedMessage).toList());
}
+ /**
+ * fleetd #184 item 5: the unknown-environment WARN must state the honest reason for the
+ * UNKNOWN conclusion — fleetd has no channel to confirm what OS user the second herdr runs
+ * as — and must NOT assert as fact that member panes run under a different OS user just
+ * because {@code memberHerdrSocket} is configured. An operator may point it at a second herdr
+ * running as the SAME user, for pane isolation alone; in that case members DO inherit fleetd's
+ * environment, and asserting otherwise would tell the operator to disregard a real, known gap.
+ */
+ @Test
+ void unknownEnvironmentWarnStatesUncertaintyNotAnAssertedDifferentUser() {
+ FakeHerdr herdr = new FakeHerdr();
+ Set