| name | cos-setup |
| description | The single first-run entry point that stands up the WHOLE Cos system, sequencing the four component setup skills in dependency order — setup-vault → guard-setup → mcp-bridge-setup → backup-recovery. Use when setting up the chief of staff system, doing a first-run setup, onboarding a new machine, or asking for the full setup; also when you're unsure which component skill to run first and want the guided end-to-end runbook. |
| allowed-tools | Bash, Read |
Cos — full first-run setup (orchestrator)
This is the root runbook that brings the whole system up on a fresh machine. It does not
re-implement the component skills — it sequences them in the one order that works, because
each step produces what the next one needs: the vault must exist before the MCP can point at
it and before backup has something to protect; the guard model must be configured before the
guard bridge can report a real classifier; all the bridges + sidecars must be wired before
backup can snapshot live, populated stores. Run the four sub-skills in order, stop at each
CHECKPOINT, and only advance when it passes. End with the § End-to-end verification.
Every shell step below begins with the loader line
source "$(git rev-parse --show-toplevel)/config/load-config.sh", which exports $REPO_ROOT
(git-derived), $BREW_PREFIX, $LAUNCH_AGENTS_DIR, $COWORK_CONFIG, $VAULT_DIR, $BOARD_URL,
and the bridge/sidecar ports + URLs — use those instead of hardcoding paths, ports, or your
username. The only value still derived inline is $U=$(id -u), which launchctl needs at runtime.
Prerequisites checklist (gather these BEFORE step 1)
The whole system runs on these ports — keep them free
(lsof -nP -iTCP:<port> -sTCP:LISTEN): 3000 board app · 8001 board · 8003 calendar ·
8004 guard · 8005 vault (core bridges) · 8002 openwhispr · 8006 whatsapp · 8007
nutrition (optional add-on bridges) · 8008 search · 8009 guard · 8010 whatsapp-go
(sidecars). The 8002 (openwhispr), 8006/8010 (WhatsApp), and 8007 (Nutrition & Chef) ports
are only needed if you run those optional add-ons (Steps 3.4 / 3.5 / 3.6).
The sequence
Step 0 — seed runtime stores (fresh public clone only)
Step 0.5 — generate config/cos.env (machine paths / ports for the skills)
- What it does — writes
config/cos.env, the machine-local public config every skill
reads through the loader: the absolute Homebrew prefix, the node/uv/supergateway binary
paths, the LaunchAgents + Cowork config locations, the OpenWhispr store paths, the backup repo,
and all the ports (board :3000, bridges :8001–8005, sidecars :8008/:8009). It writes
only these public values — no secrets. The config split is deliberate, four files under
config/:
cos.env — machine paths + ports for the skills/setup (this step). Public, gitignored.
secrets.env — the ANTHROPIC_API_KEY (step 3). The loader does not source it; the
vault bridge's own launch wrapper (launch.sh) does.
settings.json — board/app prefs incl. principalEmail (step 0).
auto-sync.json — the ingest router's auto-sync switch.
- Idempotent — never overwrites an existing
cos.env (delete the file to regenerate, e.g. if
you changed machines and BREW_PREFIX moved). VAULT_NAME is left blank here — step 1
(setup-vault) fills it once your vault exists.
- Why before the vault — from here on every shell step in this skill and the component skills
begins with the loader line
source "$(git rev-parse --show-toplevel)/config/load-config.sh",
which sources this file and exports $REPO_ROOT, $BREW_PREFIX, $LAUNCH_AGENTS_DIR,
$COWORK_CONFIG, $OPENWHISPR_DB, $VAULT_DIR, $BOARD_URL, the bridge/sidecar ports + URLs,
etc. ($REPO_ROOT is git-derived, never stored in the file.) Generate it first so every step
downstream resolves to your machine's real values.
- Run (the heredoc body must stay flush-left —
<<EOF preserves leading whitespace, so do not
indent these lines or cos.env would gain stray spaces):
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
cd "$REPO_ROOT"
if [ -f config/cos.env ]; then
echo "config/cos.env exists — leaving it untouched (delete it to regenerate). Current values:"
grep -E '^[A-Z]' config/cos.env
[ -d "$BREW_PREFIX" ] || echo "WARNING: BREW_PREFIX '$BREW_PREFIX' not found — changed machines? delete config/cos.env and re-run."
else
tmp="$(mktemp)"
case "$(uname -s)" in
Darwin) COWORK_CONFIG="$HOME/Library/Application Support/Claude/claude_desktop_config.json" ;;
MINGW* | MSYS* | CYGWIN*) COWORK_CONFIG="${APPDATA:-$HOME/AppData/Roaming}/Claude/claude_desktop_config.json" ;;
*) COWORK_CONFIG="$HOME/.config/Claude/claude_desktop_config.json" ;;
esac
[ -d "$(dirname "$COWORK_CONFIG")" ] || echo "WARNING: Cowork config dir '$(dirname "$COWORK_CONFIG")' not found — is Claude Cowork Desktop installed? If it lives elsewhere, set COWORK_CONFIG in config/cos.env to the real path before running mcp-bridge-setup."
cat > "$tmp" <<EOF
# Cos — machine-local PUBLIC config (paths/ports for SKILLS + SETUP).
# Generated by cos-setup. Safe to edit. NO SECRETS (those live in config/secrets.env).
# Skills read this via: source "\$(git rev-parse --show-toplevel)/config/load-config.sh"
# ALWAYS QUOTE values (paths contain spaces). REPO_ROOT is git-derived, never stored here.
BREW_PREFIX="$BREW_PREFIX"
NODE_BIN="$BREW_PREFIX/bin/node"
UV_BIN="$BREW_PREFIX/bin/uv"
SUPERGATEWAY_BIN="$BREW_PREFIX/bin/supergateway"
LAUNCH_AGENTS_DIR="$HOME/Library/LaunchAgents"
COWORK_CONFIG="$COWORK_CONFIG"
# OpenWhispr voice add-on (optional; wired by openwhispr-mcp-setup) — paths to the desktop app's store.
OPENWHISPR_DB="$HOME/Library/Application Support/open-whispr/transcriptions.db"
OPENWHISPR_AUDIO_DIR="$HOME/Library/Application Support/open-whispr/audio"
BACKUP_REPO="$HOME/.cos-backups"
VAULT_NAME=""
BOARD_PORT="3000"
BOARD_BRIDGE_PORT="8001"
OPENWHISPR_BRIDGE_PORT="8002"
CALENDAR_BRIDGE_PORT="8003"
GUARD_BRIDGE_PORT="8004"
VAULT_BRIDGE_PORT="8005"
SEARCH_SIDECAR_PORT="8008"
GUARD_SIDECAR_PORT="8009"
# WhatsApp MCP add-on (optional; wired by whatsapp-mcp-setup). WHATSAPP_GO_PORT is the Go
# whatsmeow bridge sidecar (8010; whatsmeow's default 8080 is usually taken).
WHATSAPP_MCP_DIR="$HOME/Code/whatsapp-mcp"
WHATSAPP_MCP_BRIDGE_PORT="8006"
WHATSAPP_GO_PORT="8010"
# Nutrition & Chef add-on (optional; built-in in-repo mcp/nutrition-server, wired by
# nutrition-mcp-setup). NUTRITION_BRIDGE_PORT is the supergateway HTTP bridge for Claude Code;
# the add-on is gated per-board via Settings.addons, so naming the port only documents it.
NUTRITION_BRIDGE_PORT="8007"
EOF
mv "$tmp" config/cos.env
echo "Wrote config/cos.env — review it, then continue. setup-vault (Step 1) fills VAULT_NAME."
fi
Step 1 — setup-vault (FIRST: create the knowledge target)
- What it does — creates a private vault instance from the committed template
$REPO_ROOT/vault/example-vault/ (cp -R vault/example-vault vault/<name>), records the slug as
VAULT_NAME in config/cos.env (the loader derives $VAULT_DIR from it, and the vault plist's
COS_VAULT_DIR regenerates from that via scripts/gen-launchd.mjs when step 3 installs it),
registers it with Obsidian and records its unique vault ID in
config/settings.json (so the board's obsidian:// deep-links open THIS in-repo vault, not a
same-named copy elsewhere — STEP 3.5 of setup-vault), folds it into the backup SCOPE
(auto-derived from VAULT_NAME in backup/config.mjs — no hand-edit), and confirms it is
gitignored (the real vault holds PII and is NOT git-backed — its durability comes from step 4's
encrypted off-site backup; only example-vault is tracked).
- Why FIRST — the MCP needs a target to scope
COS_VAULT_DIR to, and backup needs something
to protect. Nothing downstream works without the vault directory existing.
- Prereq — the committed template
$REPO_ROOT/vault/example-vault/ is present (it ships with
the repo). Confirm: ls "$REPO_ROOT/vault/example-vault/".
- Run — invoke
/setup-vault (or follow its steps): cp -R vault/example-vault vault/<name>,
record VAULT_NAME="<name>" in config/cos.env (backup SCOPE then auto-derives from it; the
vault plist's COS_VAULT_DIR regenerates from $VAULT_DIR when step 3 installs the bridge).
- CHECKPOINT — all three must hold before step 2 (
VAULT_DIR resolves to
$REPO_ROOT/vault/$VAULT_NAME once setup-vault has set VAULT_NAME in cos.env):
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
ls "$VAULT_DIR" >/dev/null && echo "vault dir OK"
git -C "$REPO_ROOT" check-ignore "vault/$VAULT_NAME" && echo "gitignored OK"
grep -q "vault/$VAULT_NAME" "$REPO_ROOT/backup/config.mjs" && echo "in backup SCOPE OK"
Plus, for board deep-links to open the right vault: config/settings.json may carry a 16-char
obsidianVaultId (setup-vault STEP 3.5). A blank id is non-fatal and now self-heals — the
board reads the id through from Obsidian's own registry (vault-config.ts realpath-matches
obsidian.json) the moment the vault has been Open-folder-as-vault'd, so the ↗ deep-link and the
/vault "Registered with Obsidian" check light up on the next Refresh without re-running
anything; the in-app vault preview works regardless. So only flag the case where the vault is in
neither place (no id AND never opened in Obsidian):
grep -o '"obsidianVaultId": *"[0-9a-f]\{16\}"' "$REPO_ROOT/config/settings.json" && echo "obsidianVaultId OK" \
|| echo "note: no obsidianVaultId in settings.json — fine IF you've opened vault/$VAULT_NAME in Obsidian (the board self-detects it); otherwise do File → Open Vault → Open folder as vault to enable the ↗ deep-link"
Step 2 — guard-setup (the prompt-injection classifier model)
Step 3 — mcp-bridge-setup (wire the CORE servers + sidecars)
- What it does — wires the four core stdio MCP servers (board, calendar, guard,
vault) into both clients: Cowork spawns them directly as stdio
command entries in
claude_desktop_config.json; Claude Code reaches them over HTTP via $REPO_ROOT/.mcp.json,
each a supergateway + launchd bridge on 8001/8003/8004/8005. Also loads the two uv
sidecars — search :8008 and guard :8009 — and mcp/ensure-bridges.sh (chained into
board's dev/start) so the app brings the bridges up. (The optional openwhispr voice
server on :8002 is wired by Step 3.4's add-on skill, not here.)
- The vault bridge is special —
:8005, launchd label com.chiefofstaff.mcp-vault. Unlike the
other four it embeds the Agent SDK and makes outbound Anthropic calls, so it needs an
ANTHROPIC_API_KEY. The key is NOT in the plist: the plist's ProgramArguments runs
the launch wrapper mcp/vault-server/launch.sh, which sources the gitignored
config/secrets.env (copied from config/secrets.env.example) and exports the key before
exec'ing supergateway — so the secret lives in one machine-local file, never in the installed
plist or a committed file. COS_VAULT_DIR (= $VAULT_DIR, i.e. $REPO_ROOT/vault/$VAULT_NAME)
IS set in the installed plist (EnvironmentVariables) and the Cowork config; the plist is
generated from mcp/vault-server/vault.service.json by scripts/gen-launchd.mjs (it resolves
$REPO_ROOT / $VAULT_DIR from config/load-config.sh — see mcp/CLAUDE.md; the API key has no
placeholder, it stays in config/secrets.env). Restart it after editing the key (rotating the key
needs only this restart, no plist regen):
source "$(git rev-parse --show-toplevel)/config/load-config.sh"; U=$(id -u)
launchctl bootout gui/$U/com.chiefofstaff.mcp-vault 2>/dev/null || true
launchctl bootstrap gui/$U "$LAUNCH_AGENTS_DIR/com.chiefofstaff.mcp-vault.plist"
launchctl kickstart -k gui/$U/com.chiefofstaff.mcp-vault
- Prereq — node + supergateway; the vault from step 1 (the bridge's
COS_VAULT_DIR
target); the guard model from step 2 (so :8004 reports the real classifier); an
ANTHROPIC_API_KEY in config/secrets.env (cp config/secrets.env.example config/secrets.env,
then edit in the sk-ant-… key) for the vault bridge.
- Run — invoke
/mcp-bridge-setup (or follow its steps: node "$REPO_ROOT/scripts/gen-launchd.mjs" --install renders + loads the core plists — board/calendar/guard/vault bridges on 8001/8003/8004/8005
plus the search + guardsvc sidecars; node "$REPO_ROOT/scripts/gen-mcp-json.mjs" regenerates the
Claude Code .mcp.json; node "$REPO_ROOT/scripts/gen-cowork-config.mjs" board calendar guard vault
merges the Cowork stdio entries; mcp/ensure-bridges.sh).
- CHECKPOINT — all four core bridges answer an MCP
initialize, and the vault one is scoped:
source "$(git rev-parse --show-toplevel)/config/load-config.sh"; U=$(id -u)
launchctl list | grep chiefofstaff
for p in "$BOARD_BRIDGE_PORT" "$CALENDAR_BRIDGE_PORT" "$GUARD_BRIDGE_PORT" "$VAULT_BRIDGE_PORT"; do
curl -s -X POST "http://127.0.0.1:$p/mcp" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}' \
| grep -o '"name":"[a-z]*"' | head -1
done
The vault server's ready line ($REPO_ROOT/mcp/logs/vault.out.log) must echo your
COS_VAULT_DIR (= $VAULT_DIR). serverInfo.name=="vault" confirms the vault bridge
($VAULT_BRIDGE_PORT).
Step 3.4 — openwhispr-mcp-setup (OPTIONAL: the voice-notes add-on)
- What it does — wires the
openwhispr voice server (a Node stdio MCP fronted by a
supergateway + launchd bridge on :8002, plus a direct Cowork stdio entry) so /voice-operations
and /second-brain-ingest can read your OpenWhispr transcripts and route them onto the
board / vault. Entirely optional: skip it if you don't use the OpenWhispr desktop app.
- Why HERE (after the core bridges) — it reuses the same supergateway/launchd/
ensure-bridges.sh
machinery Step 3 set up, and routing voice notes depends on the board + vault MCPs already
being live (Steps 1–3). Its only external state is the app's own store, so its position relative
to Step 4 doesn't matter.
- Prereq — node + supergateway (from Step 3) and the OpenWhispr desktop app installed with at
least one recorded note (so
$OPENWHISPR_DB exists). No app yet? Skip this step, or wire it
against the bundled fixtures for a dry run (see the skill).
- Run — invoke
/openwhispr-mcp-setup (confirm the store → install the :8002 bridge plist
→ register both clients → wire ensure-bridges.sh → verify list_transcripts reports
Source: sqlite).
- CHECKPOINT — the openwhispr MCP answers and reads the real store:
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
curl -s -X POST "$OPENWHISPR_BRIDGE_URL/mcp" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}' \
| grep -o '"name":"openwhispr"' && echo "openwhispr MCP OK"
Step 3.5 — whatsapp-mcp-setup (OPTIONAL: the WhatsApp add-on)
- What it does — stands up the external
whatsapp-mcp repo (the Go whatsmeow bridge as a
launchd SIDECAR on :8010, the Python stdio MCP as a supergateway BRIDGE on :8006), does the
one-time QR pairing against your phone, and registers whatsapp in .mcp.json + the Cowork
config — so the /whatsapp-triage skill can reconcile WhatsApp onto the board exactly like
/mail-to-board does Gmail. Entirely optional: skip it if you don't want WhatsApp on the board.
- Why HERE (after the bridges) — it reuses the same supergateway/launchd/
ensure-bridges.sh
machinery Step 3 set up, and /whatsapp-triage depends on the board + guard MCPs already
being live (Steps 2–3). Its external store/ is not covered by Step 4's backup (that protects
the Cos repo's own stores), so its position relative to Step 4 doesn't matter.
- Prereq — go (builds the bridge), uv, supergateway, the whatsapp-mcp checkout
at
$WHATSAPP_MCP_DIR, and a phone running WhatsApp for the QR pairing.
- Run — invoke
/whatsapp-mcp-setup (clone/build → QR pair → install both LaunchAgents →
register both clients → wire ensure-bridges.sh → verify list_chats).
- CHECKPOINT — the whatsapp MCP answers and returns chat data:
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
curl -s -X POST "$WHATSAPP_MCP_BRIDGE_URL/mcp" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}' \
| grep -o '"name":"whatsapp"' && echo "whatsapp MCP OK"
Step 3.6 — nutrition-mcp-setup (OPTIONAL: the Nutrition & Chef add-on)
- What it does — wires the built-in
nutrition add-on MCP (a thin in-repo Node fetch
wrapper over the board's /api/nutrition/* routes, fronted by a supergateway + launchd bridge on
:8007, plus a direct Cowork stdio entry), then ENABLES the add-on
(Settings.addons.nutrition.enabled) so its food-log / pantry / meal-plan writes land and the
three /nutrition/* nav pages appear — so the /nutrition-chef operator skill can log meals,
track the pantry, and plan a week. Entirely optional and the SIMPLEST add-on (no sidecar, no
external repo, no secret); skip it if you don't want nutrition on the board.
- Why HERE (after the core bridges) — it reuses the same supergateway/launchd/
ensure-bridges.sh
machinery Step 3 set up, and it is a pure wrapper over the board API (Steps 1–3 must be live).
Its data rides cases.json, so it is covered by Step 4's backup automatically — but its position
relative to Step 4 doesn't matter.
- Prereq — node + supergateway (from Step 3) and the board reachable on
CRM_BASE_URL.
There is nothing external to install, clone, build, pair, or authenticate.
- Run — invoke
/nutrition-mcp-setup (confirm NUTRITION_BRIDGE_PORT=8007 → install the
:8007 bridge plist → register both clients → ENABLE the add-on → verify a log_food write
round-trips).
- CHECKPOINT — the nutrition MCP answers and the add-on is enabled:
source "$(git rev-parse --show-toplevel)/config/load-config.sh"
curl -s -X POST "$NUTRITION_BRIDGE_URL/mcp" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}' \
| grep -o '"name":"nutrition"' && echo "nutrition MCP OK"
curl -s "$BOARD_URL/api/addons" | grep -o '"id":"nutrition"[^}]*"enabled":true' && echo "add-on enabled"
Step 4 — backup-recovery (LAST: protect the now-populated stores)
End-to-end verification (the whole system, after all four steps)
Run from the repo root with the board dev app up (cd board && npm run dev, port $BOARD_PORT).
Start each shell with source "$(git rev-parse --show-toplevel)/config/load-config.sh":
- All four core bridges answer (the step-3 loop) →
board / calendar / guard / vault (plus
openwhispr on :8002, whatsapp on :8006, and/or nutrition on :8007 if you ran the
Step 3.4 / 3.5 / 3.6 add-ons). launchctl list | grep chiefofstaff shows each with exit 0.
- Guard sidecar healthy —
curl -s "$GUARD_SIDECAR_URL/healthz" → {"ok":true, "classifier":"model:…"} (or the deliberate heuristic-fallback). The search sidecar is
best-effort: curl -s "$SEARCH_SIDECAR_URL/healthz" → {"ok":true} (a cold/absent one just
degrades the board to keyword search — not a failure).
- A test vault ingest + query round-trips — drop a throwaway note into the vault and read it
back through the vault MCP (
$VAULT_BRIDGE_URL); the inner Agent SDK session is scoped to your
COS_VAULT_DIR (= $VAULT_DIR), so it must find what you just wrote and return it.
(Equivalently, use /second-brain-ingest then /second-brain-query.) Remove the throwaway note
after.
- A backup dry-run verifies —
node backup/backup.mjs && node backup/restore.mjs →
auth tag OK ✓ / sha256 OK ✓ / JSON-verified ✓, with vault/$VAULT_NAME present in the
manifest.
- The Cowork config is actually wired (don't just assume it — it's the one client whose file is
off-repo at
$COWORK_CONFIG): the file exists, parses, and lists the core stdio servers.
node -e 'const fs=require("fs"),p=process.env.COWORK_CONFIG;let c;try{c=JSON.parse(fs.readFileSync(p,"utf8"))}catch(e){console.log("FAIL: Cowork config missing/invalid at "+p+" — set COWORK_CONFIG in cos.env + re-run /mcp-bridge-setup §5");process.exit(1)}const m=c.mcpServers||{},miss=["board","calendar","guard","vault"].filter(n=>!m[n]);console.log(miss.length?"FAIL: missing from Cowork config: "+miss.join(", ")+" — re-run /mcp-bridge-setup §5":"OK: Cowork config lists "+Object.keys(m).join(", "))'
(Cowork only reads it at launch — the next section is the one-time UI activation; this just
proves the file is on disk and correct first.)
If all five pass, the Cos system is fully stood up: vault populated, guard classifying,
all bridges live for Cowork + Claude Code, and the live data under encrypted off-site backup.
Tell the user so, then hand off with the first-open and Day-to-day notes below.
First open in Claude Cowork — confirm the connectors + allow their tools
The config is wired, but Cowork reads claude_desktop_config.json only at launch and gates tool
calls behind a permission prompt. Walk the user through the one-time activation:
- Quit + reopen Cowork (⌘Q) so it re-reads the config.
- Settings → Connectors — confirm the local MCP servers (board, calendar, guard,
vault, plus openwhispr/whatsapp/nutrition if added) are listed and enabled. They run as local
stdio
command servers (not custom HTTP connectors), so they appear automatically once the config
is read — if they don't, it didn't parse: re-check §5 of /mcp-bridge-setup.
- Allow their tools — the first time an agent calls a server's tool, Cowork asks for permission;
choose "Always allow" per server so routine agent runs aren't interrupted by a prompt every
call (or approve per-tool if you prefer — "Always allow" is the smooth default for your own local
servers).
(Claude Code uses the HTTP bridges via .mcp.json and skips this — the connector-approval step is
Cowork-only.)
Day-to-day: running Cos in later sessions
After setup, most of it runs itself — make sure the user knows how to live with it and how to check health:
- The bridges + sidecars are launchd-managed. The core four (
board, calendar, guard,
vault), the search/guardsvc uv sidecars, and any optional add-ons you wired
(openwhispr/whatsapp/nutrition) start at login and crash-restart on their own
(KeepAlive). A normal next session needs no action — Cowork and Claude Code reach them
through the bridges whether or not the board dev app is running.
- Starting the board self-heals them.
cd board && npm run dev (or npm run start) runs
mcp/ensure-bridges.sh first, which bootstraps + kickstarts every service and prints one line each
([mcp] vault bridge up on :$VAULT_BRIDGE_PORT … or WARN: <name> bridge DOWN on :<port> — see mcp/logs/<name>.err.log). Reading that startup block IS the fastest health check.
- One-shot health check anytime: run
mcp/ensure-bridges.sh directly — it is re-runnable and never
stops anything, so it's the canonical "is everything up?" probe. Backstops: launchctl list | grep chiefofstaff (each agent + last exit code; 0 = clean) and curl -s "$GUARD_SIDECAR_URL/healthz" /
curl -s "$SEARCH_SIDECAR_URL/healthz" for the sidecars.
- In-app health surfaces:
/security (guard model, deps, master toggle), /backups (last
snapshot + recovery-key readiness), /vault (vault wiring + Obsidian deep-link).
- When a client can't see a server: re-run
mcp/ensure-bridges.sh; if it's still missing, re-run
/mcp-bridge-setup. Per-service logs for any WARN/DOWN live in mcp/logs/<name>.{err,out}.log.
- The two standing gestures: Guard ships OFF — flip it ON in
/security once its model deps are
ready (or deliberately leave it OFF; see /guard-setup). Backups then run nightly at 03:30 on
their own once /backup-recovery is set up.
If something fails
Each component skill owns its own troubleshooting — jump straight there:
/setup-vault (template/gitignore/SCOPE), /guard-setup (GatedRepoError, silent heuristic
fallback, cold sidecar), /mcp-bridge-setup (the node/simdjson + pm2 gotchas, a bridge DOWN,
Cowork can't see a server), /backup-recovery (bad magic / auth-tag throw / sha256 mismatch on
restore). Re-run only the failing step, re-check its CHECKPOINT, then resume the sequence.