Collaborator identity is matched by bare tab label in every space, so one config entry adopts panes from other fleets #777

Open
opened 2026-10-05 19:06:53 +02:00 by ltms · 0 comments
Owner

Operator rule, given 2026-10-05: for same-host fleets there is a clear and absolute boundary — a leader and its members share the space name. One space is one fleet. fleetd.yaml:259 already encodes it for this fleet (workspace: fleet # one shared space: the lead + every worker are tabs in it).

LeadTabScanner enforces that boundary for leads and not for collaborators. That asymmetry is the defect.

The code

Both kinds are matched in the same method, two lines apart:

return new Entry(leadName, Kind.LEAD);                              // from leadLabelsHere
String collaboratorName = collaboratorTabToName.get(normalized);    // flat, no space
return collaboratorName == null ? null : new Entry(collaboratorName, Kind.COLLABORATOR);

LeadTabScanner.java:336-339.

  • Leads: leadLabelsBySpace is keyed by space label, then tab label. The constructor javadoc says "matched case-insensitively on both the space and the label". This is what fleetd #770 fixed.
  • Collaborators: collaboratorTabToName is "every configured collaborator's exact tab label → its name" (LeadTabScanner.java:143-144) — a flat map with no space key, built at :152.

So one fleet.collaborators.<name>.tab: <label> matches that label in every space herdr reports, minus excludedWorkspaceLabels.

Why this got worse today, not better

fleetd #770 made every fleet's lead tab the fixed label lead. Duplicate labels across spaces are now the designed state, not an accident. Measured with herdr tab list at 19:0x today — three tabs labelled lead at once:

tab space fleet_list role
w2:tY fleet lead
wA:t1 trinotes observer
wB:t1 anki observer

The lead half is proven correct by that table: three lead labels, exactly one role: lead. A fleet.collaborators.x.tab: lead entry would have matched all three.

Consequence

A pane in an unrelated fleet's space is adopted as this fleet's collaborator. That is a role upgrade — a collaborator may SEND to leads and to other collaborators, where an observer may only send to another observer — and collaborators() returns terminal_id → name, so two terminals would carry one identity and any name→terminal reverse read picks one arbitrarily.

Reachability — not live

fleet.collaborators is empty in this fleetd.yaml, and fleet_list reports "collaborators":[]. So nothing is mis-assigned right now. This is latent, and it is filed because the #770 convention makes the triggering config the natural thing to write: an operator who wants one collaborator in one space writes the label they see, and silently gets every space.

Fix

Give collaborators the same space key leads have: fleet.collaborators.<name>.workspace, matched on (space, label) like leadLabelsBySpace. An absent workspace: must not mean "any space" — that is the widening recorded in [an absent config block widens, it does not empty]. Decide whether it defaults to the lead's space or is required; required is the safer reading of the operator rule above.

A test must pin the boundary with a positive control: one collaborator configured for space A, one pane with that label in space A (matched) and one in space B (not matched). Asserting only the non-match would pass if the lookup returned nothing at all.

Not measured

I did not configure a collaborator and watch two panes be adopted. The claim above is read from :336-339 and :143-152, plus the live three-lead-tab table. No misdelivery was observed on any channel today: the fleet lead, the anki lead and the trinotes lead each checked their own traffic and every message landed in the intended pane.

Operator rule, given 2026-10-05: **for same-host fleets there is a clear and absolute boundary — a leader and its members share the space name.** One space is one fleet. `fleetd.yaml:259` already encodes it for this fleet (`workspace: fleet # one shared space: the lead + every worker are tabs in it`). `LeadTabScanner` enforces that boundary for leads and **not** for collaborators. That asymmetry is the defect. ## The code Both kinds are matched in the same method, two lines apart: ```java return new Entry(leadName, Kind.LEAD); // from leadLabelsHere String collaboratorName = collaboratorTabToName.get(normalized); // flat, no space return collaboratorName == null ? null : new Entry(collaboratorName, Kind.COLLABORATOR); ``` `LeadTabScanner.java:336-339`. - Leads: `leadLabelsBySpace` is keyed by space label, then tab label. The constructor javadoc says "matched case-insensitively on both the space and the label". This is what fleetd #770 fixed. - Collaborators: `collaboratorTabToName` is "every configured collaborator's exact tab label → its name" (`LeadTabScanner.java:143-144`) — a flat map with **no space key**, built at `:152`. So one `fleet.collaborators.<name>.tab: <label>` matches that label in *every* space herdr reports, minus `excludedWorkspaceLabels`. ## Why this got worse today, not better fleetd #770 made every fleet's lead tab the fixed label `lead`. Duplicate labels across spaces are now the **designed** state, not an accident. Measured with `herdr tab list` at 19:0x today — three tabs labelled `lead` at once: | tab | space | fleet_list role | |---|---|---| | `w2:tY` | `fleet` | `lead` | | `wA:t1` | `trinotes` | `observer` | | `wB:t1` | `anki` | `observer` | The lead half is proven correct by that table: three `lead` labels, exactly one `role: lead`. A `fleet.collaborators.x.tab: lead` entry would have matched all three. ## Consequence A pane in an unrelated fleet's space is adopted as this fleet's collaborator. That is a role upgrade — a collaborator may `SEND` to leads and to other collaborators, where an observer may only send to another observer — and `collaborators()` returns `terminal_id → name`, so two terminals would carry one identity and any name→terminal reverse read picks one arbitrarily. ## Reachability — not live `fleet.collaborators` is empty in this `fleetd.yaml`, and `fleet_list` reports `"collaborators":[]`. So nothing is mis-assigned right now. This is latent, and it is filed because the #770 convention makes the triggering config the natural thing to write: an operator who wants one collaborator in one space writes the label they see, and silently gets every space. ## Fix Give collaborators the same space key leads have: `fleet.collaborators.<name>.workspace`, matched on (space, label) like `leadLabelsBySpace`. An absent `workspace:` must not mean "any space" — that is the widening recorded in [an absent config block widens, it does not empty]. Decide whether it defaults to the lead's space or is required; required is the safer reading of the operator rule above. A test must pin the boundary with a **positive control**: one collaborator configured for space A, one pane with that label in space A (matched) and one in space B (not matched). Asserting only the non-match would pass if the lookup returned nothing at all. ## Not measured I did not configure a collaborator and watch two panes be adopted. The claim above is read from `:336-339` and `:143-152`, plus the live three-`lead`-tab table. No misdelivery was observed on any channel today: the fleet lead, the anki lead and the trinotes lead each checked their own traffic and every message landed in the intended pane.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: fleet/fleetd#777