Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .chezmoiscripts/run_onchange_after_enable-gpg-preset.sh.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# chezmoi template: enable gpg-sign-preset systemd user service
# Trigger: {{ if (stat (joinPath .chezmoi.sourceDir "dot_config/systemd/user/gpg-sign-preset.service")) }}changed{{ end }}
set -euo pipefail

if command -v systemctl >/dev/null 2>&1; then
systemctl --user daemon-reload

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard systemd user calls when no user bus exists

In non-systemd user sessions such as devcontainers or headless SSH sessions where systemctl is installed but no user manager is running, this command fails (systemctl --user daemon-reload returns Failed to connect to bus: No medium found in the current container). Because the script has set -e, chezmoi apply aborts before completing; the binary check needs to also verify a usable user bus or tolerate this failure.

Useful? React with 👍 / 👎.

systemctl --user enable --now gpg-sign-preset.service
fi
12 changes: 11 additions & 1 deletion .chezmoiscripts/run_onchange_after_install_packages.sh.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@ set -euo pipefail
MISE_BIN_PATH="{{ if $m := lookPath "mise" }}{{ $m }}{{ else if $l := lookPath (joinPath .chezmoi.homeDir ".local/bin/mise") }}{{ $l }}{{ else }}mise{{ end }}"

$MISE_BIN_PATH trust {{ .chezmoi.homeDir }}/.config/mise/mise.toml
$MISE_BIN_PATH install
$MISE_BIN_PATH install

# install system packages for GPG signing preset (gpg-preset-passphrase + secret-tool)
if command -v apt-get >/dev/null 2>&1; then
missing=""
command -v gpg-preset-passphrase >/dev/null 2>&1 || missing="$missing gpg-agent"
command -v secret-tool >/dev/null 2>&1 || missing="$missing libsecret-tools"
if [ -n "$missing" ]; then
sudo apt-get install -y $missing
fi
fi
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ runtime/
.aeos/
*.log
node_modules/*
private_*
SESSION_STATE.md
22 changes: 13 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,24 @@ mise exec -- chezmoi apply # Run via mise if chezmoi not on PATH
## Security

- `private_*` prefix = never commit to git
- GPG config in `private_dot_gnupg/` (not tracked)
- GPG config in `dot_gnupg/`
- `.chezmoiignore` uses gitignore semantics only — does NOT redact secrets
- Never hardcode secrets; use `.chezmoi.toml.tmpl` env vars or prompts

## Key files to read

| Purpose | File |
| --------- | ------ |
| chezmoi config + template vars | `.chezmoi.toml.tmpl` |
| Tool versions | `dot_config/mise/mise.toml` |
| OpenCode config | `.opencode/ocx.jsonc` |
| Shell config | `dot_bashrc.tmpl`, `dot_zshrc.tmpl` |
| Bootstrap logic | `setup` |
| Branch rules | `BRANCHING.md` |
| Purpose | File | Notes |
| --------- | ------ | ----- |
| chezmoi config + template vars | `.chezmoi.toml.tmpl` | |
| Tool versions | `dot_config/mise/mise.toml` | |
| OpenCode config | `.opencode/ocx.jsonc` | |
| Shell config | `dot_bashrc.tmpl`, `dot_zshrc.tmpl` | |
| Bootstrap logic | `setup` | |
| Branch rules | `BRANCHING.md` | |
| GPG agent config | `dot_gnupg/gpg-agent.conf` | Allows preset passphrase |
| GPG crypto settings | `dot_gnupg/gpg.conf` | Digest/cipher preferences |
| GPG signing preset | `dot_local/bin/executable_gpg-sign-preset.tmpl` | Silent preset at login/shell start |
| GPG systemd service | `dot_config/systemd/user/gpg-sign-preset.service` | Presets passphrase at login |

## Git safety

Expand Down
7 changes: 7 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,10 @@

- Dockerfile uses `mcr.microsoft.com/devcontainers/base:debian-13`
- Ensure Docker is running and has network access

### Agent commits hang on GPG passphrase prompt

- **Cause**: `commit.gpgsign=true` and `gpg-agent` cache expires → `pinentry-curses` seizes the TTY agent (opencode/codex) uses. Visible as a frozen commit.
- **Fix**: Ensure the preset script ran: `~/.local/bin/gpg-sign-preset`. Run it manually to verify.
- **If keyring locked** (e.g., headless/SSH before graphical login): `secret-tool lookup service gpg-signing keygrip <grip>` will fail. Log in graphically, or unlock gnome-keyring with `gnome-keyring-daemon --unlock`.
- **Verify signing works**: after preset, run `echo test | gpg --batch --sign -u C8B994F19E7D34D9` — should succeed with no prompt. Check `git log --show-signature` for "Good signature".
3 changes: 3 additions & 0 deletions dot_bashrc.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
fi

# preset GPG signing passphrase for non-interactive commits (agents)
[ -x "$HOME/.local/bin/gpg-sign-preset" ] && "$HOME/.local/bin/gpg-sign-preset" >/dev/null 2>&1 || true

if command -v mise >/dev/null; then
eval "$(mise activate bash)"
fi
Expand Down
15 changes: 9 additions & 6 deletions dot_config/mise/mise.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dot_config/mise/mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ go = "1.25.6"
"ubi:supabase/cli" = { version = "2.98.2", exe = "supabase" }
deno = "2.7.14"
"npm:wrangler" = "4.88.0"
"github:anomalyco/opencode" = "1.17.4"
"github:anomalyco/opencode" = "1.18.4"
"npm:@google/gemini-cli" = "0.41.2"
"npm:aicommits" = "3.1.2"
"npm:@agentmemory/agentmemory" = "latest"
Expand Down
4 changes: 0 additions & 4 deletions dot_config/private_jj/config.toml

This file was deleted.

9 changes: 9 additions & 0 deletions dot_config/systemd/user/gpg-sign-preset.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Unit]
Description=Preset GPG signing passphrase into gpg-agent

[Service]
Type=oneshot
ExecStart=%h/.local/bin/gpg-sign-preset

[Install]
WantedBy=default.target
5 changes: 5 additions & 0 deletions dot_gnupg/gpg-agent.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pinentry-program /usr/bin/pinentry-curses

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid hard-coding a Linux pinentry path

For macOS installs supported by this dotfiles repo, applying this writes a Linux-specific /usr/bin/pinentry-curses into ~/.gnupg/gpg-agent.conf. When the signing cache is empty, gpg-agent will try that missing path instead of the platform's pinentry, so signed commits or decryption fail rather than prompting; this should be templated via lookPath/OS guards or left to GnuPG defaults.

Useful? React with 👍 / 👎.

allow-preset-passphrase
pinentry-timeout 30
default-cache-ttl 86400
max-cache-ttl 34560000
File renamed without changes.
18 changes: 18 additions & 0 deletions dot_local/bin/executable_gpg-sign-preset.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# chezmoi template: preset GPG signing passphrase into gpg-agent
set -euo pipefail

key="{{ .gpg_signing_key }}"
[ -n "$key" ] || exit 0

gpg_preset_passphrase=""
command -v gpg-preset-passphrase >/dev/null 2>&1 && gpg_preset_passphrase="gpg-preset-passphrase" || { libexec=$(gpgconf --list-dirs libexecdir 2>/dev/null); [ -x "$libexec/gpg-preset-passphrase" ] && gpg_preset_passphrase="$libexec/gpg-preset-passphrase"; }
[ -n "$gpg_preset_passphrase" ] || exit 0

command -v secret-tool >/dev/null 2>&1 || exit 0

grip="$(gpg --batch --with-colons --with-keygrip -K "$key" 2>/dev/null | awk -F: -v key="$key" '$1=="fpr" && $10 ~ key{f=1; next} f && $1=="grp"{print $10; exit}')"
[ -n "$grip" ] || exit 0

pass="$(secret-tool lookup service gpg-signing key "$key" 2>/dev/null)" || exit 0
printf '%s' "$pass" | "$gpg_preset_passphrase" --preset "$grip" 2>/dev/null || true
3 changes: 3 additions & 0 deletions dot_zshrc.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
fi

# preset GPG signing passphrase for non-interactive commits (agents)
[ -x "$HOME/.local/bin/gpg-sign-preset" ] && "$HOME/.local/bin/gpg-sign-preset" >/dev/null 2>&1 || true

# enable brew if found on linux
if [ -f /home/linuxbrew/.linuxbrew/bin/brew ] ; then
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
Expand Down
Loading