chezmoi parses attribute prefixes left-to-right and 'encrypted_' must
precede 'private_'. The 'private_encrypted_' order silently breaks
decryption: chezmoi consumes 'private_', then treats 'encrypted_foo.age'
as a literal filename and copies the ciphertext verbatim instead of
decrypting it.
models.yml was named 'private_encrypted_models.yml.age' since commit
3c3fab7 and was never decrypting — a stale 'encrypted_models.yml.age'
blob was sitting in ~/.omp/agent/ and the plaintext models.yml was an
unmanaged leftover. .env and zai.key used the plain 'encrypted_'
prefix and were decrypting at umask 0644 (world-readable in isolation;
the 700 ~/.omp/agent/ dir was the only thing shielding them).
This commit:
* renames all three to encrypted_private_{zai.key,.env,models.yml}.age
so chezmoi decrypts AND lands them at 0600 natively
* rewrites run_onchange_35 as 'ensure-omp-secret-perms.sh' covering
all three, as belt-and-suspenders for any box where a secret still
sits at 0644 from a prior apply
* removes the stale encrypted_models.yml.age verbatim blob and its
orphan state entry
* corrects the README perms section to document the prefix-order
gotcha (was misleadingly claiming 'private_' alone gave 0600)
Verified end-to-end on this box: chezmoi managed lists all three as
decrypted targets, scoped apply writes them at 600, chmod script is
idempotent. Other boxes need a 'chezmoi apply' to pick up the rename
and the onchange chmod.
omp v16.1.16 hardcodes the built-in zai provider to
https://api.z.ai/api/anthropic (Anthropic-compatible endpoint) which
requires 'x-api-key' header. omp only sends 'Authorization: Bearer', so
the built-in zai provider always returns 401 on real Z.ai API keys.
The OpenAI-compatible endpoint https://api.z.ai/api/coding/paas/v4
accepts Authorization: Bearer and works fine. The user's existing
~/.omp/agent/models.yml defines a custom 'zai-coding' provider pointed at
this endpoint, but the apiKey field was set to 'ZAI_CODING_API_KEY' which
omp treats as a literal string (not an env var reference), so the request
was 'Authorization: Bearer ZAI_CODING_API_KEY' which 401s.
Fix: include the literal zai API key in the apiKey field. This is
identical to the existing encrypted zai.key secret (same key, decrypted
on apply). The encrypted file is in chezmoi so it stays encrypted at rest
and chmod 644 on disk — same perms as the existing zai.key.
Verified manually on byte before commit:
omp --print --model zai-coding/glm-5.2:xhigh 'hi'
→ 'Hi! What can I help you with today?'
(was: '401 token expired or incorrect')
After applying this commit on all boxes:
omp --print --model zai-coding/glm-5.2:xhigh 'hi'
should work everywhere.
Tadbit is the 7th machine in the homelab. Generated a per-machine
age key on tadbit, added the pubkey as 8th recipient in
.chezmoi.yaml.tmpl, and re-encrypted the two .age secrets with
all 8 recipients (1 recovery + 7 machines).
bit-cachyos is the 6th machine in the hive. Generated a per-machine
age key on bit, added the pubkey to the recipients list in
.chezmoi.yaml.tmpl, and re-encrypted the two .age secrets
(dot_omp/agent/encrypted_.env.age and encrypted_zai.key.age) with all
7 recipients (1 recovery + 6 machines).
Bit's existing partial setup (pre-existing chezmoi source dir, omp
native binary at ~/.local/bin/omp) is backed up during the bootstrap
script to ~/.local/share/chezmoi.bak.<timestamp>.
See onboard-bit.sh on bit:/tmp/onboard-bit.sh for the no-sudo
bootstrap flow.
1. dot_omp/agent/encrypted_.env.age (NEW)
Encrypted shell-sourceable file with all omp provider API keys.
Decrypts to ~/.omp/agent/.env on apply. omp reads .env on startup
per docs/environment-variables.md. All 6 recipients (recovery +
5 boxes) can decrypt. Placeholder values for keys the user hasn't
added yet — fill in real values per-provider.
2. run_onchange_30-ensure-cargo.sh.tmpl (UPDATED)
- Add topgrade install: pacman on arch (via chaotic-aur), cargo on
debian (not in apt)
- Add cargo-update install: pacman on arch, cargo on debian
- Prefer OS package managers over cargo install when both are
available. cargo install only as fallback.
3. dot_omp/agent/config.yml (UNCHANGED)
Per user request: keep .local host endpoints (llama-swap.miche,
kaiser.local:8800). If a box can't reach them, it's not on the
local network and omp will error gracefully at request time.
The previous approach (private_dot_omp/agent/zai.key.age + manual
re-encryption) didn't work because:
1. The 'private_' prefix is for files NOT to push to remote, not for
encrypted files. The 'encrypted_' prefix is what chezmoi recognizes
as an encryption marker.
2. The encrypted file needs to be at dot_<path>/encrypted_<name>.age
so chezmoi can both decrypt on apply AND strip the .age suffix
to write the destination file as <name> (without .age).
Also fix chezmoi age config to actually decrypt non-interactively:
- Add useBuiltinAge: false to force external age binary
- Add age.command: /usr/bin/age (absolute path) so PATH issues
don't matter in non-interactive SSH contexts
The encrypted file is at dot_omp/agent/encrypted_zai.key.age, decrypts
to ~/.omp/agent/zai.key on apply. Encrypted to all 6 recipients
(recovery + miche + byte + kaiser + rye + crouton).
Tested on miche:
- chezmoi apply: rc=0
- live zai.key: 50 bytes (correct content)
- decrypts with miche per-machine key
- would decrypt on other boxes with their respective keys