On this page
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.
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.
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:
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.
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.
node scripts/wiki-publishable-manifest.mjs . > /tmp/manifest.json
# manifest.json lists every page to publish + the source commit
The importer reconciles Wiki.js to the manifest. Create and update are obvious; the part people miss is delete.
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.
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)
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.
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
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:
- Why Auto-Syncing Your Notes Repo Will Corrupt It — how to write to the authority so projections rebuild cleanly
- Prove Your Homelab Docs Are Actually Up to Date — the operations discipline behind stale-detection
- Never Move a Note When a Project Ends — why stable vault paths give stable public URLs
- Never Lose a Homelab Note Again: A Git + Markdown Vault — the authority this projects from
- Semantic Search for Your Notes, Running Locally — another projection, this one for semantic search
Comments
Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.