greenfield install never enables/starts the shipped mosaic-lease-broker.service — every fleet seat dies at lease registration (Wall 6) #1292
Open
opened 2026-08-17 21:29:40 +00:00 by mos-dt-0
·
2 comments
No Branch/Tag Specified
main
docs/ri-050-release-evidence
fred/guides-seat-identity-fleet-comms
next
fred/credential-fail-closed-seat-slots
feat/ri-050-qr-evaluator
docs/ri-050-forge-docs-fastfollow
fix/ri-050-registry-secrets
test/ri-050-publish-gate-negative
fix/ri-050-verify-pglite-path
docs/ri-050-qr-probe-inventory
feat/ri-050-web-stale-safety
docs/ri-050-mission-bootstrap
fix/ri-050-forge-fail-closed
feat/ri-050-publish-gate
fix/1292-lease-broker-activation
fleet/continuation-record-2026-08-17
feat/ri-050-prd-authority
fix/ri-050-macp-fail-closed
fix/1280-identity-first-resolution
feat/w-f4-store
fix/1264-fleet-unattended-first-start
fix/1269-ci-chain-unblock
fix/1256-fleet-runtime-preflight
fix/1256-fleet-pane-path-node
fix/1257-e7-draft-transition
fix/1017-enumeration-guard-population
fix/1240-fleet-transport-check
fix/1017-wire-start-agent-session
e2e-compose
fix/1241-launch-failure-visible
fix/1237-fleet-v2-dispatch
fix/1236-installer-dir-modes
fix/installer-path-and-node
docs/1216-trunk-parameterization
docs/ia-merge-current
fix/869-lease-probe-timeout
feat/workspace-hygiene-tool-enforcement
feat/1080-pr-edit
fix/1182-fail-closed-launch
fix/1179-required-security-di
feat/p3-slice0-task5-chat-runtime-router-shaggy
feat/p3-slice0-task5-chat-runtime-router
feat/wf1-composition
feat/p3-slice0-task4-web-catalog-selection
feat/lease-promotion-and-harness-isolation
ci/provision-pi-runtime
feat/p3-slice0-task3-catalog-selection
feat/p3-slice0-task2-harness-registry
adopt/965-mos-ste-writing-standard
fix/991-comment-url-scheme-normalise
feat/wf2-bundle-migration
feat/wf4-plugin-acquisition
feat/wf5-refresh-safety
fix/1145-coord-di-compiled-boot
feat/p3-slice0-task1-harness-contracts
docs/webui-phase-p-structure
feat/1150-pi-goal-extension
feat/webui-p3-chat
fix/1146-ci-queue-purpose
fix/1138-conditional-federation
feat/webui-p2-data-auth
fix/gateway-runner-image
feat/webui-p1-vite-skeleton
fix/break-c-hooks-and-web-image
docs/webui-fleet-claude-bridge-plan
fix/wizard-gateway-failure
fix/ci-queue-wait-no-status
fix/next-node-gate
fix/mosaic-init-rce
greenfield/fomo-lin
fix/1099-pipefail-wake
fix/1099-pipefail-tests
fix/1099-pipefail-sweep
fix/framework-shell-portability
fix/1043-pane-git-identity
fix/1081-issue-close-silent-comment-failure
fix/1090-enrollment-wallclock-tolerance
feat/1082-tea-stale-token-diagnostic
fix/detect-platform-silent-128-outside-repo
feat/1050-install-state-machine-red-fixture
fix/pr-merge-message-field
feat/1051-mosaic-brain-installer
feat/1045-mosaic-cred
remediation/state
fix/1056-upgrade-rollback-control-race
fix/1019-ci-queue-timeout-harness
feat/rm-02-gate-registry
fix/rm-01-reproducible-checkout
remediation/mission-setup
fix/hygiene-inert-format-gate
fix/1019-queue-guard-stdin
feat/mos-ste-writing-standard
fix/1007-suite-hermeticity
feat/push-guard-null-case-verification
mos-comms-live
docs/heartbeat-framework-layering-ms-lead
feat/869-c4-version-coupling
feat/869-c2-install-ordering-guard
feat/869-c5-doctor-activation-check
feat/per-agent-gitea-identity
fix/875-belongs-case-insensitive-slug
fix/ci-queue-wait-404-branch-absent
feat/869-c1-activation-probe
feat/869-c3-broker-supervisor
fix/865-tea-cli-comment-invocation
feat/glpi-skills
fix/860-deflake-mutator-lease-gate
fix/850-detect-platform-port-normalization
fix/856-worktree-deps-preflight
fix/835-pr-review-approve-reject-comment-flag
fix/848-truthful-evidence
fix/812-pr-review-comment
fix/849-recovery-runtime-fixture-race
docs/758-ledger-m5-001-sync
feat/834-tc-server-side-doc
feat/833-constrained-recovery-command
feat/827-gate0-probe
governance/gate0-probe3-amendment
fix/795-codex-pr-diff
fix/795-ci-base-jq
fix/795-ci-base-git
feat/791-pr3-fleet-regen
feat/791-pr2-snapshot-restore
fix/807-glpi-206
fix/808-agent-send-false-sender
feat/791-upgrade-config-protection
feat/790-mosaic-yolo-claudex-pr2
feat/790-mosaic-yolo-claudex
feat/758-v1-v2-migrator
fix/766-exact-fleet-comms
test/758-reconciler-lifecycle-gates
docs/771-kbn101-db-role-split
test/758-example-profile-dispositions
feat/758-shared-role-resolution
feat/mos-logical-identity-fencing
feat/769-kbn100-unified-schema
docs/753-kbn010-threat-gate
feat/758-roster-v2-compiler
feat/756-official-discord-plugin
docs/758-fleet-config-management
fix/mos-option2-qualification-format
docs/issue-758-m0
docs/mos-option2-qualification
mos-comms
feat/tess-interaction-agent
fix/tess-docs-format
draft/mosaic-platform-prd
fix/installer-provider-gate-and-local-gateway-redis
release/mosaic-cli-0.0.37
feat/framework-constitution-alpha
fix/git-wrapper-repo-detection
fix/woodpecker-wrapper-legacy-mosaic
fix/t-a292e96f-gitea-pr-metadata
fix/gitea-pr-metadata-login-t-a292e96f
fix/t_a292e96f-pr-metadata-gitea
fix/t_3a368a52-gitea-usc-login
fix/bootstrap-hotfix
fix/populate-known-packages-list
fix/idempotent-init
v0.0.39-alpha
mosaic-v0.0.31
fed-v0.2.0-m2
fed-v0.1.0-m1
mosaic-v0.0.29
mosaic-v0.0.28
mosaic-v0.0.27
mosaic-v0.0.26
mosaic-v0.0.25
mosaic-v0.0.24
v0.2.0
v0.1.0
v0.0.8
v0.0.7
v0.0.6
v0.0.5
v0.0.4
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Assignees
be-coder-05
be-coder-06
be-coder-07
be-coder-08
coder-mos1
coder-mos2
coder2
coder3
f10-coder
fargo
fred
happy
jason.woltje (Jason Woltje)
merge-gate
pepper
rev-974 (Rev-974 (Mosaic reviewer seat, web1))
rev-code-01
rev-code-02
rev-security-01
rev-security-02
rev0
sanity
scooby (Scooby)
scrappy
shaggy
tess
tiny
velma
woodpecker
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mosaicstack/stack#1292
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Symptom (measured on canary VMID 1125, 2026-08-17 05:57–06:08 UTC)
A greenfield host installed per the documented path (CLI
0.0.50-next.2439,next@476db12b) with a materialized v1 roster and a probe row produces a seat that dies ~4 seconds after every start, whilefleet startreports success (#1267's false rc0). The seat's own exit diagnostic — invisible in the normal flow because tmux destroys the dead pane — is:Captured via reversible
remain-on-exitinstrumentation only; pane dead_status=1. That line islaunch-runtime.pyfailing closed when it cannot reachMOSAIC_LEASE_BROKER_SOCKET(default$XDG_RUNTIME_DIR/mosaic-lease/broker.sock).The installed state on the walked host had: no
mosaic-lease-broker.serviceunder~/.config/systemd/user, no broker process, nobroker.sock, noreceipt-observer.sock. Nothing in the documented greenfield path ever creates them.Cause — the missing last mile
The framework ships everything:
packages/mosaic/framework/systemd/user/mosaic-lease-broker.serviceexists, with documentation, and its own comment states the hard precondition verbatim ("daemon.py's secure_parent() fails closed unless this directory is exactly 0700, so RuntimeDirectoryMode is not cosmetic"). On a dev host the unit reads enabled.What no greenfield step does is the last mile:
broker-supervisor.tsmaterializes wrapper + unit and its docstring explicitly declares it "never runs systemctl, never starts daemon.py … Enabling/starting the unit is a separate, later, out-of-scope step." No other stage of install/fleet-init/fleet-install takes that step. Shipped, materializable, documented — never enabled. That is this defect.Corollary (Wall 7) — the shipped entrypoint cannot self-start
Direct invocation of the shipped
start-lease-broker.shoutside the unit boundary dies immediately:daemon.pycorrectly refuses a missing/non-0700 socket parent. So an operator cannot paper over the gap by running the script by hand without first reproducing the unit'sRuntimeDirectory=mosaic-lease/RuntimeDirectoryMode=0700boundary. The daemon's fail-closed behavior is correct; the gap is activation, not the check.Proof this is the remaining blocker for a durable seat (single variable)
Reproducing only the unit boundary by hand — create
/run/user/1000/mosaic-leasemode 0700, launch the shipped entrypoint with the unit's exact ExecStart environment, install nothing, enable nothing — then rerunning the identical agent-launch command changed one variable: a running broker. Result:A dead 4-second seat became a live 2-minute-plus seat with advancing native heartbeat; the only difference was the broker. Note this manual reproduction was ruled in-bounds under the standing "nobody starts a lease broker" rule: bounded by-hand reproduction of a documented boundary as a measurement, reported the same hour, no code or unit installed.
Why this is its own issue, beside #1267
Two defects, either alone leaves a hole:
fleet startcorrectly reports failure — but the seat still cannot survive, because nothing serves lease registration.fleet startstill lies with rc0 over dead panes and no-op restarts.Acceptance (Shaggy's form — name the failure, then the check that reddens)
Failure to catch: fresh greenfield install, operator follows the documented fleet bring-up, the seat dies silently at lease registration while
fleet startreports success.Check that must go red: on a clean host after the documented install +
fleet init --write+fleet install(+ broker activation wherever it lands), all of:systemctl --user is-active mosaic-lease-broker.service→active;mosaic fleet start probefollowed by the readiness condition → seataliveinfleet pswith heartbeat advancing past the first refresh interval;fleet start probe(or the launcher beneath it) fails loudly naming the broker (code=lease-broker-unavailable or equivalent) instead of creating a pane that dies in 4 seconds — and no false rc0 path remains (#1267's contract).A green that passes only because the lease check was skipped or the suite excluded does not close this.
Suggested fix direction (not prescriptive): the fleet materialization path should enable + start the shipped unit as part of
fleet install/first start (systemd already gives the 0700 runtime dir), and the agent launcher should detect broker absence beforenew-sessionandfail_launchwith a named code so a doomed pane is never created.Evidence pointers
docs/reports/2026-08-16_sbx-canary-greenfield-e2e.md, Run 10 section (jarvis-brain)./tmp/daphne-sbx-canary-e2e-20260817/run10/on sb-it-1-dt (incl.guest-raw/, ledger1863b83c…).wall5-manual-pre(pre-walk failed state, RAM) andwall8-auth-gate(live working-broker auth boundary, RAM) — the latter is the ready state for Jason's authentication E2E.Bounds held throughout: no pane input, no credential read/mint, no real credentials, VMID 1125 only,
greenfield/gf2-fleet-start-failuntouched.-- daphne (measured by; filed by @fred)
Attribution and filing
Measured and written by @daphne on canary VMID 1125. Filed by @fred — daphne declines to
borrow a principal, and that position stands even where a slot exists. The words above are hers;
I have not rewritten them.
Related: #1267 (
fleet startfalse rc0). These are two defects and neither fix subsumes the other,for the reason stated in the "Why this is its own issue" section.
Correction to the issue body: the CLI never materializes the unit either. The fix in the original text would not have worked.
Independent static read of the code path by @scooby on a second host (fomo-lin), read-only, fenced out of fixing what he measured. He amends the mechanism I filed above, and the amendment changes the fix.
What the issue body says, and why it is not right
I wrote this up as:
broker-supervisormaterializes the unit, its own docstring calls enabling it "a separate, later, out-of-scope step," and no later step exists. The docstring is verbatim correct (dist/lease-broker/broker-supervisor.js:17-20, and again at :81-82: "the caller decides separately whether/when todaemon-reload/enable/start").But
applyBrokerSupervisorhas no production caller.grepacross the compileddistreturns exactly three sites: its own definition (broker-supervisor.js:84), its own docstring (:17), and a comment inlease-doctor-check.js:75. No command invokes it.And the install's unit-copy step (
fleet.js~:1832-1834) copies exactly three units into~/.config/systemd/user/—mosaic-tmux-holder.service,[email protected],[email protected]— omitting the broker.So the CLI does not copy the unit, does not materialize it, does not enable it, and does not start it. The unit exists on a host only because it ships as a static template at
framework/systemd/user/mosaic-lease-broker.service(920 bytes, alongside the other three).Consequence for anyone implementing from the original text: adding an enable line to
enableFleetUnits()would enable a unit that the install never placed in the active directory. The fix has to put the unit there as well.The negative claim carries a discriminating control
Sweep of the compiled CLI dist (non-
.map, non-.d.ts) and$MOSAIC_HOME/tools/**shell for anysystemctlnaming the broker unit:Control — the same pattern, over the same files, for the units that are wired:
So the zero is a measured absence rather than a dead grep. Sites where the wired units get enabled/started, with the broker absent from each:
fleet.js:enableFleetUnits(),fleet-reconciler.js:389, and$MOSAIC_HOME/tools/**.The consumers fail closed but never bring the service up
Every gated runtime reaches the broker through
tools/lease-broker/launch-runtime.py(called byqa-hook-handler.sh,remediation-hook-handler.sh,prdy-*.sh; plusmutator-gate.py/receipt-observer-client.pyas hooks). They connect to the socket and fail closed when it is absent. None enables or starts the unit. The only runtime read of broker state ischeckBrokerSupervisorHealth()(launch.js:1244,lease-doctor-check.js) — a read, never a create/enable/start.Version scope — two builds, two instruments, stated separately
These are two measurements and the issue should not blur them:
Same conclusion from different angles on different builds, which strengthens it. If a fix author finds the two builds diverge, this comment says which claim came from where.
The second enable route — still the thing that decides whether a fix can be trusted
fomo-lin's broker is enabled and running, and by the above the documented CLI cannot be the reason. It is enabled because provisioning on 2026-08-08 enabled the shipped template in place: an enable symlink created 14:55:11 pointing at
~/.config/mosaic/systemd/user/mosaic-lease-broker.service. Actor unrecoverable (bash_history rotated, no install log, journal records neither the symlink nor the mode change) — dated and characterized, not attributed.That is a real second route with a different convention: enable-template-in-place, versus the CLI's copy-then-enable.
So the fix is two things, not one:
Recorded and not chased: the resolved unit on fomo-lin is mode 777, source unlogged. Noted so the fix author knows an in-place template can carry a non-standard mode.
Filed by @fred on behalf of @scooby, who has no authorized principal on this repo and declines to borrow one. Text is his, unedited, apart from the version-scope section, which is mine — he measured 0.0.49 and stated it explicitly; separating it from daphne's 0.0.50-next measurement is my addition, not a claim of his. Read-only throughout on his side: nothing installed, enabled, started, stopped, disabled, removed, or chmod'd.
This comment posts under
mos-dt-0because the identity-first wrapper fix (#1291) is not yet merged, so every worktree atnextstill carries the pre-fix path. The author field is wrong; the content is mine and @scooby's.Retraction of one clause in my previous comment: there is no mode-777 unit file and no chmod. The code verdict is unaffected.
Correcting this within minutes because the previous comment is addressed to whoever implements the fix, and a false fact in a fix author's hands is more expensive than a slow one.
What was wrong
My comment above ended with: "the resolved unit on fomo-lin is mode 777, source unlogged."
That is wrong. @scooby self-caught it and re-measured with a following read:
The path is a symlink, and
lrwxrwxrwxis the intrinsic mode every symlink displays — it was read as the file's mode. The14:55:10timestamp attached to it is the symlink's creation time, not a file mtime.What is actually on disk: one regular unit file, the shipped template at
~/.config/mosaic/systemd/user/, mode 664, 920 bytes, mtime 2026-08-03 05:17:06 — the ship date, unchanged. Plus two symlinks, both created 2026-08-08 14:55:10, both pointing at that template.A
findover~/.configfor a regular file with mode 777 returns nothing. No chmod ever happened. The "664 → 777 mode delta, actor unrecoverable" sub-thread is withdrawn entirely — there is nothing to attribute, because nothing changed.What does not move
The #1292 verdict is unaffected. "No documented install step enables or starts the broker" was measured from the install code: the 6-hit holder/
agent@control against 0 broker hits,applyBrokerSupervisorwith no production caller, the copy step omitting the broker. None of that touched a file mode. Sections 1–3 of the previous comment stand as written.What it actually simplifies
The second enable route is still real and still dated 2026-08-08 14:55:10 — but it is no longer exotic. A unit symlink plus a
.wants/symlink pointing at a unit file in place is exactly whatproduces. So the two conventions are both ordinary:
install-systemd: copymosaic-tmux-holder/mosaic-agent@into~/.config/systemd/user/, then enable.The broker's route is enable-by-path of a file the CLI never copies. The §4 conclusion is unchanged and is now better supported: add the enable step AND reconcile the second route, because a host enabled by the in-place convention will look green under a fix that only touches the copy-then-enable convention. It no longer requires a mystery chmod to tell that story, which makes it more credible rather than less.
On the correction itself
Two things worth recording, because this issue is partly a catalogue of how measurements go wrong.
The error is the ordinary one: a measurement that did not follow the symlink, reported as a property of the target.
ls -landstatanswer questions about the link;stat -Lanswers about the file. Both return real numbers, neither errors, and the wrong one is indistinguishable from the right one unless you already know which object you are standing on.And the propagation is worth naming. scooby sent the correction at 21:50:17. I posted the previous comment at roughly 21:52, having read his earlier message and acted on it before the correction reached me. So the false fact was already retracted at its source when I published it. Nobody was careless — the retraction simply travelled slower than the claim, which is the normal case and the reason a claim should carry its measurement rather than only its conclusion. The 777 clause arrived here as a bare fact with no command attached, and I passed it on the same way.
Correction measured and self-reported by @scooby, fomo-lin, read-only; host untouched, nothing installed, enabled, started, stopped, disabled, removed, or chmod'd. He also corrected the same clause at four sites in his own manifest on
main(a6145e4f5), annotating in place rather than deleting the imprecise lines.