Wall 6: no documented path ever enabled or started the shipped
mosaic-lease-broker.service — every gated runtime died ~4s in at lease
registration while fleet start reported rc0, and a broker not in the
reconciler plan could not be reported as drifted.
Activation lands in the control plane, not the launcher:
- fleet install places ALL FOUR units through placeUnitFile — a placement
helper that unlinks any by-path-enable symlink at the destination
BEFORE copying (Node copyFile follows the link and overwrites the SEED
template; measured on a throwaway systemd user instance 2026-08-17,
with both cp and fs.copyFile), removes a stale wants-symlink pointing
outside the active dir (readlink — readFile returns the target's
content, not the link path), then copies and daemon-reloads. The same
measurement showed systemctl enable <name> does NOT rewrite an existing
by-path wants-symlink — reconciliation must be explicit. Idempotent:
second install on by-path residue converges to the identical state.
Until now the copy block named three units and omitted the broker, and
the residue set / copy set were disjoint only by accident (fomo-lin
survived copy-through because its one symlink was the one unit not
copied); adding the broker made them intersect on first run. See the
SET-INDEPENDENCE note on the helper before adding a fifth unit.
- enableFleetUnits enables the broker first, alongside the holder.
- fleet start / reconciler start the broker BEFORE any holder/agent
lifecycle effect, then RE-CHECK the socket (not unit state) and exit
nonzero with a named code if it did not appear. Re-probed on every
invocation — a RemainAfterExit=yes dead-looking-active unit can never
make retry look like repair (the sticky-retry check).
- The reconciler plan carries broker {unitInstalled, socketPresent} as a
first-class member; the socket is the signal (enabled-but-dead units
report socketPresent=false).
- start-agent-session.sh preflights the broker socket BEFORE any tmux
effect (moved ahead of the ownership probe): absent -> exit 75
(EX_TEMPFAIL), named refusal with socket path and remedy, no doomed
pane. The agent@ unit is Type=oneshot with no Restart=, so the message
survives instead of looping. The preflight detects and refuses; it
never starts the broker.
- mosaic doctor's lease check names one convention-neutral remedy:
'mosaic fleet install (it reconciles either enable convention)' —
written from the measurement; teaching a manual systemctl line could
leave a host with competing wants-symlinks.
Tests: fleet-place-unit.spec.ts (8: clean-host negative control,
by-path residue -> seed bytes AND mtime unchanged [the finding-2 check],
wants-residue cleared, idempotence single + double-install convergence);
fleet.spec.ts broker-first enable ordering, refused start emits no
holder/agent calls, second-start re-probe; reconciler broker plan member
(enabled-but-dead shape) + broker-before-agent ordering in both command
and apply paths; test-agent-session-broker-preflight.sh (CI-fit: fake
tmux, real unix socket at a short /tmp path — AF_UNIX caps at 108 bytes,
hermetic env; absent -> exit 75 + no tmux session, live socket passes,
explicit env wins, --stop not fenced). 1563/1563 vitest, lint, root
build 25/25, root typecheck 45/45.
Sabotage controls: placement unlink removed -> exactly the seed-integrity
test reddens (1/8); socket re-check disabled -> exactly the two preflight
specs redden; shell preflight removed -> the bash suite reddens (6 FAIL
assertions, rc=1). All restored byte-identically (sha256-verified), all
green again.
Test 6 (greenfield 1124, seat alive 2min + second fleet start) runs on
sandbox after daphne's baseline, coordinated with fred.
Note: the preflight uses exit 75 measured against the unit's Restart=
policy (oneshot, none) — no restart loop.
Mosaic Stack
Self-hosted, multi-user AI agent platform. One config, every runtime, same standards.
Mosaic gives you a unified launcher for Claude Code, Codex, OpenCode, and Pi — injecting consistent system prompts, guardrails, skills, and mission context into every session. A NestJS gateway provides the API surface, a Next.js dashboard gives you the UI, and a plugin system connects Discord, Telegram, and more.
Quick Install
curl -fsSL https://mosaicstack.dev/install.sh | bash
Or use the direct URL:
bash <(curl -fsSL https://git.mosaicstack.dev/mosaicstack/stack/raw/branch/main/tools/install.sh)
The installer auto-launches the setup wizard, which walks you through gateway install and verification. Flags for non-interactive use:
bash <(curl -fsSL …) --yes # Accept all defaults
bash <(curl -fsSL …) --yes --no-auto-launch # Install only, skip wizard
This installs both components:
| Component | What | Where |
|---|---|---|
| Framework | Bash launcher, guides, runtime configs, tools, skills | ~/.config/mosaic/ |
| @mosaicstack/mosaic | Unified mosaic CLI — TUI, gateway client, wizard, auto-updater |
~/.npm-global/bin/ |
Install lanes
| Lane | Command | Use when | Source |
|---|---|---|---|
| Stable | bash tools/install.sh |
You want the released Mosaic CLI/framework | npm registry @mosaicstack/mosaic@latest + framework archive at main |
| Prerelease integration | bash tools/install.sh --next |
You want the current next integration branch |
Build-from-source at next |
| Contributor/source build | bash tools/install.sh --dev --ref X |
You are testing a branch before release; --ref wins |
Build-from-source at the requested ref |
--next is shorthand for the prerelease integration lane: it enables source-build mode and uses next unless an explicit --ref or MOSAIC_REF is provided.
After install, the wizard runs automatically or you can invoke it manually:
mosaic wizard # Full guided setup (gateway install → verify)
Requirements
- Node.js ≥ 22
- npm (for global @mosaicstack/mosaic install)
- One or more runtimes:
Usage
Launching Agent Sessions
mosaic pi # Launch Pi with Mosaic injection
mosaic claude # Launch Claude Code with Mosaic injection
mosaic codex # Launch Codex with Mosaic injection
mosaic opencode # Launch OpenCode with Mosaic injection
mosaic yolo claude # Claude with dangerous-permissions mode
mosaic yolo pi # Pi in yolo mode
The launcher verifies your config, checks for SOUL.md, injects your AGENTS.md standards into the runtime, and forwards all arguments.
Pi launches default to a token-lean skill posture: mosaic pi passes --no-skills so Pi does not preload every global skill description into the system prompt. Use MOSAIC_PI_SKILL_MODE=all mosaic pi for the legacy all-skills catalog, or MOSAIC_PI_SKILL_MODE=discover mosaic pi to let Pi use its native settings/project skill discovery.
TUI & Gateway
mosaic tui # Interactive TUI connected to the gateway
mosaic gateway login # Authenticate with a gateway instance
mosaic sessions list # List active agent sessions
Gateway Management
mosaic gateway install # Install and configure the gateway service
mosaic gateway verify # Post-install health check
mosaic gateway login # Authenticate and store a session token
mosaic gateway config rotate-token # Rotate your API token
mosaic gateway config recover-token # Recover a token via BetterAuth cookie
If you already have a gateway account but no token, use mosaic gateway config recover-token to retrieve one without recreating your account.
Configuration
Mosaic supports three storage tiers: local (PGlite, single-host), standalone (PostgreSQL, single-host), and federated (PostgreSQL + pgvector + Valkey, multi-host). See Federated Tier Setup for multi-user and production deployments, or Migrating to Federated to upgrade from existing tiers.
mosaic config show # Print full config as JSON
mosaic config get <key> # Read a specific key
mosaic config set <key> <val># Write a key
mosaic config edit # Open config in $EDITOR
mosaic config path # Print config file path
Management
mosaic doctor # Health audit — detect drift and missing files
mosaic sync # Sync skills from canonical source
mosaic skill list # Audit Claude skill registrations and conflicts
mosaic skill register <name> # Register one canonical skill with Claude Code
mosaic skill unregister <name> # Remove one Mosaic-owned Claude link
mosaic update # Update CLI/framework and auto-register canonical skills
mosaic wizard # Full guided setup wizard
mosaic bootstrap <path> # Bootstrap a repo with Mosaic standards
mosaic coord init # Initialize a new orchestration mission
mosaic prdy init # Create a PRD via guided session
Sub-package Commands
Each Mosaic sub-package exposes its API surface through the unified CLI:
# User management
mosaic auth users list
mosaic auth users create
mosaic auth sso
# Agent brain (projects, missions, tasks)
mosaic brain projects
mosaic brain missions
mosaic brain tasks
mosaic brain conversations
# Agent forge pipeline
mosaic forge run
mosaic forge status
mosaic forge resume
mosaic forge personas
# Structured logging
mosaic log tail
mosaic log search
mosaic log export
mosaic log level
# MACP protocol
mosaic macp tasks
mosaic macp submit
mosaic macp gate
mosaic macp events
# Agent memory
mosaic memory search
mosaic memory stats
mosaic memory insights
mosaic memory preferences
# Task queue (Valkey)
mosaic queue list
mosaic queue stats
mosaic queue pause
mosaic queue resume
mosaic queue jobs
mosaic queue drain
# Object storage
mosaic storage status
mosaic storage tier
mosaic storage export
mosaic storage import
# Schema migration is unavailable in this release. The current storage wrapper shells
# directly to `pnpm --filter @mosaicstack/db db:migrate`; it is legacy N-1,
# uncertified, and MUST NOT be invoked pending KBN-101-02/-03/-06/-08 activation.
# Future schema migration is non-operative: external bootstrap → TLS/roles → runner
# --run → runner --verify → readiness. Tier copy uses only the separately held secure
# migrate-tier route.
Telemetry
# Local observability (OTEL / Jaeger)
mosaic telemetry local status
mosaic telemetry local tail
mosaic telemetry local jaeger
# Remote telemetry (dry-run by default)
mosaic telemetry status
mosaic telemetry opt-in
mosaic telemetry opt-out
mosaic telemetry test
mosaic telemetry upload # Dry-run unless opted in
Consent state is persisted in config. Remote upload is a no-op until you run mosaic telemetry opt-in.
Development
Prerequisites
- Node.js ≥ 22
- pnpm 10.6+
- Docker & Docker Compose
Setup
git clone [email protected]:mosaicstack/stack.git
cd stack
# Install dependencies. The local tier uses in-process PGlite; leave DATABASE_URL unset.
# The pnpm store defaults to $HOME/.local/share/pnpm/store. Override it without
# editing the checkout with NPM_CONFIG_STORE_DIR=$HOME/another-store if needed.
pnpm install
# Verify dependencies and generated state before running source-quality gates.
# Missing dependencies exit 42; stale/foreign apps/web/.next state exits 43.
# The web build certifies its exact standalone symlink manifest; added, removed,
# retargeted, or manifest-only-tampered generated links also exit 43. This detects
# accidental, independent, stale, and foreign-residue mutation—the class exposed by
# a five-month-stale .next that produced 19 phantom TS2307 errors.
# It does NOT defend against a same-UID actor that can rewrite both manifest and
# marker consistently (CWE-345). RM-59 tracks the required executor/spine-side
# trust anchor outside worktree authority.
pnpm preflight
# Optional local queue service only. This does not start PostgreSQL.
docker compose up -d valkey
# The current Gateway/Web local process is held; see docs/guides/dev-guide.md.
# Do not start it until KBN-101-02 makes inherited dotenv/DSN state fail closed.
Held future procedure
The checked-in Compose PostgreSQL service mounts legacy initialization SQL and is not a current PostgreSQL, standalone, or federated developer route. Do not start it with Compose, invoke initialization SQL, or treat the planned migrator as currently executable.
Held future activation procedure — non-operative and no current command authority until KBN-101-00, KBN-101-03, and KBN-101-05
land: external bootstrap → TLS/roles → mosaic-db-migrator --run →
mosaic-db-migrator --verify → Gateway/Compose readiness. The future deployment artifacts—not
this README—will provide the reviewed commands and secret-consumer interface.
For local data-layer work, PGlite needs no PostgreSQL service. The optional Compose command above
starts only Valkey; OTEL Collector and Jaeger may likewise be started individually if needed,
without starting PostgreSQL. A Gateway/Web local process is not currently a safe PGlite route:
its unguarded dotenv loader may inherit a daemon PostgreSQL DSN. Do not use root pnpm dev or a
Gateway start command until KBN-101-02 makes that state fail closed.
Quality Gates
pnpm preflight # Checkout/dependency/generated-state validation
pnpm typecheck # TypeScript type checking (all packages)
pnpm lint # ESLint (all packages)
pnpm test # Vitest (all packages)
pnpm format:check # Prettier check
pnpm format # Prettier auto-fix
CI
Woodpecker CI runs on every push:
pnpm install --frozen-lockfile- Legacy N-1 CI status only — active, uncertified, and non-authorizing as an operator route: the checked-in job currently invokes
pnpm --filter @mosaicstack/db run db:migratewithDATABASE_URLagainst an isolated disposable PostgreSQL CI database. It performs direct DDL in that CI database, is not approved ordinary behavior or an operator route, and remains a known exception pending KBN-101-06 removal/replacement by the certified runner-backed CI path. pnpm test(Turbo-orchestrated across all packages)
npm packages are published to the Gitea package registry on main merges.
Architecture
stack/
├── apps/
│ ├── gateway/ NestJS API + WebSocket hub (Fastify, Socket.IO, OTEL)
│ └── web/ Next.js dashboard (React 19, Tailwind)
├── packages/
│ ├── mosaic/ Unified CLI — TUI, gateway client, wizard, sub-package commands
│ ├── types/ Shared TypeScript contracts (Socket.IO typed events)
│ ├── db/ Drizzle ORM schema + migrations (pgvector)
│ ├── auth/ BetterAuth configuration
│ ├── brain/ Data layer (PG-backed)
│ ├── queue/ Valkey task queue + MCP
│ ├── coord/ Mission coordination
│ ├── forge/ Multi-stage AI pipeline (intake → board → plan → code → review)
│ ├── macp/ MACP protocol — credential resolution, gate runner, events
│ ├── agent/ Agent session management
│ ├── memory/ Agent memory layer
│ ├── log/ Structured logging
│ ├── prdy/ PRD creation and validation
│ ├── quality-rails/ Quality templates (TypeScript, Next.js, monorepo)
│ └── design-tokens/ Shared design tokens
├── plugins/
│ ├── discord/ Discord channel plugin (discord.js)
│ ├── telegram/ Telegram channel plugin (Telegraf)
│ ├── macp/ OpenClaw MACP runtime plugin
│ └── mosaic-framework/ OpenClaw framework injection plugin
├── tools/
│ └── install.sh Unified installer (framework + npm CLI, --yes / --no-auto-launch)
├── scripts/agent/ Agent session lifecycle scripts
├── docker-compose.yml Dev infrastructure
└── .woodpecker/ CI pipeline configs
Key Design Decisions
- Gateway is the single API surface — all clients (TUI, web, Discord, Telegram) connect through it
- ESM everywhere —
"type": "module",.jsextensions in imports, NodeNext resolution - Socket.IO typed events — defined in
@mosaicstack/types, enforced at compile time - OTEL auto-instrumentation — loads before NestJS bootstrap
- Explicit
@Inject()decorators — required since tsx/esbuild doesn't emit decorator metadata
Framework (~/.config/mosaic/)
The framework is the bash-based standards layer installed to every developer machine:
~/.config/mosaic/
├── AGENTS.md ← Central standards (loaded into every runtime)
├── SOUL.md ← Agent identity (name, style, guardrails)
├── USER.md ← User profile (name, timezone, preferences)
├── TOOLS.md ← Machine-level tool reference
├── bin/mosaic ← Unified launcher (claude, codex, opencode, pi, yolo)
├── guides/ ← E2E delivery, orchestrator protocol, PRD, etc.
├── runtime/ ← Per-runtime configs (claude/, codex/, opencode/, pi/)
├── skills/ ← Universal skills (synced from agent-skills repo)
├── tools/ ← Tool suites (orchestrator, git, quality, prdy, etc.)
└── memory/ ← Persistent agent memory (preserved across upgrades)
Forge Pipeline
Forge is a multi-stage AI pipeline for autonomous feature delivery:
Intake → Discovery → Board Review → Planning (3 stages) → Coding → Review → Remediation → Test → Deploy
Each stage has a dispatch mode (exec for research/review, yolo for coding), quality gates, and timeouts. The board review uses multiple AI personas (CEO, CTO, CFO, COO + specialists) to evaluate briefs before committing resources.
Upgrading
Run the installer again — it handles upgrades automatically:
curl -fsSL https://mosaicstack.dev/install.sh | bash
Or use the direct URL:
bash <(curl -fsSL https://git.mosaicstack.dev/mosaicstack/stack/raw/branch/main/tools/install.sh)
Or use the CLI:
mosaic update # Check + install CLI updates
mosaic update --check # Check only, don't install
The CLI also performs a background update check on every invocation (cached for 1 hour).
Installer Flags
bash tools/install.sh --check # Version check only
bash tools/install.sh --framework # Framework only (skip npm CLI)
bash tools/install.sh --cli # npm CLI only (skip framework)
bash tools/install.sh --next # Prerelease lane: source build from next
bash tools/install.sh --dev # Contributor lane: source build at --ref/main
bash tools/install.sh --ref v1.0 # Install from a specific git ref (--ref wins over --next)
bash tools/install.sh --yes # Non-interactive, accept all defaults
bash tools/install.sh --no-auto-launch # Skip auto-launch of wizard
The installer rejects unrecognized flags or positional arguments before making changes and prints the supported-option usage.
Contributing
# Create a feature branch
git checkout -b feat/my-feature
# Make changes, then verify
pnpm typecheck && pnpm lint && pnpm test && pnpm format:check
# Commit (husky runs lint-staged automatically)
git commit -m "feat: description of change"
# Push and create PR
git push -u origin feat/my-feature
DTOs go in *.dto.ts files at module boundaries. Scratchpads (docs/scratchpads/) are mandatory for non-trivial tasks. See AGENTS.md for the full standards reference.
License
Proprietary — all rights reserved.