Share Your Vault Across Machines Without Split-Brain

Your laptop, your cluster, and your agents all need the vault — but only one copy can be authoritative. Use a direct mount for editing and a read-only forced-command SSH reader for everyone else.

On this page
  1. The access topology
  2. Building the read-only reader
  3. The swap-in box
  4. What you have now

The vault is on an always-on host, your laptop edits it, and now the cluster and your AI agents want it too. The naive answer — clone it everywhere — quietly recreates the exact ⚠️ split-brain we’ve fought all series: multiple writable copies, each drifting. This post gives everyone access without a second authority.

The principle

One editing client, many readers. The machine you edit from gets a direct read-write mount; everything else — cluster services, agents, other machines — reaches the vault through a read-only forced-command SSH interface that can only status, read, and search, and reports the commit on every read.

First: make these values your own

Replace the stand-ins with your own: 10.0.0.76 → your vault host’s IP; vaultreader → the account you create for readers; the ssh-ed25519 AAAA… key → your own generated public key; /wiki-data/git → your repo path; the commit 64dc195 is just an example.


The access topology

One writer, many read-only readerscanonical vaultthe one authorityyour laptopdirect mount · read-writerwcluster serviceread-onlyAI agentread-onlyother machineread-onlyforced-commandSSH (ro)buildahomelab.dev

The laptop writes; everyone else reads. Because the readers cannot write, none of them can become a competing authority — the split-brain is structurally impossible, not merely discouraged.


Building the read-only reader

An SSH command= restriction (authorized_keys forced command) pins a key to exactly one program. Connect with that key and you get the reader, never a shell.

1Write a restricted reader program10 min

The reader accepts a small verb set — status, read <path>, search <query> — and always prints the current commit. It refuses anything else.

/usr/local/bin/vault-reader (on the vault host)

#!/bin/bash
# Invoked via SSH forced command. $SSH_ORIGINAL_COMMAND holds the request.
set -euo pipefail
VAULT=/wiki-data/git
COMMIT=$(git -C "$VAULT" rev-parse HEAD)
read -r verb arg <<< "$SSH_ORIGINAL_COMMAND"
case "$verb" in
status) echo "commit $COMMIT" ;;
read)   echo "commit $COMMIT"; git -C "$VAULT" show "HEAD:$arg" ;;
search) echo "commit $COMMIT"; git -C "$VAULT" grep -n "$arg" -- '*.md' ;;
*)      echo "denied: read-only reader (status|read|search)"; exit 1 ;;
esac
2Pin a key to the reader with a forced command5 min
~/.ssh/authorized_keys on the vault host

command="/usr/local/bin/vault-reader",no-pty,no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA... vault-reader-key

Now that key can only run the reader. A cluster agent using it can read and search the vault — and learns the commit on every call — but cannot write, cannot get a shell, cannot forward anything. That’s least privilege (NIST’s term) made concrete: each consumer gets exactly the access its job requires and nothing more.

3Consume it from a reader machine3 min
From a cluster host or agent

ssh -i ~/.ssh/vault-reader vaultreader@10.0.0.76 "status"
# → commit 64dc195

ssh -i ~/.ssh/vault-reader vaultreader@10.0.0.76 "read runbooks/example.md"
# → commit 64dc195  (then the file contents)
Every read returns the commit — so callers can fail closed

The reader prints the commit first on purpose. A consumer (like an agent’s begin step) compares it to the expected HEAD and stops if they differ. A read interface that hides its revision turns a stale cache into invisible misinformation.

The full reader, key restrictions, and a consumer wrapper are in the paired playbook: Canonical Vault Restricted Reader.


The swap-in box

On other tools

Git natively: a bare repo with read-only deploy keys gives readers a clone they can pull but not push — pair it with a commit-reporting wrapper. HTTP: a read-only API in front of the repo (returning content plus the commit header) is the same idea over a different transport. The rule: exactly one writable path to the authority; every other consumer is read-only and reveals its revision.


What you have now

Every machine and agent can read current vault state, and not one of them can fork it — the split-brain failure mode is closed off by construction. The readers above return raw pages; the next post gives them semantic search that still knows its own version: local RAG that knows its own version.


Related posts:

Comments

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