17 changed files with 1068 additions and 1056 deletions
@ -0,0 +1,237 @@ |
|||||||
|
# Custom Event Kinds |
||||||
|
|
||||||
|
This document describes the custom event kinds used by GitRepublic that are not part of any standard NIP. These may be proposed as NIPs in the future. |
||||||
|
|
||||||
|
## Kind 1640: Commit Signature |
||||||
|
|
||||||
|
**Status**: Custom implementation (not in any NIP) |
||||||
|
|
||||||
|
Git commit signature events are used to cryptographically sign git commits using Nostr keys. This provides cryptographic proof that a commit was made by a specific Nostr user. |
||||||
|
|
||||||
|
### Event Structure |
||||||
|
|
||||||
|
```jsonc |
||||||
|
{ |
||||||
|
"kind": 1640, |
||||||
|
"pubkey": "committer_pubkey_hex...", |
||||||
|
"created_at": 1234567890, |
||||||
|
"content": "Signed commit: <commit-message>\n\n<optional-additional-info>", |
||||||
|
"tags": [ |
||||||
|
["commit", "abc123def456..."], // Final commit hash (added after commit is created) |
||||||
|
["author", "John Doe"], // Author name |
||||||
|
["author", "john@example.com"], // Author email (second author tag) |
||||||
|
["message", "Fix bug in feature"], // Commit message |
||||||
|
["e", "nip98_auth_event_id"] // Optional: Reference to NIP-98 auth event |
||||||
|
], |
||||||
|
"id": "...", |
||||||
|
"sig": "..." |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### Tag Descriptions |
||||||
|
|
||||||
|
- **`commit`** (required): The final commit hash after the commit is created. This tag is added after the commit is created, as the hash is not known beforehand. |
||||||
|
- **`author`** (required, appears twice): First occurrence contains the author name, second contains the author email. |
||||||
|
- **`message`** (required): The commit message text. |
||||||
|
- **`e`** (optional): Reference to a NIP-98 authentication event if the commit was made via HTTP git operations. |
||||||
|
|
||||||
|
### Usage in GitRepublic |
||||||
|
|
||||||
|
1. **Client-Side Signing**: When users make commits through the web interface, they can sign commits using NIP-07 (browser extension). The signature event is created client-side and keys never leave the browser. |
||||||
|
|
||||||
|
2. **Server-Side Signing**: For git operations (push via git client), commits can be signed using NIP-98 authentication events. The commit signature event references the NIP-98 event. |
||||||
|
|
||||||
|
3. **Commit Message Embedding**: The signature is embedded in the git commit message as a trailer: |
||||||
|
``` |
||||||
|
Nostr-Signature: <event_id> <signature> |
||||||
|
``` |
||||||
|
|
||||||
|
4. **Verification**: Commit signatures can be verified by: |
||||||
|
- Checking the event signature |
||||||
|
- Verifying the commit hash matches |
||||||
|
- Confirming the author information matches the commit |
||||||
|
|
||||||
|
### Rationale |
||||||
|
|
||||||
|
Using a dedicated kind (1640) instead of kind 1 (text note) prevents spamming the user's feed with commit signatures. It also provides a clear, searchable way to find all commits signed by a specific user. |
||||||
|
|
||||||
|
**Implementation**: `src/lib/services/git/commit-signer.ts` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Kind 1641: Ownership Transfer |
||||||
|
|
||||||
|
**Status**: Custom implementation (not in any NIP) |
||||||
|
|
||||||
|
Repository ownership transfer events enable transferring repository ownership from one pubkey to another. This is a **non-replaceable event** to maintain an immutable chain of ownership. |
||||||
|
|
||||||
|
### Event Structure |
||||||
|
|
||||||
|
#### Regular Ownership Transfer |
||||||
|
|
||||||
|
```jsonc |
||||||
|
{ |
||||||
|
"kind": 1641, |
||||||
|
"pubkey": "old_owner_pubkey_hex...", |
||||||
|
"created_at": 1234567890, |
||||||
|
"content": "Transferring ownership of repository my-repo to new maintainer", |
||||||
|
"tags": [ |
||||||
|
["a", "30617:old_owner_pubkey.../my-repo"], // Repository address |
||||||
|
["p", "new_owner_pubkey_hex..."], // New owner pubkey (hex or npub) |
||||||
|
["d", "my-repo"] // Repository identifier |
||||||
|
], |
||||||
|
"id": "...", |
||||||
|
"sig": "..." |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
#### Self-Transfer (Initial Ownership Proof) |
||||||
|
|
||||||
|
```jsonc |
||||||
|
{ |
||||||
|
"kind": 1641, |
||||||
|
"pubkey": "owner_pubkey_hex...", |
||||||
|
"created_at": 1234567890, |
||||||
|
"content": "Initial ownership proof for repository my-repo", |
||||||
|
"tags": [ |
||||||
|
["a", "30617:owner_pubkey.../my-repo"], |
||||||
|
["p", "owner_pubkey_hex..."], // Same as pubkey (self-transfer) |
||||||
|
["d", "my-repo"], |
||||||
|
["t", "self-transfer"] // Marker for initial ownership proof |
||||||
|
], |
||||||
|
"id": "...", |
||||||
|
"sig": "..." |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### Tag Descriptions |
||||||
|
|
||||||
|
- **`a`** (required): Repository address in format `30617:<owner-pubkey>:<repo-name>` |
||||||
|
- **`p`** (required): New owner pubkey (can be hex or npub format). For self-transfers, this is the same as the event `pubkey`. |
||||||
|
- **`d`** (required): Repository identifier (d-tag from repository announcement) |
||||||
|
- **`t`** (optional): `"self-transfer"` marker for initial ownership proofs |
||||||
|
|
||||||
|
### Usage in GitRepublic |
||||||
|
|
||||||
|
1. **Initial Ownership**: When a repository is first created, a self-transfer event (owner → owner) is published to establish initial ownership proof. This creates an immutable record that the owner created the repository. |
||||||
|
|
||||||
|
2. **Ownership Transfers**: When transferring ownership to another user: |
||||||
|
- The current owner creates a kind 1641 event with the new owner's pubkey |
||||||
|
- The new owner must accept the transfer (future enhancement) |
||||||
|
- The transfer creates an immutable chain of ownership |
||||||
|
|
||||||
|
3. **Ownership Verification**: To verify current ownership: |
||||||
|
- Find the repository announcement (kind 30617) |
||||||
|
- Find all ownership transfer events for that repository |
||||||
|
- Follow the chain from the initial self-transfer to the most recent transfer |
||||||
|
- The most recent transfer's `p` tag indicates the current owner |
||||||
|
|
||||||
|
4. **Fork Operations**: When forking a repository, a self-transfer event is created to prove the fork owner's claim to the forked repository. |
||||||
|
|
||||||
|
### Rationale |
||||||
|
|
||||||
|
NIP-34 doesn't define ownership transfers. This custom kind provides: |
||||||
|
- **Immutability**: Non-replaceable events create an unchangeable chain |
||||||
|
- **Auditability**: Full history of ownership changes |
||||||
|
- **Security**: Cryptographic proof of ownership transfers |
||||||
|
- **Fork Integrity**: Ensures forks can be traced back to their origin |
||||||
|
|
||||||
|
**Implementation**: `src/lib/services/nostr/ownership-transfer-service.ts` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Kind 30620: Branch Protection |
||||||
|
|
||||||
|
**Status**: Custom implementation (not in any NIP) |
||||||
|
|
||||||
|
Branch protection rules allow repository owners to enforce policies on specific branches, such as requiring pull requests, reviewers, or status checks before merging. |
||||||
|
|
||||||
|
### Event Structure |
||||||
|
|
||||||
|
```jsonc |
||||||
|
{ |
||||||
|
"kind": 30620, |
||||||
|
"pubkey": "owner_pubkey_hex...", |
||||||
|
"created_at": 1234567890, |
||||||
|
"content": "", |
||||||
|
"tags": [ |
||||||
|
["d", "my-repo"], // Repository name |
||||||
|
["a", "30617:owner_pubkey.../my-repo"], // Repository address |
||||||
|
|
||||||
|
// Branch: main |
||||||
|
["branch", "main"], // Branch name |
||||||
|
["branch", "main", "require-pr"], // Require pull request (no value) |
||||||
|
["branch", "main", "require-reviewers", "npub1reviewer..."], // Required reviewer |
||||||
|
["branch", "main", "require-reviewers", "npub2reviewer..."], // Another reviewer |
||||||
|
["branch", "main", "require-status", "ci"], // Required status check |
||||||
|
["branch", "main", "require-status", "lint"], // Another required status |
||||||
|
|
||||||
|
// Branch: develop |
||||||
|
["branch", "develop"], |
||||||
|
["branch", "develop", "require-pr"], |
||||||
|
["branch", "develop", "allow-force-push"], // Allow force push |
||||||
|
["branch", "develop", "allowed-maintainers", "npub1maintainer..."] // Can bypass protection |
||||||
|
], |
||||||
|
"id": "...", |
||||||
|
"sig": "..." |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### Tag Descriptions |
||||||
|
|
||||||
|
- **`d`** (required): Repository name/identifier |
||||||
|
- **`a`** (required): Repository address in format `30617:<owner>:<repo>` |
||||||
|
- **`branch`** (required, appears multiple times): Branch name and protection settings |
||||||
|
- `["branch", "<name>"]`: Declares a protected branch |
||||||
|
- `["branch", "<name>", "require-pr"]`: Requires pull request before merging |
||||||
|
- `["branch", "<name>", "allow-force-push"]`: Allows force push to this branch |
||||||
|
- `["branch", "<name>", "require-reviewers", "<pubkey>"]`: Required reviewer (can appear multiple times) |
||||||
|
- `["branch", "<name>", "require-status", "<check-name>"]`: Required status check (can appear multiple times) |
||||||
|
- `["branch", "<name>", "allowed-maintainers", "<pubkey>"]`: Maintainer who can bypass protection (can appear multiple times) |
||||||
|
|
||||||
|
### Protection Rules |
||||||
|
|
||||||
|
1. **Require Pull Request**: Direct pushes to protected branches are blocked. All changes must come through pull requests. |
||||||
|
|
||||||
|
2. **Require Reviewers**: Pull requests to protected branches must be approved by at least one of the specified reviewers. |
||||||
|
|
||||||
|
3. **Require Status Checks**: All specified status checks must pass before a PR can be merged. |
||||||
|
|
||||||
|
4. **Allow Force Push**: By default, force pushes are blocked on protected branches. This tag allows them. |
||||||
|
|
||||||
|
5. **Allowed Maintainers**: Specified maintainers can bypass protection rules (e.g., for emergency fixes). |
||||||
|
|
||||||
|
### Usage in GitRepublic |
||||||
|
|
||||||
|
1. **Setting Protection**: Repository owners create/update branch protection rules by publishing a kind 30620 event. |
||||||
|
|
||||||
|
2. **Enforcement**: When users attempt to: |
||||||
|
- Push directly to a protected branch → Blocked if `require-pr` is set |
||||||
|
- Merge a PR to a protected branch → Requires reviewers and status checks if specified |
||||||
|
- Force push → Blocked unless `allow-force-push` is set or user is an allowed maintainer |
||||||
|
|
||||||
|
3. **Replaceable Events**: Branch protection events are replaceable (same `d` tag). Publishing a new event replaces all previous rules. |
||||||
|
|
||||||
|
4. **Access Control**: Only the repository owner can create/update branch protection rules. |
||||||
|
|
||||||
|
### Rationale |
||||||
|
|
||||||
|
NIP-34 doesn't define branch protection. This custom kind provides: |
||||||
|
- **Code Quality**: Enforces code review and testing before merging |
||||||
|
- **Security**: Prevents direct pushes to critical branches |
||||||
|
- **Flexibility**: Configurable rules per branch |
||||||
|
- **Maintainability**: Clear, auditable protection rules |
||||||
|
|
||||||
|
**Implementation**: `src/lib/services/nostr/branch-protection-service.ts` (if implemented), referenced in repository settings |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Summary |
||||||
|
|
||||||
|
| Kind | Name | Replaceable | Purpose | |
||||||
|
|------|------|-------------|---------| |
||||||
|
| 1640 | Commit Signature | No | Cryptographically sign git commits | |
||||||
|
| 1641 | Ownership Transfer | No | Transfer repository ownership (immutable chain) | |
||||||
|
| 30620 | Branch Protection | Yes | Enforce branch protection rules | |
||||||
|
|
||||||
|
These custom kinds extend NIP-34's git collaboration features with additional functionality needed for a production git hosting platform. They may be proposed as NIPs in the future to standardize these features across the Nostr ecosystem. |
||||||
Loading…
Reference in new issue