Beads (Steve Yegge) vs WikiHub — Source-of-Truth Comparison
Date: 2026-04-10 Status: Decided
Context
Both Beads and WikiHub use the same three-layer pattern — a fast queryable store, a text-serialized git-trackable export, and git hooks/plumbing to keep them in sync. But they flip the source-of-truth direction.
The shared pattern
- A fast queryable store (SQLite / Postgres)
- A text-serialized git-trackable export (JSONL / markdown files)
- Git hooks or plumbing to keep them in sync
The inversion
| Beads | WikiHub | |
|---|---|---|
| Source of truth | SQLite | Git bare repos |
| Derived/export | JSONL (for git tracking) | Postgres (metadata + search index) |
| Why this direction | Issue fields are structured data — queries need to be fast | Wiki pages are authored markdown — users expect git clone |
| Bidirectionality | Fully symmetric | Asymmetric by design — private content only in Postgres |
| Content duplication | Yes — same data in both | No — public in git only, private in Postgres only |
Why WikiHub chose git-as-truth
The whole thesis of WikiHub vs Notion/Google Docs is that the wiki IS a git repo. Real commits, real history, real git clone. If Postgres were the truth and git just an export, you'd have a slightly fancier ListHub.
Beads can afford SQLite-as-truth because nobody cares about the git history of an issue tracker. WikiHub users — Karpathy-wiki people, Obsidian vault owners — care about git log, git blame, and offline editing.
WikiHub's no-duplication model
- Public pages: content in git ONLY. Postgres has metadata/search. Reads via
git cat-file blob. - Private pages: content in Postgres ONLY (
private_contentcolumn). Never enters git. - Visibility change moves content between stores, never copies.
Postgres is rebuildable from git for public content. Private content + social graph are non-rebuildable — both backups required.
What WikiHub steals from Beads
.wikihub/events.jsonl— audit events as append-only JSONL in git- Line-oriented formats under
.wikihub/— merge-friendly - Hash-based IDs via nanoid
- Daemon pattern for concurrent CLI access
Open question: defer git push → Postgres to v2?
If nobody pushes via git in early days, defer the post-receive hook. Keep web→git sync only. Cuts half the complexity.
Sources
- Steve Yegge — Beads
- Introducing Beads — Medium
- WikiHub:
app/git_sync.py,hooks/post-receive - Fork C session (2026-04-08)