Bootstrap walkthrough
What this layer does
Phase 5 is Joe Blow's first contact with snappy-os. One command at a fresh terminal turns a bare laptop into a fully wired snappy-os node: canonical files on disk, symlinks fanned out to every detected runtime, hooks wired, doctor cron installed. No git. No package manager. No interactive ceremony beyond pasting a key.
Files involved
bin/install.js— single-file no-deps Node bootstrapper, ~250 lines.bin/cli.js— entry thatinitcalls into for the post-install steps.state/bin/sync/symlink-runtimes.sh— Phase 2 fan-out.state/bin/sync/migrate-realdirs.sh— Phase 11 conversion (only on
Robert's machine; Joes start clean).
bin/wire-hooks.js— Phase 3 hook installer.state/bin/sync-runtimes.ts— instruction-file generator.~/projects/snappy-os/.bootstrap-version— version file the
three-band policy reads.
~/projects/snappy-os/.bootstrap-report.json— per-runtime decisions.
Step-by-step
- Detect platform (mac / linux / windows). No package-manager
dependency — bin/do-spaces.js is bundled.
- Detect runtimes on PATH (
claude,codex,gemini,openclaw,
cursor, windsurf). Apply the three-band version policy.
- Prompt for
SNAPPY_MASTER_KEY(or read from env, 1Password CLI,
Doppler, or the <inviteCode> baked into /install/<code>).
- Pull both canonicals via Worker:
snappy-os pull --force --scope all --repo os
snappy-os pull --force --scope all --repo kernel
- Run
symlink-runtimes.shfor every detected runtime. - Wire hooks (canonical order, dedup on re-run).
- Generate Codex 3-file shims.
- Sync runtime instruction files (CLAUDE.md →
AGENTS.md / GEMINI.md / .cursorrules / .windsurfrules / .github/copilot-instructions.md).
- Install the doctor self-test cron:
30 */6 * * * snappy-os doctor --silent || snappy-os alert "doctor-failed".
- Smoke-test:
program.mdexists, symlinks resolve,_status200,
at least one skill visible per runtime in the parity matrix.
- Print "next steps" punch list (sample slash commands, where to
read program.md, telemetry opt-out, support channel).
Three-band version policy
| Band | Behaviour |
|---|---|
version < min | REFUSE to wire that runtime; print "runtime upgrade required: <name> >= <ver>"; continue with others |
min ≤ version < recommended | Warn; proceed with degraded-mode notes |
version ≥ recommended | Silent green |
Every decision lands in .bootstrap-report.json under runtimes[].decision. The /snappy-ops "System / ops" picker reads this file for the "Bootstrap report" sub-option.
Operational gotchas
- Idempotent re-run is non-negotiable. Every step checks current state
and only writes on diff. Smoke step C.1 / step 14 runs bootstrap 3x and asserts hook arrays don't grow.
- Windows reality:
mklink /Jworks without admin for junctions on
the same drive. PowerShell and cmd.exe both supported. Cross-drive junctions are rejected with a clear error.
- A previously-installed real dir (e.g.
~/.gemini/skills/snappy-image/)
must be migrated before symlinking; symlink-runtimes.sh calls migrate-realdirs.sh first when divergence is safe (SHA match) and surfaces a manual diff prompt when it isn't.
- The bootstrap NEVER auto-upgrades. If the remote
.bootstrap-version is more than one ahead, it prompts the user to re-run npx snappy-os@latest; it does not silently overwrite.
How to verify it's working
~/projects/snappy-os/program.mdexists.readlink ~/.claude/skills/snappy-os/program.mdresolves to that file.curl https://skills.snappy.ai/_statusreturns 200..bootstrap-report.jsonshows every detected runtime with
decision: "wired" | "warned" | "refused".
- Three consecutive
npx snappy-osruns leave hook lengths constant.