Skip to content

fix(secrets): keep env, deployed, and repo in sync after every mutation#9

Merged
mlorentedev merged 2 commits into
mainfrom
fix/secrets-sync-and-env-refresh
May 13, 2026
Merged

fix(secrets): keep env, deployed, and repo in sync after every mutation#9
mlorentedev merged 2 commits into
mainfrom
fix/secrets-sync-and-env-refresh

Conversation

@mlorentedev
Copy link
Copy Markdown
Owner

Closes #7.

Root cause of #7 (TL;DR)

secrets_rotate was working correctly — the deployed .age file was being updated. The bug was env-vs-disk drift: load-secrets.sh exports $VAR once at shell startup, and rotate didn't refresh it, so any verification that read $VAR (gh, curl, secrets_show without --raw, etc.) saw the old value and looked like rotate had failed silently.

What this PR does

Concern Before After
Env-vs-disk drift after rotate/add Manual secrets_refresh required (easy to forget) Auto-exported in current shell
Repo sync without DOTFILES_REPO_DIR Silent no-op Auto-detects $HOME/Projects/dotfiles; warns if neither resolves
Deleting a secret No function — manual editing of mapping + rm + sync New secrets_remove VAR_NAME [--yes]
_secrets_sync_to_repo deletion path Not supported New mode=delete arg
secrets_rotate mapping sync to repo false (mapping never changes in rotate) true (defensive idempotency)

secrets_remove covers both plain and file secrets. For file secrets it also removes the deployed plaintext file at dest_path.

Test plan

  • ~/.local/bin/bats tests/*.bats → 403/403 (11 new tests for remove + auto-detect + sync warning)
  • ~/.local/bin/shellcheck scripts/load-secrets.sh → only pre-existing SC1091 info
  • bash -n and zsh -n syntax check on load-secrets.sh
  • Manual smoke after merge (two-tier deploy reminder — repo edit alone won't take effect):
    1. ./setup-linux.sh to refresh ~/.dotfiles/scripts/load-secrets.sh
    2. New shell → secrets_rotate <SOME_TEST_VAR> → confirm echo $VAR shows new value immediately, no manual refresh
    3. git status in repo → confirm sensitive/<file>.secret.age shows as modified (sync to repo worked)
    4. secrets_remove <SOME_TEST_VAR> → confirm mapping line gone, .age deleted, repo also reflects deletion

Notes

  • Local *.dec files in sensitive/ were stale plaintext from old secrets_decrypt runs. Already covered by .gitignore. Not deleted in this PR — run secrets_clean locally to remove them.
  • secrets_help updated to document secrets_remove, the auto-detect behavior, and the no-manual-refresh guarantee.

🤖 Generated with Claude Code

Closes #7. Root cause was env-vs-disk drift after rotate, not a real
encryption failure: secrets_rotate updated the .age file correctly, but
the shell's exported $VAR stayed at the value loaded at startup, so
verification via $VAR (or anything that read it) showed the old token.

Changes:
- secrets_rotate, secrets_add, secrets_add_file, and the new
  secrets_remove now auto-update the current shell so $VAR matches disk
  immediately. No manual secrets_refresh needed.
- _get_secrets_repo_dir auto-detects $HOME/Projects/dotfiles when
  DOTFILES_REPO_DIR is unset, so the repo's sensitive/ folder stays
  current for git-status verification without per-shell config.
- _secrets_sync_to_repo gains a "delete" mode and warns to stderr
  (instead of silently no-op'ing) when no repo can be resolved.
- New secrets_remove VAR_NAME [--yes]: drops mapping line + .age file
  + repo copy + deployed plaintext (file secrets) + env var, with
  audit-log entry and confirmation prompt.

Tests: 11 new bats covering remove (plain + file), repo auto-detect,
sync warning, and help text. Full suite 403/403, shellcheck clean.
@mlorentedev mlorentedev merged commit 708d261 into main May 13, 2026
5 checks passed
@mlorentedev mlorentedev deleted the fix/secrets-sync-and-env-refresh branch May 17, 2026 02:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

secrets_rotate doesn't update the encrypted file

1 participant