4.0 KiB
Fleet First-Start Identity Boundary
Status: Implemented by issue #1264. Requirements:
FCM-REQ-12,AC-FCM-10.
Problem
launchRuntime() called checkSoul() before runtime execution. A missing top-level SOUL.md
caused checkSoul() to spawn a child mosaic wizard with inherited stdio. Under a systemd-created
fleet pane with no TTY, that child blocked or failed before the runtime boundary even though generic
defaults/SOUL.md and defaults/USER.md already shipped in the same MOSAIC_HOME.
Chosen boundary
The fix remains at checkSoul() and does not add flags to yolo, fleet commands, systemd units, or
start-agent-session.sh:
- A present, nonblank, whitespace-exact
MOSAIC_AGENT_NAMEselects the fleet path. resolveFleetIdentity()must resolve that exact member through the existing roster/helper boundary, and any ambientMOSAIC_AGENT_CLASSmust canonicalize to the roster class, before any identity seed.lstatSync()preflights every destination directory entry without following links, so a dangling link is rejected before its counterpart can be published.- Safe bounded snapshots are read from only the missing contracts under
defaults/. - Each snapshot is written to a random owner-private temporary file in
MOSAIC_HOME. linkSync()publishes the complete file without overwriting an existing path.EEXISTmeans a concurrent seat or operator won; the existing path is preserved and revalidated.- Temporary files are removed, and both installed contracts are re-opened through the no-symlink secure-file reader before launch continues.
composeContract()independently re-resolves the roster, securely readsUSER.mdthrough a descriptor at the point of use, and injects exact member identity and communications data.
A standalone launch with no MOSAIC_AGENT_NAME retains the interactive wizard.
Identity and authority
The copied defaults deliberately say “Mosaic agent”; they are a generic behavioral base. They are not the source of a fleet seat's identity. The canonical roster controls:
- exact agent/session name;
- canonical role/class and persona;
- peer rows and point of contact;
- tmux socket and helper target; and
- communications generation.
An unknown/padded ambient name or mismatched ambient class fails before any file is seeded. This avoids replacing the interactive wall with a fleet of indistinguishable or ambiently invented identities.
Concurrency and filesystem properties
- Sources and final destinations are bounded regular files beneath
MOSAIC_HOME; target and dangling symlinks are not followed. - New files have mode
0600. - Hard-link publication is same-filesystem, atomic, and no-clobber.
- A temporary path is removed only when this process successfully created it.
- All required source snapshots are validated before the first destination is published, preventing a missing second default from leaving a partial seed.
- Existing operator files are never chmodded or rewritten.
Verification
src/commands/launch-first-start.spec.ts uses the production-kind boundary: the real built CLI in a
no-TTY subprocess, not a direct wizard test. The package test:vitest gate builds Mosaic before
Vitest, while the clean-checkout command builds its workspace dependencies first, so ignored
dist/cli.js cannot be absent or stale. A fake lease launcher records whether execution reached the
runtime boundary and captures the composed prompt.
Positive and negative cases prove the check can both proceed and refuse. Composition coverage also
replaces a previously validated USER.md with an external symlink and proves point-of-use refusal.
Real Pi authentication and provider task execution remain environment tests, not claims of this fixture.
Non-goals
- Runtime installation or pane-PATH resolution (#1256/#1258).
- The held
~/.mosaiclaunch-composition layer in PR #1213. - Personalizing the operator's standalone identity without a wizard.
- Changing fleet systemd or shell launcher code.