Skip to content

Self-host guide

Commonwealth is self-hostable with zero vendor lock-in: the brain is an ordinary git repo you own, synced over any git remote you control (GitHub, GitLab, a bare repo on your own box). There is no Commonwealth server, no database to run, and no account.

The pieces

  • Brain repo — plain markdown notes (memory/, decisions/, work-state/, people/) plus a generated COMMONWEALTH.md router. The source of truth.
  • Git remote — any remote all teammates can push/pull. This is the sync backbone.
  • Lifecycle sync (default, daemonless — ADR-0032) — the plugin hooks commit local changes, pull teammates' work, resolve same-file conflicts as siblings (no data loss), and push at session start and after each capture. No resident process, nothing to install or babysit; a cross-process lock keeps concurrent syncs from racing. Safe without coordination because atomic notes union-merge (ADR-0003).
  • Sync daemon (optional profile — ADR-0032) — a local, resident process that additionally pulls on a poll interval for continuous background propagation. Opt in for headless/server installs, shared machines, or high-frequency teams; when it runs, the lifecycle hooks stand down (it owns sync). Start it with commonwealth sync start.
  • Per-user scope config (~/.commonwealth/config.json, never synced) — which directories are in capture scope (ADR-0008).
  • Registry (~/.commonwealth/registry.json, never synced) — maps each project directory to its brain.

Stand up a shared brain

  1. Create the brain and its remote. On the machine of whoever seeds it first:
cd ~/work/my-project
commonwealth init --remote git@github.com:my-org/my-project-brain.git

--remote sets the brain repo's origin; the first lifecycle sync (or commonwealth sync once) pushes the seeded canon up, and the registry mapping records the remote so teammates can clone-on-demand (ADR-0019). (No remote yet? Run init without it, create the empty remote, then git -C <brain> remote add origin <url> && commonwealth sync once.)

  1. A teammate joins. They install the CLI (npm i -g @cmnwlth/cli), then from the same project run commonwealth init. When a brain already exists for the project they join it (clone-and-go, time-to-first-value ≈ 0) rather than re-seeding. If their registry maps the project to a brain that isn't checked out locally yet — and the mapping carries a remote — lifecycle sync (or commonwealth sync once) clones it on demand on first use, under their own git identity, before the first sync. From then on each session start/end keeps both clones converged.

Access control = git permissions (ADR-0019). There is no separate ACL. Whether a teammate can read/write a brain is exactly whether their git identity can clone/push its repo — enforced by the git host (GitHub/GitLab teams, SSO, deploy keys). So a private brain over HTTPS needs a working git credential helper, and an SSH remote needs an agent key; a clone that fails for lack of access surfaces git's own error (run commonwealth doctor — it reports "mapped but not cloned yet / clone failed"). No access → the session degrades to no-brain, never a crash.

  1. Keep it converged. Convergence is automatic — the plugin hooks sync at session start and after each capture (ADR-0032), so there's nothing to run day to day. Manual/optional controls:
commonwealth sync once         # flush + pull now (also runs debt recovery)
commonwealth sync start | stop # opt into / out of the continuous daemon profile
commonwealth status            # review queue + sync state

Per-brain configuration (committed, team-wide)

The brain's .commonwealth/config.json is committed, so these apply to everyone (ADR-0009):

  • features.autoPromote (default on) — captured notes promote straight to canon (gates still run). Set false to hold captures in the review queue for commonwealth promote.
  • features.autoAdr (default on) — record decision notes (auto-detected and via /commonwealth:decide). Set false to stop tracking decisions in a brain.
  • features.semanticDedup (default off) — also catch near-duplicate notes phrased differently, using on-machine embeddings (needs an optional model package installed on enable).
  • secretScan (default { "entropy": false, "allowlist": [] }) — opt into high-entropy secret detection beyond the named patterns, with an allowlist for accepted values.

Toggle feature flags with commonwealth config / commonwealth-curate feature enable <name>; edit secretScan directly in the committed config file.

Codex and mixed-tool teams

Codex can use the live Commonwealth MCP server through the plugin:

commonwealth init --agent codex   # or --agent both

This also refreshes the project's AGENTS.md brain snapshot as a read-only fallback. The Codex plugin injects context at session and prompt boundaries, captures before compaction, and performs throttled capture when an agent turn reaches Stop. Codex has no SessionEnd; Stop is a turn boundary, not the end of the thread. After plugin install or update, run /hooks in Codex and trust the reviewed Commonwealth hook definitions.

For mixed-host operations, diagnose and update integrations independently:

commonwealth doctor --json
commonwealth update --agent both

One host's failed update does not skip the other. Codex marketplace refresh is idempotent and does not remove/re-add the plugin, preserving its enabled/trust state; updated hook definitions still require review in /hooks.

Zero-runtime fallback — commonwealth emit

For agents without the plugin, commonwealth emit writes the current project's team-brain slice into the generated context files Cursor, Copilot, and Codex already honor — so those teammates read the brain with zero runtime integration (no MCP server, no plugin):

commonwealth emit            # regenerate the context files for this repo
commonwealth emit --commit   # track them in git instead of gitignoring

It writes two wholly-owned files — .cursor/rules/commonwealth.mdc and .github/instructions/commonwealth.instructions.md — plus a sentinel-fenced block in AGENTS.md (your own content around the block is preserved). Every file is marked "generated — do not edit"; regenerate with emit, never hand-edit (ADR-0003, pointed outward).

By default the wholly-owned files are gitignored — they're per-machine derived output and would otherwise churn in teammates' diffs. Pass --commit to track them (e.g. to hand the context to teammates who won't run emit themselves). The rendered slice is deterministic and token-budgeted, and contains only canon (superseded notes are excluded).

Scope — control what gets captured (per user)

Capture is gated by your per-user scope so personal or out-of-bounds projects never feed the shared brain:

commonwealth scope allow ~/work        # only capture work under here…
commonwealth scope deny  ~/work/secret # …except this (deny wins)
commonwealth scope check               # in-scope | out-of-scope for the cwd
commonwealth scope show

Rule: in scope if (allow is empty OR under an allow entry) AND under no deny entry.

One directory is always excluded from automatic capture: the brain itself. A session run from inside the brain (or any subfolder of it) is a session about administering the brain, so capturing there would fill canon with notes about the brain — commonwealth doctor says so when your cwd is inside a brain, and the session receipt explains the silence. Recording something deliberately still works: /commonwealth:remember and /commonwealth:decide are never suppressed.

Scope only gates whether capture may happen — which brain a folder writes to comes from the registry mapping. To wire a new folder to your brain (allowlist + mapping + symlink in one go):

commonwealth add ~/work/new-project [--brain <dir>]

Secrets never leave the brain

Credentials are detected and blocked at capture and scrubbed pre-commit (defense in depth) — covering note files and the generated COMMONWEALTH.md/INDEX.md, and non-ASCII paths. A withheld note is reported and left uncommitted in your working tree to fix. This holds even across a rebase conflict. Tune detection per brain via secretScan.

If a secret slipped in before those gates existed — or was only "redacted" in the working tree, which leaves the raw value recoverable from the prior commit's blob (git log -p) and in every clone — commonwealth redact purges it from all history: it scans every blob in the object database, rewrites each leaked value to [REDACTED:<kind>], and force-pushes the rewrite to scrub the remote (ADR-0037). It is destructive and human-gated (type the brain name; --dry-run previews; --engine filter-branch forces the no-git-filter-repo fallback) and is never run by the sync daemon. Afterwards every teammate must git fetch origin && git reset --hard origin/<branch>, and the exposed credential should be rotated regardless — a history rewrite doesn't un-leak a value that was already served.

Trust & decay

Run commonwealth health for a freshness/trust score plus counts of stale, unverified, contradicted, and orphaned notes — so a lead can see the brain rotting before it does.

Because two teammates can independently capture the same fact, run commonwealth consolidate periodically to reconcile cross-user near-duplicates: it supersedes duplicates onto a single survivor (supersede-not-delete — the files are kept), single-writer and safe to re-run. Preview with --dry-run (ADR-0017).

When something's off — commonwealth doctor

The setup spans several parts that can fail silently: the plugin install, the plugin's live curate runtime (vendored or npx fallback), brain resolution for your cwd, a dangling .commonwealth/brain marker, and remote lag / review-queue depth / index freshness / scope. Sync health follows the daemonless model (ADR-0032): lifecycle sync with no daemon is reported as healthy; a live daemon is the daemon profile; the real red flag is sync debt — uncommitted or unpushed work that lifecycle sync hasn't flushed, surfaced with its age. commonwealth doctor walks the whole chain and prints pass/fail with the exact one-line fix per failed link:

commonwealth doctor          # human-readable pass/fail + fixes
commonwealth doctor --json   # structured report for agents/CI (exit 1 if any link failed)
commonwealth doctor --fix    # self-heal — restarts a stale daemon for daemon-profile users (the only auto-fix)

Paste the output into any support thread — it's the first triage step. Exit code is non-zero when a critical link failed, so CI can gate on it.

Prove you can restore it — commonwealth verify-restore

"Your knowledge is portable git you own" is only worth as much as your last successful restore. commonwealth verify-restore clones the brain into a throwaway temp dir and proves full recovery — every note schema-valid, ids unique, supersede chains resolving, no secrets in canon, and the derived COMMONWEALTH.md/INDEX.md regenerating byte-for-byte — then prints an RPO line (the age of the last commit = your worst-case data-loss window). Exit code is 0 only when recovery is verified, so it's a green/red CI gate.

commonwealth verify-restore                # prove the committed local state restores
commonwealth verify-restore --from-remote  # the real off-site proof: clone origin and verify
commonwealth verify-restore --json         # structured report for CI / dashboards

Weekly CI gate (GitHub Actions)

Drop this in the brain repo at .github/workflows/verify-restore.yml. It re-clones the brain from its own remote every Monday and fails the run if recovery can't be proven:

name: verify-restore
on:
  schedule:
    - cron: "0 6 * * 1" # 06:00 UTC every Monday
  workflow_dispatch: {}
jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npx -y @cmnwlth/cli verify-restore --from-remote
        env:
          COMMONWEALTH_BRAIN_DIR: ${{ github.workspace }}

A green check every week is the proof an eng lead wants before a team migrates tribal knowledge in — and doubles as the backup/compliance answer.

Offline escrow (git bundle)

For an air-gapped/off-platform copy, a brain is just a git repo, so snapshot it into a single file you can store anywhere (S3, a USB drive, a safe):

git -C <brain> bundle create brain-$(date +%F).bundle --all
# restore later, anywhere: git clone brain-2026-07-05.bundle recovered-brain

That's the whole export story — no proprietary format, no export command to trust.

Distribution

The @cmnwlth/* packages are published to npm, so no build step is needed:

  • CLI: npm i -g @cmnwlth/cli (or npx @cmnwlth/cli init).
  • Plugin: installing it from the marketplace works from a bare clone in Claude Code, Codex, or both. The host-specific manifests share the MCP server and brain registry but select different lifecycle files: Claude uses SessionEnd; Codex uses throttled, turn-scoped Stop because Codex has no session-end event. The MCP server and hooks run the published packages on demand via npx (@cmnwlth/mcp, @cmnwlth/curate), which pull better-sqlite3's per-platform prebuilt binary transitively. Nothing platform-locked is committed.

Codex users must run /hooks after install or update and trust the reviewed Commonwealth hook definitions; unreviewed plugin hooks are skipped. Scope remains local to each teammate: denied projects neither receive injected context nor send transcripts to the extractor, regardless of which host is installed. Codex's asynchronous hook option is not supported, so Commonwealth's command hook returns quickly after launching its own detached capture worker.