Status: implemented, with the same memory as macOS — install.ps1 installs GBrain (PGLite + pgvector) and runs end to end on a real Windows in CI (windows job in .github/workflows/test.yml); field reports welcome (originally a proposal, per CONTRIBUTING: approach before code). Scope: make
brain-in-a-box installable and runnable for a solo user on Windows. Team mode
(setup-company.sh) is out of scope for v1.
Update 2026-10 — gbrain on Windows, the gate is open. gbrain #1549 (pgvector missing from PGLite on Windows) does not reproduce. A spike on four Windows runners (windows-latest, windows-2022, windows-11-arm, and windows-latest with Defender real-time scanning on), gbrain 0.60.210.0 and Bun 1.4.3, no secret:
- pgvector 0.8.1 loads in gbrain's own PGLite build:
CREATE EXTENSION, HNSW index and<=>distance on disk, under a path with a space and an accent too;- keyword brain (
init --no-embedding): init, import, query and search in about 1 s, and reads exit on their own (the PGLite read hang thatgbqworks around on macOS does not happen here);- Voyage path (
voyage:voyage-4, 1024 d,rerank-3-lite) against a local stand-in (.github/ci/fake_voyage.py, wired throughprovider_base_urls): embeddings written, the query by meaning ranks the right page first;- 1000 pages, the macOS nightly cycle (
sync --no-embed,embed --stale --max-usd): sync 39-66 s (13 s on an M-series Mac), embed 15-23 s; a writer killed mid-sync leaves no corruption; an S4U scheduled task runs it all.What ships, in
install.ps1:
- Bun from its official installer (per user, no admin,
-NoCompletions) and GBrain at$GBRAIN_REF, a commit fetched alone (--depth 1, about 46 MB instead of 600 MB of history), thenbun install+bun link. Both versions are pinned: gbrain moves daily, and thewindowsCI job installs exactly that pin, so bumping it is a PR whose CI must pass. Running the install line again moves a user to the new pin;self_upgrade.modeis set tooffsogbrain upgradenever bypasses it. There is no nightly self-update on Windows.- Voyage key, optional, same text and one-token check as
install.sh. Asked only when a console is there (irm | iexfrom Win+R); without one,VOYAGE_API_KEYfrom the process. The key goes to~\.gbrain\config.jsonthrough Python's environment, never in a command line or the install log. A key given later moves an existing keyword brain to search by meaning.- The vault is a gbrain source (
sources add vault --path <vault>), thensync --source vault. On a fresh gbrain thedefaultsource is gbrain's own content folder, sosync --repo <vault>without a registered source fails with "not a git repository" (macOS too). The vault is committed first: gbrain reads it through git, and Windows installs before this one never committed it.gbq(gbq.cmd, andgbqfor Git Bash, Claude Code's Bash tool) runsengine/search/gbq.py: gbrain when~\.brain-search\enginesays so; a busy brain (pglite_busy, a long write holds PGLite's single writer for more than 30 s) is retried once; a missing, broken or still-busy gbrain, or a read still running after 60 s (killed with its Bun process), is answered from brain_search, the offline keyword index, which is always installed and rebuilt. If gbrain cannot be installed at all, the install still succeeds in keyword mode.- BrainNightly (Task Scheduler 04:00, S4U, runs at wake-up when missed, on battery too):
engine/nightly/windows-nightly.pycommits the vault, runssyncandembed --stale --max-usd 0.10, then rebuilds the keyword index. It replacesBrainSearchReindex. No dream cycle, no weekly lint yet.Not covered by CI: third-party antivirus, SmartScreen, a non-admin account, Windows Home, real traffic to api.voyageai.com from Bun (corporate proxy or TLS inspection),
gbrain serve(MCP). First indexing is 3 to 5 times slower than on an M-series Mac: a large vault can take several minutes.
Update 2026-06 — Phase 1 done. The engine hooks are now cross-platform (pure Python):
/tmp→tempfile.gettempdir(),clauderesolved viashutil.which(), home resolved at runtime withPath.home()(no__HOME__bake-in), and.gitattributesenforces LF so CRLF can't break the hooks on Windows. This benefits macOS too and is not gated by gbrain.Update 2026-06 — Windows path now implemented (search un-gated). Rather than wait on gbrain #1549, the search is replaced on Windows by
engine/search/brain_search.py— a local BM25 search in pure Python (no bun, no pgvector, no model download, offline).install.ps1wires it up: copies the vault skeleton, installs the (now cross-platform) hooks, builds the index, drops agbq.cmdshim so the existinggbq query "..."interface still works, registers Task Scheduler jobs (reindex 04:00 + reflection 12:00/23:00), and merges the globalCLAUDE.md. Tested:brain_searchindexes + queries a real vault on macOS;test-hooks.sh15/15.install.ps1still needs a real-Windows smoke test. (BM25 = keyword ranking; embeddings remain an optional upgrade for machines that canpip install sentence-transformers.)
Everything macOS-specific, by layer:
| Layer | macOS today | Windows needs |
|---|---|---|
| Installer | install.sh (bash); uname = Darwin || die; brew |
install.ps1 (PowerShell); OS detect; winget |
| Scheduler | launchd plists (nightly 04:00 + reflection 12:00/23:00) |
Task Scheduler (Register-ScheduledTask) |
| Safe wrapper | gbq (zsh) |
cross-platform wrapper (Node or Python) |
| Nightly | gbrain-nightly.sh (bash) |
PowerShell, or rewrite logic in Python |
| Hooks (Python) | hardcoded /tmp/... locks, __HOME__/.local/bin/claude |
tempfile.gettempdir(), resolve claude on PATH |
| Obsidian | brew install --cask obsidian |
winget install Obsidian.Obsidian |
The gating dependency — gbrain on Windows (researched 2026-06, lifted 2026-10: see the update above)
brain-in-a-box sits on gbrain (separate project, bun-based). The foundation is Windows-ready: bun's Windows support went stable in bun 1.2 (Jan 2026; ARM64 in 1.3.10), and PGLite is WASM so it runs wherever bun/node runs. So the building blocks are fine.
gbrain itself is the blocker. It is not CI-tested on Windows (CI = macOS + Ubuntu only), and its issue tracker has open/known Windows bugs — crucially on the solo/PGLite path we'd target:
- #1549 — PGLite on Windows 10: the
pgvectorextension is missing from the WASM binary →search/thinkdon't work. This is the dealbreaker: no semantic search = no brain. - #1605 — Supabase-pooler migration
getaddrinfo ENOTFOUNDon Windows (team path, less relevant to solo). - #1554 — cross-platform node shim (PR): the POSIX-shell postinstall didn't run on Windows.
- #1665 — "critical fix wave" merged Windows migration-spawn fixes.
So fixes are actively landing upstream, but as of this research a solo Windows
user cannot get working semantic search until #1549 (PGLite pgvector on Windows)
is resolved. Our port is gated on that. Building install.ps1 before gbrain's
PGLite works on Windows would ship a broken brain.
Phase 0 — Spike (gating). On a real Windows box: install bun (≥1.3.10),
gbrain init --pglite, embed, gbrain sync, and crucially gbrain query —
this is the check for issue #1549 (PGLite pgvector on Windows). If query returns
ranked results → green, proceed. If pgvector is still missing → stop; the port
is blocked upstream. Track gbrain #1549 / #1554 / #1665. Decision point.
Phase 1 — Cross-platform hooks (also benefits macOS). Replace /tmp with
tempfile.gettempdir(); resolve the Claude binary via shutil.which("claude")
with the __HOME__ path as fallback; audit for any other POSIX assumptions. Pure
Python, low risk. Keep test-hooks.sh green; add a PowerShell sibling.
Phase 2 — Scheduler abstraction. A thin installer step that registers the two
jobs with the OS scheduler: launchd on darwin (today), Task Scheduler on
Windows (nightly 04:00 + reflection 12:00/23:00, running python daily-reflection.py
and the nightly).
Phase 3 — gbq cross-platform. Port the zsh wrapper (force-kill on PGLite
read hangs, clean wait on writes, stale-lock sweep) to a small Node or Python
script that runs everywhere. Single source of truth, drop the zsh version or keep
it as a thin shim.
Phase 4 — install.ps1. PowerShell mirror of install.sh: copy vault
skeleton, install hooks (__HOME__ → $HOME replace), install/clone gbrain,
Obsidian via winget, register scheduled tasks, merge global CLAUDE.md.
Non-destructive, same as the bash installer. Replace the uname guard with OS
detection that routes to the right scheduler.
Phase 5 — Tests + CI. A cross-platform test-hooks (PowerShell or a Python
runner) and a windows-latest entry in the CI matrix so it doesn't regress.
Phase 6 — Docs. README + CONTRIBUTING: drop "macOS only", add Windows setup.
- Solo + PGLite only for v1. Defer team mode and the Supabase engine (that's where the known Windows bug lives).
- WSL is not the target — native PowerShell, so a non-technical Windows user isn't asked to install a Linux subsystem. (WSL would "work" trivially but isn't a real Windows port.)
- gbrain-on-Windows (gating, Phase 0).
- CRLF line endings breaking the Python/JSON hooks — enforce LF via
.gitattributes. - Task Scheduler quirks (working dir, env, user session) vs launchd's model.
- Path separators /
%USERPROFILE%vs~— handled bypathlib/os.pathif we remove the remaining hardcoded POSIX paths (Phase 1).
Don't build the Windows port yet — it's gated on gbrain fixing PGLite/pgvector on Windows (#1549). The right move now:
- Phase 1 (cross-platform hooks) regardless — pure Python cleanup, makes the hooks better on macOS too, and is the only part not blocked by gbrain.
- Watch gbrain #1549 (semantic search on Windows PGLite). The instant it's fixed, run the Phase 0 spike to confirm, then do Phases 2–6.
- Until then, point Windows users at the standalone git-only journal tool
(
devjournal) for the part that doesn't need gbrain — they get the journal, just not the searchable brain.