KeiSeiKit-1.0/docs/USB-BRAIN-GUIDE.md
Parfii-bot f12eb9f83c fix(v0.21.1): wave-audit consolidated — 5 critic HIGH + 2 security HIGH + 3 polish
Closes 10 audit findings from 4-agent wave (critic + security +
architect + validator) on v0.21.0.

CRITIC HIGH (5):
  H1 s3_cloud::commit() was listing with delimiter='/' — nested
     writes silently dropped from manifest hash. Added
     list_recursive() (no delimiter), filter manifest-*.json from
     hash input.
  H2 S3Cfg access_key_env + secret_key_env were advertised in TOML
     but never read. Wired via resolve_explicit_creds() with
     aws-credential-types. Partial-set or empty-resolve → error.
  H3 display::sanitize_display missing in detach.rs + mount.rs
     (regression of v0.19.2 L9 ANSI injection fix). Applied at 8
     print sites. 2 new integration tests.
  H4 adapters/jsonmcp.rs RESTORED (was lost in earlier merge).
     107 LOC shared module: load_json_or_empty / upsert_under_key /
     remove_under_key / persist. claude_code 163→105, cursor 165→106,
     zed 178→114. Unified error handling via ConfigParseError.
  H5 ENV_LOCK shared across kei-store tests. New test_env.rs (24 LOC)
     exposed under cfg(any(test, feature='s3')). github.rs +
     s3_cloud/tests.rs + s3_smoke.rs all use shared mutex. Fixes
     parallel-test race on KEI_STORE_S3_ENDPOINT.

SECURITY HIGH (2):
  SEC-H1 scripts/install-actionlint.sh — added sha256 verify
     (shasum/sha256sum) before extract. ACTIONLINT_SHA256_OVERRIDE
     env var for CI injection. Per-platform constants marked
     [UNVERIFIED: SKIP] pending live checksums.txt fetch (agent had
     no WebFetch this session — user follow-up: paste from
     https://github.com/rhysd/actionlint/releases/download/v1.7.12/checksums.txt).
  SEC-H2 S3 SSRF/IMDS guard. validate_endpoint() rejects:
     loopback (127/8, ::1, localhost), link-local (169.254/16,
     fe80::/10), metadata hostnames (google/azure). Override via
     KEI_STORE_S3_ALLOW_INTERNAL=1. HTTP rejected unless
     KEI_STORE_S3_ALLOW_INSECURE=1. Custom endpoint now REQUIRES
     explicit creds (no IMDS chain leak via third-party endpoint).
     4 reject + 3 accept tests pass.

POLISH (3):
  D1 docs/USB-BRAIN-GUIDE.md — ⚠️ WARNING block under Prerequisites:
     exFAT/FAT32 NOT safe for multi-client attach (SQLite WAL needs
     shared-mem mmap). Use ONE client at a time on those FSes.
     New Troubleshooting entry 'SQLite corruption on mount-attach'.
  D2 '~5 MB release binary growth' now labelled [estimate, E5 —
     not yet measured] in CHANGELOG.md + s3_cloud/mod.rs header.
  D3 scripts/validate-workflow-shas.sh exits 2 (not 0) when
     UNVERIFIED_COUNT > 0 and GITHUB_TOKEN absent. Distinguishes
     'network denied' from 'all good'.

REAL VERIFICATION (pasted by agent):
  cargo check -p keisei -p kei-store: Finished (clean)
  cargo test -p keisei --release: 30 passed 0 failed
  cargo test -p kei-store --release: 10 + 9 passed (default features)
  cargo test -p kei-store --features s3 --release:
    31 + 9 + 6 = 46 passed (with s3)
  bash -n scripts/*.sh: OK
  regen-counts.sh --check: no drift

Constructor Pattern: largest new src 200 LOC (s3_cloud/mod.rs, at
limit). jsonmcp.rs 107 LOC. test_env.rs 24 LOC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:03:17 +08:00

11 KiB
Raw Blame History

USB Exobrain — Step-by-Step Test Recipe

Goal: put a KeiSeiKit "brain" on a physical USB drive or flash card, mount it into Claude Code (and Cursor / Continue / Zed if installed), verify MCP tools are live, unplug — verify clean detach.

Target audience: someone trying the v0.21 exobrain feature for the first time on macOS (adapt paths for Linux).


0. Prerequisites

On the host (your laptop):

  • KeiSeiKit installed: ./install.sh --profile=full ran successfully, cargo test -p keisei shows 28/28 pass.
  • keisei CLI on PATH: ls ~/.claude/agents/_primitives/_rust/target/release/keisei returns the binary. Optionally ln -sf ~/.claude/agents/_primitives/_rust/target/release/keisei ~/.local/bin/keisei so you can just type keisei.
  • Claude Code installed (or Cursor / Continue / Zed — the CLI auto-detects all four).
  • A USB stick or flash card mounted on macOS — expected path /Volumes/<NAME> (e.g. /Volumes/EXOBRAIN).

On the USB drive:

  • Filesystem: exFAT or APFS (not HFS+ if you want cross-platform). FAT32 works but has 4 GB per-file limit — fine for brain dir (< 200 MB total even with 5 platform binaries).
  • Free space: ~500 MB recommended (5 mcp-server binaries × ~90 MB each = ~450 MB, plus room for memory/artifacts SQLite).

⚠️ WARNING — exFAT / FAT32 are NOT safe for multi-client attach.

SQLite WAL mode (used by kei-memory, kei-artifact, kei-social-store inside a brain) requires a filesystem with reliable shared-memory mmap. exFAT and FAT32 do NOT provide this, and keisei mount (multi-client fan-out in step 7 below) WILL corrupt the memory DBs if the brain lives on one of those filesystems.

  • On exFAT / FAT32, use the brain with ONE client at a time (single keisei attach --scope=user). Do not run keisei mount.
  • For reliable multi-client use, put the brain on APFS (macOS), ext4 (Linux), or NTFS (Windows). exFAT is fine for single-client or read-only cross-platform transport.
  • If you've already mounted a brain from exFAT and suspect corruption, see "SQLite corruption on mount-attach" in Troubleshooting.

1. Create the brain directory

Replace EXOBRAIN with your actual USB volume name.

BRAIN=/Volumes/EXOBRAIN/my-brain
mkdir -p "$BRAIN"/{bin,memory,artifacts,manifests}

Structure after step 1:

/Volumes/EXOBRAIN/my-brain/
├── bin/
├── memory/
├── artifacts/
└── manifests/

2. Download MCP server binaries for every platform

Grab the 5 single-binary compiles from the latest GitHub release. For v0.21.0:

BASE=https://github.com/KeiSei84/KeiSeiKit/releases/download/v0.21.0
cd "$BRAIN/bin"

# macOS Apple Silicon
curl -fL -O "$BASE/kei-mcp-server-darwin-arm64"
curl -fL -O "$BASE/kei-mcp-server-darwin-arm64.sha256"

# macOS Intel
curl -fL -O "$BASE/kei-mcp-server-darwin-x64"
curl -fL -O "$BASE/kei-mcp-server-darwin-x64.sha256"

# Linux x86_64
curl -fL -O "$BASE/kei-mcp-server-linux-x64"
curl -fL -O "$BASE/kei-mcp-server-linux-x64.sha256"

# Linux arm64 (may be unavailable on older releases — continue-on-error in CI)
curl -fL -O "$BASE/kei-mcp-server-linux-arm64" 2>/dev/null || echo "skipped linux-arm64"

# Windows x86_64
curl -fL -O "$BASE/kei-mcp-server-windows-x64.exe"
curl -fL -O "$BASE/kei-mcp-server-windows-x64.exe.sha256"

# Verify every downloaded binary against its .sha256
for f in kei-mcp-server-*.sha256; do
  shasum -a 256 -c "$f" || echo "FAIL: $f"
done

# Strip macOS Gatekeeper quarantine + chmod +x on Unix binaries
chmod +x kei-mcp-server-darwin-* kei-mcp-server-linux-* 2>/dev/null || true
xattr -d com.apple.quarantine kei-mcp-server-darwin-* 2>/dev/null || true

Expected: every shasum -c prints OK.


3. Write manifest.toml (schema v2, per-platform)

cat > "$BRAIN/manifest.toml" <<'EOF'
[brain]
schema_version = 2
name = "my-brain"
created = "2026-04-22T00:00:00Z"

[paths]
memory = "memory/"
artifacts = "artifacts/"
manifests = "manifests/"

[paths.mcp_server]
darwin-arm64 = "bin/kei-mcp-server-darwin-arm64"
darwin-x64   = "bin/kei-mcp-server-darwin-x64"
linux-x64    = "bin/kei-mcp-server-linux-x64"
linux-arm64  = "bin/kei-mcp-server-linux-arm64"
windows-x64  = "bin/kei-mcp-server-windows-x64.exe"
EOF

Validation: keisei rejects the brain if name contains anything outside ^[a-z][a-z0-9_-]{0,63}$, if any path is absolute, if any path contains .., or if the root is a symlink.


4. Verify the brain loads before attaching

keisei list-adapters

Expected: 2-column table showing every adapter, detected: yes for at least claude-code (the others depend on whether Cursor / Continue / Zed are installed on this host).

If you want a dry-run of the brain itself without touching any client config:

# Loads manifest, validates path confinement + schema — then errors because
# nothing attached yet. Use the error to confirm load-path is clean.
keisei status
# Expected: "no brain attached"

5. Attach the brain to Claude Code

Single-client, user scope (the safe default):

keisei attach "$BRAIN" --scope=user

Expected output (literally):

attached brain 'my-brain' to claude-code (user scope)
  brain path: /Volumes/EXOBRAIN/my-brain
  mcp server: /Volumes/EXOBRAIN/my-brain/bin/kei-mcp-server-darwin-arm64
  client cfg: /Users/you/.claude/settings.json
  marker:     /Users/you/.keisei/attached.toml
run /help in Claude Code to verify the MCP server is reachable

The last line is the client-specific post-attach hint. Each adapter emits its own.


6. Verify in Claude Code

Close and reopen Claude Code (or run /help → "MCP servers" section). You should see my-brain listed with the /Volumes/EXOBRAIN/my-brain/bin/kei-mcp-server-darwin-arm64 command.

# On the host, inspect what just got written:
cat ~/.claude/settings.json | jq '.mcpServers["my-brain"]'
# Expected:
# {
#   "command": "/Volumes/EXOBRAIN/my-brain/bin/kei-mcp-server-darwin-arm64",
#   "env": {
#     "KEISEI_BRAIN_ROOT": "/Volumes/EXOBRAIN/my-brain"
#   }
# }

Run a Claude Code command that uses a tool from kei-mcp-server (any of the 25+ MCP tool endpoints). If Claude Code reports success, the brain is live.


7. Multi-client mount (all detected clients at once)

keisei mount "$BRAIN"

Fan-out attach to every client detected on this host. For each, writes to user-scope config (~/.claude/settings.json, ~/.cursor/mcp.json, ~/.continue/config.json, ~/Library/Application Support/Zed/settings.json).

Expected summary:

mounted brain 'my-brain' to:
  claude-code ✓
  cursor      ✓
  continue    ✗ (not detected)
  zed         ✓

8. Project-scope attach (Claude Code / Cursor only)

Useful for per-project brains that stay with the repo:

cd ~/path/to/your-repo
keisei attach "$BRAIN" --scope=project

Writes to ./.claude/settings.json (inside the repo) instead of ~/.claude/. Continue and Zed reject this scope with a clear error — they don't have per-project MCP config.


9. Verify brain health

keisei status

Expected:

attached: my-brain
  brain path: /Volumes/EXOBRAIN/my-brain
  attached at: 2026-04-22T17:30:00Z
  clients:
    - claude-code (user scope)        ~/.claude/settings.json
    - cursor       (user scope)        ~/.cursor/mcp.json
    - zed          (user scope)        ~/Library/Application Support/Zed/settings.json
  health: OK (manifest readable, mcp_server binary exists)

If you unplug the USB, re-run keisei status:

  health: WARN — mcp_server binary at <path> is not reachable
    (brain was mounted at /Volumes/EXOBRAIN/my-brain, which no longer exists)

10. Detach — clean up when done

keisei detach

Strips the mcpServers.my-brain entry from every client's config (preserving any other MCP servers you have configured), then deletes the marker.

Expected:

detached 'my-brain' from:
  claude-code  ✓
  cursor       ✓
  zed          ✓
marker removed: ~/.keisei/attached.toml

Confirm:

keisei status   # → "no brain attached"
jq '.mcpServers' ~/.claude/settings.json   # → your own entries only

11. Eject the USB safely

# macOS:
diskutil eject /Volumes/EXOBRAIN

# Linux:
umount /media/$USER/EXOBRAIN

Physically unplug.


Troubleshooting

"BrainNotFound" on attach

  • Check /Volumes/EXOBRAIN/my-brain/manifest.toml exists
  • Check the path to keisei attach is absolute (/Volumes/...), not relative

"PathEscape" on attach

  • Every path under [paths] must be relative and resolve inside the brain root. No ../, no absolute paths.
  • /Volumes/EXOBRAIN/my-brain is a symlink to elsewhere. Pass the real resolved path instead — keisei refuses symlink roots to prevent accidental host-dir pivot via crafted USB.

"NoPlatformBinary" on Claude Code first-use

  • Your platform's binary isn't in bin/. Check std::env::consts::{OS, ARCH} on your host — the expected filename is kei-mcp-server-<os-renamed>-<arch-renamed> where macos→darwin, x86_64→x64, aarch64→arm64.

Claude Code can't spawn the MCP server

  • Ensure chmod +x applied to the binary
  • On macOS: xattr -d com.apple.quarantine <binary> to clear Gatekeeper
  • Check the binary actually runs standalone: /Volumes/EXOBRAIN/my-brain/bin/kei-mcp-server-darwin-arm64 --help

"NameConflict" on attach

  • Another MCP server with the same name already exists in the client's config. Either rename your brain (name in manifest.toml) or remove the existing entry manually.

SQLite corruption on mount-attach

  • kei-memory / kei-artifact / kei-social-store databases show "disk image is malformed" or "database is locked" errors after a keisei mount on a USB drive.
  • Root cause: the brain lives on exFAT or FAT32, which do not reliably support SQLite WAL-mode shared-memory mmap. Multi-client attach corrupts the DB.
  • Fix:
    1. Copy the brain dir to an APFS (macOS) / ext4 (Linux) volume, either internal disk or a reformatted USB.
    2. Re-attach via keisei attach <new-path> or keisei mount <new-path>.
    3. For single-client use on the same exFAT drive, restrict to keisei attach --scope=user only — do NOT use keisei mount.
  • Prevention: re-read step 0 → Prerequisites → "⚠️ WARNING" above.

What this tests end-to-end

  1. Portable brain — works from read-only USB, no installer needed on the brain itself
  2. Per-platform dispatch — schema v2 picks the right binary based on host OS+arch
  3. Multi-client fan-outkeisei mount attaches to every detected client in one call
  4. Clean detach — zero residue in host configs, preserves unrelated MCP servers
  5. Safety gates — path confinement, name regex, symlink rejection, size bound (64 KiB manifest cap)
  6. Schema v1 compat — drop in an older v1 brain with a single-string mcp_server, still works

If all 11 steps above pass, the v0.21 exobrain is production-ready for single-user workflows.

For shared-brain scenarios (team members all mounting the same brain over git / S3) see the kei-store backend docs — S3 backend via keisei attach s3://my-bucket/brain/ (requires --features s3 at install).