Never Commit a Secret to Your Knowledge Base

Never commit a password to your knowledge base — and if you already have, scrubbing it from the working tree isn't enough. Here's the store-pointers-not-values rule and the Git history purge.

On this page
  1. Why a knowledge base is uniquely bad for secrets
  2. The right pattern: pointers, not values
  3. Prevention: a guard that refuses the commit
  4. ⚠️ Recovery: you already committed a secret
  5. Other hosts, same rule
  6. What this buys you

You’re building a knowledge base that documents your whole lab. The temptation to paste “the Proxmox token, so it’s handy” into a note is enormous. Resist it completely, from your very first commit — because a knowledge base is the worst place to keep a secret. This is a ⚠️-flagged post in the series for a reason: this is a mistake that’s genuinely painful to undo.

The rule

Never commit a secret value to your vault. Store the value in a purpose-built secret store; put only a pointer — the variable name and its location — in your notes. Git never forgets, so a secret committed once is a secret you must rotate.

First: make these values your own

The examples use stand-ins — replace them with your own. /root/.myofficelab-secrets → wherever you keep your secret store; PROXMOX_API_TOKEN_ID/_SECRET → your actual variable names; CHANGE_ME and the-leaked-secret-value → your real values (which live in the store, never in a note). Never paste a real secret into an example just because a placeholder is sitting there.


Why a knowledge base is uniquely bad for secrets

The whole point of the vault is that it gets copied and projected everywhere. That’s a feature for knowledge and a catastrophe for credentials. This isn’t a niche concern — hard-coded credentials are catalogued as CWE-798 by MITRE, and OWASP’s Secrets Management Cheat Sheet gives the same guidance this post does: keep secrets out of source and reference them from a dedicated store. Trace where a single note travels:

One committed secret → five exposure surfacesnote.md (secret)Git historylaptop mountbackup mirrorpublished wikisearch indexpeira.dev

Delete the secret from the note tomorrow and it still lives in Git history, in last night’s backup, and possibly in a search index that already embedded it. One paste, five places to clean.


The right pattern: pointers, not values

Keep credentials in a purpose-built store and reference them by name. In my lab the store is a single root-only file on one host, and every note that needs a credential names the variable, never the value.

The secret store (root-only, never in Git)

# /root/.myofficelab-secrets  —  chmod 600, outside any repo
PROXMOX_API_TOKEN_ID=youruser@pve!app-token
PROXMOX_API_TOKEN_SECRET=CHANGE_ME
GRAFANA_API_KEY=CHANGE_ME
What actually goes in the note

## Proxmox API access

Token lives as PROXMOX_API_TOKEN_ID / PROXMOX_API_TOKEN_SECRET
in /root/.myofficelab-secrets on the ops host. PVEAdmin role.
Do not paste the secret here.
For application credentials, go one better

If an app reads the vault-documented service, store its secret in an OS keystore (libsecret / GNOME Keyring on Linux, Keychain on macOS) or a dedicated manager like Vaultwarden. The note still only records the variable name and which store holds it.

Value in the note vs. pointer to the storenote.md — the trap## Proxmox APItoken: aG7x9-K2mP-Qw…↑ now in Git foreverleaks to every copynote.md — the pattern## Proxmox APIPROXMOX_API_TOKEN inthe secret storesafe to mirror + publishthe value lives in a purpose-built store; the note only names it

Legitimate access resolves the pointer in seconds; the note stays safe to mount, mirror, publish, and index.


Prevention: a guard that refuses the commit

Make it hard to commit a secret by accident. A pre-commit hook that greps staged content for obvious secret shapes turns a silent mistake into a blocked commit.

1Add a secret-scanning pre-commit hook5 min
Create .git/hooks/pre-commit in the vault

cat > .git/hooks/pre-commit <<'EOF'
#!/bin/bash
# Block commits that look like they contain secrets.
if git diff --cached -U0 | grep -nEi \
'(api[_-]?key|token|secret|password|passwd|BEGIN [A-Z ]*PRIVATE KEY)[[:space:]]*[:=]' \
| grep -v 'CHANGE_ME'; then
echo "✋ Possible secret in staged changes — commit blocked."
echo "   Store the value in your secret store; commit only a variable-name pointer."
exit 1
fi
EOF
chmod +x .git/hooks/pre-commit
A hook is a seatbelt, not a vault

This catches the obvious mistakes — an assignment that looks like a credential. It won’t catch a bare high-entropy string with no label. Use a dedicated scanner like gitleaks for real coverage; the hook is your cheap first line.


⚠️ Recovery: you already committed a secret

If a secret is already in your history, deleting it in a new commit is not enough — it’s still one command away in the history. You must rewrite history and rotate the credential. GitHub’s own guidance on removing sensitive data says exactly this: purge every commit and treat the credential as compromised.

1Rotate the credential firstvaries

Before touching Git, invalidate the exposed secret at its source — regenerate the API token, change the password. Assume it leaked. This is the step people skip and regret.

2Purge it from all history with git-filter-repo10 min

git-filter-repo is the modern, recommended tool for rewriting history:

On a fresh clone of the vault

# Install (Debian/Ubuntu): sudo apt-get install -y git-filter-repo
# Replace every occurrence of the leaked value across ALL commits
echo 'the-leaked-secret-value==>REDACTED' > /tmp/replacements.txt
git filter-repo --replace-text /tmp/replacements.txt

To remove an entire file that should never have been tracked:

Purge a whole file from history

git filter-repo --path secrets.env --invert-paths
3Force-update the canonical repo and re-clone everywhere10 min
Push the rewritten history

git push --force --all
git push --force --tags

Every existing clone and mount now has divergent history — re-clone them fresh. Anyone who pulled the bad history must discard their copy. This blast radius is exactly why prevention beats recovery.

The full copy-paste recovery sequence is in the paired playbook: Vault Secret Store + History Scrub.


Other hosts, same rule

On other tools

GitHub/GitLab hosted: enable native push protection / secret scanning — it blocks known secret formats at push time. Any Git host: gitleaks as a pre-commit hook or CI step is tool-agnostic. Secret store choice: a root-only file is the minimum; HashiCorp Vault, Bitwarden/Vaultwarden, or your OS keystore are all valid — the note-side rule (pointer, never value) is identical regardless.


What this buys you

The vault is now safe to publish and mirror, because the one class of content that must never leak is structurally kept out of it. With that settled, the next post makes the vault smart: turning a flat pile of pages into a connected graph, modeling knowledge as a brain.


Related posts:

Comments

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