Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
78fb480
feat(vc): implement SSH commit signing (ENG-2002)
hieuntg81 Apr 16, 2026
fb6667d
fix(vc): address PR #435 review issues in SSH commit signing (ENG-2002)
hieuntg81 Apr 16, 2026
48ac694
fix(vc): address remaining minor observations from PR #435 re-review …
hieuntg81 Apr 16, 2026
d90806b
Merge branch 'main' into feat/ENG-2002
hieuntg81 Apr 16, 2026
65618ac
fix(vc): extract public key from OpenSSH header without decryption (E…
hieuntg81 Apr 16, 2026
bdd6414
Merge branch 'main' into feat/ENG-2002
hieuntg81 Apr 17, 2026
d4821d5
feat: add support for SSH-agent signing, improved key parsing, and se…
hieuntg81 Apr 17, 2026
8880b5f
Merge branch 'main' into feat/ENG-2002
hieuntg81 Apr 20, 2026
36e2682
fix(ssh): use 6-byte SSHSIG preamble for signed data per PROTOCOL.sshsig
hieuntg81 Apr 20, 2026
fb1c71b
fix(ssh): drop 'unsupported' from passphrase-error regex (ENG-2002 B6)
hieuntg81 Apr 20, 2026
d7ceeec
test(ssh): add regression test confirming B6 closes ENG-2002 B7
hieuntg81 Apr 20, 2026
77464d7
fix(oclif): remove interactive passphrase prompt from vc commit (ENG-…
hieuntg81 Apr 20, 2026
608aa9f
docs(ssh): rewrite SSH signing guide in English with full coverage (E…
hieuntg81 Apr 20, 2026
c174c31
fix(ssh): narrow ERR_OSSL whitelist to passphrase-only codes (ENG-200…
hieuntg81 Apr 20, 2026
3b1d28c
refactor(ssh): consolidate SSHSIG_MAGIC + drop stale 7-byte comments …
hieuntg81 Apr 20, 2026
7c9ba89
test(ssh): plug tempdir leaks + tighten B7 assertion (ENG-2002 M2+M3+M5)
hieuntg81 Apr 20, 2026
be121dd
style(ssh): minor cleanups from code review (ENG-2002 m1+m2+m3+m4+m6)
hieuntg81 Apr 20, 2026
7b469a0
Merge remote-tracking branch 'origin/feat/ENG-2002' into feat/ENG-2002
hieuntg81 Apr 20, 2026
ab3787d
docs(ssh): correct misleading comment for ERR_OSSL_CRYPTO_INTERRUPTED…
hieuntg81 Apr 20, 2026
d02c5f8
refactor(ssh): extract SSHSIG_MAGIC into dedicated sshsig-constants m…
hieuntg81 Apr 20, 2026
0bf6b07
test(ssh): replace `as Error`, justify before-pattern, drop B0 refere…
hieuntg81 Apr 20, 2026
bb9dd0e
docs(ssh): align troubleshooting symptoms with actual error strings (…
hieuntg81 Apr 20, 2026
dd53ab9
test(ssh): proper narrowing instead of dead-guard `as Error` (review-3)
hieuntg81 Apr 20, 2026
ba0efb1
refactor(ssh): export SSHSIG preamble as immutable string, not shared…
hieuntg81 Apr 20, 2026
e5a8f99
chore(ssh): correct constants comment + consolidate dynamic import (r…
hieuntg81 Apr 20, 2026
c13397d
fix(ssh): user-grade errors for wrong format and wrong passphrase (EN…
hieuntg81 Apr 20, 2026
010e653
feat(tui): Ink passphrase prompt for encrypted SSH signing keys (ENG-…
hieuntg81 Apr 20, 2026
8feaa2c
refactor(vc): drop camel-case `user.signingKey` from exported union (…
hieuntg81 Apr 20, 2026
0b4f7a5
feat(signing-key): require --yes to confirm destructive remove (ENG-2…
hieuntg81 Apr 20, 2026
ce82e17
refactor(ssh): scope SigningKeyCache entries by (projectPath, keyPath…
hieuntg81 Apr 20, 2026
2e4d228
feat(ssh): add scrubPassphrase helper for safe payload logging (ENG-2…
hieuntg81 Apr 20, 2026
4adcbdc
refactor(ssh): consolidate SSHSIG_HASH_ALGORITHM constant + document …
hieuntg81 Apr 20, 2026
db56ec0
refactor(tui): drop dead passphraseRef in VcCommitFlow (ENG-2002 revi…
hieuntg81 Apr 21, 2026
00931f8
docs(oclif): warn about --passphrase visibility in process list (ENG-…
hieuntg81 Apr 21, 2026
2c510e4
Merge branch 'main' into feat/ENG-2002
hieuntg81 Apr 22, 2026
598837a
fix(ssh): actionable hint for encrypted PEM in extractPublicKey (ENG-…
hieuntg81 Apr 22, 2026
03a1f71
chore(ssh): remove unwired scrubPassphrase helper (ENG-2002 review #1)
hieuntg81 Apr 22, 2026
5c78743
test(signing-key): drop fake 'returns mapped key list' test (ENG-2002…
hieuntg81 Apr 22, 2026
7c04d97
fix(vc): invalidate cache entry for the previous signing-key on confi…
hieuntg81 Apr 22, 2026
e836706
fix(vc): reject non-absolute signing-key path in config (ENG-2002 rev…
hieuntg81 Apr 22, 2026
c368efd
fix(vc): distinguish passphrase vs other parse errors in config hint …
hieuntg81 Apr 22, 2026
29658cc
fix(tui): reset commit mutation to drop passphrase after terminal sta…
hieuntg81 Apr 22, 2026
c0b44fe
fix(ssh): guard agent response length and sigLen bounds (ENG-2002 rev…
hieuntg81 Apr 22, 2026
637a061
fix(vc): detect signing-key duplicate via VcErrorCode + check respons…
hieuntg81 Apr 22, 2026
6dc8df2
test(vc): handleImportGitSigning picks up user.signingKey from global…
hieuntg81 Apr 22, 2026
9f3225e
test(ssh): RSA-via-agent sets SSH_AGENT_RSA_SHA2_512 flag (ENG-2002 r…
hieuntg81 Apr 22, 2026
4b24422
refactor(ssh): type predicates in place of as/! casts (ENG-2002 revie…
hieuntg81 Apr 22, 2026
9c0a68e
refactor: convert data-only DTO interfaces to type (ENG-2002 review #…
hieuntg81 Apr 22, 2026
57db09e
refactor(ssh): internal null sentinels → undefined (ENG-2002 review #…
hieuntg81 Apr 22, 2026
6605747
refactor(ssh): extract TTL sweep for symmetric eviction (ENG-2002 rev…
hieuntg81 Apr 22, 2026
10b903f
Merge branch 'main' into feat/ENG-2002
hieuntg81 Apr 22, 2026
4be8e74
style(ssh): fix missing indent on SigningKeyCache.ttlMs field (ENG-20…
hieuntg81 Apr 22, 2026
cd420c0
Merge branch 'main' into feat/ENG-2002
hieuntg81 Apr 22, 2026
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
160 changes: 160 additions & 0 deletions docs/ssh-commit-signing.md
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

issue (block):
I think this file should not exits, or if really needed, please make it 100% english.

Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# SSH Commit Signing

ByteRover signs commits with an SSH key. When enabled, every commit carries a
cryptographic signature and shows as **Verified** in the ByteRover UI and via
`git verify-commit`.

## Quick start

```bash
# 1. Generate (skip if you already have a key)
ssh-keygen -t ed25519 -C "you@example.com" -f ~/.ssh/id_ed25519_signing

# 2. Register the public key with ByteRover
brv signing-key add --key ~/.ssh/id_ed25519_signing.pub --title "My laptop"

# 3. Tell brv where the private key lives
brv vc config user.signingkey ~/.ssh/id_ed25519_signing

# 4. Sign every commit automatically
brv vc config commit.sign true
```

From here `brv vc commit -m "..."` produces a signed commit.

---

## Supported key formats

| Key format | Direct (file) | Via ssh-agent |
| --------------------------------------------------- | :-----------: | :-----------: |
| Ed25519, OpenSSH format, **unencrypted** | ✅ | ✅ |
| Ed25519, OpenSSH format, passphrase-protected | ❌ | ✅ |
| RSA / ECDSA, any format | ❌ | ✅ |

If your key falls into a row that only supports the ssh-agent column, load it
into the agent before signing:

```bash
ssh-add ~/.ssh/id_rsa # macOS / Linux / WSL
```

On Windows PowerShell with the OpenSSH agent service running:

```powershell
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $HOME\.ssh\id_rsa
```

`brv vc commit --sign` automatically prefers ssh-agent when it is available
(`SSH_AUTH_SOCK` set on Unix, `\\.\pipe\openssh-ssh-agent` on Windows) — you
do not need to change any brv config.

---

## Passphrase-protected keys (without ssh-agent)

For an unencrypted Ed25519 key on disk you do not need a passphrase. For any
other passphrase-protected key, **use ssh-agent** (above) — that is the
expected path for the vast majority of users.

The narrow exception is an Ed25519 key saved in legacy PEM/PKCS8 format
(rather than the modern OpenSSH format that `ssh-keygen` produces by
default). Keys in that format support direct passphrase entry:

```bash
# Pass once via flag
brv vc commit -m "msg" --sign --passphrase "$MY_PASS"

# Or via env var (preferred for CI / scripts — keeps the secret out of shell history)
BRV_SSH_PASSPHRASE="$MY_PASS" brv vc commit -m "msg" --sign
```

`brv` does **not** prompt interactively for the passphrase — `brv vc` is a
non-interactive oclif command. If a passphrase is required and neither
`--passphrase` nor `BRV_SSH_PASSPHRASE` is provided, the command exits with a
clear error pointing to both options.

---

## Cross-platform notes

### macOS / Linux

Default key location: `~/.ssh/id_ed25519`. ssh-agent is started by your shell
or DE; check with `ssh-add -l`.

### Windows (PowerShell, native OpenSSH)

- Install OpenSSH from "Optional Features" if not present.
- Default key location: `$HOME\.ssh\id_ed25519`.
- The agent runs as a Windows service, not a per-shell process.

### WSL

WSL has its own ssh-agent independent of the Windows agent. Either:

- Generate keys inside WSL and use `ssh-add` there, or
- Bridge to the Windows agent via `npiperelay` + `socat` (community guides
exist) — beyond this doc's scope.

When pointing brv at a key, use the WSL path (`/mnt/c/...` for Windows-side
files, plain `~/...` for WSL-side).

---

## Existing git SSH signing config

If you already configured `git config gpg.format ssh` and
`git config user.signingKey ...`, brv can import directly:

```bash
brv vc config --import-git-signing
```

This reads `user.signingKey` and `commit.gpgSign` from your git config and
copies them into brv's project config — no manual setup.

---

## Verification

Verify any signed commit with the standard `git verify-commit`:

```bash
# Build an allowed_signers file once
echo "you@example.com $(cat ~/.ssh/id_ed25519_signing.pub)" > ~/.config/brv/allowed_signers

# Verify
cd .brv/context-tree
git -c gpg.ssh.allowedSignersFile=~/.config/brv/allowed_signers verify-commit HEAD
```

Expected: `Good "git" signature for you@example.com with ED25519 key SHA256:...`.

---

## Removing a key

```bash
brv signing-key list # list registered keys + IDs
brv signing-key remove <key-id> # remove from ByteRover
```

This deletes the public key from ByteRover only. The private key on disk and
any `brv vc config user.signingkey` setting are untouched.

---

## Troubleshooting

Symptom column shows the leading substring of each error — the actual
output continues with a key path or key-type detail. Match by prefix.

| Symptom (starts with) | Likely cause | Fix |
| --- | --- | --- |
| `Error: Encrypted OpenSSH private keys are not supported for direct signing. Load the key into ssh-agent first: ssh-add …` | brv cannot decrypt OpenSSH-format encrypted keys natively. | Run the `ssh-add` command from the error message, then retry. |
| `Error: Unsupported OpenSSH key type: …` | RSA / ECDSA / non-Ed25519 OpenSSH keys are not parsed natively. | Load the key into ssh-agent (`ssh-add <keypath>`). |
| `Error: Signing key requires a passphrase. Provide it via the --passphrase flag or the BRV_SSH_PASSPHRASE environment variable, then retry.` | PEM-format key needs a passphrase and none was supplied. | Pass `--passphrase` or set `BRV_SSH_PASSPHRASE`. |
| `Could not verify signature.` from `git verify-commit` | `allowed_signers` file missing or wrong fingerprint. | Re-create as shown in **Verification**. |
30 changes: 29 additions & 1 deletion package-lock.json

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

Loading
Loading