82 lines
4.3 KiB
Markdown
82 lines
4.3 KiB
Markdown
# 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`:
|
|
|
|
1. A present, nonblank, whitespace-exact `MOSAIC_AGENT_NAME` selects the fleet path.
|
|
2. `resolveFleetIdentity()` must resolve that exact member through the existing roster/helper
|
|
boundary, and any defined `MOSAIC_AGENT_CLASS` (including blank/whitespace) must canonicalize to
|
|
the roster class, before any identity seed. Only undefined means absent.
|
|
3. `lstatSync()` preflights every destination directory entry without following links, so a dangling
|
|
link is rejected before its counterpart can be published.
|
|
4. Safe bounded snapshots are read from only the missing contracts under `defaults/`.
|
|
5. Each snapshot is written to a random owner-private temporary file in `MOSAIC_HOME`.
|
|
6. `linkSync()` publishes the complete file without overwriting an existing path. `EEXIST` means a
|
|
concurrent seat or operator won; the existing path is preserved and revalidated.
|
|
7. Temporary files are removed, and both installed contracts are re-opened through the no-symlink
|
|
secure-file reader before launch continues.
|
|
8. `composeContract()` independently re-resolves the roster, securely reads fleet `USER.md` through
|
|
a Linux descriptor at the point of use, and injects exact member identity and communications data.
|
|
|
|
A standalone launch with no `MOSAIC_AGENT_NAME` retains the portable tolerant USER read and the
|
|
interactive wizard. Fleet-only no-follow enforcement must not make supported standalone macOS
|
|
composition depend on Linux `/proc` descriptor traversal.
|
|
|
|
## 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, mismatched class, or explicitly blank/whitespace 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. Fleet composition coverage
|
|
replaces a previously validated `USER.md` with an external symlink and proves point-of-use refusal;
|
|
a standalone unreadable-optional-USER case proves the portable tolerant branch remains separate.
|
|
|
|
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 `~/.mosaic` launch-composition layer in PR #1213.
|
|
- Personalizing the operator's standalone identity without a wizard.
|
|
- Changing fleet systemd or shell launcher code.
|