Skip to content

🐋🔧:fix SSH-format commit signing & verification in devcontainer - #1778

Merged
OpenINFbot merged 9 commits into
livefrom
fix/devcontainer-commit-signing
Aug 9, 2026
Merged

🐋🔧:fix SSH-format commit signing & verification in devcontainer#1778
OpenINFbot merged 9 commits into
livefrom
fix/devcontainer-commit-signing

Conversation

@DerekNonGeneric

@DerekNonGeneric DerekNonGeneric commented Aug 9, 2026

Copy link
Copy Markdown
Member

🐋 dev container
🔧 bug fix

Why

git commit -S in the devcontainer produced a real signature, but
git log --show-signature reported No signature -- a message that reads
identically to signing being broken, when the actual gap was that
SSH-format verification was never configured. Chasing that report as a
signing bug is a dead end: commit.gpgsign, user.signingkey, and the
forwarded agent were all already correct.

What this does

  • Adds gpg.ssh.allowedSignersFile configuration to post-start.sh, so
    git log --show-signature can verify what it signs instead of failing
    with a message indistinguishable from unsigned commits.
  • Rewrites agent-forwarding.md for the current stock devcontainer (no
    more sshd/vscode user/port 2222 from before 🐋♻️:replace the dev container with a stock image and features #1775), documents the
    commit.gpgsign per-repo override footgun, and adds a
    "Verifying signatures locally" section plus matching troubleshooting
    entries -- including one for the exact "No signature" trap above.
  • Teaches cspell the three words the rewrite introduced
    (repoints, retargets, unconfigured) in project-terms.txt.

Test plan

  • Tore down ~/.ssh/id_ed25519.pub, ~/.ssh/allowed_signers, and the
    related git config, then re-ran post-start.sh cold to confirm it
    rebuilds both from a fresh forwarded agent.
  • git commit --allow-empty -S followed by git log --show-signature
    reports Good "git" signature.
  • Exercised the missing-user.email branch of post-start.sh and
    confirmed its warning fires without touching signing.
  • nps verify.md (prettier, markdownlint-cli2, remark, cspell) and
    nps verify.json both clean locally.
  • Rendered the doc through this repo's actual markdown-it +
    markdown-it-anchor pipeline to confirm in-doc anchor links
    resolve.

🤖 Generated with Claude Code

DerekNonGeneric and others added 7 commits August 9, 2026 01:48
#1775 replaced postCreateCommand+postStartCommand with postCreateCommand
alone. postCreateCommand only ever runs once, at container build time, but
the SSH agent VS Code forwards is a fresh, per-session socket -- so nothing
was left to re-materialize gpg.format=ssh's signing-key file on later
attaches. VS Code forwards the running agent automatically but never copies
key files in, so a user.signingkey path copied verbatim from the host's
gitconfig (e.g. ~/.ssh/id_ed25519.pub) pointed at a file that had never
existed in the container, and `git commit -S` failed with "Couldn't load
public key ...: No such file or directory".

post-start.sh now runs on every attach and writes the forwarded agent's
public key to user.signingkey's path whenever the agent is holding exactly
one identity. It can't sign with the wrong key even if the agent's identity
turns out to be unrelated: the actual signature still goes through the agent
by fingerprint, so a mismatched file just makes ssh-keygen report no
matching identity instead of mis-signing silently.

Also drops the "To sign commits: git config --global commit.gpgsign true"
hint from post-create.sh's closing banner -- it was often already true (git
config copied from the host) and was never the actual blocker, so it just
gave false reassurance. post-start.sh's own output now reports accurate,
per-attach signing status instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The doc still described the container architecture #1775 removed: a
custom base image with its own sshd on a forwarded port 2222, a `vscode`
user, and a manual `RemoteForward` SSH tunnel (`host gpgtunnel`) to reach a
GPG agent extra socket -- mostly written for Git Bash/Gpg4Win on Windows.
None of that exists anymore; following it now would send someone looking
for a port and a user that are no longer there.

Replaced it with what's actually true of the current devcontainer: VS Code
forwards a running SSH or GPG agent automatically with no devcontainer.json
config needed, but never copies key material in, which is why
gpg.format=ssh needs post-start.sh's help (see the previous commit) and
gpg.format=openpgp doesn't. Covers setup for both signing formats, notes
that GitHub Desktop's "Automatically sign commits" preference is the
gpg.format=ssh path, and adds a troubleshooting section keyed to
post-start.sh's own output.

Also updates project-terms.txt: adds the git-config tokens the rewrite now
uses (gpgsign, openpgp, signingkey) and drops updatestartuptty and psusan,
whose only uses were in the content just removed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous commit's rewrite asserted that GitHub Desktop has an
"Automatically sign commits" preference which configures gpg.format=ssh for
you. It has no such setting. Desktop has no commit-signing UI and no key of
its own; it shells out to Git and inherits whatever git config already says,
so configuring Git is the whole job whether you commit from Desktop or a
terminal.

Says so explicitly now, and adds the thing that actually misleads people
here: a "Verified" badge on GitHub proves nothing about local signing.
Commits made or squash-merged through github.com are signed server-side with
GitHub's web-flow key (B5690EEEBB952194) and render identically to locally
signed ones. Every signature in this repository's history is that key --
there is not one locally signed commit -- which is easy to mistake for
working local signing. Adds a `git cat-file commit HEAD` check so the
question can be settled by looking rather than assuming.

Also notes that step 2 has to run on the host: the container gets its own
copy of ~/.gitconfig at build time, so running those commands inside the
container configures only the container and leaves the Mac untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A `commit.gpgsign = false` in .git/config beats `true` in the global config
and turns signing off for every tool touching the clone, while every
`--global` check keeps reporting that signing is enabled. Nothing in a diff
shows it either, since .git/config isn't version controlled -- so it is
worth ruling out early rather than after re-auditing the global config.

Worth documenting rather than only fixing: the working tree is bind-mounted
from the host, so .git is shared, and a `--local` setting applied from inside
the container silently applies to the host clone as well.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
post-start.sh wrote the forwarded agent's public key to user.signingkey's
existing path, which VS Code copies verbatim from the host's gitconfig --
typically /Users/<you>/.ssh/id_ed25519.pub on a Mac, /home/<you>/... on
Linux. Both live under a directory this container's non-root `node` user has
no write access to (/Users and /home are root-owned, 755), so `mkdir -p` on
that literal path failed with EACCES every time, on every host OS, not just
macOS. `set -uo pipefail` without `-e` let the failure pass silently, so the
script still printed "Commit signing ready" while nothing had been written --
confirmed live: git commit -S failed with "Couldn't load public key
/Users/derek/.ssh/id_ed25519.pub: No such file or directory" right after a
fresh rebuild reported success.

Now writes to $HOME/.ssh/<same filename>, a path the container user actually
owns, and repoints the container's own copy of user.signingkey there instead
of trying to recreate the host's path byte-for-byte. Verified with a real
commit against the forwarded agent: signing succeeds and the object carries
a gpgsig header.

agent-forwarding.md updated to match: the host and container signingkey
paths no longer need to be identical, since post-start.sh retargets the
container's config to wherever it actually writes the file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signing already worked. Verification never did, and its failure mode is
indistinguishable from signing being off, which is why it read as the
former:

    $ git commit --allow-empty -S -m "Test signed commit"
    $ git log --show-signature -1
    error: gpg.ssh.allowedSignersFile needs to be configured and exist
           for ssh signature verification
    No signature

That `No signature` is the verifier reporting it could not run, not a
statement about the commit -- `git cat-file commit HEAD` on the very
same commit shows a `BEGIN SSH SIGNATURE` header. Nothing about the
signing path was broken, so every re-check of `commit.gpgsign`,
`user.signingkey` and the forwarded agent came back correct, and the
`error:` line that says so is easy to lose above the commit it precedes.

ssh-format verification needs a file mapping principals to the keys they
may sign with, and Git ships no default location for one. So write
`$HOME/.ssh/allowed_signers` next to the signing key post-start.sh
already sets up, listing `user.email` against that key, and point
`gpg.ssh.allowedSignersFile` at it.

Scoped to this user's own key deliberately: other contributors' commits
will report `No principal matched`, since verifying those means a shared
allowed-signers file, which is a project decision and not one a
container script should make on its own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- The rebuild-wipes-it troubleshooting entry named only the signing-key
  file. `allowed_signers` and `gpg.ssh.allowedSignersFile` live under the
  same non-volume path and are wiped by a rebuild the same way, but
  weren't mentioned; a reader who rebuilt would find signing restored and
  verification silently not, with no entry pointing at why. Broadened the
  bullet and added a sibling entry for post-start.sh's other verification
  failure mode, missing user.email, to match the existing zero/multiple-
  identity entries in shape.

- The new "No signature" subheading used a quoted phrase --
  `### When show-signature says "No signature"` -- and both `[above]`/
  `[below]` links pointed at `#when-show-signature-says-no-signature`.
  Rendering it through this repo's actual markdown-it-anchor pipeline
  shows the literal id is `when-show-signature-says-%22no-signature%22`:
  the slugifier percent-encodes quotes rather than stripping them, unlike
  GitHub's slugger. Both anchor links were dead. Dropped the quotes from
  the heading so the generated id matches what the links already assumed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploy Preview for gh-pages-openinf ready!

Name Link
🔨 Latest commit f6ec8e7
🔍 Latest deploy log https://app.netlify.com/projects/gh-pages-openinf/deploys/6a77f28dcbd0090008d75d64
😎 Deploy Preview https://deploy-preview-1778--gh-pages-openinf.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

CI's Verify Markdown step failed on these three: cspell has no dictionary
entry for them and agent-forwarding.md's rewrites introduced all three.
Added them to project-terms.txt in their alphabetical slots, the same as
gpgsig picked up two commits ago on this branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@DerekNonGeneric DerekNonGeneric changed the title Fix devcontainer SSH commit signing and its verification 🐋🔧:fix SSH-format commit signing and its verification in the devcontainer Aug 9, 2026
CI's Test step runs editorconfig-checker, which this branch had never
actually exercised end to end -- the earlier commits landed before this
was ever pushed as a PR, so the "Lint and test" job's markdown-only path
was the only thing that had run against them. It requires every
continuation line's indentation to be a multiple of two spaces, repo-wide,
no per-language exception.

Both "Setting up ... signing" sections used numbered lists whose
continuation lines -- code fences and follow-on sentences under each step
-- were indented 3 spaces, matching a single-digit `1. ` marker. That's
also what Prettier's markdown printer produces and keeps idempotent for
ordered lists, and it's CommonMark-correct, but 3 isn't a multiple of 2.
Padding the marker to `1.  ` (making the continuation indent 4) satisfies
ec-checker but not markdownlint's MD030, which wants exactly one space
after either marker. No combination of ordered-list spacing satisfies
every tool at once.

Switched both lists to `-` bullets instead, which this same doc's
Troubleshooting section already uses with nested fences at a 2-space
indent -- already proven compatible with all three tools. Numbering
carried no meaning nothing else in the doc depended on (no "step 2"
cross-references), so nothing is lost.

Also caught by the same rule: the wrapped second line of the "No
signature" error message inside its code fence was indented 7 spaces to
align under "error: "; reindented to 2.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@OpenINFbot OpenINFbot changed the title 🐋🔧:fix SSH-format commit signing and its verification in the devcontainer 🐋🔧:fix SSH-format commit signing & verification in devcontainer Aug 9, 2026
@OpenINFbot
OpenINFbot merged commit 93a522e into live Aug 9, 2026
13 checks passed
@OpenINFbot
OpenINFbot deleted the fix/devcontainer-commit-signing branch August 9, 2026 03:42
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.

2 participants