Secrets

Secrets

private/ works exactly like autolink/ (same layers, same
linking rules, except it never folds), but git only ever sees
encrypted *.age files.

File Committed? What it is
private/common/netrc never plaintext, linked to ~/.netrc
private/common/netrc.age yes the same, encrypted with age
private/recipients.txt yes public key: anything can seal without a passphrase
private/identity.age yes private key, encrypted with your passphrase; needed only to unlock
Lose the passphrase, lose the secrets

There is no recovery. Keep the passphrase in the password manager.

Commands

homebot keys init                    # once, ever: make the key pair, pick a passphrase
homebot pack ~/.netrc --private      # move a secret in, seal it, link it back
homebot unlock                       # decrypt everything (passphrase asked once)
homebot lock                         # seal what changed; --wipe also deletes plaintext

Sealing is automatic: the commit guard and the hourly helper encrypt changed
plaintext on their own, because sealing needs only the public key.

You never encrypt by hand. Edit the plaintext (it is what ~/.netrc links
to); private/.gitignore keeps git from ever seeing it. The next commit,
whether homebot save, the autosave or a plain git commit, seals it into the
.age file next to it and commits that. The guard’s refusal of plaintext
(step 2 below) is only a safety net for a file force-added with git add -f.

Two machines, one secret

Each machine keeps hashes of what it last sealed or unlocked, so homebot knows
where a change came from.

State Means Fix
new / modified plaintext changed here sealed automatically
locked .age exists, no plaintext here yet homebot unlock
upstream .age changed on another machine homebot unlock
conflict changed here and elsewhere homebot unlock --theirs or homebot lock --ours

A pull never silently overwrites a local edit; conflicts land in the inbox as urgent.

The commit guard

homebot setup points core.hooksPath at homebot/hooks. Before every commit it:

  1. Seals changed secrets and stages the .age files.
  2. Refuses plaintext from private/, even if force-added (git add -f).
  3. Refuses any file over limits.maxFileMB (50 MB; GitHub hard-fails at 100).
  4. Refuses anything gitleaks flags in the staged changes.
  5. Refuses everything if gitleaks isn’t installed. No scanner, no commit.

The hook prefers bun homebot/src/cli.ts from the repo, so the guard always
matches the checked-out tool. A refused automatic commit becomes an urgent
inbox item with the reason.

Don’t bypass it

git commit --no-verify works, and is exactly how secrets end up on GitHub.
If the guard is wrong, fix the guard.