Skip to content

chore: SSH backup + Claude overlay + sync (2026-04-23)#44

Merged
tieubao merged 7 commits intomainfrom
tieubao/chore/sync-2026-04-23
Apr 22, 2026
Merged

chore: SSH backup + Claude overlay + sync (2026-04-23)#44
tieubao merged 7 commits intomainfrom
tieubao/chore/sync-2026-04-23

Conversation

@tieubao
Copy link
Copy Markdown
Member

@tieubao tieubao commented Apr 22, 2026

Summary

Three scopes bundled as a single sync batch:

  • S-38 SSH key inventory + backup (3 commits). Adds dotfiles ssh audit/adopt/backup subcommands, integrates status into /dotfiles-sync, switches adopt to a guided-manual flow after discovering the 1Password CLI can't import existing SSH private keys (op 2.x rejects every write path for pre-existing key material).
  • Claude Code personal overlay expansion (2 commits). home/dot_claude/modify_settings.json now guarantees four personal PreToolUse hooks in addition to the learning-capture Stop hook: git-push BLOCK to main|master|production, soft-warn variant, curl\|wget pipe-to-shell BLOCK, and rm -rf BLOCK against /|~|$HOME. home/dot_claude/modify_CLAUDE.md.tmpl now appends the full # Self-verification rules section (intro bullets, ## Commit style, ## Git branch naming, ## When implementing from specs) below the auto-gen marker so these rules survive the next ~/workspace/claude-context/sync-claude-context.sh run.
  • Secrets + Brewfile sync (2 commits). Split CLOUDFLARE_API_TOKEN from R2 credentials in .chezmoidata/secrets.toml (the old binding was pointing at the R2 S3 secret, not a Workers/Zones API token). Bumped claude-guardrails pin v0.3.7 -> v0.3.8. Fixed cask "zen" -> cask "zen-browser" drift (upstream rename). Added wispr-flow + 3 IBM Plex font casks.

Full per-commit detail visible in docs/sync-log.md for the 2026-04-23 entry.

Test plan

  • chezmoi apply --dry-run exits clean
  • chezmoi apply completes and is idempotent (second run = no-op)
  • dotfiles secret list shows [cached] for CLOUDFLARE_API_TOKEN, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY
  • ~/.claude/settings.json has all four personal PreToolUse hooks after apply (BLOCK direct push, solo-worker warn, pipe-to-shell BLOCK, rm -rf BLOCK)
  • ~/.claude/CLAUDE.md contains # Self-verification rules exactly once, below the # --- END claude-context --- marker
  • brew bundle check --file=~/.Brewfile clean after brew bundle installs wispr-flow + IBM Plex fonts
  • dotfiles ssh audit surfaces current unbacked-key count correctly

🤖 Generated with Claude Code

tieubao and others added 7 commits April 22, 2026 22:49
Adds `dotfiles ssh` subcommand with three actions:

- `audit` prints a read-only inventory across disk, agent, and 1Password,
  flags unencrypted disk keys and weak/old keys, and reports how many
  disk keys have no 1P counterpart.
- `adopt <key-path>` imports a disk private key into 1Password as an
  SSH Key item. Idempotent by fingerprint. The on-disk file is never
  touched; retirement is a separate manual step the user performs after
  observation.
- `backup --destination <path>` writes an age-encrypted bundle of all
  1P SSH items to a user-specified path (e.g. a USB drive), reusing
  the existing chezmoi age identity as recipient. No default path; no
  silent fallbacks.

The adopt-then-investigate flow is specifically designed for keys with
unknown usage (like a decade-old id_rsa): get a 1P backup in place first,
keep the disk copy, investigate what trusts it over the next few months,
then retire deliberately.

Spec: docs/specs/S-38-ssh-key-backup.md
Walkthrough: docs/guide.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the sync skill (both mirror copies) with a notify-only
detection block that surfaces disk SSH keys without a 1Password
counterpart. Mirrors the S-37 guardrails pattern: purely
informational, silent when op is unavailable or not signed in,
never auto-executes adoption (which requires interactive
confirmation and an active op session).

README "Offline fallback" block lists the three new subcommands
so they're discoverable alongside secret/local.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1Password CLI 2.x explicitly does not support importing existing
SSH private keys. The only CLI-automatable path is generating a
new key (`--ssh-generate-key`). Creation via assignment syntax or
a template file both fail with "creating items through piped
input is not supported for SSH Key items" because 1P requires
key material to be entered through the desktop app for integrity
validation.

The original spec and implementation assumed CLI import worked.
It does not. Replacing the write path with a guided manual flow:

1. Compute fingerprint, check idempotency (already adopted?).
2. Confirm with user (preserves the spec's confirmation gate).
3. pbcopy the private key to the macOS clipboard.
4. Open the 1Password desktop app.
5. Print the five-step paste guide (category, title, paste, vault,
   save).
6. Wait on read -P until the user presses Enter.
7. Clear the clipboard (printf "" | pbcopy).
8. Re-query 1P, match by fingerprint, print verification result.

The disk key is never touched regardless of outcome. The spec,
guide walkthrough, and backup restore instructions were updated
to reflect the same constraint. The `backup --destination` flow
is unaffected; only the adopt path needed the rewrite.

Discovered when auto-adoption attempt surfaced two bugs in one
go: the CLI rejected the assignment-syntax write, and the
shortcut `echo y | fish -c` bypassed the spec-mandated
confirmation gate (correctly flagged by guardrails).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…P ref

Previously CLOUDFLARE_API_TOKEN pointed at the R2 item's credential field,
which is the S3 secret, not a Workers/Zones API token. Split into:
- CLOUDFLARE_API_TOKEN -> "Cloudflare API Token" (Workers/Zones API)
- R2_ACCESS_KEY_ID     -> "Cloudflare R2" / username
- R2_SECRET_ACCESS_KEY -> "Cloudflare R2" / credential

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extend the chezmoi modify_ overlays so personal Claude Code config
survives across machines instead of existing only in the live files:

- modify_settings.json: add four PreToolUse hooks (git push BLOCK,
  git push soft-warn, curl|wget pipe-to-shell BLOCK, rm -rf BLOCK),
  each deduplicated by marker string in its command text. Additive
  merge with guardrails' scan-commit entry.
- modify_CLAUDE.md.tmpl: append a new "# Git workflow / ## Branch
  naming" section below the auto-gen marker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erlay

Before this change only the "Git branch naming" rule was durable; the
rest of the `# Self-verification rules (all projects)` section (intro
bullets, `## Commit style`, `## When implementing from specs`) lived
only in the above-marker auto-gen region of ~/.claude/CLAUDE.md, which
sync-claude-context.sh would wipe on next run since none of that
content exists in its shared/private source files.

Fold all four subsections into the dotfiles modify_ overlay below the
marker, restoring the original parent "# Self-verification rules"
heading so `## Git branch naming` sits under it (matching the user's
mental model before the split). The live file has had the above-marker
copies removed manually in the same operation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brewfile (core):
  - rename cask "zen" -> "zen-browser" (upstream renamed back)
  - add casks: wispr-flow, font-ibm-plex-sans,
    font-ibm-plex-sans-hebrew, font-ibm-plex-serif

Guardrails: bump pinned REF v0.3.7 -> v0.3.8 in
run_onchange_after_claude-guardrails.sh.tmpl (both occurrences:
the hash comment line and the REF variable).

22 brew / 13 cask drifts deferred; 2 SSH keys still unbacked.
Full classification table in docs/sync-log.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tieubao tieubao merged commit 8c3b6da into main Apr 22, 2026
2 checks passed
@tieubao tieubao deleted the tieubao/chore/sync-2026-04-23 branch April 22, 2026 18:05
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.

1 participant