a73x

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.