Turn Your Git Vault Into a Searchable Wiki.js Site

Derive a published Wiki.js site from your Git vault as a read-only projection — one that reports the exact commit it was built from and cascades deletes so it never drifts from the source.

On this page
  1. Authority and projections
  2. Building the Wiki.js projection
  3. Why parity, not just sync
  4. The swap-in box
  5. What you have now

Your vault is authoritative but ugly to browse — raw Markdown over an SSHFS mount isn’t something you hand to a phone or a guest. So you publish it: a nice searchable site anyone can read. The trap is publishing in a way that creates a second copy that can disagree with the source. This post shows the discipline that prevents that.

The principle

A published view is a projection, not a fork. It is generated from the authoritative vault, is read-only, and records the exact commit it was built from — so you can always tell whether it’s current. Edits happen in the vault; the projection is rebuilt, never hand-corrected.

First: make these values your own

Stand-ins to replace: the commit 64dc195 is illustrative — yours will be a different hash; WIKIJS_URL/WIKIJS_TOKEN → your own Wiki.js address and API token; script paths assume this series’ layout. Don’t expect a hash or URL from the article to match your setup.


Authority and projections

One source of truth, many derived views — each stamped with its origin commit:

Authority → projections (each reports its source commit)Git vaultHEAD 64dc195Wiki.js sitebuilt @ 64dc195 ✓search indexbuilt @ 64dc195 ✓backup mirrorbuilt @ 64dc195 ✓buildahomelab.dev

When all three report the same commit as the vault’s HEAD, the whole system is coherent. A projection reporting an older commit is stale — still useful, but you must not present it as current. That single rule (covered in depth in keeping projections honest) is what keeps a many-view system trustworthy.


Building the Wiki.js projection

Wiki.js reads Markdown and gives you a polished, searchable, access-controlled site. The projection is a script that pushes the vault’s publishable pages into it.

1Generate a publishable manifest from the vault5 min

Not every page should be public. A manifest step selects the publishable set (excluding drafts, private notes, and agent-only pages) and records the commit it was generated at.

On the vault host

node scripts/wiki-publishable-manifest.mjs . > /tmp/manifest.json
# manifest.json lists every page to publish + the source commit
2Import: create, update — and delete10 min

The importer reconciles Wiki.js to the manifest. Create and update are obvious; the part people miss is delete.

The delete-cascade — the bug a naive importer has

An importer that only creates and updates pages leaves orphans: delete a page in the vault and it lives on in the wiki forever, because nothing ever removes it. The fix is a pages.delete pass over the set difference actual − manifest — every published page not in the manifest gets removed — followed by a final exact-parity assertion that published set equals manifest set.

Reconcile Wiki.js to the manifest

node tools/wiki/update-pages.mjs /tmp/manifest.json
# 1. create pages new in the manifest
# 2. update pages whose content changed
# 3. delete published pages ABSENT from the manifest  ← the cascade
# 4. assert: published set == manifest set (fail loudly if not)
Guard the cascade against a bad manifest

A delete pass is only as safe as the manifest driving it. If the manifest is truncated or mis-scoped, the cascade would happily delete half your wiki. Guard it: refuse to run if the manifest is suspiciously small or empty, so a generation bug can’t turn into mass deletion.

3Verify parity and the recorded commit3 min
Confirm the projection is current

node tools/wiki/verify-projection.mjs /tmp/manifest.json
# → published pages == manifest pages
# → projection commit == vault HEAD

The full manifest → import → verify sequence, including the guarded cascade, is in the paired playbook: Wiki.js Import + Parity Verify.


Why parity, not just sync

“Sync” quietly implies two peers reconciling toward each other. That’s exactly the wrong model — it invites edits on the wiki side that never make it back to the vault, and now the two disagree. A projection with parity is one-directional and asserted: the wiki is made to equal the manifest, deletions included, and the run fails if it doesn’t. It’s the same declarative, desired-state model Kubernetes uses for object management — you declare the target set and a reconciler makes reality match it, rather than issuing imperative one-off changes and hoping they converge. This is the same instinct we apply to writes in the next post.


The swap-in box

On other tools

Static site generators (Hugo, Astro, MkDocs, Quartz): the build is the projection — commit the source, CI rebuilds the site, and the deployed commit hash is your parity check. Obsidian Publish / Digital Garden: projection-shaped already; just confirm deletes propagate. The universal rule: publish one-directionally from the source, make deletes cascade, and stamp the output with the source revision so staleness is visible.


What you have now

A public, searchable wiki that is provably a view of your vault — same content, deletions included, tagged with the commit it came from. With a projection defined, the next question is how you write to the authority safely so these projections always rebuild from a clean, valid state. That’s the controlled-write contract: a lock, not a sync.


Related posts:

Comments

Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.