specs/001-gpg-event-signing/contracts/cli-commands.md
Ref: Size: 2.3 KiB
# CLI Contract: Ed25519 Signing Commands
## New Command: `collab init-key`
**Synopsis**: `collab init-key`
**Description**: Generates an Ed25519 keypair for signing events.
**Arguments**: None
**Output (stdout)**:
```
Ed25519 keypair generated.
Private key: ~/.config/git-collab/signing-key
Public key: ~/.config/git-collab/signing-key.pub
Your public key: <base64-encoded-32-byte-pubkey>
```
**Errors (stderr)**:
- Key already exists: `Error: Signing key already exists at ~/.config/git-collab/signing-key. Use --force to overwrite.`
**Exit codes**: 0 success, 1 error
**Options**:
- `--force`: Overwrite existing keypair
---
## Modified Behavior: All Event Commands
All commands that create events (`issue open`, `issue comment`, `issue close`, `issue edit`, `issue label`, `issue unlabel`, `issue assign`, `issue unassign`, `issue reopen`, `patch create`, `patch revise`, `patch review`, `patch comment`, `patch close`, `patch merge`) now:
1. Load the signing key from `~/.config/git-collab/signing-key`
2. Sign the event data
3. Embed signature and pubkey in event.json
**Error if no key**: `Error: No signing key found. Run 'collab init-key' to generate one.` (exit code 1)
---
## Modified Behavior: `collab sync`
**New behavior**: Before reconciliation, verifies Ed25519 signatures on all incoming event commits.
**Verification failure output (stderr)**:
```
Error: Signature verification failed for refs/collab/issues/<id>:
Commit <short-oid>: <reason>
Commit <short-oid>: <reason>
Ref not updated. <N> commit(s) failed verification.
```
**Reasons**: `missing signature`, `invalid signature`, `unknown signing key`
**Behavior**: Valid refs are synced normally. Invalid refs are skipped with warnings. Push still proceeds for locally-valid refs.
---
## event.json Schema (v2 — with signing)
```json
{
"timestamp": "string (RFC3339)",
"author": {
"name": "string",
"email": "string"
},
"action": {
"type": "string (action discriminator)",
"...": "action-specific fields"
},
"signature": "string (base64-encoded Ed25519 signature, 64 bytes)",
"pubkey": "string (base64-encoded Ed25519 public key, 32 bytes)"
}
```
The signed payload is the canonical JSON serialization of the object *without* the `signature` and `pubkey` fields, with keys sorted alphabetically.