Browse Source

revamp tutoral and ReadMe documentation

update API docs

Nostr-Signature: 9d1fb9db75e26a5fcdcab54253ef1c6126ea1e01e98728554435e655354f6238 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 0cfde0ac8a7083479c982c7f212a91eee7e8ed33b43b30291677fe3a126638ae4fde03893aa5d492f8f6fc8e066b7c46bd6f07246376a5d9e56becc73e4e48d8
main
Silberengel 3 weeks ago
parent
commit
cbe6abb837
  1. 439
      AUDIT_REPORT.md
  2. 559
      README.md
  3. 196
      docs/01.md
  4. 173
      docs/02.md
  5. 58
      docs/07.md
  6. 76
      docs/09.md
  7. 107
      docs/10.md
  8. 95
      docs/19.md
  9. 237
      docs/22.md
  10. 345
      docs/34.md
  11. 73
      docs/65.md
  12. 95
      docs/84.md
  13. 107
      docs/98.md
  14. 352
      docs/ARCHITECTURE_FAQ.md
  15. 139
      docs/IMPLEMENTATION.md
  16. 123
      docs/LOGGING.md
  17. 402
      docs/MESSAGING_FORWARDING.md
  18. 117
      docs/NIP_COMPLIANCE.md
  19. 206
      docs/PUBLISH.md
  20. 191
      docs/SECURITY.md
  21. 268
      docs/SECURITY_IMPLEMENTATION.md
  22. 76
      docs/about.md
  23. 250
      docs/api-and-cli.md
  24. 128
      docs/deployment-modes.md
  25. 190
      docs/editing-repos.md
  26. 108
      docs/grasp-servers.md
  27. 239
      docs/managing-repos.md
  28. 86
      docs/nostr-integration.md
  29. 111
      docs/profile-pages.md
  30. 153
      docs/repo-operations.md
  31. 110
      docs/search-and-external-repos.md
  32. 90
      docs/settings-and-dashboard.md
  33. 186
      docs/specs.md
  34. 159
      docs/tech-stack.md
  35. 907
      docs/tutorial.md
  36. 21
      k8s/ENTERPRISE_MODE.md
  37. 1
      nostr/commit-signatures.jsonl
  38. 102
      src/routes/api/openapi.json/openapi.json
  39. 10
      src/routes/docs/+page.server.ts
  40. 94
      src/routes/docs/[slug]/+page.server.ts
  41. 226
      src/routes/docs/[slug]/+page.svelte

439
AUDIT_REPORT.md

@ -1,439 +0,0 @@
# Code Audit Report
**Date:** 2024-12-19
**Project:** GitRepublic Web
**Auditor:** Auto (AI Code Auditor)
## Executive Summary
This audit examined the GitRepublic Web codebase for security vulnerabilities, code quality issues, and best practices. The codebase demonstrates **strong security awareness** with good practices in place, but several areas need attention.
### Overall Assessment: **B+ (Good with room for improvement)**
**Strengths:**
- ✅ Strong path traversal protection
- ✅ Good use of `spawn()` instead of `exec()` for command execution
- ✅ Comprehensive input validation utilities
- ✅ Proper error sanitization
- ✅ NIP-98 authentication implementation
- ✅ Audit logging in place
- ✅ Rate limiting implemented
**Areas for Improvement:**
- ⚠ Some error handling inconsistencies
- ⚠ Missing input validation in a few endpoints
- ⚠ Potential race conditions in concurrent operations
- ⚠ Some hardcoded values that should be configurable
- ⚠ Missing type safety in some areas
---
## 1. Security Issues
### 🔴 Critical Issues
#### 1.1 Missing Input Validation in Issues Endpoint
**File:** `src/routes/api/repos/[npub]/[repo]/issues/+server.ts`
**Lines:** 26-27, 61-62
**Issue:** The POST and PATCH endpoints accept JSON bodies without validating the structure or content of the `issueEvent` object beyond basic signature checks.
**Risk:** Malformed or malicious events could be published to Nostr relays.
**Recommendation:**
```typescript
// Add validation for issueEvent structure
if (!issueEvent.kind || issueEvent.kind !== KIND.ISSUE) {
throw handleValidationError('Invalid event kind', {...});
}
// Validate tags structure
if (!Array.isArray(issueEvent.tags)) {
throw handleValidationError('Invalid event tags', {...});
}
```
#### 1.2 Request Body Consumption in Clone Endpoint
**File:** `src/routes/api/repos/[npub]/[repo]/clone/+server.ts`
**Lines:** 63-86
**Issue:** The code attempts to read the request body as text to extract a proof event, but this consumes the body stream. If the body is needed later, it won't be available.
**Risk:** This could cause issues if the body needs to be read multiple times or if the parsing fails.
**Recommendation:** Use a proper body cloning mechanism or restructure to read body once and pass it through.
### 🟡 High Priority Issues
#### 1.3 Path Traversal Protection - Good Implementation
**File:** `src/routes/api/git/[...path]/+server.ts`
**Lines:** 196-205, 585-593
**Status:** ✅ **Well Protected**
The code properly validates paths using `resolve()` and checks that resolved paths are within `repoRoot`:
```typescript
const resolvedPath = resolve(repoPath).replace(/\\/g, '/');
const resolvedRoot = resolve(repoRoot).replace(/\\/g, '/');
if (!resolvedPath.startsWith(resolvedRoot + '/')) {
return error(403, 'Invalid repository path');
}
```
**Recommendation:** This pattern is excellent. Ensure it's used consistently across all file path operations.
#### 1.4 Command Injection Protection - Good Implementation
**File:** `src/routes/api/git/[...path]/+server.ts`
**Lines:** 359-364, 900-905
**Status:** ✅ **Well Protected**
The code uses `spawn()` with argument arrays instead of string concatenation:
```typescript
const gitProcess = spawn(gitHttpBackend, [], {
env: envVars,
stdio: ['pipe', 'pipe', 'pipe'],
shell: false
});
```
**Recommendation:** Continue using this pattern. No issues found with `exec()` or shell execution.
#### 1.5 Environment Variable Exposure
**File:** `src/routes/api/git/[...path]/+server.ts`
**Lines:** 321-335, 855-869
**Status:** ✅ **Well Protected**
The code whitelists only necessary environment variables:
```typescript
const envVars: Record<string, string> = {
PATH: process.env.PATH || '/usr/bin:/bin',
HOME: process.env.HOME || '/tmp',
// ... only necessary vars
};
```
**Recommendation:** Good practice. Continue this approach.
#### 1.6 NIP-98 Authentication Implementation
**File:** `src/lib/services/nostr/nip98-auth.ts`
**Status:** ✅ **Well Implemented**
The NIP-98 authentication is properly implemented with:
- Event signature verification
- Timestamp validation (60-second window)
- URL and method matching
- Payload hash verification
**Recommendation:** No changes needed.
### 🟢 Medium Priority Issues
#### 1.7 Error Message Information Disclosure
**File:** `src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts`
**Line:** 40
**Issue:** Error message reveals internal path structure:
```typescript
throw handleApiError(new Error('Repository not cloned locally. Please clone the repository first.'), ...);
```
**Risk:** Low - but could reveal system structure to attackers.
**Recommendation:** Use generic error messages for users, detailed messages only in logs.
#### 1.8 Missing Rate Limiting on Some Endpoints
**Files:** Various API endpoints
**Issue:** Not all endpoints appear to use rate limiting middleware.
**Recommendation:** Audit all endpoints and ensure rate limiting is applied consistently.
---
## 2. Code Quality Issues
### 2.1 Inconsistent Error Handling
#### Issue: Mixed Error Handling Patterns
**Files:** Multiple
Some endpoints use `handleApiError()`, others use direct `error()` calls, and some use try-catch with different patterns.
**Example:**
- `src/routes/api/repos/[npub]/[repo]/fork/+server.ts` uses `error()` directly
- `src/routes/api/repos/[npub]/[repo]/issues/+server.ts` uses `handleApiError()`
**Recommendation:** Standardize on using the error handler utilities consistently.
### 2.2 Type Safety Issues
#### Issue: Missing Type Assertions
**File:** `src/routes/api/repos/[npub]/[repo]/fork/+server.ts`
**Line:** 372
```typescript
await fileManager.saveRepoEventToWorktree(workDir, signedForkAnnouncement as NostrEvent, 'announcement')
```
**Issue:** Type assertion without runtime validation.
**Recommendation:** Add runtime validation or improve type definitions.
### 2.3 Code Duplication
#### Issue: Duplicate Path Validation Logic
**Files:** Multiple files
The path traversal protection pattern is duplicated across multiple files:
- `src/routes/api/git/[...path]/+server.ts` (lines 196-205, 585-593)
- `src/routes/api/repos/[npub]/[repo]/fork/+server.ts` (lines 146-150, 166-169)
**Recommendation:** Extract to a shared utility function:
```typescript
export function validateRepoPath(repoPath: string, repoRoot: string): { valid: boolean; error?: string } {
const resolvedPath = resolve(repoPath).replace(/\\/g, '/');
const resolvedRoot = resolve(repoRoot).replace(/\\/g, '/');
if (!resolvedPath.startsWith(resolvedRoot + '/')) {
return { valid: false, error: 'Invalid repository path' };
}
return { valid: true };
}
```
### 2.4 Missing Input Validation
#### Issue: Branch Name Validation Not Applied Everywhere
**File:** `src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts`
**Line:** 24
```typescript
const { prId, prAuthor, prCommitId, targetBranch = 'main', mergeMessage } = body;
```
**Issue:** `targetBranch` is not validated before use.
**Recommendation:**
```typescript
if (!isValidBranchName(targetBranch)) {
throw handleValidationError('Invalid branch name', {...});
}
```
### 2.5 Hardcoded Values
#### Issue: Magic Numbers and Strings
**Files:** Multiple
Examples:
- `src/routes/api/git/[...path]/+server.ts` line 356: `const timeoutMs = 5 * 60 * 1000;` (5 minutes)
- `src/lib/services/nostr/nip98-auth.ts` line 75: `if (eventAge > 60)` (60 seconds)
**Recommendation:** Move to configuration constants:
```typescript
const GIT_OPERATION_TIMEOUT_MS = parseInt(process.env.GIT_OPERATION_TIMEOUT_MS || '300000', 10);
const NIP98_AUTH_WINDOW_SECONDS = parseInt(process.env.NIP98_AUTH_WINDOW_SECONDS || '60', 10);
```
---
## 3. Error Handling
### 3.1 Inconsistent Error Responses
#### Issue: Different Error Formats
Some endpoints return JSON errors, others return plain text, and some use SvelteKit's `error()` helper.
**Recommendation:** Standardize error response format across all endpoints.
### 3.2 Error Logging
#### Status: ✅ **Good Implementation**
The codebase uses structured logging with Pino:
```typescript
logger.error({ error: sanitizedError, ...context }, 'Error message');
```
**Recommendation:** Continue this practice. Ensure all errors are logged with appropriate context.
### 3.3 Error Sanitization
#### Status: ✅ **Well Implemented**
The `sanitizeError()` function properly redacts sensitive data:
- Private keys (nsec patterns)
- 64-character hex keys
- Passwords
- Long pubkeys
**Recommendation:** No changes needed.
---
## 4. Performance Concerns
### 4.1 Potential Race Conditions
#### Issue: Concurrent Repository Operations
**File:** `src/routes/api/repos/[npub]/[repo]/clone/+server.ts`
**Lines:** 155-161
```typescript
if (existsSync(repoPath)) {
return json({ success: true, message: 'Repository already exists locally', alreadyExists: true });
}
```
**Issue:** Between the check and the clone operation, another request could create the repo, causing conflicts.
**Recommendation:** Use file locking or atomic operations.
### 4.2 Missing Request Timeouts
#### Issue: Some Operations Lack Timeouts
**File:** `src/lib/services/git/repo-manager.ts`
**Line:** 438
The `spawn()` call for git clone doesn't have an explicit timeout.
**Recommendation:** Add timeout handling similar to git-http-backend operations.
### 4.3 Memory Usage
#### Issue: Large File Handling
**File:** `src/routes/api/git/[...path]/+server.ts`
**Lines:** 391-400
Git operation responses are buffered in memory. For very large repositories, this could cause memory issues.
**Recommendation:** Consider streaming for large responses.
---
## 5. Best Practices
### 5.1 ✅ Good Practices Found
1. **Path Traversal Protection:** Excellent implementation using `resolve()` and path validation
2. **Command Injection Prevention:** Proper use of `spawn()` with argument arrays
3. **Environment Variable Whitelisting:** Only necessary vars are passed to child processes
4. **Error Sanitization:** Comprehensive sanitization of error messages
5. **Structured Logging:** Consistent use of Pino for logging
6. **Input Validation Utilities:** Good set of validation functions
7. **TypeScript Strict Mode:** Enabled in tsconfig.json
8. **Audit Logging:** Security events are logged
### 5.2 ⚠ Areas for Improvement
1. **Consistent Error Handling:** Standardize error handling patterns
2. **Code Reusability:** Extract duplicate validation logic
3. **Configuration Management:** Move hardcoded values to configuration
4. **Type Safety:** Improve type definitions and reduce assertions
5. **Testing:** No test files found in audit - consider adding unit tests
---
## 6. Dependency Security
### 6.1 Package Dependencies
**Status:** ⚠ **Needs Review**
The audit did not include a dependency vulnerability scan. Recommended actions:
1. Run `npm audit` to check for known vulnerabilities
2. Review dependencies for:
- `simple-git` - Git operations
- `nostr-tools` - Nostr protocol
- `ws` - WebSocket client
- `svelte` and `@sveltejs/kit` - Framework
**Recommendation:** Regularly update dependencies and monitor security advisories.
---
## 7. Recommendations Summary
### Priority 1 (Critical)
1. ✅ **Add input validation** to issues endpoint (POST/PATCH)
2. ✅ **Fix request body consumption** in clone endpoint
3. ✅ **Add branch name validation** in merge endpoint
### Priority 2 (High)
1. ✅ **Extract path validation** to shared utility
2. ✅ **Standardize error handling** across all endpoints
3. ✅ **Add timeouts** to all git operations
4. ✅ **Add rate limiting** to all endpoints
### Priority 3 (Medium)
1. ✅ **Move hardcoded values** to configuration
2. ✅ **Improve type safety** (reduce type assertions)
3. ✅ **Add file locking** for concurrent operations
4. ✅ **Run dependency audit** (`npm audit`)
### Priority 4 (Low)
1. ✅ **Add unit tests** for critical functions
2. ✅ **Document error handling patterns**
3. ✅ **Add performance monitoring**
---
## 8. Positive Findings
The codebase demonstrates **strong security awareness**:
1. ✅ **No `eval()` or `Function()` calls** found
2. ✅ **No SQL injection risks** (no database queries found)
3. ✅ **Proper path traversal protection**
4. ✅ **Command injection prevention** via `spawn()`
5. ✅ **Comprehensive input validation utilities**
6. ✅ **Error message sanitization**
7. ✅ **Structured logging**
8. ✅ **NIP-98 authentication properly implemented**
9. ✅ **Audit logging for security events**
10. ✅ **Rate limiting infrastructure in place**
---
## 9. Conclusion
The GitRepublic Web codebase shows **good security practices** overall. The main areas for improvement are:
1. **Consistency** - Standardize error handling and validation patterns
2. **Completeness** - Ensure all endpoints have proper validation and rate limiting
3. **Maintainability** - Reduce code duplication and improve type safety
**Overall Grade: B+**
The codebase is **production-ready** with the recommended fixes applied. The security foundation is solid, and most issues are minor improvements rather than critical vulnerabilities.
---
## Appendix: Files Audited
### Critical Security Files
- `src/routes/api/git/[...path]/+server.ts` (1064 lines)
- `src/routes/api/repos/[npub]/[repo]/fork/+server.ts` (497 lines)
- `src/routes/api/repos/[npub]/[repo]/clone/+server.ts` (314 lines)
- `src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts` (85 lines)
- `src/routes/api/repos/[npub]/[repo]/issues/+server.ts` (89 lines)
### Security Utilities
- `src/lib/utils/security.ts`
- `src/lib/utils/input-validation.ts`
- `src/lib/utils/error-handler.ts`
- `src/lib/utils/api-auth.ts`
- `src/lib/services/nostr/nip98-auth.ts`
- `src/lib/services/security/rate-limiter.ts`
### Core Services
- `src/lib/services/git/repo-manager.ts` (536 lines)
- `src/lib/services/git/git-remote-sync.ts` (383 lines)
- `src/lib/services/git/announcement-manager.ts` (317 lines)
---
**Report Generated:** 2024-12-19
**Total Files Audited:** 20+
**Total Lines Reviewed:** 5000+

559
README.md

@ -2,21 +2,16 @@
A decentralized, Nostr-based git server that enables git repository hosting and collaboration using Nostr events. Repositories are announced via NIP-34, and all operations (clone, push, pull) are authenticated using NIP-98 HTTP authentication. A decentralized, Nostr-based git server that enables git repository hosting and collaboration using Nostr events. Repositories are announced via NIP-34, and all operations (clone, push, pull) are authenticated using NIP-98 HTTP authentication.
## Command Line Interface (CLI) ## Quick Start
**The GitRepublic CLI is published and available via npm:** ### Web Interface
```bash
npm install -g gitrepublic-cli
```
The CLI provides: 1. Start the server: `npm run dev`
- **Git wrapper** with enhanced error messages for GitRepublic operations 2. Open browser: `http://localhost:5173`
- **Credential helper** for automatic NIP-98 authentication 3. Connect NIP-07 extension (Alby, nos2x, etc.)
- **Commit signing hook** that automatically signs commits using Nostr keys 4. Visit `/signup` to create your first repository
- **Full API access** from the command line
### Quick Start with CLI ### Command Line Interface
```bash ```bash
# Install # Install
@ -27,527 +22,113 @@ export NOSTRGIT_SECRET_KEY="nsec1..."
# Setup (configures credential helper and commit hook) # Setup (configures credential helper and commit hook)
gitrep-setup gitrep-setup
```
# Use gitrep (or gitrepublic) for git operations For complete CLI documentation and all available commands, run:
gitrep clone https://your-domain.com/api/git/npub1.../repo.git ```bash
gitrep push origin main gitrep --help
# Use gitrep for API commands
gitrep repos list # List repositories
gitrep push-all main # Push to all remotes
gitrep publish repo-announcement myrepo
``` ```
**Note**: `gitrep` is a shorter alias for `gitrepublic` - both work the same way. ## Documentation
For complete CLI documentation, see [gitrepublic-cli/README.md](./gitrepublic-cli/README.md). **Complete documentation is available in the [docs](./docs/) directory:**
--- - **[About GitRepublic](./docs/about.md)** - Overview and table of contents for all documentation topics
## Overview ### Key Topics
- [How this git server integrates Nostr](./docs/nostr-integration.md)
- [Creating, forking, transferring, or cloning a repo](./docs/repo-operations.md)
- [Editing a repo](./docs/editing-repos.md)
- [Managing a repo](./docs/managing-repos.md)
- [Search and viewing external git repos](./docs/search-and-external-repos.md)
- [REST API and CLI](./docs/api-and-cli.md)
- [Profile pages](./docs/profile-pages.md)
- [Settings and Dashboard](./docs/settings-and-dashboard.md)
- [Working with GRASP servers](./docs/grasp-servers.md)
- [Lightweight versus Enterprise modes](./docs/deployment-modes.md)
- [Tech stack used](./docs/tech-stack.md)
- [Specs used](./docs/specs.md)
## Components
GitRepublic consists of three main components: GitRepublic consists of three main components:
1. **Web Interface** - Full-featured web application for browsing, editing, and managing repositories 1. **Web Interface** - Full-featured web application for browsing, editing, and managing repositories
2. **Command Line Interface (CLI)** - Git wrapper and API client for command-line operations 2. **Command Line Interface (CLI)** - Git wrapper and API client for command-line operations
3. **REST API** - Complete API for programmatic access (see `/api/openapi.json` for full documentation) 3. **REST API** - Complete API for programmatic access
All three interfaces use the same underlying Nostr-based authentication and repository management system. All three interfaces use the same underlying Nostr-based authentication and repository management system.
## Features ## Installation
### Core Functionality ### Development
- **NIP-34 Repo Announcements**: Create and manage repository announcements on Nostr
- **NIP-07 Authentication**: Web UI authentication via browser extensions (e.g., Alby, nos2x)
- **NIP-98 HTTP Authentication**: Git operations (clone, push, pull) authenticated using ephemeral Nostr events
- **Auto-provisioning**: Automatically creates git repositories from NIP-34 announcements
- **Multi-remote Sync**: Automatically syncs repositories to multiple remotes listed in announcements
- **GRASP Interoperability**: Minimal GRASP support for seamless compatibility with GRASP servers
- **Repository Size Limits**: Enforces 2 GB maximum repository size
- **Relay Write Proof**: Verifies users can write to at least one default Nostr relay before allowing operations
### Repository Management
- **Repository Ownership Transfer**: Transfer ownership using kind 1641 events with a chain of ownership
- **Private Repositories**: Mark repositories as private, limiting access to owners and maintainers
- **Maintainer Management**: Add/remove maintainers who can push to repositories
- **Forking**: Fork repositories with automatic announcement creation and ownership setup
- **Repository Settings**: Manage privacy, maintainers, and description via web UI
- **Branch Protection**: Protect branches from direct pushes, require pull requests and reviews
### Collaboration Features
- **Issues**: Create and manage issues (kind 1621) with status tracking
- **Pull Requests**: Create pull requests (kind 1618) with status management
- **Highlights & Comments**:
- NIP-84 highlights (kind 9802) for code selections
- NIP-22 comments (kind 1111) for threaded discussions
- Comment on PRs, issues, and code highlights
- **Status Events**: Track issue/PR status (open, applied/merged, closed, draft)
### Web Interface
- **Repository Browser**: Browse files, directories, and commit history
- **Code Editor**: Edit files directly in the browser with syntax highlighting
- **Branch Management**: Create, switch, and manage branches
- **Tag Management**: Create and view git tags
- **README Rendering**: Automatic markdown rendering for README files
- **Search**: Search repositories by name, description, or author
- **User Profiles**: View user repositories and activity with full profile event support
- Supports both old JSON format (in content) and new tags format
- Displays payment targets (NIP-A3 kind 10133) with payto:// URIs
- Merges and deduplicates lightning addresses from NIP-01 (lud16) and kind 10133
- **Raw File View**: Direct access to raw file content
- **Download Repository**: Download repositories as ZIP archives
- **OpenGraph Metadata**: Rich social media previews with repository images and banners
### Security & Validation
- **Path Traversal Protection**: Validates and sanitizes file paths
- **Input Validation**: Validates commit messages, author names, emails, and file paths
- **File Size Limits**: 500 MB maximum per file (allows for images and demo videos)
- **Ownership Verification**: Verifies repository ownership via self-transfer events or verification files
- **Commit Signing**: Sign commits using Nostr private keys
- Signatures embedded in commit messages as trailers
- **Web UI**: Uses NIP-07 browser extension (secure, keys never leave browser)
- **Git Operations**: Uses NIP-98 HTTP authentication (ephemeral signed events)
- ⚠ **Security Note**: Never send private keys (nsec) in API requests. Use NIP-07 for web UI or NIP-98 for git operations.
## Getting Started
### Prerequisites
- Node.js 18+
- Git with `git-http-backend` installed
- NIP-07 browser extension (for web UI) - [Alby](https://getalby.com/) or [nos2x](https://github.com/fiatjaf/nos2x) recommended
### Installation
#### For Web Server
```bash ```bash
# Clone the repository
git clone https://github.com/silberengel/gitrepublic-web.git git clone https://github.com/silberengel/gitrepublic-web.git
cd gitrepublic-web cd gitrepublic-web
# Install dependencies
npm install npm install
# Start development server
npm run dev npm run dev
``` ```
#### For CLI (Command Line) ### Production
```bash
# Install globally
npm install -g gitrepublic-cli
# Set your Nostr private key See deployment documentation in the [docs](./docs/) directory.
export NOSTRGIT_SECRET_KEY="nsec1..."
# Setup credential helper and commit hook ## API Access
gitrep-setup
```
### Quick Start All API endpoints are documented in OpenAPI format. Access the API documentation at:
- **Development**: `http://localhost:5173/api/openapi.json`
- **Production**: `https://your-domain.com/api/openapi.json`
#### Web Interface Use NIP-98 authentication for all write operations.
1. Start the server: `npm run dev`
2. Open browser: `http://localhost:5173`
3. Connect NIP-07 extension
4. Visit `/signup` to create your first repository
#### Command Line ## Server Administration
```bash
# Clone a repository
gitrep clone https://your-domain.com/api/git/npub1.../repo.git
# Make changes and push For server administrators and DevOps engineers:
git add .
git commit -m "Update README"
gitrep push origin main
```
#### API Access ### Deployment Modes
All API endpoints are documented in OpenAPI format at `/api/openapi.json`. Use NIP-98 authentication for all write operations.
Example: GitRepublic supports two deployment modes:
```bash
curl -X GET https://your-domain.com/api/repos/list
```
## How It Works - **Lightweight Mode** (default): Single container deployment with application-level security
- **Enterprise Mode**: Kubernetes-based multi-tenant deployment with complete isolation
### Repository Creation Flow
1. **User Creates Announcement**:
- **Web**: Visit `/signup` and connect NIP-07 extension
- **CLI**: Run `gitrep publish repo-announcement <repo-name>`
- **API**: POST to `/api/repos/[npub]/[repo]` with announcement event
- System automatically creates a self-transfer event (kind 1641) for initial ownership proof
- Both announcement and self-transfer are published to Nostr relays
2. **Auto-Provisioning**:
- Server polls Nostr relays for new repository announcements (kind 30617)
- When found, server:
- Creates a bare git repository at `/repos/{npub}/{repo-name}.git`
- Fetches the self-transfer event for ownership verification
- Creates initial commit with README.md and saves announcement/transfer events to `nostr/repo-events.jsonl` for offline papertrail
- If repository has `clone` tags pointing to other remotes, syncs from those remotes
3. **Repository Access**:
- Public repositories: Anyone can clone and view
- Private repositories: Only owners and maintainers can access
- Access is checked via NIP-98 authentication for git operations
### Git Operations Flow
1. **Clone/Fetch**:
- **CLI**: `gitrep clone https://{domain}/api/git/{npub}/{repo}.git`
- **Git**: `git clone https://{domain}/api/git/{npub}/{repo}.git`
- Server handles GET requests to `info/refs?service=git-upload-pack`
- For private repos, verifies NIP-98 authentication
- Proxies request to `git-http-backend` which serves the repository
2. **Push**:
- **CLI**: `gitrep push origin main` (automatic authentication)
- **Git**: `git push origin main` (requires credential helper setup)
- Before push, client creates a NIP-98 event (kind 27235) with:
- `u` tag: Request URL
- `method` tag: HTTP method (POST)
- `payload` tag: SHA256 hash of request body
- Client signs event and includes in `Authorization: Nostr {event}` header
- Server verifies:
- Event signature
- Event timestamp (within 60 seconds)
- URL and method match
- Payload hash matches request body
- Pubkey is current owner or maintainer
- Server checks repository size limit (2 GB)
- Server proxies to `git-http-backend`
- After successful push, server:
- Extracts other `clone` URLs from announcement
- Syncs to all other remotes using `git push --all`
### Ownership Transfer Flow
1. **Current Owner Initiates Transfer**:
- **Web**: Use transfer UI in repository settings
- **CLI**: `gitrep transfer <npub> <repo> <new-owner-npub>`
- **API**: POST to `/api/repos/[npub]/[repo]/transfer`
- Owner creates a kind 1641 event with:
- `a` tag: Repository identifier (`30617:{owner}:{repo}`)
- `p` tag: New owner pubkey
- `d` tag: Repository name
- Signs and publishes event to Nostr relays
- Transfer event is saved to repository in `nostr/repo-events.jsonl` for offline papertrail
2. **New Owner Completes Transfer**:
- New owner is notified when logging into GitRepublic web
- New owner publishes a new repository announcement (kind 30617) to complete the transfer
- New announcement is saved to repository for verification
3. **Server Processes Transfer**:
- Server fetches all ownership transfer events for repository
- Validates chain of ownership chronologically
- Updates current owner for all permission checks
- Maintainers remain valid (checked against current owner)
## Nostr Event Kinds Used
This project uses the following Nostr event kinds. For complete JSON examples and tag documentation, see [docs/NIP_COMPLIANCE.md](./docs/NIP_COMPLIANCE.md#complete-event-kind-reference).
### Standard NIP Event Kinds
- **30617** (`REPO_ANNOUNCEMENT`): Repository announcements (NIP-34)
- **30618** (`REPO_STATE`): Repository state announcements (NIP-34, optional)
- **1617** (`PATCH`): Git patches (NIP-34)
- **1618** (`PULL_REQUEST`): Pull request events (NIP-34)
- **1619** (`PULL_REQUEST_UPDATE`): Pull request updates (NIP-34)
- **1621** (`ISSUE`): Issue events (NIP-34)
- **1630** (`STATUS_OPEN`): Open status (NIP-34)
- **1631** (`STATUS_APPLIED`): Applied/merged status (NIP-34)
- **1632** (`STATUS_CLOSED`): Closed status (NIP-34)
- **1633** (`STATUS_DRAFT`): Draft status (NIP-34)
- **9802** (`HIGHLIGHT`): NIP-84 highlight events for code selections
- **1111** (`COMMENT`): NIP-22 comment events for threaded discussions
- **27235** (`NIP98_AUTH`): NIP-98 HTTP authentication events
- **3**: Contact list (NIP-02, for relay discovery)
- **10002**: Relay list metadata (NIP-65, for relay discovery)
- **24**: Public message (NIP-24, for relay write proof)
- **5**: Event deletion request (NIP-09)
- **10133**: Payment targets (NIP-A3, payto:// URI scheme)
- Replaceable event kind for payment and tip invocations
- Tags: `payto` (type, authority, optional extras)
- Supports lightning, bitcoin, nano, monero, ethereum, and other payment types
- See [docs/NIP-A3.md](./docs/NIP-A3.md) for complete documentation
### Custom Event Kinds
These are not part of any NIP but are used by this application:
- **1640** (`COMMIT_SIGNATURE`): Git commit signature events
- Used to cryptographically sign git commits using Nostr keys
- Tags: `commit` (hash), `author` (name, email), `message` (commit message), `e` (NIP-98 auth event reference, optional)
- See [docs/NIP_COMPLIANCE.md](./docs/NIP_COMPLIANCE.md#1640---commit_signature) for complete example
- **1641** (`OWNERSHIP_TRANSFER`): Repository ownership transfer events (non-replaceable)
- Transfers ownership from one pubkey to another
- Self-transfers (owner → owner) used for initial ownership proof
- Non-replaceable to maintain immutable chain of ownership
- Tags: `a` (repo identifier), `p` (new owner), `d` (repo name), `t` (self-transfer marker, optional)
- See [docs/NIP_COMPLIANCE.md](./docs/NIP_COMPLIANCE.md#1641---ownership_transfer) for complete example
- **30620** (`BRANCH_PROTECTION`): Branch protection rules (replaceable)
- Allows requiring pull requests, reviewers, status checks for protected branches
- Tags: `d` (repo name), `a` (repo identifier), `branch` (branch name and protection settings)
- See [docs/NIP_COMPLIANCE.md](./docs/NIP_COMPLIANCE.md#30620---branch_protection) for complete example
## Architecture
### Frontend
- **Framework**: SvelteKit + TypeScript
- **Authentication**: NIP-07 browser extension integration
- **Components**: Code editor, PR detail view, repository browser
### Backend
- **Git Server**: `git-http-backend` wrapper for git operations
- **Authentication**: NIP-98 HTTP authentication for git operations
- **Repository Management**: Automatic provisioning and syncing
- **Nostr Integration**: WebSocket client for relay communication
### Services
- **NostrClient**: WebSocket client for fetching and publishing Nostr events
- **RepoManager**: Server-side repository provisioning, syncing, and size management
- **FileManager**: File operations within git repositories with validation
- **CommitSigner**: Git commit signing using Nostr keys (supports nsec and hex formats)
- **OwnershipTransferService**: Manages repository ownership transfers
- **MaintainerService**: Checks maintainer permissions and privacy settings
- **HighlightsService**: Manages NIP-84 highlights and NIP-22 comments
- **RelayWriteProof**: Verifies user can write to Nostr relays
## Security Features
### Lightweight Mode (Single Container) - Default
- **Resource Limits**: Per-user repository count and disk quota limits
- **Rate Limiting**: Per-IP and per-user rate limiting for all operations
- **Audit Logging**: Comprehensive logging of all security-relevant events
- **Path Validation**: Strict path validation to prevent traversal attacks
- **git-http-backend Hardening**: Timeouts, process isolation, scoped access
- **Mode**: Set `ENTERPRISE_MODE=false` or leave unset (default)
### Enterprise Mode (Kubernetes) See [Deployment Modes Documentation](./docs/deployment-modes.md) for detailed comparison.
- **Process Isolation**: Container-per-tenant architecture
- **Network Isolation**: Kubernetes Network Policies
- **Resource Quotas**: Per-tenant CPU, memory, and storage limits
- **Separate Volumes**: Each tenant has their own PersistentVolume
- **Mode**: Set `ENTERPRISE_MODE=true` environment variable
- **Deployment**: See `k8s/ENTERPRISE_MODE.md` for setup instructions
See `docs/SECURITY.md` and `docs/SECURITY_IMPLEMENTATION.md` for detailed information. ### Enterprise Mode (Kubernetes)
## Environment Variables For production multi-tenant deployments with maximum security and isolation:
### Core Configuration - **[Enterprise Mode Setup Guide](./k8s/ENTERPRISE_MODE.md)** - Complete Kubernetes deployment guide
- `ENTERPRISE_MODE`: Enable enterprise mode with Kubernetes (default: `false`). When `true`, expects container-per-tenant architecture. See `k8s/ENTERPRISE_MODE.md` for details. - Container-per-tenant architecture
- `GIT_REPO_ROOT`: Path to store git repositories (default: `/repos`) - Network isolation with Kubernetes Network Policies
- `GIT_DOMAIN`: Domain for git repositories (default: `localhost:6543`) - Resource quotas and limits
- `NOSTR_RELAYS`: Comma-separated list of Nostr relays (default: `wss://theforest.nostr1.com`) - PersistentVolume per tenant
- `NOSTR_SEARCH_RELAYS`: Comma-separated list of Nostr relays for searching (default: includes multiple relays) - Ingress configuration
- Monitoring and troubleshooting
### Git Operations ### Configuration
- `NOSTRGIT_SECRET_KEY`: User's Nostr private key (nsec bech32 or hex) for git command-line operations via credential helper. Required for `git clone`, `git push`, and `git pull` operations from the command line. **Note**: Install via `npm install -g gitrepublic-cli` to use this.
### Tor Support Key environment variables for server configuration:
- `TOR_SOCKS_PROXY`: Tor SOCKS proxy address (format: `host:port`, default: `127.0.0.1:9050`). Set to empty string to disable Tor support. When configured, the server will automatically route `.onion` addresses through Tor for both Nostr relay connections and git operations.
- `TOR_ONION_ADDRESS`: Tor hidden service .onion address (optional). If not set, the server will attempt to read it from Tor's hostname file. When configured, every repository will automatically get a `.onion` clone URL in addition to the regular domain URL, making repositories accessible via Tor even if the server is only running on localhost.
### Security Configuration - `ENTERPRISE_MODE`: Enable enterprise mode (`true`/`false`)
- `GIT_REPO_ROOT`: Path to store git repositories (default: `/repos`)
- `GIT_DOMAIN`: Domain for git repositories (default: `localhost:6543`)
- `NOSTR_RELAYS`: Comma-separated Nostr relays
- `MAX_REPOS_PER_USER`: Maximum repositories per user (default: `100`) - `MAX_REPOS_PER_USER`: Maximum repositories per user (default: `100`)
- `MAX_DISK_QUOTA_PER_USER`: Maximum disk quota per user in bytes (default: `10737418240` = 10GB) - `MAX_DISK_QUOTA_PER_USER`: Maximum disk quota per user in bytes (default: `10737418240` = 10GB)
- `RATE_LIMIT_ENABLED`: Enable rate limiting (default: `true`) - `RATE_LIMIT_ENABLED`: Enable rate limiting (default: `true`)
- `AUDIT_LOGGING_ENABLED`: Enable audit logging (default: `true`) - `AUDIT_LOGGING_ENABLED`: Enable audit logging (default: `true`)
## Usage Examples See the [deployment modes documentation](./docs/deployment-modes.md) and [enterprise mode guide](./k8s/ENTERPRISE_MODE.md) for complete configuration details.
### Web Interface
#### Creating a Repository
1. Go to `/signup`
2. Connect your NIP-07 extension
3. Enter repository name and description
4. Optionally add clone URLs (your domain will be added automatically)
5. Optionally add images/banners for OpenGraph previews
6. Publish the announcement
The server will automatically provision the repository.
#### Viewing Repositories
- Go to `/` to see all public repositories
- Go to `/repos/{npub}/{repo}` to view a specific repository
- Go to `/users/{npub}` to view a user's repositories
- Go to `/search` to search for repositories
#### Managing Repositories
- **Settings**: Visit `/repos/{npub}/{repo}/settings` to manage privacy, maintainers, and description
- **Forking**: Click "Fork" button on repository page
- **Transfer Ownership**: Use the transfer UI in repository settings
### Command Line Interface
#### Cloning a Repository
```bash
# Using GitRepublic CLI (recommended)
gitrep clone https://{domain}/api/git/{npub}/{repo-name}.git
# Or using standard git (requires credential helper setup)
git clone https://{domain}/api/git/{npub}/{repo-name}.git
```
**Note**: Use `/api/git/` or `/repos/` paths to ensure proper detection by the commit signing hook. All three paths (`/api/git/`, `/repos/`, and root `/`) work for cloning, but `/api/git/` is recommended for best compatibility.
#### Pushing to a Repository
```bash
# Using GitRepublic CLI (automatic authentication)
gitrep push origin main
# Or using standard git (requires credential helper)
git push origin main
```
The credential helper will automatically generate NIP-98 authentication tokens for push operations. The commit signing hook will automatically sign commits for GitRepublic repositories.
#### API Commands
```bash
# List repositories
gitrep repos list
# Get repository details (includes clone URL reachability)
gitrep repos get <npub> <repo>
# Push to all remotes
gitrep push-all main
# Pull from all remotes and merge
gitrep pull-all --merge
# Publish repository announcement
gitrep publish repo-announcement <repo-name>
```
For complete CLI documentation, see [gitrepublic-cli/README.md](./gitrepublic-cli/README.md).
### API Access
All API endpoints are documented in OpenAPI format. Access the API documentation at:
- **Development**: `http://localhost:5173/api/openapi.json`
- **Production**: `https://your-domain.com/api/openapi.json`
#### Authentication
All write operations require NIP-98 HTTP authentication:
```bash
# Example: Create a file (requires NIP-98 auth)
curl -X POST https://your-domain.com/api/repos/{npub}/{repo}/file \
-H "Authorization: Nostr <base64-encoded-event-json>" \
-H "Content-Type: application/json" \
-d '{"path": "test.txt", "content": "Hello World"}'
```
The CLI handles authentication automatically. For manual API access, see the [NIP-98 specification](https://github.com/nostr-protocol/nips/blob/master/98.md).
## Development
### Prerequisites
- Node.js 18+
- Git with `git-http-backend` installed
- NIP-07 browser extension (for web UI)
### Setup
```bash
npm install # Installs dependencies for both web and CLI (workspace)
npm run dev
```
**Note**: This repository uses npm workspaces. The CLI (`gitrepublic-cli`) is included as a workspace package but can also be published independently. See `gitrepublic-cli/SYNC.md` for details on syncing to a separate repository.
### Project Structure
```
src/
├── lib/
│ ├── services/
│ │ ├── nostr/
│ │ │ ├── nostr-client.ts # WebSocket client for Nostr relays
│ │ │ ├── nip07-signer.ts # NIP-07 browser extension integration
│ │ │ ├── nip98-auth.ts # NIP-98 HTTP authentication
│ │ │ ├── repo-polling.ts # Auto-provision repos from announcements
│ │ │ ├── user-relays.ts # Fetch user's preferred relays
│ │ │ ├── ownership-transfer-service.ts # Repository ownership transfers
│ │ │ ├── maintainer-service.ts # Maintainer permission checks
│ │ │ ├── highlights-service.ts # NIP-84 highlights & NIP-22 comments
│ │ │ ├── relay-write-proof.ts # Relay write proof verification
│ │ │ ├── prs-service.ts # Pull request management
│ │ │ └── issues-service.ts # Issue management
│ │ └── git/
│ │ ├── repo-manager.ts # Repository provisioning & syncing
│ │ └── file-manager.ts # File operations with validation
│ ├── components/
│ │ ├── CodeEditor.svelte # Code editor with syntax highlighting
│ │ └── PRDetail.svelte # Pull request detail view
│ └── types/
│ └── nostr.ts # TypeScript types for Nostr events
├── routes/
│ ├── +page.svelte # Main page: list repositories
│ ├── signup/
│ │ └── +page.svelte # Create/update repo announcements
│ ├── repos/[npub]/[repo]/
│ │ ├── +page.svelte # Repository detail page
│ │ ├── +page.ts # OpenGraph metadata loader
│ │ └── settings/
│ │ └── +page.svelte # Repository settings UI
│ ├── users/[npub]/
│ │ └── +page.svelte # User profile page
│ ├── search/
│ │ └── +page.svelte # Search interface
│ └── api/
│ ├── git/[...path]/
│ │ └── +server.ts # Git HTTP backend API
│ └── repos/[npub]/[repo]/
│ ├── file/+server.ts # File read/write API
│ ├── tree/+server.ts # Directory listing API
│ ├── branches/+server.ts # Branch management API
│ ├── commits/+server.ts # Commit history API
│ ├── tags/+server.ts # Tag management API
│ ├── issues/+server.ts # Issues API
│ ├── prs/+server.ts # Pull requests API
│ ├── highlights/+server.ts # Highlights & comments API
│ ├── fork/+server.ts # Fork repository API
│ ├── readme/+server.ts # README fetching API
│ ├── raw/+server.ts # Raw file view API
│ ├── download/+server.ts # Download repository as ZIP
│ ├── settings/+server.ts # Repository settings API
│ ├── transfer/+server.ts # Ownership transfer API
│ └── verify/+server.ts # Ownership verification API
└── hooks.server.ts # Server initialization (starts polling)
```
## GRASP Support
GitRepublic provides **minimal GRASP (Git Repository Announcement and Synchronization Protocol) interoperability**:
- ✅ **GRASP Server Detection**: Automatically identifies GRASP servers from repository announcements ## License
- ✅ **Clone URL Reachability**: Tests and displays reachability status for all clone URLs
- ✅ **Multi-Remote Sync**: Syncs to all remotes (including GRASP servers) when you push
- ✅ **Local Pull Command**: `gitrep pull-all --merge` to fetch and merge from all remotes
- ✅ **Standard Git Operations**: Full compatibility with GRASP servers (clone, push, pull)
**What we don't support** (by design): MIT
- ❌ Full GRASP-01 server compliance (we're not a full GRASP server)
- ❌ GRASP-02 proactive sync (no server-side hourly pulls - user-controlled via CLI)
- ❌ GRASP-05 archive mode
## Additional Documentation ## Links
- [Architecture FAQ](./docs/ARCHITECTURE_FAQ.md) - Answers to common architecture questions - **Website**: https://gitcitadel.com
- [NIP Compliance](./docs/NIP_COMPLIANCE.md) - Complete event kind reference with JSON examples - **GitHub**: https://github.com/silberengel/gitrepublic-web
- [NIP-A3: Payment Targets](./docs/NIP-A3.md) - Payment targets (payto://) support and documentation - **CLI**: https://github.com/silberengel/gitrepublic-cli
- [Security Documentation](./docs/SECURITY.md) - Security features and considerations
- [CLI Documentation](./gitrepublic-cli/README.md) - Complete CLI usage guide
- [Enterprise Mode](./k8s/ENTERPRISE_MODE.md) - Kubernetes deployment guide
- [API Documentation](./src/routes/api/openapi.json) - OpenAPI specification

196
docs/01.md

@ -1,196 +0,0 @@
NIP-01
======
Basic protocol flow description
-------------------------------
`draft` `mandatory` `relay`
This NIP defines the basic protocol that should be implemented by everybody. New NIPs may add new optional (or mandatory) fields and messages and features to the structures and flows described here.
## Events and signatures
Each user has a keypair. Signatures, public key, and encodings are done according to the [Schnorr signatures standard for the curve `secp256k1`](https://bips.xyz/340).
The only object type that exists is the `event`, which has the following format on the wire:
```yaml
{
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <unix timestamp in seconds>,
"kind": <integer between 0 and 65535>,
"tags": [
[<arbitrary string>...],
// ...
],
"content": <arbitrary string>,
"sig": <64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
}
```
To obtain the `event.id`, we `sha256` the serialized event. The serialization is done over the UTF-8 JSON-serialized string (which is described below) of the following structure:
```
[
0,
<pubkey, as a lowercase hex string>,
<created_at, as a number>,
<kind, as a number>,
<tags, as an array of arrays of non-null strings>,
<content, as a string>
]
```
To prevent implementation differences from creating a different event ID for the same event, the following rules MUST be followed while serializing:
- UTF-8 should be used for encoding.
- Whitespace, line breaks or other unnecessary formatting should not be included in the output JSON.
- The following characters in the content field must be escaped as shown, and all other characters must be included verbatim:
- A line break (`0x0A`), use `\n`
- A double quote (`0x22`), use `\"`
- A backslash (`0x5C`), use `\\`
- A carriage return (`0x0D`), use `\r`
- A tab character (`0x09`), use `\t`
- A backspace, (`0x08`), use `\b`
- A form feed, (`0x0C`), use `\f`
### Tags
Each tag is an array of one or more strings, with some conventions around them. Take a look at the example below:
```jsonc
{
"tags": [
["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"],
["p", "f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca"],
["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"],
["alt", "reply"],
// ...
],
// ...
}
```
The first element of the tag array is referred to as the tag _name_ or _key_ and the second as the tag _value_. So we can safely say that the event above has an `e` tag set to `"5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"`, an `alt` tag set to `"reply"` and so on. All elements after the second do not have a conventional name.
This NIP defines 3 standard tags that can be used across all event kinds with the same meaning. They are as follows:
- The `e` tag, used to refer to an event: `["e", <32-bytes lowercase hex of the id of another event>, <recommended relay URL, optional>, <32-bytes lowercase hex of the author's pubkey, optional>]`
- The `p` tag, used to refer to another user: `["p", <32-bytes lowercase hex of a pubkey>, <recommended relay URL, optional>]`
- The `a` tag, used to refer to an addressable or replaceable event
- for an addressable event: `["a", "<kind integer>:<32-bytes lowercase hex of a pubkey>:<d tag value>", <recommended relay URL, optional>]`
- for a normal replaceable event: `["a", "<kind integer>:<32-bytes lowercase hex of a pubkey>:", <recommended relay URL, optional>]` (note: include the trailing colon)
As a convention, all single-letter (only english alphabet letters: a-z, A-Z) key tags are expected to be indexed by relays, such that it is possible, for example, to query or subscribe to events that reference the event `"5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"` by using the `{"#e": ["5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36"]}` filter. Only the first value in any given tag is indexed.
### Kinds
Kinds specify how clients should interpret the meaning of each event and the other fields of each event (e.g. an `"r"` tag may have a meaning in an event of kind 1 and an entirely different meaning in an event of kind 10002). Each NIP may define the meaning of a set of kinds that weren't defined elsewhere. [NIP-10](10.md), for instance, specifies the `kind:1` text note for social media applications.
This NIP defines one basic kind:
- `0`: **user metadata**: the `content` is set to a stringified JSON object `{name: <nickname or full name>, about: <short bio>, picture: <url of the image>}` describing the user who created the event. [Extra metadata fields](24.md#kind-0) may be set. A relay may delete older events once it gets a new one for the same pubkey.
And also a convention for kind ranges that allow for easier experimentation and flexibility of relay implementation:
- for kind `n` such that `1000 <= n < 10000 || 4 <= n < 45 || n == 1 || n == 2`, events are **regular**, which means they're all expected to be stored by relays.
- for kind `n` such that `10000 <= n < 20000 || n == 0 || n == 3`, events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event MUST be stored by relays, older versions MAY be discarded.
- for kind `n` such that `20000 <= n < 30000`, events are **ephemeral**, which means they are not expected to be stored by relays.
- for kind `n` such that `30000 <= n < 40000`, events are **addressable** by their `kind`, `pubkey` and `d` tag value -- which means that, for each combination of `kind`, `pubkey` and the `d` tag value, only the latest event MUST be stored by relays, older versions MAY be discarded.
In case of replaceable events with the same timestamp, the event with the lowest id (first in lexical order) should be retained, and the other discarded.
When answering to `REQ` messages for replaceable events such as `{"kinds":[0],"authors":[<hex-key>]}`, even if the relay has more than one version stored, it SHOULD return just the latest one.
These are just conventions and relay implementations may differ.
## Communication between clients and relays
Relays expose a websocket endpoint to which clients can connect. Clients SHOULD open a single websocket connection to each relay and use it for all their subscriptions. Relays MAY limit number of connections from specific IP/client/etc.
### From client to relay: sending events and creating subscriptions
Clients can send 3 types of messages, which must be JSON arrays, according to the following patterns:
* `["EVENT", <event JSON as defined above>]`, used to publish events.
* `["REQ", <subscription_id>, <filters1>, <filters2>, ...]`, used to request events and subscribe to new updates.
* `["CLOSE", <subscription_id>]`, used to stop previous subscriptions.
`<subscription_id>` is an arbitrary, non-empty string of max length 64 chars. It represents a subscription per connection. Relays MUST manage `<subscription_id>`s independently for each WebSocket connection. `<subscription_id>`s are not guaranteed to be globally unique.
`<filtersX>` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
```yaml
{
"ids": <a list of event ids>,
"authors": <a list of lowercase pubkeys, the pubkey of an event must be one of these>,
"kinds": <a list of a kind numbers>,
"#<single-letter (a-zA-Z)>": <a list of tag values, for #e a list of event ids, for #p a list of pubkeys, etc.>,
"since": <an integer unix timestamp in seconds. Events must have a created_at >= to this to pass>,
"until": <an integer unix timestamp in seconds. Events must have a created_at <= to this to pass>,
"limit": <maximum number of events relays SHOULD return in the initial query>
}
```
Upon receiving a `REQ` message, the relay SHOULD return events that match the filter. Any new events it receives SHOULD be sent to that same websocket until the connection is closed, a `CLOSE` event is received with the same `<subscription_id>`, or a new `REQ` is sent using the same `<subscription_id>` (in which case a new subscription is created, replacing the old one).
Filter attributes containing lists (`ids`, `authors`, `kinds` and tag filters like `#e`) are JSON arrays with one or more values. At least one of the arrays' values must match the relevant field in an event for the condition to be considered a match. For scalar event attributes such as `authors` and `kind`, the attribute from the event must be contained in the filter list. In the case of tag attributes such as `#e`, for which an event may have multiple values, the event and filter condition values must have at least one item in common.
The `ids`, `authors`, `#e` and `#p` filter lists MUST contain exact 64-character lowercase hex values.
The `since` and `until` properties can be used to specify the time range of events returned in the subscription. If a filter includes the `since` property, events with `created_at` greater than or equal to `since` are considered to match the filter. The `until` property is similar except that `created_at` must be less than or equal to `until`. In short, an event matches a filter if `since <= created_at <= until` holds.
All conditions of a filter that are specified must match for an event for it to pass the filter, i.e., multiple conditions are interpreted as `&&` conditions.
A `REQ` message may contain multiple filters. In this case, events that match any of the filters are to be returned, i.e., multiple filters are to be interpreted as `||` conditions.
The `limit` property of a filter is only valid for the initial query and MUST be ignored afterwards. When `limit: n` is present it is assumed that the events returned in the initial query will be the last `n` events ordered by the `created_at`. Newer events should appear first, and in the case of ties the event with the lowest id (first in lexical order) should be first. Relays SHOULD use the `limit` value to guide how many events are returned in the initial response. Returning fewer events is acceptable, but returning (much) more should be avoided to prevent overwhelming clients.
### From relay to client: sending events and notices
Relays can send 5 types of messages, which must also be JSON arrays, according to the following patterns:
* `["EVENT", <subscription_id>, <event JSON as defined above>]`, used to send events requested by clients.
* `["OK", <event_id>, <true|false>, <message>]`, used to indicate acceptance or denial of an `EVENT` message.
* `["EOSE", <subscription_id>]`, used to indicate the _end of stored events_ and the beginning of events newly received in real-time.
* `["CLOSED", <subscription_id>, <message>]`, used to indicate that a subscription was ended on the server side.
* `["NOTICE", <message>]`, used to send human-readable error messages or other things to clients.
This NIP defines no rules for how `NOTICE` messages should be sent or treated.
- `EVENT` messages MUST be sent only with a subscription ID related to a subscription previously initiated by the client (using the `REQ` message above).
- `OK` messages MUST be sent in response to `EVENT` messages received from clients, they must have the 3rd parameter set to `true` when an event has been accepted by the relay, `false` otherwise. The 4th parameter MUST always be present, but MAY be an empty string when the 3rd is `true`, otherwise it MUST be a string formed by a machine-readable single-word prefix followed by a `:` and then a human-readable message. Some examples:
* `["OK", "b1a649ebe8...", true, ""]`
* `["OK", "b1a649ebe8...", true, "pow: difficulty 25>=24"]`
* `["OK", "b1a649ebe8...", true, "duplicate: already have this event"]`
* `["OK", "b1a649ebe8...", false, "blocked: you are banned from posting here"]`
* `["OK", "b1a649ebe8...", false, "blocked: please register your pubkey at https://my-expensive-relay.example.com"]`
* `["OK", "b1a649ebe8...", false, "rate-limited: slow down there chief"]`
* `["OK", "b1a649ebe8...", false, "invalid: event creation date is too far off from the current time"]`
* `["OK", "b1a649ebe8...", false, "pow: difficulty 26 is less than 30"]`
* `["OK", "b1a649ebe8...", false, "restricted: not allowed to write."]`
* `["OK", "b1a649ebe8...", false, "error: could not connect to the database"]`
* `["OK", "b1a649ebe8...", false, "mute: no one was listening to your ephemeral event and it wasn't handled in any way, it was ignored"]`
- `CLOSED` messages MUST be sent in response to a `REQ` when the relay refuses to fulfill it. It can also be sent when a relay decides to kill a subscription on its side before a client has disconnected or sent a `CLOSE`. This message uses the same pattern of `OK` messages with the machine-readable prefix and human-readable message. Some examples:
* `["CLOSED", "sub1", "unsupported: filter contains unknown elements"]`
* `["CLOSED", "sub1", "error: could not connect to the database"]`
* `["CLOSED", "sub1", "error: shutting down idle subscription"]`
- The standardized machine-readable prefixes for `OK` and `CLOSED` are: `duplicate`, `pow`, `blocked`, `rate-limited`, `invalid`, `restricted`, `mute` and `error` for when none of that fits.
## GitRepublic Usage
GitRepublic uses NIP-01 as the foundation for all Nostr event handling. All events follow the standard event structure defined in NIP-01.
### Event Structure
All repository-related events (announcements, PRs, issues, patches, etc.) follow the NIP-01 event format:
- Events are serialized according to NIP-01 rules (UTF-8, no whitespace, proper escaping)
- Event IDs are computed as SHA256 of the serialized event
- Signatures use Schnorr signatures on secp256k1
- The `nostr-tools` library is used for event serialization, ID computation, and signature verification
### Kind 24 (Public Message) Usage for Relay Write Proof
GitRepublic uses kind 24 (public message) events for relay write proofs when NIP-98 authentication events are not available. This ensures git operations can still be authenticated even if the NIP-98 flow fails.
**Implementation**: `src/lib/services/nostr/nostr-client.ts`, `src/lib/types/nostr.ts`

173
docs/02.md

@ -1,173 +0,0 @@
NIP-02
======
Follow List
-----------
`final` `optional`
A special event with kind `3`, meaning "follow list" is defined as having a list of `p` tags, one for each of the followed/known profiles one is following.
Each tag entry should contain the key for the profile, a relay URL where events from that key can be found (can be set to an empty string if not needed), and a local name (or "petname") for that profile (can also be set to an empty string or not provided), i.e., `["p", <32-bytes hex key>, <main relay URL>, <petname>]`.
The `.content` is not used.
For example:
```jsonc
{
"kind": 3,
"tags": [
["p", "91cf9..4e5ca", "wss://alicerelay.com/", "alice"],
["p", "14aeb..8dad4", "wss://bobrelay.com/nostr", "bob"],
["p", "612ae..e610f", "ws://carolrelay.com/ws", "carol"]
],
"content": "",
// other fields...
}
```
Every new following list that gets published overwrites the past ones, so it should contain all entries. Relays and clients SHOULD delete past following lists as soon as they receive a new one.
Whenever new follows are added to an existing list, clients SHOULD append them to the end of the list, so they are stored in chronological order.
## Uses
### Follow list backup
If one believes a relay will store their events for sufficient time, they can use this kind-3 event to backup their following list and recover on a different device.
### Profile discovery and context augmentation
A client may rely on the kind-3 event to display a list of followed people by profiles one is browsing; make lists of suggestions on who to follow based on the follow lists of other people one might be following or browsing; or show the data in other contexts.
### Relay sharing
A client may publish a follow list with good relays for each of their follows so other clients may use these to update their internal relay lists if needed, increasing censorship-resistance.
### Petname scheme
The data from these follow lists can be used by clients to construct local ["petname"](http://www.skyhunter.com/marcs/petnames/IntroPetNames.html) tables derived from other people's follow lists. This alleviates the need for global human-readable names. For example:
A user has an internal follow list that says
```json
[
["p", "21df6d143fb96c2ec9d63726bf9edc71", "", "erin"]
]
```
And receives two follow lists, one from `21df6d143fb96c2ec9d63726bf9edc71` that says
```json
[
["p", "a8bb3d884d5d90b413d9891fe4c4e46d", "", "david"]
]
```
and another from `a8bb3d884d5d90b413d9891fe4c4e46d` that says
```json
[
["p", "f57f54057d2a7af0efecc8b0b66f5708", "", "frank"]
]
```
When the user sees `21df6d143fb96c2ec9d63726bf9edc71` the client can show _erin_ instead;
When the user sees `a8bb3d884d5d90b413d9891fe4c4e46d` the client can show _david.erin_ instead;
When the user sees `f57f54057d2a7af0efecc8b0b66f5708` the client can show _frank.david.erin_ instead.
## GitRepublic Usage
GitRepublic uses KIND 3 (Contact List) events to enable repository filtering based on social connections.
### Repository Filtering
When a user enables the "Show only my repos and those of my contacts" filter on the landing page, GitRepublic:
1. **Fetches the user's contact list**: Retrieves the latest KIND 3 event published by the logged-in user
2. **Extracts contact pubkeys**: Parses all `p` tags from the contact list event to build a set of followed pubkeys
3. **Includes the user's own pubkey**: Automatically adds the logged-in user's pubkey to the filter set
4. **Filters repositories**: Shows only repositories where:
- The repository owner (event `pubkey`) is in the contact set, OR
- Any maintainer (from `maintainers` tags) is in the contact set
### Implementation Details
- **Pubkey normalization**: GitRepublic handles both hex-encoded pubkeys and bech32-encoded npubs in contact lists
- **Maintainer matching**: The filter checks all maintainers listed in repository announcement `maintainers` tags
- **Real-time updates**: The contact list is fetched when the user logs in and can be refreshed by reloading the page
### Example Use Case
A user follows several developers on Nostr. By enabling the contact filter:
- They see repositories owned by people they follow
- They see repositories where their contacts are maintainers
- They see their own repositories
- They don't see repositories from people they don't follow
This creates a personalized, social discovery experience for finding relevant code repositories based on trust relationships established through Nostr's follow mechanism.
### Technical Implementation
```typescript
// Fetch user's kind 3 contact list
const contactEvents = await nostrClient.fetchEvents([
{
kinds: [KIND.CONTACT_LIST], // KIND 3
authors: [userPubkey],
limit: 1
}
]);
// Extract pubkeys from 'p' tags
const contactPubkeys = new Set<string>();
contactPubkeys.add(userPubkey); // Include user's own repos
if (contactEvents.length > 0) {
const contactEvent = contactEvents[0];
for (const tag of contactEvent.tags) {
if (tag[0] === 'p' && tag[1]) {
let pubkey = tag[1];
// Decode npub if needed
try {
const decoded = nip19.decode(pubkey);
if (decoded.type === 'npub') {
pubkey = decoded.data as string;
}
} catch {
// Assume it's already a hex pubkey
}
contactPubkeys.add(pubkey);
}
}
}
// Filter repositories
const filteredRepos = allRepos.filter(event => {
// Check if owner is in contacts
if (contactPubkeys.has(event.pubkey)) return true;
// Check if any maintainer is in contacts
const maintainerTags = event.tags.filter(t => t[0] === 'maintainers');
for (const tag of maintainerTags) {
for (let i = 1; i < tag.length; i++) {
let maintainerPubkey = tag[i];
// Decode npub if needed
try {
const decoded = nip19.decode(maintainerPubkey);
if (decoded.type === 'npub') {
maintainerPubkey = decoded.data as string;
}
} catch {
// Assume it's already a hex pubkey
}
if (contactPubkeys.has(maintainerPubkey)) return true;
}
}
return false;
});
```
This implementation provides a seamless way for users to discover repositories from their social network while maintaining the decentralized, trustless nature of Nostr.

58
docs/07.md

@ -1,58 +0,0 @@
NIP-07
======
`window.nostr` capability for web browsers
------------------------------------------
`draft` `optional`
The `window.nostr` object may be made available by web browsers or extensions and websites or web-apps may make use of it after checking its availability.
That object must define the following methods:
```
async window.nostr.getPublicKey(): string // returns a public key as hex
async window.nostr.signEvent(event: { created_at: number, kind: number, tags: string[][], content: string }): Event // takes an event object, adds `id`, `pubkey` and `sig` and returns it
```
Aside from these two basic above, the following functions can also be implemented optionally:
```
async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip44.encrypt(pubkey, plaintext): string // returns ciphertext as specified in nip-44
async window.nostr.nip44.decrypt(pubkey, ciphertext): string // takes ciphertext as specified in nip-44
```
### Recommendation to Extension Authors
To make sure that the `window.nostr` is available to nostr clients on page load, the authors who create Chromium and Firefox extensions should load their scripts by specifying `"run_at": "document_end"` in the extension's manifest.
### Implementation
See https://github.com/aljazceru/awesome-nostr#nip-07-browser-extensions.
## GitRepublic Usage
NIP-07 is the primary authentication method for GitRepublic. All user interactions that require signing events use the NIP-07 browser extension interface.
### Authentication Flow
1. **Availability Check**: GitRepublic checks for `window.nostr` availability on page load
2. **Public Key Retrieval**: When users need to authenticate, `getPublicKey()` is called to get their pubkey
3. **Event Signing**: All repository announcements, PRs, issues, and other events are signed using `signEvent()`
### Key Features
- **Repository Creation**: Users sign repository announcement events (kind 30617) using NIP-07
- **Repository Updates**: Settings changes, maintainer additions, and other updates are signed via NIP-07
- **Pull Requests**: PR creation and updates are signed by the PR author
- **Issues**: Issue creation and comments are signed by the author
- **Commit Signatures**: Git commits can be signed using NIP-07 (client-side only, keys never leave browser)
### Security
- Keys never leave the browser - all signing happens client-side
- No server-side key storage required
- Users maintain full control of their private keys
**Implementation**: `src/lib/services/nostr/nip07-signer.ts`

76
docs/09.md

@ -1,76 +0,0 @@
NIP-09
======
Event Deletion Request
----------------------
`draft` `optional` `relay`
A special event with kind `5`, meaning "deletion request" is defined as having a list of one or more `e` or `a` tags, each referencing an event the author is requesting to be deleted. Deletion requests SHOULD include a `k` tag for the kind of each event being requested for deletion.
The event's `content` field MAY contain a text note describing the reason for the deletion request.
For example:
```jsonc
{
"kind": 5,
"pubkey": <32-bytes hex-encoded public key of the event creator>,
"tags": [
["e", "dcd59..464a2"],
["e", "968c5..ad7a4"],
["a", "<kind>:<pubkey>:<d-identifier>"],
["k", "1"],
["k", "30023"]
],
"content": "these posts were published by accident",
// other fields...
}
```
Relays SHOULD delete or stop publishing any referenced events that have an identical `pubkey` as the deletion request. Clients SHOULD hide or otherwise indicate a deletion request status for referenced events.
Relays SHOULD continue to publish/share the deletion request events indefinitely, as clients may already have the event that's intended to be deleted. Additionally, clients SHOULD broadcast deletion request events to other relays which don't have it.
When an `a` tag is used, relays SHOULD delete all versions of the replaceable event up to the `created_at` timestamp of the deletion request event.
## Client Usage
Clients MAY choose to fully hide any events that are referenced by valid deletion request events. This includes text notes, direct messages, or other yet-to-be defined event kinds. Alternatively, they MAY show the event along with an icon or other indication that the author has "disowned" the event. The `content` field MAY also be used to replace the deleted events' own content, although a user interface should clearly indicate that this is a deletion request reason, not the original content.
A client MUST validate that each event `pubkey` referenced in the `e` tag of the deletion request is identical to the deletion request `pubkey`, before hiding or deleting any event. Relays can not, in general, perform this validation and should not be treated as authoritative.
Clients display the deletion request event itself in any way they choose, e.g., not at all, or with a prominent notice.
Clients MAY choose to inform the user that their request for deletion does not guarantee deletion because it is impossible to delete events from all relays and clients.
## Relay Usage
Relays MAY validate that a deletion request event only references events that have the same `pubkey` as the deletion request itself, however this is not required since relays may not have knowledge of all referenced events.
## Deletion Request of a Deletion Request
Publishing a deletion request event against a deletion request has no effect. Clients and relays are not obliged to support "unrequest deletion" functionality.
## GitRepublic Usage
GitRepublic uses NIP-09 deletion requests for repository fork failures and other error scenarios where an event needs to be removed.
### Fork Failure Deletion
When a repository fork operation fails (e.g., ownership transfer event cannot be published), GitRepublic creates a deletion request to remove the failed fork announcement:
```typescript
{
kind: 5,
tags: [
['a', `30617:${userPubkeyHex}:${forkRepoName}`],
['k', KIND.REPO_ANNOUNCEMENT.toString()]
],
content: 'Fork failed: ownership transfer event could not be published...'
}
```
This ensures that failed fork attempts don't leave orphaned repository announcements in the system.
**Implementation**: `src/routes/api/repos/[npub]/[repo]/fork/+server.ts`

107
docs/10.md

@ -1,107 +0,0 @@
NIP-10
======
Text Notes and Threads
----------------------
`draft` `optional`
This NIP defines `kind:1` as a simple plaintext note.
## Abstract
The `.content` property contains some human-readable text.
`e` tags can be used to define note thread roots and replies. They SHOULD be sorted by the reply stack from root to the direct parent.
`q` tags MAY be used when citing events in the `.content` with [NIP-21](21.md).
```json
["q", "<event-id> or <event-address>", "<relay-url>", "<pubkey-if-a-regular-event>"]
```
Authors of the `e` and `q` tags SHOULD be added as `p` tags to notify of a new reply or quote.
Markup languages such as markdown and HTML SHOULD NOT be used.
## Marked "e" tags (PREFERRED)
Kind 1 events with `e` tags are replies to other kind 1 events. Kind 1 replies MUST NOT be used to reply to other kinds, use [NIP-22](22.md) instead.
`["e", <event-id>, <relay-url>, <marker>, <pubkey>]`
Where:
* `<event-id>` is the id of the event being referenced.
* `<relay-url>` is the URL of a recommended relay associated with the reference. Clients SHOULD add a valid `<relay-url>` field, but may instead leave it as `""`.
* `<marker>` is optional and if present is one of `"reply"`, `"root"`.
* `<pubkey>` is optional, SHOULD be the pubkey of the author of the referenced event
Those marked with `"reply"` denote the id of the reply event being responded to. Those marked with `"root"` denote the root id of the reply thread being responded to. For top level replies (those replying directly to the root event), only the `"root"` marker should be used.
A direct reply to the root of a thread should have a single marked "e" tag of type "root".
>This scheme is preferred because it allows events to mention others without confusing them with `<reply-id>` or `<root-id>`.
`<pubkey>` SHOULD be the pubkey of the author of the `e` tagged event, this is used in the outbox model to search for that event from the authors write relays where relay hints did not resolve the event.
## The "p" tag
Used in a text event contains a list of pubkeys used to record who is involved in a reply thread.
When replying to a text event E the reply event's "p" tags should contain all of E's "p" tags as well as the `"pubkey"` of the event being replied to.
Example: Given a text event authored by `a1` with "p" tags [`p1`, `p2`, `p3`] then the "p" tags of the reply should be [`a1`, `p1`, `p2`, `p3`]
in no particular order.
## Deprecated Positional "e" tags
This scheme is not in common use anymore and is here just to keep backward compatibility with older events on the network.
Positional `e` tags are deprecated because they create ambiguities that are difficult, or impossible to resolve when an event references another but is not a reply.
They use simple `e` tags without any marker.
`["e", <event-id>, <relay-url>]` as per NIP-01.
Where:
* `<event-id>` is the id of the event being referenced.
* `<relay-url>` is the URL of a recommended relay associated with the reference. Many clients treat this field as optional.
**The positions of the "e" tags within the event denote specific meanings as follows**:
* No "e" tag: <br>
This event is not a reply to, nor does it refer to, any other event.
* One "e" tag: <br>
`["e", <id>]`: The id of the event to which this event is a reply.
* Two "e" tags: `["e", <root-id>]`, `["e", <reply-id>]` <br>
`<root-id>` is the id of the event at the root of the reply chain. `<reply-id>` is the id of the article to which this event is a reply.
* Many "e" tags: `["e", <root-id>]` `["e", <mention-id>]`, ..., `["e", <reply-id>]`<br>
There may be any number of `<mention-ids>`. These are the ids of events which may, or may not be in the reply chain.
They are citing from this event. `root-id` and `reply-id` are as above.
## GitRepublic Usage
GitRepublic uses NIP-10 event references primarily in patch events (kind 1617) to create patch series. Patches in a patch set use `e` tags with `reply` markers to link to previous patches in the series.
### Patch Series Threading
When multiple patches are sent as part of a patch series, each patch after the first includes an `e` tag with a `reply` marker pointing to the previous patch:
```jsonc
{
"kind": 1617,
"tags": [
["a", "30617:owner:repo"],
["e", "previous_patch_event_id", "wss://relay.example.com", "reply"],
// ... other tags
]
}
```
This creates a threaded chain of patches that can be applied in sequence.
**Implementation**: Used in patch creation and processing throughout the codebase

95
docs/19.md

@ -1,95 +0,0 @@
NIP-19
======
bech32-encoded entities
-----------------------
`draft` `optional`
This NIP standardizes bech32-formatted strings that can be used to display keys, ids and other information in clients. These formats are not meant to be used anywhere in the core protocol, they are only meant for displaying to users, copy-pasting, sharing, rendering QR codes and inputting data.
It is recommended that ids and keys are stored in either hex or binary format, since these formats are closer to what must actually be used the core protocol.
## Bare keys and ids
To prevent confusion and mixing between private keys, public keys and event ids, which are all 32 byte strings. bech32-(not-m) encoding with different prefixes can be used for each of these entities.
These are the possible bech32 prefixes:
- `npub`: public keys
- `nsec`: private keys
- `note`: note ids
Example: the hex public key `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d` translates to `npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6`.
The bech32 encodings of keys and ids are not meant to be used inside the standard NIP-01 event formats or inside the filters, they're meant for human-friendlier display and input only. Clients should still accept keys in both hex and npub format for now, and convert internally.
## Shareable identifiers with extra metadata
When sharing a profile or an event, an app may decide to include relay information and other metadata such that other apps can locate and display these entities more easily.
For these events, the contents are a binary-encoded list of `TLV` (type-length-value), with `T` and `L` being 1 byte each (`uint8`, i.e. a number in the range of 0-255), and `V` being a sequence of bytes of the size indicated by `L`.
These are the possible bech32 prefixes with `TLV`:
- `nprofile`: a nostr profile
- `nevent`: a nostr event
- `naddr`: a nostr _addressable event_ coordinate
- `nrelay`: a nostr relay (deprecated)
These possible standardized `TLV` types are indicated here:
- `0`: `special`
- depends on the bech32 prefix:
- for `nprofile` it will be the 32 bytes of the profile public key
- for `nevent` it will be the 32 bytes of the event id
- for `naddr`, it is the identifier (the `"d"` tag) of the event being referenced. For normal replaceable events use an empty string.
- `1`: `relay`
- for `nprofile`, `nevent` and `naddr`, _optionally_, a relay in which the entity (profile or event) is more likely to be found, encoded as ascii
- this may be included multiple times
- `2`: `author`
- for `naddr`, the 32 bytes of the pubkey of the event
- for `nevent`, _optionally_, the 32 bytes of the pubkey of the event
- `3`: `kind`
- for `naddr`, the 32-bit unsigned integer of the kind, big-endian
- for `nevent`, _optionally_, the 32-bit unsigned integer of the kind, big-endian
## Examples
- `npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg` should decode into the public key hex `7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e` and vice-versa
- `nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5` should decode into the private key hex `67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa` and vice-versa
- `nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p` should decode into a profile with the following TLV items:
- pubkey: `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d`
- relay: `wss://r.x.com`
- relay: `wss://djbas.sadkb.com`
## Notes
- `npub` keys MUST NOT be used in NIP-01 events or in NIP-05 JSON responses, only the hex format is supported there.
- When decoding a bech32-formatted string, TLVs that are not recognized or supported should be ignored, rather than causing an error.
## GitRepublic Usage
GitRepublic extensively uses NIP-19 bech32 encoding for user-friendly display and input of Nostr entities throughout the application.
### User Interface
- **Repository URLs**: Repositories are accessed via URLs like `/repos/{npub}/{repo-name}` where `npub` is the bech32-encoded public key
- **User Profiles**: User profile pages use npub in URLs: `/users/{npub}`
- **Search**: Users can search for repositories using npub, naddr, or nevent formats
- **Repository References**: The "Load Existing Repository" feature accepts hex event IDs, nevent, or naddr formats
### Internal Handling
- **Conversion**: All npub values are converted to hex format internally for event creation and filtering
- **Maintainers**: Maintainer lists accept both npub and hex formats, converting as needed
- **Contact Lists**: When processing kind 3 contact lists, GitRepublic handles both npub and hex pubkey formats
### Search Support
The repository search feature can decode and search by:
- **npub**: User public keys (converted to hex for filtering)
- **naddr**: Repository announcement addresses (decoded to extract pubkey, kind, and d-tag)
- **nevent**: Event references (decoded to extract event ID)
**Implementation**: `src/lib/services/nostr/nip19-utils.ts`, used throughout the codebase for encoding/decoding

237
docs/22.md

@ -1,237 +0,0 @@
NIP-22
======
Comment
-------
`draft` `optional`
A comment is a threading note always scoped to a root event or an [`I`-tag](73.md).
It uses `kind:1111` with plaintext `.content` (no HTML, Markdown, or other formatting).
Comments MUST point to the root scope using uppercase tag names (e.g. `K`, `E`, `A` or `I`)
and MUST point to the parent item with lowercase ones (e.g. `k`, `e`, `a` or `i`).
Comments MUST point to the authors when one is available (i.e. tagging a nostr event). `P` for the root scope
and `p` for the author of the parent item.
```jsonc
{
"kind": 1111,
"content": "<comment>",
"tags": [
// root scope: event addresses, event ids, or I-tags.
["<A, E, I>", "<address, id or I-value>", "<relay or web page hint>", "<root event's pubkey, if an E tag>"],
// the root item kind
["K", "<root kind>"],
// pubkey of the author of the root scope event
["P", "<root-pubkey>", "relay-url-hint"],
// parent item: event addresses, event ids, or i-tags.
["<a, e, i>", "<address, id or i-value>", "<relay or web page hint>", "<parent event's pubkey, if an e tag>"],
// parent item kind
["k", "<parent comment kind>"],
// parent item pubkey
["p", "<parent-pubkey>", "relay-url-hint"]
]
// other fields
}
```
Tags `K` and `k` MUST be present to define the event kind of the root and the parent items.
`I` and `i` tags create scopes for hashtags, geohashes, URLs, and other external identifiers.
The possible values for `i` tags – and `k` tags, when related to an external identity – are listed on [NIP-73](73.md).
Their uppercase versions use the same type of values but relate to the root item instead of the parent one.
`q` tags MAY be used when citing events in the `.content` with [NIP-21](21.md).
```json
["q", "<event-id> or <event-address>", "<relay-url>", "<pubkey-if-a-regular-event>"]
```
`p` tags SHOULD be used when mentioning pubkeys in the `.content` with [NIP-21](21.md).
Comments MUST NOT be used to reply to kind 1 notes. [NIP-10](10.md) should instead be followed.
## Examples
A comment on a blog post looks like this:
```jsonc
{
"kind": 1111,
"content": "Great blog post!",
"tags": [
// top-level comments scope to event addresses or ids
["A", "30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "wss://example.relay"],
// the root kind
["K", "30023"],
// author of root event
["P", "3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289", "wss://example.relay"]
// the parent event address (same as root for top-level comments)
["a", "30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "wss://example.relay"],
// when the parent event is replaceable or addressable, also include an `e` tag referencing its id
["e", "5b4fc7fed15672fefe65d2426f67197b71ccc82aa0cc8a9e94f683eb78e07651", "wss://example.relay"],
// the parent event kind
["k", "30023"],
// author of the parent event
["p", "3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289", "wss://example.relay"]
]
// other fields
}
```
A comment on a [NIP-94](94.md) file looks like this:
```jsonc
{
"kind": 1111,
"content": "Great file!",
"tags": [
// top-level comments have the same scope and reply to addresses or ids
["E", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"],
// the root kind
["K", "1063"],
// author of the root event
["P", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"],
// the parent event id (same as root for top-level comments)
["e", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"],
// the parent kind
["k", "1063"],
["p", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"]
]
// other fields
}
```
A reply to a comment looks like this:
```jsonc
{
"kind": 1111,
"content": "This is a reply to \"Great file!\"",
"tags": [
// nip-94 file event id
["E", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "fd913cd6fa9edb8405750cd02a8bbe16e158b8676c0e69fdc27436cc4a54cc9a"],
// the root kind
["K", "1063"],
["P", "fd913cd6fa9edb8405750cd02a8bbe16e158b8676c0e69fdc27436cc4a54cc9a"],
// the parent event
["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://example.relay", "93ef2ebaaf9554661f33e79949007900bbc535d239a4c801c33a4d67d3e7f546"],
// the parent kind
["k", "1111"],
["p", "93ef2ebaaf9554661f33e79949007900bbc535d239a4c801c33a4d67d3e7f546"]
]
// other fields
}
```
A comment on a website's url looks like this:
```jsonc
{
"kind": 1111,
"content": "Nice article!",
"tags": [
// referencing the root url
["I", "https://abc.com/articles/1"],
// the root "kind": for an url
["K", "web"],
// the parent reference (same as root for top-level comments)
["i", "https://abc.com/articles/1"],
// the parent "kind": for an url
["k", "web"]
]
// other fields
}
```
A podcast comment example:
```jsonc
{
"id": "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05",
"pubkey": "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111",
"kind": 1111,
"content": "This was a great episode!",
"tags": [
// podcast episode reference
["I", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"],
// podcast episode type
["K", "podcast:item:guid"],
// same value as "I" tag above, because it is a top-level comment (not a reply to a comment)
["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"],
["k", "podcast:item:guid"]
]
// other fields
}
```
A reply to a podcast comment:
```jsonc
{
"kind": 1111,
"content": "I'm replying to the above comment.",
"tags": [
// podcast episode reference
["I", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"],
// podcast episode type
["K", "podcast:item:guid"],
// this is a reference to the above comment
["e", "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05", "wss://example.relay", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111"],
// the parent comment kind
["k", "1111"]
["p", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111"]
]
// other fields
}
```
## GitRepublic Usage
GitRepublic uses NIP-22 comments for threaded discussions on pull requests, issues, and patches. Comments enable collaborative code review and issue discussion.
### Comment Threading
Comments on PRs, issues, and patches use NIP-22's uppercase/lowercase tag convention:
- **Uppercase tags** (`E`, `K`, `P`, `A`) reference the root event (PR, issue, or patch)
- **Lowercase tags** (`e`, `k`, `p`, `a`) reference the parent comment (for reply threads)
### Implementation Details
- **Root Comments**: Comments directly on a PR/issue use uppercase tags pointing to the root event
- **Reply Comments**: Replies to other comments use lowercase tags for the parent comment
- **Relay Hints**: Relay URLs are included in `E` and `e` tags to help clients locate events
- **Plaintext Content**: Comment content is plaintext (no HTML/Markdown) as per NIP-22 spec
### Example: Comment on Pull Request
```jsonc
{
"kind": 1111,
"content": "This looks good, but consider adding error handling here.",
"tags": [
["E", "pr_event_id", "wss://relay.example.com", "pr_author_pubkey"],
["K", "1618"],
["P", "pr_author_pubkey", "wss://relay.example.com"],
["e", "pr_event_id", "wss://relay.example.com", "pr_author_pubkey"],
["k", "1618"],
["p", "pr_author_pubkey", "wss://relay.example.com"]
]
}
```
**Implementation**: `src/lib/services/nostr/highlights-service.ts` (comment creation), used throughout PR and issue discussion features
```

345
docs/34.md

@ -1,345 +0,0 @@
NIP-34
======
`git` stuff
-----------
`draft` `optional`
This NIP defines all the ways code collaboration using and adjacent to [`git`](https://git-scm.com/) can be done using Nostr.
## Repository announcements
Git repositories are hosted in Git-enabled servers, but their existence can be announced using Nostr events. By doing so the author asserts themselves as a maintainer and expresses a willingness to receive patches, bug reports and comments in general, unless `t` tag `personal-fork` is included.
```jsonc
{
"kind": 30617,
"content": "",
"tags": [
["d", "<repo-id>"], // usually kebab-case short name
["name", "<human-readable project name>"],
["description", "brief human-readable project description>"],
["web", "<url for browsing>", ...], // a webpage url, if the git server being used provides such a thing
["clone", "<url for git-cloning>", ...], // a url to be given to `git clone` so anyone can clone it
["relays", "<relay-url>", ...], // relays that this repository will monitor for patches and issues
["r", "<earliest-unique-commit-id>", "euc"],
["maintainers", "<other-recognized-maintainer>", ...],
["t","personal-fork"], // optionally indicate author isn't a maintainer
["t", "<arbitrary string>"], // hashtags labelling the repository
]
}
```
The tags `web`, `clone`, `relays`, `maintainers` can have multiple values.
The `r` tag annotated with the `"euc"` marker should be the commit ID of the earliest unique commit of this repo, made to identify it among forks and group it with other repositories hosted elsewhere that may represent essentially the same project. In most cases it will be the root commit of a repository. In case of a permanent fork between two projects, then the first commit after the fork should be used.
Except `d`, all tags are optional.
## Repository state announcements
An optional source of truth for the state of branches and tags in a repository.
```jsonc
{
"kind": 30618,
"content": "",
"tags": [
["d", "<repo-id>"], // matches the identifier in the corresponding repository announcement
["refs/<heads|tags>/<branch-or-tag-name>","<commit-id>"]
["HEAD", "ref: refs/heads/<branch-name>"]
]
}
```
The `refs` tag may appear multiple times, or none.
If no `refs` tags are present, the author is no longer tracking repository state using this event. This approach enables the author to restart tracking state at a later time unlike [NIP-09](09.md) deletion requests.
The `refs` tag can be optionally extended to enable clients to identify how many commits ahead a ref is:
```jsonc
{
"tags": [
["refs/<heads|tags>/<branch-or-tag-name>", "<commit-id>", "<shorthand-parent-commit-id>", "<shorthand-grandparent>", ...],
]
}
```
## Patches and Pull Requests (PRs)
Patches and PRs can be sent by anyone to any repository. Patches and PRs to a specific repository SHOULD be sent to the relays specified in that repository's announcement event's `"relays"` tag. Patch and PR events SHOULD include an `a` tag pointing to that repository's announcement address.
### When to Use Patches vs. Pull Requests
**Patches SHOULD be used if each event is under 60kb, otherwise PRs SHOULD be used.**
However, the choice between patches and pull requests isn't just about size. Here are the key differences:
#### Patches (Kind 1617)
- **Content**: Patch content is embedded directly in the Nostr event content field
- **Size**: Each patch event must be under 60KB
- **Workflow**: Event-based, sequential patch series linked via NIP-10 reply tags
- **Use Cases**:
- Small, self-contained changes
- Bug fixes and typo corrections
- Simple feature additions
- When you want to send code changes as self-contained events without maintaining a fork
- **Series**: Patches in a series are linked via NIP-10 reply tags
- **Format**: Patch content can be in any format (git format-patch, unified diff, or plain text description)
- **Discovery**: Patches are discoverable as Nostr events on relays, independent of git repository access
#### Pull Requests (Kind 1618)
- **Content**: Markdown description with references to commits via clone URLs
- **Size**: No strict limit (commits are stored in git repositories, not in the event)
- **Workflow**: Branch-based, iterative collaboration
- **Use Cases**:
- Large, complex changes
- Multi-file refactoring
- Features requiring discussion and iteration
- When working with forks and branches
- **Updates**: PRs can be updated (kind 1619) to change the tip commit
- **Format**: References commits that must be accessible via clone URLs
#### Key Differences Summary
| Aspect | Patches | Pull Requests |
|--------|---------|---------------|
| **Event Size** | Under 60KB | No limit (commits stored separately) |
| **Content Location** | In event content | In referenced git repository |
| **Workflow Style** | Email-style, sequential | Branch-based, iterative |
| **Best For** | Small, self-contained changes | Large, complex changes |
| **Maintenance** | Static (new patch for revisions) | Dynamic (can update tip commit) |
| **Application** | Extract from event, then `git am` (if applicable) | `git merge` or `git cherry-pick` |
| **Discussion** | Via NIP-22 comments | Via NIP-22 comments + inline code comments |
### Patches
wPatches are created as Nostr events (kind 1617) with the patch content embedded in the event's content field. This makes patches self-contained and discoverable on Nostr relays without requiring access to git repositories.
Patches in a patch set SHOULD include a [NIP-10](10.md) `e` `reply` tag pointing to the previous patch.
The first patch revision in a patch revision SHOULD include a [NIP-10](10.md) `e` `reply` to the original root patch.
```jsonc
{
"kind": 1617,
"content": "<patch-content>", // Patch content in any format (git format-patch, unified diff, or plain text)
"tags": [
["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
["r", "<earliest-unique-commit-id-of-repo>"], // so clients can subscribe to all patches sent to a local git repo
["p", "<repository-owner>"],
["p", "<other-user>"], // optionally send the patch to another user to bring it to their attention
["t", "root"], // omitted for additional patches in a series
// for the first patch in a revision
["t", "root-revision"],
// optional tags for when it is desirable that the merged patch has a stable commit id
// these fields are necessary for ensuring that the commit resulting from applying a patch
// has the same id as it had in the proposer's machine -- all these tags can be omitted
// if the maintainer doesn't care about these things
["commit", "<current-commit-id>"],
["r", "<current-commit-id>"], // so clients can find existing patches for a specific commit
["parent-commit", "<parent-commit-id>"],
["commit-pgp-sig", "-----BEGIN PGP SIGNATURE-----..."], // empty string for unsigned commit
["committer", "<name>", "<email>", "<timestamp>", "<timezone offset in minutes>"],
]
}
```
The patch content can be in any format that describes code changes. Common formats include git format-patch output, unified diff format, or plain text descriptions. The first patch in a series MAY be a cover letter describing the patch series.
### Pull Requests
The PR or PR update tip SHOULD be successfully pushed to `refs/nostr/<[PR|PR-Update]-event-id>` in all repositories listed in its `clone` tag before the event is signed.
An attempt SHOULD be made to push this ref to all repositories listed in the repository's announcement event's `"clone"` tag, for which their is reason to believe the user might have write access. This includes each [grasp server](https://njump.me/naddr1qvzqqqrhnypzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqy28wumn8ghj7un9d3shjtnwva5hgtnyv4mqqpt8wfshxuqlnvh8x) which can be identified using this method: `clone` tag includes `[http|https]://<grasp-path>/<valid-npub>/<string>.git` and `relays` tag includes `[ws/wss]://<grasp-path>`.
Clients MAY fallback to creating a 'personal-fork' `repository announcement` listing other grasp servers, e.g. from the `User grasp list`, for the purpose of serving the specified commit(s).
```jsonc
{
"kind": 1618,
"content": "<markdown text>",
"tags": [
["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
["r", "<earliest-unique-commit-id-of-repo>"] // so clients can subscribe to all PRs sent to a local git repo
["p", "<repository-owner>"],
["p", "<other-user>"], // optionally send the PR to another user to bring it to their attention
["subject", "<PR-subject>"],
["t", "<PR-label>"], // optional
["t", "<another-PR-label>"], // optional
["c", "<current-commit-id>"], // tip of the PR branch
["clone", "<clone-url>", ...], // at least one git clone url where commit can be downloaded
["branch-name", "<branch-name>"], // optional recommended branch name
["e", "<root-patch-event-id>"], // optionally indicate PR is a revision of an existing patch, which should be closed
["merge-base", "<commit-id>"], // optional: the most recent common ancestor with the target branch
]
}
```
### Pull Request Updates
A PR Update changes the tip of a referenced PR event.
```jsonc
{
"kind": 1619,
"content": "",
"tags": [
["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
["r", "<earliest-unique-commit-id-of-repo>"] // so clients can subscribe to all PRs sent to a local git repo
["p", "<repository-owner>"],
["p", "<other-user>"], // optionally send the PR to another user to bring it to their attention
// NIP-22 tags
["E", "<pull-request-event-id>"],
["P", "<pull-request-author>"],
["c", "<current-commit-id>"], // updated tip of PR
["clone", "<clone-url>", ...], // at least one git clone url where commit can be downloaded
["merge-base", "<commit-id>"], // optional: the most recent common ancestor with the target branch
]
}
```
## Issues
Issues are Markdown text that is just human-readable conversational threads related to the repository: bug reports, feature requests, questions or comments of any kind. Like patches, these SHOULD be sent to the relays specified in that repository's announcement event's `"relays"` tag.
Issues may have a `subject` tag, which clients can utilize to display a header. Additionally, one or more `t` tags may be included to provide labels for the issue.
```json
{
"kind": 1621,
"content": "<markdown text>",
"tags": [
["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
["p", "<repository-owner>"]
["subject", "<issue-subject>"]
["t", "<issue-label>"]
["t", "<another-issue-label>"]
]
}
```
## Replies
Replies to either a `kind:1621` (_issue_), `kind:1617` (_patch_) or `kind:1618` (_pull request_) event should follow [NIP-22 comment](22.md).
## Status
Root Patches, PRs and Issues have a Status that defaults to 'Open' and can be set by issuing Status events.
```jsonc
{
"kind": 1630, // Open
"kind": 1631, // Applied / Merged for Patches; Resolved for Issues
"kind": 1632, // Closed
"kind": 1633, // Draft
"content": "<markdown text>",
"tags": [
["e", "<issue-or-PR-or-original-root-patch-id-hex>", "", "root"],
["e", "<accepted-revision-root-id-hex>", "", "reply"], // for when revisions applied
["p", "<repository-owner>"],
["p", "<root-event-author>"],
["p", "<revision-author>"],
// optional for improved subscription filter efficiency
["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>", "<relay-url>"],
["r", "<earliest-unique-commit-id-of-repo>"]
// optional for `1631` status
["q", "<applied-or-merged-patch-event-id>", "<relay-url>", "<pubkey>"], // for each
// when merged
["merge-commit", "<merge-commit-id>"]
["r", "<merge-commit-id>"]
// when applied
["applied-as-commits", "<commit-id-in-master-branch>", ...]
["r", "<applied-commit-id>"] // for each
]
}
```
The most recent Status event (by `created_at` date) from either the issue/patch author or a maintainer is considered valid.
The Status of a patch-revision is to either that of the root-patch, or `1632` (_Closed_) if the root-patch's Status is `1631` (_Applied/Merged_) and the patch-revision isn't tagged in the `1631` (_Applied/Merged_) event.
## User grasp list
List of [grasp servers](https://njump.me/naddr1qvzqqqrhnypzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqy28wumn8ghj7un9d3shjtnwva5hgtnyv4mqqpt8wfshxuqlnvh8x) the user generally wishes to use for NIP-34 related activity. It is similar in function to the NIP-65 relay list and NIP-B7 blossom list.
The event SHOULD include a list of `g` tags with grasp service websocket URLs in order of preference.
```jsonc
{
"kind": 10317,
"content": "",
"tags": [
["g", "<grasp-service-websocket-url>"], // zero or more grasp sever urls
]
}
```
## Possible things to be added later
- inline file comments kind (we probably need one for patches and a different one for merged files)
## GitRepublic Usage
NIP-34 is the core NIP that GitRepublic implements. All repository functionality is built on top of NIP-34 event kinds.
### Repository Announcements (Kind 30617)
GitRepublic uses repository announcements to:
- **Discover Repositories**: The landing page fetches all kind 30617 events to display available repositories
- **Repository Creation**: Users create repository announcements via the signup page
- **Repository Updates**: Repository information updates are done via the signup page, which updates the repository announcement event
- **Fork Detection**: Forks are identified by `a` tags pointing to the original repository
- **Offline Papertrail**: Repository announcements are saved to `nostr/repo-events.jsonl` in the repository for offline verification
### Pull Requests (Kind 1618)
- **PR Creation**: Users create PRs by pushing commits and creating kind 1618 events
- **PR Updates**: PR updates (kind 1619) change the tip commit of existing PRs
- **PR Display**: PRs are displayed with their full markdown content and metadata
- **PR Merging**: Merging a PR creates a status event (kind 1631) with merge commit information
### Issues (Kind 1621)
- **Issue Creation**: Users create issues for bug reports, feature requests, and questions
- **Issue Threading**: Issues use NIP-22 comments for discussion
- **Issue Status**: Status events (kinds 1630-1633) track issue state (open, resolved, closed, draft)
### Status Events (Kinds 1630-1633)
- **Status Tracking**: PRs and issues use status events to track their state
- **Most Recent Wins**: Only the most recent status event from the author or a maintainer is considered valid
- **Merge Tracking**: When a PR is merged (status 1631), the merge commit ID is included in tags
### Repository State (Kind 30618)
- **Branch Tracking**: Optional repository state events track branch and tag positions
- **State Updates**: Repository state can be updated to reflect current branch/tag positions
### Patches (Kind 1617)
GitRepublic supports event-based patches for small, self-contained code changes:
- **Patch Creation**: Users create patches as Nostr events (kind 1617) via the web interface
- **Patch Content**: Patch content is embedded directly in the event's content field (can be in any format: git format-patch, unified diff, or plain text)
- **Event-Based**: Patches are self-contained Nostr events, discoverable on relays without requiring git repository access
- **Patch Series**: Multiple related patches can be linked using NIP-10 reply tags
- **Patch Status**: Patches use status events (kinds 1630-1633) to track state (open, applied, closed, draft)
- **Patch Application**: Maintainers extract patch content from events and apply them, then mark as applied with status events
**Implementation**:
- Repository announcements: `src/routes/signup/+page.svelte`, `src/lib/services/nostr/repo-polling.ts`
- Pull requests: `src/lib/services/nostr/prs-service.ts`
- Issues: `src/lib/services/nostr/issues-service.ts`
- Status events: Used throughout PR, patch, and issue management

73
docs/65.md

@ -1,73 +0,0 @@
NIP-65
======
Relay List Metadata
-------------------
`draft` `optional`
Defines a replaceable event using `kind:10002` to advertise relays where the user generally **writes** to and relays where the user generally **reads** mentions.
The event MUST include a list of `r` tags with relay URLs as value and an optional `read` or `write` marker. If the marker is omitted, the relay is both **read** and **write**.
```jsonc
{
"kind": 10002,
"tags": [
["r", "wss://alicerelay.example.com"],
["r", "wss://brando-relay.com"],
["r", "wss://expensive-relay.example2.com", "write"],
["r", "wss://nostr-relay.example.com", "read"]
],
"content": "",
// other fields...
}
```
When downloading events **from** a user, clients SHOULD use the **write** relays of that user.
When downloading events **about** a user, where the user was tagged (mentioned), clients SHOULD use the user's **read** relays.
When publishing an event, clients SHOULD:
- Send the event to the **write** relays of the author
- Send the event to all **read** relays of each tagged user
- Send the author's `kind:10002` event to all relays the event was published to
### Size
Clients SHOULD guide users to keep `kind:10002` lists small (2-4 relays of each category).
### Discoverability
Clients SHOULD spread an author's `kind:10002` event to as many relays as viable, paying attention to relays that, at any moment, serve naturally as well-known public indexers for these relay lists (where most other clients and users are connecting to in order to publish and fetch those).
## GitRepublic Usage
GitRepublic uses NIP-65 relay lists to discover user's preferred relays for publishing events. This ensures events are published to relays the user trusts and uses.
### Relay Discovery
When publishing repository announcements, PRs, issues, or other events:
1. **Fetch User's Relay List**: GitRepublic fetches the user's kind 10002 event
2. **Extract Write Relays**: Parses `r` tags with `write` marker or no marker (both read and write)
3. **Combine with Defaults**: Combines user's outbox relays with default relays for redundancy
4. **Publish**: Sends events to the combined relay list
### Fallback to Kind 3
If no kind 10002 event is found, GitRepublic falls back to kind 3 (contact list) for relay discovery, maintaining compatibility with older clients.
### Implementation
```typescript
// Fetch user's relay preferences
const { inbox, outbox } = await getUserRelays(pubkey, nostrClient);
const userRelays = combineRelays(outbox);
// Publish to user's preferred relays
await nostrClient.publishEvent(signedEvent, userRelays);
```
**Implementation**: `src/lib/services/nostr/user-relays.ts`, used when publishing all repository-related events

95
docs/84.md

@ -1,95 +0,0 @@
NIP-84
======
Highlights
----------
`draft` `optional`
This NIP defines `kind:9802`, a "highlight" event, to signal content a user finds valuable.
## Format
The `.content` of these events is the highlighted portion of the text.
`.content` might be empty for highlights of non-text based media (e.g. NIP-94 audio/video).
### References
Events SHOULD tag the source of the highlight, whether nostr-native or not.
`a` or `e` tags should be used for nostr events and `r` tags for URLs.
When tagging a URL, clients generating these events SHOULD do a best effort of cleaning the URL from trackers
or obvious non-useful information from the query string.
### Attribution
Clients MAY include one or more `p` tags, tagging the original authors of the material being highlighted; this is particularly
useful when highlighting non-nostr content for which the client might be able to get a nostr pubkey somehow
(e.g. prompting the user or reading a `<link rel="me" href="nostr:nprofile1..." />` tag on the document). A role MAY be included as the
last value of the tag.
```jsonc
{
"tags": [
["p", "<pubkey-hex>", "<relay-url>", "author"],
["p", "<pubkey-hex>", "<relay-url>", "author"],
["p", "<pubkey-hex>", "<relay-url>", "editor"]
],
// other fields...
}
```
### Context
Clients MAY include a `context` tag, useful when the highlight is a subset of a paragraph and displaying the
surrounding content might be beneficial to give context to the highlight.
## Quote Highlights
A `comment` tag may be added to create a quote highlight. This MUST be rendered like a quote repost with the highlight as the quoted note.
This is to prevent the creation and multiple notes (highlight + kind 1) for a single highlight action, which looks bad in micro-blogging clients where these notes may appear in succession.
p-tag mentions MUST have a `mention` attribute to distinguish it from authors and editors.
r-tag urls from the comment MUST have a `mention` attribute to distinguish from the highlighted source url. The source url MUST have the `source` attribute.
## GitRepublic Usage
GitRepublic extends NIP-84 highlights for code review and code selection features. Highlights enable users to select and comment on specific code sections in pull requests.
### Code Highlighting
When users select code in a PR for review:
1. **Highlight Creation**: A kind 9802 event is created with the selected code in the `content` field
2. **File Context**: Custom tags (`file`, `line-start`, `line-end`) specify which file and lines are highlighted
3. **PR Reference**: The highlight references the PR using `a` and `e` tags
4. **Attribution**: `P` and `p` tags reference the PR author and highlight creator
### Extended Tags
GitRepublic extends NIP-84 with file-specific tags for code context:
- `file`: File path being highlighted
- `line-start`: Starting line number
- `line-end`: Ending line number
- `context`: Optional surrounding context
- `comment`: Optional comment text for quote highlights
### Example: Code Highlight on PR
```jsonc
{
"kind": 9802,
"content": "const result = await fetch(url);",
"tags": [
["a", "1618:pr_author_pubkey.../repo-name"],
["e", "pr_event_id"],
["P", "pr_author_pubkey"],
["K", "1618"],
["file", "src/main.ts"],
["line-start", "42"],
["line-end", "45"],
["context", "// Fetch data from API"],
["p", "pr_author_pubkey", "wss://relay.example.com", "author"]
]
}
```
**Implementation**: `src/lib/services/nostr/highlights-service.ts`

107
docs/98.md

@ -1,107 +0,0 @@
NIP-98
======
HTTP Auth
---------
`draft` `optional`
This NIP defines an ephemeral event used to authorize requests to HTTP servers using nostr events.
This is useful for HTTP services which are built for Nostr and deal with Nostr user accounts.
## Nostr event
A `kind 27235` (In reference to [RFC 7235](https://www.rfc-editor.org/rfc/rfc7235)) event is used.
The `content` SHOULD be empty.
The following tags MUST be included.
* `u` - absolute URL
* `method` - HTTP Request Method
Example event:
```json
{
"id": "fe964e758903360f28d8424d092da8494ed207cba823110be3a57dfe4b578734",
"pubkey": "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed",
"content": "",
"kind": 27235,
"created_at": 1682327852,
"tags": [
["u", "https://api.snort.social/api/v1/n5sp/list"],
["method", "GET"]
],
"sig": "5ed9d8ec958bc854f997bdc24ac337d005af372324747efe4a00e24f4c30437ff4dd8308684bed467d9d6be3e5a517bb43b1732cc7d33949a3aaf86705c22184"
}
```
Servers MUST perform the following checks in order to validate the event:
1. The `kind` MUST be `27235`.
2. The `created_at` timestamp MUST be within a reasonable time window (suggestion 60 seconds).
3. The `u` tag MUST be exactly the same as the absolute request URL (including query parameters).
4. The `method` tag MUST be the same HTTP method used for the requested resource.
When the request contains a body (as in POST/PUT/PATCH methods) clients SHOULD include a SHA256 hash of the request body in a `payload` tag as hex (`["payload", "<sha256-hex>"]`), servers MAY check this to validate that the requested payload is authorized.
If one of the checks was to fail the server SHOULD respond with a 401 Unauthorized response code.
Servers MAY perform additional implementation-specific validation checks.
## Request Flow
Using the `Authorization` HTTP header, the `kind 27235` event MUST be `base64` encoded and use the Authorization scheme `Nostr`
Example HTTP Authorization header:
```
Authorization: Nostr
eyJpZCI6ImZlOTY0ZTc1ODkwMzM2MGYyOGQ4NDI0ZDA5MmRhODQ5NGVkMjA3Y2JhODIzMTEwYmUzYTU3ZGZlNGI1Nzg3MzQiLCJwdWJrZXkiOiI2M2ZlNjMxOGRjNTg1ODNjZmUxNjgxMGY4NmRkMDllMThiZmQ3NmFhYmMyNGEwMDgxY2UyODU2ZjMzMDUwNGVkIiwiY29udGVudCI6IiIsImtpbmQiOjI3MjM1LCJjcmVhdGVkX2F0IjoxNjgyMzI3ODUyLCJ0YWdzIjpbWyJ1IiwiaHR0cHM6Ly9hcGkuc25vcnQuc29jaWFsL2FwaS92MS9uNXNwL2xpc3QiXSxbIm1ldGhvZCIsIkdFVCJdXSwic2lnIjoiNWVkOWQ4ZWM5NThiYzg1NGY5OTdiZGMyNGFjMzM3ZDAwNWFmMzcyMzI0NzQ3ZWZlNGEwMGUyNGY0YzMwNDM3ZmY0ZGQ4MzA4Njg0YmVkNDY3ZDlkNmJlM2U1YTUxN2JiNDNiMTczMmNjN2QzMzk0OWEzYWFmODY3MDVjMjIxODQifQ
```
## Reference Implementations
- C# ASP.NET `AuthenticationHandler` [NostrAuth.cs](https://gist.github.com/v0l/74346ae530896115bfe2504c8cd018d3)
## GitRepublic Usage
NIP-98 is used extensively in GitRepublic for authenticating git operations (clone, push, pull) and API requests. This enables secure git operations without requiring traditional username/password authentication.
### Git Operations Authentication
All git operations (push, pull, clone) use NIP-98 authentication:
1. **Client Creates Auth Event**: User's browser extension creates a kind 27235 event with:
- `u` tag: Absolute URL of the git endpoint
- `method` tag: HTTP method (POST for push, GET for pull/clone)
- `payload` tag: SHA256 hash of request body (for POST requests)
2. **Base64 Encoding**: The event is base64-encoded and sent in `Authorization: Nostr {base64_event}` header
3. **Server Verification**: GitRepublic verifies:
- Event kind is 27235
- Timestamp is within 60 seconds
- URL matches exactly (normalized, trailing slashes removed)
- HTTP method matches
- Payload hash matches (for POST requests)
- Event signature is valid
### API Endpoint Authentication
API endpoints that modify repository state also use NIP-98:
- File creation/editing
- Repository settings updates
- PR/issue creation
- Comment posting
### URL Normalization
GitRepublic normalizes URLs before comparison to handle trailing slashes and ensure consistent matching:
- Removes trailing slashes
- Preserves query parameters
- Handles both HTTP and HTTPS
### Fallback to Kind 24
If NIP-98 authentication fails, GitRepublic can fall back to kind 24 (public message) events for relay write proofs.
**Implementation**: `src/lib/services/nostr/nip98-auth.ts`, used in all git operation endpoints and API routes

352
docs/ARCHITECTURE_FAQ.md

@ -1,352 +0,0 @@
# Architecture FAQ
Answers to common questions about gitrepublic-web's architecture and design decisions.
## 1. Session State
### Where does session state live?
**Answer**: Session state lives entirely on the client (browser). There is **no server-side session storage**.
- **Client-side**: User's public key (`userPubkey`) is stored in Svelte component state (`$state`)
- **No server storage**: The server does not maintain session cookies, tokens, or any session database
- **Stateless authentication**: Each request is authenticated independently using:
- **NIP-07**: Browser extension (Alby, nos2x) for web UI operations
- **NIP-98**: HTTP authentication events for git operations
### Implementation Details
```typescript
// Client-side state (src/routes/+page.svelte)
let userPubkey = $state<string | null>(null);
// Login: Get pubkey from NIP-07 extension
async function login() {
userPubkey = await getPublicKeyWithNIP07();
}
// Logout: Clear client state
function logout() {
userPubkey = null;
}
```
**Why stateless?**
- Decentralized design: No central session authority
- Scalability: No session database to manage
- Privacy: Server doesn't track user sessions
- Nostr-native: Uses Nostr's cryptographic authentication
## 2. Session Scope
### When does a session begin and end?
**Answer**: Since there's no server-side session, the "session" is really just client-side authentication state:
- **Begins**: When user connects their NIP-07 extension and calls `login()`
- The extension provides the user's public key
- This is stored in component state for the current page load
- **Ends**:
- When user calls `logout()` (sets `userPubkey = null`)
- When browser tab/window is closed (state is lost)
- When page is refreshed (state is lost unless persisted)
**Note**: There's currently **no persistence** of login state across page refreshes. Users need to reconnect their NIP-07 extension on each page load.
**Potential Enhancement**: Could add localStorage to persist `userPubkey` across sessions, but this is a design decision - some prefer explicit re-authentication for security.
## 3. Repository Settings Storage
### Where are repo settings stored?
**Answer**: Repository settings are stored **entirely in Nostr events** (kind 30617, NIP-34 repo announcements). **No database is required**.
### Storage Location
- **Nostr Events**: All settings are stored as tags in the repository announcement event:
- `name`: Repository name
- `description`: Repository description
- `clone`: Clone URLs (array)
- `maintainers`: List of maintainer pubkeys
- `private`: Privacy flag (`true`/`false`)
- `relays`: Nostr relays to publish to
### How It Works
1. **Reading Settings**:
```typescript
// Fetch from Nostr relays
const events = await nostrClient.fetchEvents([{
kinds: [KIND.REPO_ANNOUNCEMENT],
authors: [ownerPubkey],
'#d': [repoName],
limit: 1
}]);
// Extract settings from event tags
const name = event.tags.find(t => t[0] === 'name')?.[1];
const maintainers = event.tags.filter(t => t[0] === 'maintainers').map(t => t[1]);
```
2. **Updating Settings**:
```typescript
// Create new announcement event with updated tags
const updatedEvent = {
kind: KIND.REPO_ANNOUNCEMENT,
pubkey: ownerPubkey,
tags: [
['d', repoName],
['name', newName],
['maintainers', maintainer1],
['maintainers', maintainer2],
['private', 'true']
]
};
// Sign with NIP-07 and publish to relays
const signed = await signEventWithNIP07(updatedEvent);
await nostrClient.publishEvent(signed, relays);
```
### Benefits
- **Decentralized**: Settings live on Nostr relays, not a central database
- **Verifiable**: Cryptographically signed by repository owner
- **Resilient**: Multiple relays store copies
- **No database needed**: Simplifies deployment
### Limitations
- **Event replaceability**: NIP-34 announcements are replaceable (same `d` tag), so latest event wins
- **Relay dependency**: Settings are only as available as the relays
- **No complex queries**: Can't do complex database-style queries
## 4. NIP-98 Authorization Requirements
### What actions require NIP-98 authorization?
**Answer**: NIP-98 is required for **git operations** (clone, push, pull) and **optional for web UI file operations**.
### Required NIP-98 Operations
1. **Git Push Operations** (`POST /api/git/{npub}/{repo}.git/git-receive-pack`)
- **Always required** for push operations
- Verifies user is repository owner or maintainer
- Validates event signature, timestamp, URL, and method
2. **Private Repository Clone/Fetch** (`GET /api/git/{npub}/{repo}.git/info/refs?service=git-upload-pack`)
- **Required** if repository is marked as private
- Verifies user has view access (owner or maintainer)
- Public repos don't require authentication
3. **Private Repository Fetch** (`POST /api/git/{npub}/{repo}.git/git-upload-pack`)
- **Required** if repository is private
- Same authentication as clone
### Optional NIP-98 Operations
4. **File Write Operations** (`POST /api/repos/{npub}/{repo}/file`)
- **Optional**: Can use NIP-07 (browser extension) or NIP-98
- NIP-98 is useful for automated scripts or git operations
- NIP-07 is more convenient for web UI
### NIP-98 Verification Process
```typescript
// Server verifies:
1. Event signature (cryptographic verification)
2. Event timestamp (within 60 seconds)
3. URL matches request URL exactly
4. HTTP method matches
5. Payload hash matches request body (for POST)
6. Pubkey is repository owner or maintainer
```
### API Endpoints Summary
| Endpoint | NIP-98 Required? | Notes |
|----------|------------------|-------|
| `GET /api/git/.../info/refs` | Only for private repos | Public repos: no auth needed |
| `POST /api/git/.../git-upload-pack` | Only for private repos | Public repos: no auth needed |
| `POST /api/git/.../git-receive-pack` | **Always required** | All push operations |
| `POST /api/repos/.../file` | Optional | Can use NIP-07 instead |
| `GET /api/repos/.../file` | No | Uses query param `userPubkey` |
| `POST /api/repos/.../settings` | No | Uses NIP-07 (browser extension) |
## 5. Repository Announcement Polling
### Why is the server polling instead of using subscriptions?
**Answer**: The server uses **polling** (every 60 seconds) instead of persistent WebSocket subscriptions for simplicity and reliability.
### Current Implementation
```typescript
// src/lib/services/nostr/repo-polling.ts
constructor(
pollingInterval: number = 60000 // 1 minute default
) {
// Poll immediately, then every interval
this.intervalId = setInterval(() => {
this.poll();
}, this.pollingInterval);
}
```
**Polling Schedule**:
- **Frequency**: Every 60 seconds (1 minute)
- **Type**: Long-running background process
- **Location**: Started in `hooks.server.ts` when server starts
- **Not a cron job**: Runs continuously in the Node.js process
### Why Polling Instead of Subscriptions?
**Advantages of Polling**:
1. **Simplicity**: No need to maintain persistent WebSocket connections
2. **Reliability**: If a connection drops, polling automatically retries
3. **Resource efficiency**: Only connects when fetching, not maintaining long-lived connections
4. **Easier error handling**: Each poll is independent
**Disadvantages of Polling**:
1. **Latency**: Up to 60 seconds delay before new repos are discovered
2. **Relay load**: More frequent queries to relays
3. **Less real-time**: Not immediate notification of new repos
### Could We Use Subscriptions?
**Yes, but with trade-offs**:
```typescript
// Potential subscription implementation
const ws = new WebSocket(relay);
ws.send(JSON.stringify(['REQ', 'sub-id', {
kinds: [KIND.REPO_ANNOUNCEMENT],
'#clone': [domain]
}]));
ws.on('message', (event) => {
// Handle new repo announcement immediately
});
```
**Challenges**:
- Need to maintain WebSocket connections to multiple relays
- Handle connection drops and reconnections
- More complex error handling
- Higher memory usage for long-lived connections
### Recommendation
For most use cases, **60-second polling is acceptable**:
- New repos don't need to be discovered instantly
- Reduces complexity
- More reliable for production
For real-time requirements, subscriptions could be added as an enhancement, but polling is a solid default.
## 6. Branch Protection
### What is the scheme for branch protection?
**Answer**: **Branch protection is not currently implemented**. This is a missing feature.
### Current State
**What Exists**:
- Maintainers can create branches (`POST /api/repos/{npub}/{repo}/branches`)
- Only maintainers can create branches (not regular users)
- No protection for `main`/`master` branch
**What's Missing**:
- ❌ No branch protection rules
- ❌ No restriction on pushing to `main`/`master`
- ❌ No required pull request reviews
- ❌ No required status checks
- ❌ No force push restrictions
### Current Authorization
```typescript
// src/routes/api/repos/[npub]/[repo]/branches/+server.ts
const isMaintainer = await maintainerService.isMaintainer(userPubkeyHex, repoOwnerPubkey, repo);
if (!isMaintainer) {
return error(403, 'Only repository maintainers can create branches');
}
```
**Authorized Users**:
- **Repository Owner**: Can do everything
- **Maintainers**: Listed in repo announcement `maintainers` tags
- Can create branches
- Can push to any branch (including main)
- Can write files
### Proposed Branch Protection Implementation
**Option 1: Nostr Events (Recommended)**
- Create new event kind (e.g., 30620) for branch protection rules
- Store rules in Nostr events:
```json
{
"kind": 30620,
"tags": [
["d", "repo-name"],
["branch", "main", "protected"],
["branch", "main", "require-pr"],
["branch", "main", "require-reviewers", "pubkey1", "pubkey2"]
]
}
```
**Option 2: In-Repo Configuration**
- Store `.gitrepublic/branch-protection.json` in repository
- Git-based, version-controlled
- Requires pull request to change rules
**Option 3: Server Configuration**
- Store in server database (conflicts with decentralized design)
- Not recommended for this architecture
### Recommended Approach
**Hybrid: Nostr Events + In-Repo Config**
1. **Default rules**: Stored in Nostr events (kind 30620)
2. **Override rules**: Can be stored in `.gitrepublic/branch-protection.json` in repo
3. **Enforcement**: Server checks rules before allowing push to protected branches
**Example Rules**:
```json
{
"protectedBranches": ["main", "master"],
"requirePullRequest": true,
"requireReviewers": ["pubkey1", "pubkey2"],
"allowForcePush": false,
"requireStatusChecks": ["ci", "lint"]
}
```
### Implementation Priority
This is a **medium-priority feature** that would enhance security and workflow, but the current system works for basic use cases where:
- Owners trust their maintainers
- Repositories are small teams
- Formal review processes aren't needed
For enterprise use cases, branch protection would be highly recommended.
---
## Summary
| Question | Answer |
|----------|--------|
| **Session State** | Client-side only, no server storage |
| **Session Scope** | Begins on NIP-07 login, ends on logout or page close |
| **Repo Settings** | Stored in Nostr events (kind 30617), no database needed |
| **NIP-98 Required** | Git push (always), private repo clone/fetch (conditional) |
| **Polling Schedule** | Every 60 seconds, long-running background process |
| **Branch Protection** | ✅ **Implemented** - Stored in Nostr events (kind 30620) |
---

139
docs/IMPLEMENTATION.md

@ -1,139 +0,0 @@
# Implementation Guide for git-http-backend Integration
## Overview
The git-http-backend integration needs to be implemented in `/src/routes/api/git/[...path]/+server.ts`. This route will handle all git HTTP operations (clone, push, pull).
## URL Structure
All git requests will follow this pattern:
- `GET /api/git/{npub}/{repo-name}.git/info/refs?service=git-upload-pack` (clone/fetch)
- `GET /api/git/{npub}/{repo-name}.git/info/refs?service=git-receive-pack` (push capability check)
- `POST /api/git/{npub}/{repo-name}.git/git-upload-pack` (fetch)
- `POST /api/git/{npub}/{repo-name}.git/git-receive-pack` (push)
## Implementation Steps
### 1. Parse Request Path
Extract `npub` and `repo-name` from the path parameter:
```typescript
const match = params.path.match(/^([^\/]+)\/([^\/]+)\.git\/(.+)$/);
if (!match) return new Response('Invalid path', { status: 400 });
const [, npub, repoName, gitPath] = match;
```
### 2. Authenticate with NIP-98
For push operations, verify NIP-98 authentication:
```typescript
import { verifyEvent } from 'nostr-tools';
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Nostr ')) {
return new Response('Unauthorized', { status: 401 });
}
const nostrEvent = JSON.parse(authHeader.slice(7));
if (!verifyEvent(nostrEvent)) {
return new Response('Invalid signature', { status: 401 });
}
// Verify pubkey matches repo owner
if (nostrEvent.pubkey !== expectedPubkey) {
return new Response('Unauthorized', { status: 403 });
}
```
### 3. Map to Git Repository Path
Use `RepoManager` to get the full path:
```typescript
import { RepoManager } from '$lib/services/git/repo-manager.js';
const repoManager = new RepoManager(process.env.GIT_REPO_ROOT || '/repos');
const repoPath = join(repoManager.repoRoot, npub, `${repoName}.git`);
if (!repoManager.repoExists(repoPath)) {
return new Response('Repository not found', { status: 404 });
}
```
### 4. Proxy to git-http-backend
Execute git-http-backend as a subprocess:
```typescript
import { spawn } from 'child_process';
import { env } from '$env/dynamic/private';
const gitHttpBackend = '/usr/lib/git-core/git-http-backend'; // or wherever it's installed
const envVars = {
...process.env,
GIT_PROJECT_ROOT: repoManager.repoRoot,
GIT_HTTP_EXPORT_ALL: '1',
REQUEST_METHOD: request.method,
PATH_INFO: `/${npub}/${repoName}.git/${gitPath}`,
QUERY_STRING: url.searchParams.toString(),
CONTENT_TYPE: request.headers.get('Content-Type') || '',
CONTENT_LENGTH: request.headers.get('Content-Length') || '0',
};
const gitProcess = spawn(gitHttpBackend, [], {
env: envVars,
stdio: ['pipe', 'pipe', 'pipe']
});
// Pipe request body to git-http-backend
if (request.body) {
request.body.pipeTo(gitProcess.stdin);
}
// Return git-http-backend response
return new Response(gitProcess.stdout, {
headers: {
'Content-Type': 'application/x-git-upload-pack-result',
// or 'application/x-git-receive-pack-result' for push
}
});
```
### 5. Post-Receive Hook
After successful push, sync to other remotes:
```typescript
// After successful git-receive-pack
if (gitPath === 'git-receive-pack' && request.method === 'POST') {
// Fetch NIP-34 announcement for this repo
const announcement = await fetchRepoAnnouncement(npub, repoName);
if (announcement) {
const cloneUrls = extractCloneUrls(announcement);
const otherUrls = cloneUrls.filter(url => !url.includes('git.imwald.eu'));
await repoManager.syncToRemotes(repoPath, otherUrls);
}
}
```
## Alternative: Use a Git Server Library
Instead of calling git-http-backend directly, you could use a Node.js git server library:
- `isomorphic-git` with `@isomorphic-git/http-server`
- `node-git-server`
- Custom implementation using `dugite` or `simple-git`
## Testing
Test with:
```bash
# Clone
git clone https://git.imwald.eu/{npub}/{repo-name}.git
# Push (requires NIP-98 auth)
git push origin main
```
For NIP-98 authentication, you'll need a git credential helper that:
1. Intercepts git HTTP requests
2. Signs a Nostr event with the user's key
3. Adds `Authorization: Nostr {event}` header

123
docs/LOGGING.md

@ -1,123 +0,0 @@
# Logging Strategy
## Current State
The application currently uses `console.log`, `console.error`, and `console.warn` for logging, with:
- Context prefixes (e.g., `[Fork] [repo1 → repo2]`)
- Security sanitization (truncated pubkeys, redacted private keys)
- Structured audit logging via `AuditLogger` service
## Recommendation: Use Pino for Production
### Why Pino?
1. **Performance**: Extremely fast (async logging, minimal overhead)
2. **Structured Logging**: JSON output perfect for ELK/Kibana/Logstash
3. **Log Levels**: Built-in severity levels (trace, debug, info, warn, error, fatal)
4. **Child Loggers**: Context propagation (request IDs, user IDs, etc.)
5. **Ecosystem**: Excellent Kubernetes/Docker support
6. **Small Bundle**: ~4KB minified
### Implementation Plan
#### 1. Install Pino
```bash
npm install pino pino-pretty
```
#### 2. Create Logger Service
```typescript
// src/lib/services/logger.ts
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
...(process.env.NODE_ENV === 'development' && {
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname'
}
}
})
});
export default logger;
```
#### 3. Replace Console Logs
```typescript
// Before
console.log(`[Fork] ${context} Starting fork process`);
// After
import logger from '$lib/services/logger.js';
logger.info({ context, repo: `${npub}/${repo}` }, 'Starting fork process');
```
#### 4. Structured Context
```typescript
// Create child logger with context
const forkLogger = logger.child({
operation: 'fork',
originalRepo: `${npub}/${repo}`,
forkRepo: `${userNpub}/${forkRepoName}`
});
forkLogger.info('Starting fork process');
forkLogger.info({ relayCount: combinedRelays.length }, 'Using relays');
forkLogger.error({ error: sanitizedError }, 'Fork failed');
```
#### 5. ELK/Kibana Integration
Pino outputs JSON by default, which works perfectly with:
- **Filebeat**: Collect logs from files
- **Logstash**: Parse and enrich logs
- **Elasticsearch**: Store and index logs
- **Kibana**: Visualize and search logs
Example log output:
```json
{
"level": 30,
"time": 1703123456789,
"pid": 12345,
"hostname": "gitrepublic-1",
"operation": "fork",
"originalRepo": "npub1.../repo1",
"forkRepo": "npub2.../repo2",
"msg": "Starting fork process"
}
```
### Migration Strategy
1. **Phase 1**: Install Pino, create logger service
2. **Phase 2**: Replace console logs in critical paths (fork, file operations, git operations)
3. **Phase 3**: Replace remaining console logs
4. **Phase 4**: Add request ID middleware for request tracing
5. **Phase 5**: Configure log aggregation (Filebeat → ELK)
### Benefits
- **Searchability**: Query logs by operation, user, repo, etc.
- **Alerting**: Set up alerts for error rates, failed operations
- **Performance Monitoring**: Track operation durations
- **Security Auditing**: Enhanced audit trail with structured data
- **Debugging**: Easier to trace requests across services
### Alternative: Winston
Winston is also popular but:
- Slower than Pino
- More configuration overhead
- Better for complex transports (multiple outputs)
**Recommendation**: Use Pino for this project.

402
docs/MESSAGING_FORWARDING.md

@ -1,402 +0,0 @@
# Messaging Forwarding Feature
This feature allows users with **unlimited access** to forward Nostr events to messaging platforms (Telegram, SimpleX, Email) and Git hosting platforms (GitHub, GitLab, Gitea, Codeberg, Forgejo) when they publish events.
## Security Architecture
### Multi-Layer Security
1. **Encrypted Salt Storage**: Each user's salt is encrypted with a separate key (`MESSAGING_SALT_ENCRYPTION_KEY`)
2. **HMAC Lookup Keys**: User pubkeys are hashed with HMAC before being used as database keys
3. **Rate Limiting**: Decryption attempts are rate-limited (10 attempts per 15 minutes)
4. **Per-User Key Derivation**: Encryption keys derived from master key + pubkey + salt
5. **AES-256-GCM**: Authenticated encryption with random IV per encryption
### Threat Mitigation
- **Database Compromise**: All data encrypted at rest with per-user keys
- **Key Leakage**: Per-user key derivation means master key leak doesn't expose all users
- **Brute Force**: Rate limiting prevents rapid decryption attempts
- **Lookup Attacks**: HMAC hashing prevents pubkey enumeration
## Environment Variables
### Required for Encryption
```bash
# Master encryption key (256-bit hex, generate with: openssl rand -hex 32)
MESSAGING_PREFS_ENCRYPTION_KEY=<256-bit-hex>
# Salt encryption key (256-bit hex, generate with: openssl rand -hex 32)
MESSAGING_SALT_ENCRYPTION_KEY=<256-bit-hex>
# HMAC secret for lookup key hashing (256-bit hex, generate with: openssl rand -hex 32)
MESSAGING_LOOKUP_SECRET=<256-bit-hex>
```
### Optional for Messaging Platforms
```bash
# Telegram Bot Configuration
TELEGRAM_BOT_TOKEN=<bot-token>
TELEGRAM_ENABLED=true
# Email SMTP Configuration
SMTP_HOST=<smtp-host>
SMTP_PORT=587
SMTP_USER=<smtp-username>
SMTP_PASSWORD=<smtp-password>
SMTP_FROM_ADDRESS=<from-email>
SMTP_FROM_NAME=GitRepublic
EMAIL_ENABLED=true
# OR use SMTP API (alternative to direct SMTP)
SMTP_API_URL=<smtp-api-url>
SMTP_API_KEY=<api-key>
# SimpleX API Configuration
SIMPLEX_API_URL=<simplex-api-url>
SIMPLEX_API_KEY=<api-key>
SIMPLEX_ENABLED=true
# Git Platforms Forwarding Configuration
GIT_PLATFORMS_ENABLED=true
```
## Setup
### 1. Generate Encryption Keys
```bash
# Generate all three required keys
openssl rand -hex 32 # For MESSAGING_PREFS_ENCRYPTION_KEY
openssl rand -hex 32 # For MESSAGING_SALT_ENCRYPTION_KEY
openssl rand -hex 32 # For MESSAGING_LOOKUP_SECRET
```
### 2. Store Keys Securely
**Development:**
- Store in `.env` file (never commit to git)
**Production:**
- Use secret management service (AWS Secrets Manager, HashiCorp Vault, etc.)
- Or environment variables in deployment platform
- Consider using Hardware Security Modules (HSM) for maximum security
### 3. Configure Messaging Platforms
#### Telegram
1. Create a bot with [@BotFather](https://t.me/botfather)
2. Get bot token
3. Users will provide their chat ID when configuring preferences
#### Email
1. Configure SMTP settings (host, port, credentials)
2. Or use an SMTP API service
3. Users will provide their email addresses (to/cc)
#### SimpleX
1. Set up SimpleX Chat API
2. Configure API URL and key
3. Users will provide their contact ID
#### Git Platforms (GitHub, GitLab, Gitea, Codeberg, Forgejo)
1. Users create Personal Access Tokens with appropriate scopes
2. Users provide platform, username/org, repository name, and token
3. Events will be forwarded as issues or PRs on the selected platform
4. Supports self-hosted instances via custom API URL
## User Flow
### 1. User Saves Preferences (Client-Side)
```typescript
// User encrypts preferences to self on Nostr (kind 30078)
const encryptedContent = await window.nostr.nip44.encrypt(
userPubkey,
JSON.stringify(preferences)
);
// Publish to Nostr (backup/sync)
const event = {
kind: 30078,
pubkey: userPubkey,
tags: [['d', 'gitrepublic-messaging'], ['enabled', 'true']],
content: encryptedContent
};
// Sign and publish
const signedEvent = await signEventWithNIP07(event);
await nostrClient.publishEvent(signedEvent);
// Send decrypted copy to server (over HTTPS)
await fetch('/api/user/messaging-preferences', {
method: 'POST',
body: JSON.stringify({
preferences,
proofEvent: signedEvent
})
});
```
### 2. Server Stores Securely
- Verifies proof event signature
- Checks user has unlimited access
- Generates random salt
- Encrypts salt with `MESSAGING_SALT_ENCRYPTION_KEY`
- Derives per-user encryption key
- Encrypts preferences with AES-256-GCM
- Stores using HMAC lookup key
### 3. Event Forwarding
When user publishes an event (issue, PR, highlight):
1. Server checks user has unlimited access
2. Retrieves encrypted preferences
3. Decrypts (with rate limiting)
4. Checks if forwarding enabled and event kind matches
5. Forwards to configured platforms
## API Endpoints
### POST `/api/user/messaging-preferences`
Save messaging preferences.
**Request:**
```json
{
"preferences": {
"telegram": "@username",
"simplex": "contact-id",
"email": {
"to": ["user@example.com"],
"cc": ["cc@example.com"]
},
"gitPlatforms": [
{
"platform": "github",
"owner": "username",
"repo": "repository-name",
"token": "ghp_xxxxxxxxxxxx"
},
{
"platform": "gitlab",
"owner": "username",
"repo": "repository-name",
"token": "glpat-xxxxxxxxxxxx"
},
{
"platform": "codeberg",
"owner": "username",
"repo": "repository-name",
"token": "xxxxxxxxxxxx"
},
{
"platform": "onedev",
"owner": "project-path",
"repo": "repository-name",
"token": "xxxxxxxxxxxx",
"apiUrl": "https://your-onedev-instance.com"
},
{
"platform": "custom",
"owner": "username",
"repo": "repository-name",
"token": "xxxxxxxxxxxx",
"apiUrl": "https://your-git-instance.com/api/v1"
}
],
"enabled": true,
"notifyOn": ["1621", "1618"]
},
"proofEvent": { /* Signed Nostr event (kind 30078) */ }
}
```
**Response:**
```json
{
"success": true
}
```
### GET `/api/user/messaging-preferences`
Get preferences status (without decrypting).
**Response:**
```json
{
"configured": true,
"rateLimit": {
"remaining": 10,
"resetAt": null
}
}
```
### DELETE `/api/user/messaging-preferences`
Delete messaging preferences.
**Response:**
```json
{
"success": true
}
```
## Security Best Practices
1. **Key Management**
- Never commit keys to git
- Rotate keys periodically
- Use secret management services in production
- Consider HSM for maximum security
2. **Monitoring**
- Monitor rate limit violations
- Alert on decryption failures
- Audit log all preference changes
3. **Access Control**
- Only users with unlimited access can use this feature
- Requires valid signed Nostr event proof
- Server verifies all inputs
4. **Data Protection**
- All data encrypted at rest
- Per-user key derivation
- HMAC lookup keys
- Rate limiting on decryption
## Troubleshooting
### "Decryption rate limit exceeded"
User has exceeded 10 decryption attempts in 15 minutes. Wait for the window to reset.
### "Messaging forwarding requires unlimited access"
User must have relay write access (unlimited level) to use this feature.
### "Failed to forward event"
Check:
- Messaging platform API credentials
- Network connectivity
- Platform-specific error logs
## Email Setup
### Option 1: Direct SMTP
Install nodemailer:
```bash
npm install nodemailer
```
Configure environment variables:
```bash
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
SMTP_FROM_ADDRESS=noreply@yourdomain.com
SMTP_FROM_NAME=GitRepublic
EMAIL_ENABLED=true
```
### Option 2: SMTP API
Use an SMTP API service (e.g., SendGrid, Mailgun, AWS SES):
```bash
SMTP_API_URL=https://api.sendgrid.com/v3/mail/send
SMTP_API_KEY=your-api-key
EMAIL_ENABLED=true
```
## Git Platforms Setup
### Supported Platforms
- **GitHub** (`github`) - github.com
- **GitLab** (`gitlab`) - gitlab.com (also supports self-hosted with apiUrl)
- **Gitea** (`gitea`) - Self-hosted instances (defaults to codeberg.org if apiUrl not provided)
- **Codeberg** (`codeberg`) - codeberg.org (uses Gitea API)
- **Forgejo** (`forgejo`) - Self-hosted instances (defaults to forgejo.org if apiUrl not provided)
- **OneDev** (`onedev`) - Self-hosted instances (requires apiUrl)
- **Custom** (`custom`) - Any Gitea-compatible API with custom URL (requires apiUrl)
### Creating Personal Access Tokens
#### GitHub
1. Go to Settings → Developer settings → Personal access tokens → Tokens (classic)
2. Click "Generate new token (classic)"
3. Select `repo` scope
4. Generate and copy token
#### GitLab
1. Go to Settings → Access Tokens
2. Create token with `api` scope
3. Generate and copy token
#### Gitea/Codeberg/Forgejo
1. Go to Settings → Applications → Generate New Token
2. Select `repo` scope
3. Generate and copy token
#### OneDev
1. Go to User Settings → Access Tokens
2. Create a new access token
3. Select appropriate scopes (typically `write:issue` and `write:pull-request`)
4. Generate and copy token
5. **Note**: OneDev is self-hosted, so you must provide the `apiUrl` (e.g., `https://your-onedev-instance.com`)
### User Configuration
Users provide:
- **Platform**: One of `github`, `gitlab`, `gitea`, `codeberg`, `forgejo`, `onedev`, or `custom`
- **Owner**: Username or organization name (project path for OneDev)
- **Repo**: Repository name (project name for OneDev)
- **Token**: Personal access token (stored encrypted)
- **API URL**:
- **Required** for: `onedev`, `custom`
- **Optional** for: `gitea`, `forgejo`, `gitlab` (use for self-hosted instances)
- **Not used** for: `github`, `codeberg` (always use hosted instances)
- Format: Base URL of the instance (e.g., `https://your-gitea-instance.com/api/v1` or `https://your-onedev-instance.com`)
### Event Mapping
- **Nostr Issues (kind 1621)** → Platform Issues
- **Nostr PRs (kind 1618)** → Platform Pull Requests/Merge Requests (if branch info available) or Issues with PR label
- **Other events** → Platform Issues with event kind label
### Platform-Specific Notes
- **GitHub**: Uses `body` field and `head`/`base` for PRs. Always uses `https://api.github.com`
- **GitLab**: Uses `description` field instead of `body`, and `source_branch`/`target_branch` for PRs. Defaults to `https://gitlab.com/api/v4`, but supports self-hosted with `apiUrl`
- **Gitea**: Compatible with GitHub API format. Defaults to `https://codeberg.org/api/v1` (Codeberg), but supports self-hosted instances with `apiUrl` (e.g., `https://your-gitea-instance.com/api/v1`)
- **Codeberg**: Uses Gitea API format. Always uses `https://codeberg.org/api/v1`
- **Forgejo**: Compatible with GitHub API format. Defaults to `https://forgejo.org/api/v1`, but supports self-hosted instances with `apiUrl` (e.g., `https://your-forgejo-instance.com/api/v1`)
- **OneDev**: Uses `description` field and `source_branch`/`target_branch` for PRs. **Requires** `apiUrl` (self-hosted only). API endpoints: `/api/projects/{owner}/{repo}/issues` and `/api/projects/{owner}/{repo}/pull-requests`
- **Custom**: Must provide `apiUrl` pointing to Gitea-compatible API (assumes GitHub/Gitea API format)
### Security Note
All tokens are stored encrypted in the database. Users should:
- Use tokens with minimal required scopes
- Rotate tokens periodically
- Revoke tokens if compromised
## Future Enhancements
- [ ] Support for more messaging platforms
- [ ] User-configurable message templates
- [ ] Webhook support
- [ ] Encrypted preferences sync across devices
- [ ] Per-repository forwarding rules
- [ ] HTML email templates

117
docs/NIP_COMPLIANCE.md

@ -1,117 +0,0 @@
# NIP Compliance and Documentation Index
This document serves as an index to all Nostr Improvement Proposals (NIPs) used by GitRepublic and their implementation details.
## Standard NIPs
GitRepublic implements the following standard NIPs:
### Core Protocol
- **[NIP-01: Basic Protocol Flow](01.md)** - Event structure, signatures, and client-relay communication
- Foundation for all Nostr events
### Authentication & Identity
- **[NIP-02: Contact List](02.md)** - Contact list (kind 3)
- Used for repository filtering ("Show only my repos and those of my contacts")
- Fallback for relay discovery
- **[NIP-07: Browser Extension Authentication](07.md)** - `window.nostr` capability
- Primary authentication method for GitRepublic
- Used for signing all repository-related events
- **[NIP-19: bech32-encoded Entities](19.md)** - bech32 encoding (npub, nsec, note, nevent, naddr)
- User-friendly display of pubkeys and event references
- Used throughout the UI for repository URLs and search
- **[NIP-98: HTTP Authentication](98.md)** - HTTP auth events (kind 27235)
- Authenticates git operations (push, pull, clone)
- Authenticates API requests
### Event Management
- **[NIP-09: Event Deletion](09.md)** - Deletion requests (kind 5)
- Used for cleaning up failed fork attempts
- **[NIP-10: Event References](10.md)** - Event references (e, p tags)
- Used in patch series for threading patches
- **[NIP-22: Comments](22.md)** - Comment events (kind 1111)
- Threaded discussions on PRs, issues, and patches
### Git Collaboration
- **[NIP-34: Git Repository Announcements](34.md)** - Git collaboration on Nostr
- **30617**: Repository announcements
- **30618**: Repository state
- **1617**: Patches
- **1618**: Pull requests
- **1619**: Pull request updates
- **1621**: Issues
- **1630-1633**: Status events (Open, Applied/Merged, Closed, Draft)
### Relay & Discovery
- **[NIP-65: Relay List Metadata](65.md)** - Relay list (kind 10002)
- Discovers user's preferred relays for publishing events
### Content Features
- **[NIP-84: Highlights](84.md)** - Highlight events (kind 9802)
- Code selection and review features
- Extended with file/line tags for code context
## Custom Event Kinds
GitRepublic uses custom event kinds not defined in any standard NIP:
- **[Custom Event Kinds](CustomKinds.md)**
- **1640**: Commit Signature - Cryptographically sign git commits
- **1641**: Ownership Transfer - Transfer repository ownership (immutable chain)
- **30620**: Branch Protection - Enforce branch protection rules
## Quick Reference
### Event Kinds Used
| Kind | Name | NIP | Replaceable | Documentation |
|------|------|-----|-------------|---------------|
| 1 | Text Note | NIP-01 | No | [01.md](01.md) |
| 3 | Contact List | NIP-02 | Yes | [02.md](02.md) |
| 5 | Deletion Request | NIP-09 | No | [09.md](09.md) |
| 1111 | Comment | NIP-22 | No | [22.md](22.md) |
| 1617 | Patch | NIP-34 | No | [34.md](34.md) |
| 1618 | Pull Request | NIP-34 | No | [34.md](34.md) |
| 1619 | Pull Request Update | NIP-34 | No | [34.md](34.md) |
| 1621 | Issue | NIP-34 | No | [34.md](34.md) |
| 1630 | Status Open | NIP-34 | No | [34.md](34.md) |
| 1631 | Status Applied | NIP-34 | No | [34.md](34.md) |
| 1632 | Status Closed | NIP-34 | No | [34.md](34.md) |
| 1633 | Status Draft | NIP-34 | No | [34.md](34.md) |
| **1640** | **Commit Signature** | **Custom** | **No** | **[CustomKinds.md](CustomKinds.md)** |
| **1641** | **Ownership Transfer** | **Custom** | **No** | **[CustomKinds.md](CustomKinds.md)** |
| 30617 | Repo Announcement | NIP-34 | Yes | [34.md](34.md) |
| 30618 | Repo State | NIP-34 | Yes | [34.md](34.md) |
| **30620** | **Branch Protection** | **Custom** | **Yes** | **[CustomKinds.md](CustomKinds.md)** |
| 9802 | Highlight | NIP-84 | No | [84.md](84.md) |
| 10002 | Relay List | NIP-65 | Yes | [65.md](65.md) |
| 27235 | HTTP Auth | NIP-98 | No | [98.md](98.md) |
## Implementation Status
All listed NIPs are **fully implemented** and compliant with their specifications. Each NIP document includes a "GitRepublic Usage" section describing how the NIP is used in this application.
## Compliance Verification
For detailed compliance verification and implementation notes, see the individual NIP documents linked above. Each document includes:
- The original NIP specification
- GitRepublic-specific usage documentation
- Implementation details and code references
## See Also
- [NIP-34 Documentation](34.md) - Core git collaboration features
- [Custom Event Kinds](CustomKinds.md) - GitRepublic-specific event kinds
- [Architecture FAQ](ARCHITECTURE_FAQ.md) - System architecture overview
- [Implementation Details](IMPLEMENTATION.md) - Technical implementation notes

206
docs/PUBLISH.md

@ -1,206 +0,0 @@
# Publishing GitRepublic CLI to npm
## Prerequisites
1. **Create npm account** (if you don't have one):
- Visit https://www.npmjs.com/signup
- Or run: `npm adduser`
2. **Enable Two-Factor Authentication (2FA) or Create Access Token**:
npm requires either TOTP/SMS 2FA or a granular access token to publish packages. Biometric authentication (fingerprint) alone is not sufficient for CLI publishing.
**Option A: Enable TOTP/SMS 2FA** (Recommended for regular use):
- Go to https://www.npmjs.com/settings/[your-username]/security
- Look for "Two-factor authentication" section
- If you only see biometric options, you may need to:
1. Check if there's an "Advanced" or "More options" link
2. Look for "Authenticator app" or "SMS" options
3. Some accounts may need to disable biometric first to see other options
- **If using TOTP app** (recommended):
- You'll see a QR code on your computer screen
- Scan it with your phone's authenticator app (Google Authenticator, Authy, 1Password, etc.)
- The app will generate 6-digit codes that you'll use when logging in
- **If using SMS**:
- Enter your phone number
- You'll receive codes via text message
- Follow the setup instructions to complete the setup
**Option B: Create Granular Access Token** (Alternative if 2FA setup is difficult):
- Go to https://www.npmjs.com/settings/[your-username]/tokens
- Click "Generate New Token"
- Choose "Granular Access Token"
- Set permissions: Select "Publish" for the package(s) you want to publish
- Enable "Bypass 2FA" option (this is required for publishing)
- Copy the token (you'll only see it once!)
- Use it for authentication:
```bash
npm config set //registry.npmjs.org/:_authToken YOUR_TOKEN_HERE
```
- Or set it as an environment variable:
```bash
export NPM_TOKEN=YOUR_TOKEN_HERE
```
3. **Login to npm from your computer** (if using Option A):
```bash
npm logout # Log out first if already logged in
npm login
```
- Enter your username, password, and email
- If 2FA is enabled, you'll be prompted for the authentication code
- **Get the code from your phone's authenticator app** (if using TOTP) or check your SMS (if using SMS)
- Enter the 6-digit code when prompted
3. **Check if package name is available**:
```bash
npm view gitrepublic-cli
```
If it returns 404, the name is available. If it shows package info, the name is taken.
## Publishing Steps
### 1. Update version (if needed)
```bash
# Patch version (1.0.0 -> 1.0.1)
npm version patch
# Minor version (1.0.0 -> 1.1.0)
npm version minor
# Major version (1.0.0 -> 2.0.0)
npm version major
```
Or manually edit `package.json` and update the version field.
### 2. Verify package contents
```bash
# See what will be published
npm pack --dry-run
```
This shows the files that will be included (based on `files` field in package.json).
### 3. Test the package locally
```bash
# Pack the package
npm pack
# Install it locally to test
npm install -g ./gitrepublic-cli-1.0.0.tgz
# Test the commands
gitrepublic-path --credential
gitrepublic-path --hook
```
### 4. Publish to npm
```bash
cd gitrepublic-cli
npm publish
```
For scoped packages (if you want `@your-org/gitrepublic-cli`):
```bash
npm publish --access public
```
### 5. Verify publication
```bash
# Check on npm website
# Visit: https://www.npmjs.com/package/gitrepublic-cli
# Or via command line
npm view gitrepublic-cli
```
## After Publishing
Users can now install via:
```bash
npm install -g gitrepublic-cli
```
## Updating the Package
1. Make your changes
2. Update version: `npm version patch` (or minor/major)
3. Publish: `npm publish`
## Important Notes
- **Package name**: `gitrepublic-cli` must be unique on npm. If taken, use a scoped name like `@your-org/gitrepublic-cli`
- **Version**: Follow semantic versioning (semver)
- **Files**: Only files listed in `files` array (or not in `.npmignore`) will be published
- **Unpublishing**: You can unpublish within 72 hours, but it's discouraged. Use deprecation instead:
```bash
npm deprecate gitrepublic-cli@1.0.0 "Use version 1.0.1 instead"
```
## Troubleshooting
### "Access token expired or revoked"
- Your npm login session has expired
- Solution: Run `npm login` again to authenticate
- Verify you're logged in: `npm whoami`
### "403 Forbidden - Two-factor authentication or granular access token with bypass 2fa enabled is required"
- npm requires 2FA (TOTP/SMS) or a granular access token to publish packages
- Biometric authentication (fingerprint) alone is not sufficient for CLI publishing
- **Solution Option 1: Enable TOTP/SMS 2FA**
1. Visit: https://www.npmjs.com/settings/[your-username]/security
2. Look for "Two-factor authentication" section
3. If you only see biometric options:
- Check for "Advanced" or "More options" links
- Look for "Authenticator app" or "SMS" options
- You may need to disable biometric first to see other options
4. Enable TOTP app (recommended) or SMS
5. Follow setup instructions
6. After enabling, log out and log back in: `npm logout` then `npm login`
- **Solution Option 2: Use Granular Access Token** (if 2FA setup is difficult)
1. Visit: https://www.npmjs.com/settings/[your-username]/tokens
2. Click "Generate New Token" → "Granular Access Token"
3. Set permissions: Select "Publish" for your package(s)
4. **Important**: Enable "Bypass 2FA" option
5. Copy the token (save it securely - you'll only see it once!)
6. Use it for authentication:
```bash
npm config set //registry.npmjs.org/:_authToken YOUR_TOKEN_HERE
```
7. Or set as environment variable:
```bash
export NPM_TOKEN=YOUR_TOKEN_HERE
```
8. Now you can publish: `npm publish`
### "404 Not Found - PUT https://registry.npmjs.org/gitrepublic-cli"
- This is normal for a first publish (package doesn't exist yet)
- Make sure you're logged in: `npm login`
- Check if package name is available: `npm view gitrepublic-cli` (should return 404)
### "Package name already exists"
- The name `gitrepublic-cli` is taken
- Options:
1. Use a scoped package: Change name to `@your-org/gitrepublic-cli` in package.json
2. Choose a different name
3. Contact the owner of the existing package
### "You do not have permission"
- Make sure you're logged in: `npm whoami`
- If using scoped package, add `--access public` flag
### "Invalid package name"
- Package names must be lowercase
- Can contain hyphens and underscores
- Cannot start with dot or underscore
- Max 214 characters
### npm warnings about package.json
- If you see warnings about `bin` script names being "cleaned", this is usually fine - npm normalizes them
- If you see warnings about `repositories` field, remove it and use only the `repository` field (single object, not array)

191
docs/SECURITY.md

@ -1,191 +0,0 @@
# Security Analysis
## Current Security Model
This is a **multi-tenant system** where multiple users (identified by Nostr pubkeys/npubs) share the same server instance with **application-level isolation** but **no process or filesystem isolation**.
### Security Measures in Place
1. **Path Validation**
- ✅ File paths are validated and sanitized
- ✅ Path traversal attempts (`..`) are blocked
- ✅ Absolute paths are rejected
- ✅ Null bytes and control characters are blocked
- ✅ Path length limits enforced (4096 chars)
2. **Input Validation**
- ✅ npub format validation (must be valid bech32)
- ✅ Repository name validation (alphanumeric, hyphens, underscores, dots only)
- ✅ No path separators allowed in repo names
3. **Access Control**
- ✅ Repository ownership verified via Nostr events
- ✅ Private repos require NIP-98 authentication
- ✅ Maintainer checks before allowing write operations
- ✅ Ownership transfer chain validation
4. **Path Construction**
- ✅ Uses `path.join()` which prevents path traversal
- ✅ Repository path: `join(repoRoot, npub, `${repoName}.git`)`
- ✅ File paths within repos are validated separately
## Security Concerns
### ⚠ **Critical: No Process Isolation**
**Issue**: All repositories run in the same Node.js process. If an attacker compromises one repository or finds a code execution vulnerability, they could potentially:
- Access other users' repositories
- Read/write files outside the repo directory
- Access server configuration or secrets
**Mitigation**:
- Path validation prevents most traversal attacks
- Access control checks prevent unauthorized access
- But a process-level compromise would bypass these
### ✅ **High: git-http-backend Security** - IMPROVED
**Previous Issue**: `git-http-backend` was spawned with `GIT_PROJECT_ROOT` set to the entire `repoRoot`, allowing potential access to all repositories.
**Current Protection** (✅ IMPLEMENTED):
- ✅ `GIT_PROJECT_ROOT` now set to **specific repository path** (not entire repoRoot)
- ✅ `PATH_INFO` adjusted to be relative to the repository
- ✅ Path validation ensures repository path is within `repoRoot`
- ✅ Limits git-http-backend's view to only the intended repository
**Remaining Concerns**:
- No chroot/jail isolation (git-http-backend still runs in same process context)
- If git-http-backend has vulnerabilities, it could still access files within the repo
- ✅ Runs as dedicated `gitrepublic` user (non-root) - IMPLEMENTED
### ⚠ **Medium: No Resource Limits Per Tenant**
**Issue**: No per-user resource limits:
- One user could exhaust disk space (2GB per repo limit, but unlimited repos)
- One user could exhaust memory/CPU
- No rate limiting per user
**Current Protection**:
- 2GB repository size limit
- 500MB per-file limit
- But no per-user quotas
### ✅ **Medium: Filesystem Access** - IMPROVED
**Previous Issue**: Repository paths were not validated to ensure they stayed within `repoRoot`.
**Current Protection** (✅ IMPLEMENTED):
- ✅ Repository path validation using `resolve()` to check absolute paths
- ✅ Ensures resolved repository path starts with resolved `repoRoot`
- ✅ Prevents path traversal attacks at the repository level
- ✅ File path validation within repositories (already existed)
- ✅ Access control checks for private repos
**Remaining Concerns**:
- No chroot/jail isolation
- All repos readable by the same process user
- Relies on application logic, not OS-level isolation
### ⚠ **Low: Network Isolation**
**Issue**: All repos accessible from same endpoints:
- No network-level isolation between tenants
- All repos share same IP/domain
**Impact**: Low - this is expected for a multi-tenant service
## Security Improvements Made
### ✅ Implemented (2024)
1. **✅ Repository Path Validation**
- Added `resolve()` checks to ensure repository paths stay within `repoRoot`
- Prevents path traversal attacks at the repository level
- Applied to all git operations (GET and POST handlers)
2. **✅ git-http-backend Isolation**
- Changed `GIT_PROJECT_ROOT` from entire `repoRoot` to specific repository path
- Adjusted `PATH_INFO` to be relative to the repository
- Limits git-http-backend's view to only the intended repository
3. **✅ File Path Validation** (Already existed)
- Validates file paths within repositories
- Prevents path traversal within repos
- Blocks absolute paths, null bytes, control characters
## Recommendations
### ✅ Implemented (2024)
1. **✅ Resource Limits** - IMPLEMENTED
- ✅ Per-user repository count limits (configurable via `MAX_REPOS_PER_USER`)
- ✅ Per-user disk quota (configurable via `MAX_DISK_QUOTA_PER_USER`)
- ✅ Rate limiting per user/IP (configurable via `RATE_LIMIT_*` env vars)
- ✅ Applied to fork operations and repository creation
2. **✅ Audit Logging** - IMPLEMENTED
- ✅ Logs all repository access attempts
- ✅ Logs all file operations (read/write/delete)
- ✅ Logs authentication attempts
- ✅ Logs ownership transfers
- ✅ Structured JSON logging format
3. **✅ Enhanced git-http-backend Security** - IMPLEMENTED
- ✅ Operation timeouts (5 minutes max)
- ✅ Process isolation (no shell, minimal environment)
- ✅ Audit logging for all git operations
- ✅ Path validation and scoping
- ⚠ Chroot/jail still not implemented (complex, requires root or capabilities)
### Remaining (Medium Priority)
### Medium Priority
4. **Process Isolation** (Complex)
- Run each tenant in separate container/process
- Use Docker with per-tenant containers
- Significant architectural change
5. **Filesystem Isolation**
- Use bind mounts with restricted permissions
- Implement per-tenant filesystem quotas
- Use separate volumes per tenant
6. **✅ Audit Logging** - IMPLEMENTED
- ✅ Log all repository access attempts
- ✅ Log all file operations
- ⏳ Monitor for suspicious patterns (requires log analysis tools)
### Long-term
7. **Container-per-Tenant Architecture**
- Each user gets their own container
- Complete isolation
- Higher resource overhead
8. **Kubernetes Namespaces**
- Use K8s namespaces for tenant isolation
- Network policies for isolation
- Resource quotas per namespace
## Current Security Posture
**For a decentralized, open-source git hosting service**, the current security model is **reasonable but not enterprise-grade**:
**Adequate for**:
- Public repositories
- Open-source projects
- Personal/community hosting
- Low-to-medium security requirements
**Not adequate for**:
- Enterprise multi-tenant SaaS
- Highly sensitive/regulated data
- Environments requiring strict compliance (HIPAA, PCI-DSS, etc.)
- High-security government/military use
## Conclusion
The system uses **application-level security** with good input validation and access control, but lacks **OS-level isolation**. This is a common trade-off for multi-tenant services - it's simpler and more resource-efficient, but less secure than process/container isolation.
**Recommendation**: For most use cases (public repos, open-source hosting), the current model is acceptable. For enterprise or high-security use cases, consider implementing process/container isolation.

268
docs/SECURITY_IMPLEMENTATION.md

@ -1,268 +0,0 @@
# Security Implementation Plan
This document outlines the implementation of security improvements in two tiers:
1. **Lightweight** - Single container, application-level improvements
2. **Enterprise** - Multi-container/Kubernetes with process isolation
## Architecture Overview
### Lightweight (Single Container)
- Application-level security controls
- Resource limits enforced in code
- Rate limiting in application
- Audit logging
- Works with current Docker setup
### Enterprise (Kubernetes)
- Process isolation per tenant
- Network policies
- Resource quotas per namespace
- Separate volumes per tenant
- Scales horizontally
## Implementation Plan
### Phase 1: Lightweight Improvements (Single Container)
These improvements work in the current single-container setup and provide immediate security benefits.
#### 1.1 Resource Limits Per User
**Implementation**: Application-level tracking and enforcement
**Files to create/modify**:
- `src/lib/services/security/resource-limits.ts` - Track and enforce limits
- `src/routes/api/repos/[npub]/[repo]/+server.ts` - Check limits before operations
**Features**:
- Per-user repository count limit (configurable, default: 100)
- Per-user disk quota (configurable, default: 10GB)
- Per-repository size limit (already exists: 2GB)
- Per-file size limit (already exists: 500MB)
**Configuration**:
```typescript
// Environment variables
MAX_REPOS_PER_USER=100
MAX_DISK_QUOTA_PER_USER=10737418240 // 10GB in bytes
```
#### 1.2 Rate Limiting
**Implementation**: In-memory or Redis-based rate limiting
**Files to create/modify**:
- `src/lib/services/security/rate-limiter.ts` - Rate limiting logic
- `src/hooks.server.ts` - Apply rate limits to requests
**Features**:
- Per-IP rate limiting (requests per minute)
- Per-user rate limiting (operations per minute)
- Different limits for different operations:
- Git operations (clone/push): 60/min
- File operations: 30/min
- API requests: 120/min
**Configuration**:
```typescript
// Environment variables
RATE_LIMIT_ENABLED=true
RATE_LIMIT_WINDOW_MS=60000 // 1 minute
RATE_LIMIT_MAX_REQUESTS=120
```
#### 1.3 Audit Logging
**Implementation**: Structured logging to files/console
**Files to create/modify**:
- `src/lib/services/security/audit-logger.ts` - Audit logging service
- All API endpoints - Add audit log entries
**Features**:
- Log all repository access attempts
- Log all file operations (read/write/delete)
- Log authentication attempts (success/failure)
- Log ownership transfers
- Include: timestamp, user pubkey, IP, action, result
**Log Format**:
```json
{
"timestamp": "2024-01-01T12:00:00Z",
"user": "abc123...",
"ip": "192.168.1.1",
"action": "repo.clone",
"repo": "npub1.../myrepo",
"result": "success",
"metadata": {}
}
```
**Storage**:
- **Console**: Always logs to stdout (JSON format, prefixed with `[AUDIT]`)
- **File**: Optional file logging (if `AUDIT_LOG_FILE` is set)
- Daily rotation: Creates new file each day (e.g., `audit-2024-01-01.log`)
- Location: Configurable via `AUDIT_LOG_FILE` environment variable
- Default location: Console only (no file logging by default)
**Retention**:
- **Default**: 90 days (configurable via `AUDIT_LOG_RETENTION_DAYS`)
- **Automatic cleanup**: Old log files are automatically deleted
- **Rotation**: Logs rotate daily at midnight (based on date change)
- **Set to 0**: Disables automatic cleanup (manual cleanup required)
**Example Configuration**:
```bash
# Log to /var/log/gitrepublic/audit.log (with daily rotation)
AUDIT_LOG_FILE=/var/log/gitrepublic/audit.log
AUDIT_LOG_RETENTION_DAYS=90
# Or use Docker volume
AUDIT_LOG_FILE=/app/logs/audit.log
AUDIT_LOG_RETENTION_DAYS=30
```
#### 1.4 Enhanced git-http-backend Hardening
**Implementation**: Additional security measures for git-http-backend
**Files to modify**:
- `src/routes/api/git/[...path]/+server.ts` - Add security measures
**Features**:
- Validate PATH_INFO to prevent manipulation
- Set restrictive environment variables
- Timeout for git operations
- Resource limits for spawned processes
### Phase 2: Enterprise Improvements (Kubernetes)
These require multi-container architecture and Kubernetes.
#### 2.1 Container-per-Tenant Architecture
**Architecture**:
- Each user (npub) gets their own namespace
- Each namespace has:
- Application pod (gitrepublic instance)
- Persistent volume for repositories
- Service for networking
- Resource quotas
**Kubernetes Resources**:
- `k8s/namespace-template.yaml` - Namespace per tenant
- `k8s/deployment-template.yaml` - Application deployment
- `k8s/service-template.yaml` - Service definition
- `k8s/pvc-template.yaml` - Persistent volume claim
- `k8s/resource-quota.yaml` - Resource limits
#### 2.2 Network Isolation
**Implementation**: Kubernetes Network Policies
**Files to create**:
- `k8s/network-policy.yaml` - Network isolation rules
**Features**:
- Namespace-level network isolation
- Only allow traffic from ingress controller
- Block inter-namespace communication
- Allow egress to Nostr relays only
#### 2.3 Resource Quotas
**Implementation**: Kubernetes ResourceQuota
**Features**:
- CPU limits per tenant
- Memory limits per tenant
- Storage limits per tenant
- Pod count limits
#### 2.4 Separate Volumes Per Tenant
**Implementation**: Kubernetes PersistentVolumeClaims
**Features**:
- Each tenant gets their own volume
- Volume size limits
- Backup/restore per tenant
- Snapshot support
## Hybrid Approach (Recommended)
The hybrid approach implements lightweight improvements first, then provides a migration path to enterprise architecture.
### Benefits:
1. **Immediate security improvements** - Lightweight features work now
2. **Scalable architecture** - Can migrate to Kubernetes when needed
3. **Cost-effective** - Start simple, scale as needed
4. **Flexible deployment** - Works in both scenarios
### Implementation Strategy:
1. **Start with lightweight** - Implement Phase 1 features
2. **Design for scale** - Code structure supports multi-container
3. **Add Kubernetes support** - Phase 2 when needed
4. **Gradual migration** - Move tenants to K8s as needed
## File Structure
```
src/lib/services/security/
├── resource-limits.ts # Resource limit tracking
├── rate-limiter.ts # Rate limiting
├── audit-logger.ts # Audit logging
└── quota-manager.ts # Disk quota management
k8s/
├── base/
│ ├── namespace.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── pvc.yaml
├── overlays/
│ ├── single-container/ # Single container setup
│ └── multi-tenant/ # Kubernetes setup
└── helm-chart/ # Optional Helm chart
```
## Configuration
### Lightweight Mode (Single Container)
```yaml
# docker-compose.yml or .env
SECURITY_MODE=lightweight
MAX_REPOS_PER_USER=100
MAX_DISK_QUOTA_PER_USER=10737418240
RATE_LIMIT_ENABLED=true
AUDIT_LOGGING_ENABLED=true
```
### Enterprise Mode (Kubernetes)
```yaml
# Kubernetes ConfigMap
security:
mode: enterprise
isolation: container-per-tenant
networkPolicy: enabled
resourceQuotas: enabled
```
## Migration Path
### From Lightweight to Enterprise:
1. **Phase 1**: Deploy lightweight improvements (no architecture change)
2. **Phase 2**: Add Kubernetes support alongside single container
3. **Phase 3**: Migrate high-value tenants to Kubernetes
4. **Phase 4**: Full Kubernetes deployment (optional)
## Priority Implementation Order
1. ✅ **Audit Logging** - Easy, high value, works everywhere
2. ✅ **Rate Limiting** - Prevents abuse, works in single container
3. ✅ **Resource Limits** - Prevents resource exhaustion
4. ⏳ **Enhanced git-http-backend** - Additional hardening
5. ⏳ **Kubernetes Support** - When scaling needed

76
docs/about.md

@ -0,0 +1,76 @@
# About GitRepublic
GitRepublic is a decentralized git hosting platform built on Nostr. Unlike traditional git hosting services, GitRepublic provides truly decentralized repository hosting where repositories are announced on Nostr relays, giving you full control and ownership.
## Key Features
- **Decentralized**: Repositories are announced on Nostr relays, no central authority
- **Nostr-based Authentication**: Uses NIP-07 (browser extensions) and NIP-98 (HTTP authentication)
- **Full Control**: Own your repositories, transfer ownership, manage maintainers, control access
- **Open Collaboration**: Create pull requests, issues, and collaborate using Nostr events
- **Multi-remote Sync**: Automatically syncs to multiple remotes when you push
- **GRASP Compatible**: Works seamlessly with GRASP servers
## Documentation Topics
### Core Concepts
- [How this git server integrates Nostr](./nostr-integration.md) - Understanding the Nostr-based architecture
### Repository Operations
- [Creating, forking, transferring, or cloning a repo](./repo-operations.md) - Getting started with repositories
- [Editing a repo](./editing-repos.md) - Branch management, file management, auto-provisioning, file-editing and event-creation permissions
- [Managing a repo](./managing-repos.md) - Complete guide to repository management
- [Repo Header](./managing-repos.md#repo-header)
- [Clone Section](./managing-repos.md#clone-section)
- [File Tab](./managing-repos.md#file-tab)
- [Commit Tab](./managing-repos.md#commit-tab)
- [PRs Tab](./managing-repos.md#prs-tab)
- [Issues Tab](./managing-repos.md#issues-tab)
- [Patches Tab](./managing-repos.md#patches-tab)
- [Discussions Tab](./managing-repos.md#discussions-tab)
- [Docs Tab](./managing-repos.md#docs-tab)
- [History Tab](./managing-repos.md#history-tab)
- [Tags Tab](./managing-repos.md#tags-tab)
### Discovery and Access
- [Search and viewing external git repos](./search-and-external-repos.md) - Finding and viewing repositories
### API and CLI
- [REST API and CLI](./api-and-cli.md) - Programmatic access and command-line tools
### User Features
- [Profile pages](./profile-pages.md) - User profiles and payment targets
- [Settings and Dashboard](./settings-and-dashboard.md) - User account management
### Infrastructure
- [Working with GRASP servers](./grasp-servers.md) - GRASP protocol compatibility
- [Lightweight versus Enterprise modes](./deployment-modes.md) - Deployment options and security
### Technical Details
- [Tech stack used](./tech-stack.md) - Code editor, syntax highlighter, programming languages, logger, etc.
- [Specs used](./specs.md) - Links to NIPs and GRASP docs, and list of custom events
## Getting Started
1. **Install a NIP-07 browser extension** (Alby, nos2x, etc.)
2. **Visit a GitRepublic instance** and connect with your extension
3. **Create your first repository** via the signup page
4. **Start collaborating** using pull requests, issues, and patches
For detailed instructions, see the topic pages above.
## Additional Resources
- [API Documentation](../src/routes/api/openapi.json/openapi.json) - OpenAPI specification

250
docs/api-and-cli.md

@ -0,0 +1,250 @@
# REST API and CLI
Complete guide to programmatic access via REST API and command-line interface.
## REST API
GitRepublic provides a comprehensive REST API for all operations.
### Authentication
All write operations require **NIP-98 HTTP Authentication**:
```
Authorization: Nostr <base64-encoded-event-json>
```
The event must be:
- Kind 27235 (NIP-98 authentication event)
- Include `u` tag with request URL
- Include `method` tag with HTTP method
- Include `payload` tag with SHA256 hash of request body (for POST/PUT)
- Signed with your Nostr private key
### API Documentation
Full API documentation is available in OpenAPI format:
- **Development**: `http://localhost:5173/api/openapi.json`
- **Production**: `https://your-domain.com/api/openapi.json`
View interactive documentation at `/api/openapi.json` or use any OpenAPI viewer.
### Endpoints Overview
#### Repository Management
- `GET /api/repos/list` - List all repositories
- `GET /api/repos/local` - List local repositories
- `GET /api/repos/{npub}/{repo}/settings` - Get repository settings
- `POST /api/repos/{npub}/{repo}/settings` - Update repository settings
- `GET /api/repos/{npub}/{repo}/maintainers` - Get maintainers
- `POST /api/repos/{npub}/{repo}/maintainers` - Add maintainer
- `DELETE /api/repos/{npub}/{repo}/maintainers` - Remove maintainer
- `POST /api/repos/{npub}/{repo}/fork` - Fork repository
- `DELETE /api/repos/{npub}/{repo}/delete` - Delete repository
- `POST /api/repos/{npub}/{repo}/transfer` - Transfer ownership
- `POST /api/repos/{npub}/{repo}/clone` - Clone to server
#### File Operations
- `GET /api/repos/{npub}/{repo}/file` - Get file content
- `POST /api/repos/{npub}/{repo}/file` - Create/update/delete file
- `GET /api/repos/{npub}/{repo}/tree` - List files and directories
- `GET /api/repos/{npub}/{repo}/raw` - Get raw file content
- `GET /api/repos/{npub}/{repo}/readme` - Get README content
#### Git Operations
- `GET /api/repos/{npub}/{repo}/branches` - List branches
- `POST /api/repos/{npub}/{repo}/branches` - Create branch
- `GET /api/repos/{npub}/{repo}/tags` - List tags
- `POST /api/repos/{npub}/{repo}/tags` - Create tag
- `GET /api/repos/{npub}/{repo}/commits` - List commits
- `GET /api/repos/{npub}/{repo}/commits/{hash}/verify` - Verify commit signature
- `GET /api/repos/{npub}/{repo}/diff` - Get diff between commits
- `GET /api/repos/{npub}/{repo}/default-branch` - Get default branch
- `POST /api/repos/{npub}/{repo}/default-branch` - Set default branch
#### Collaboration
- `GET /api/repos/{npub}/{repo}/prs` - List pull requests
- `POST /api/repos/{npub}/{repo}/prs` - Create pull request
- `PATCH /api/repos/{npub}/{repo}/prs` - Update PR status
- `POST /api/repos/{npub}/{repo}/prs/{prId}/merge` - Merge PR
- `GET /api/repos/{npub}/{repo}/issues` - List issues
- `POST /api/repos/{npub}/{repo}/issues` - Create issue
- `PATCH /api/repos/{npub}/{repo}/issues` - Update issue status
- `GET /api/repos/{npub}/{repo}/patches` - List patches
- `POST /api/repos/{npub}/{repo}/patches` - Create patch
- `PATCH /api/repos/{npub}/{repo}/patches` - Update patch status
- `POST /api/repos/{npub}/{repo}/patches/{patchId}/apply` - Apply patch
- `GET /api/repos/{npub}/{repo}/highlights` - List highlights/comments
- `POST /api/repos/{npub}/{repo}/highlights` - Create highlight/comment
#### Search and Discovery
- `GET /api/search` - Search repositories
- `GET /api/repos/{npub}/{repo}/code-search` - Search code in repository
- `GET /api/code-search` - Global code search
- `GET /api/repos/{npub}/{repo}/clone-urls/reachability` - Check clone URL reachability
#### User Operations
- `GET /api/users/{npub}/profile` - Get user profile
- `GET /api/users/{npub}/repos` - Get user's repositories
- `GET /api/user/level` - Get user access level
- `GET /api/user/git-dashboard` - Get git dashboard
- `GET /api/user/messaging-preferences` - Get messaging preferences
- `POST /api/user/messaging-preferences` - Update messaging preferences
#### Infrastructure
- `GET /api/config` - Get server configuration
- `GET /api/tor/onion` - Get Tor .onion address
- `GET /api/transfers/pending` - Get pending ownership transfers
#### Git HTTP Backend
- `GET /api/git/{npub}/{repo}.git/{path}` - Git smart HTTP operations
- Supports: `info/refs`, `git-upload-pack`, `git-receive-pack`
- Handles: clone, fetch, push operations
### Example API Usage
```bash
# List repositories
curl https://your-domain.com/api/repos/list
# Get repository settings
curl https://your-domain.com/api/repos/{npub}/{repo}/settings
# Create file (requires NIP-98 auth)
curl -X POST https://your-domain.com/api/repos/{npub}/{repo}/file \
-H "Authorization: Nostr <base64-event>" \
-H "Content-Type: application/json" \
-d '{"path": "test.txt", "content": "Hello", "commitMessage": "Add file", "branch": "main", "action": "write"}'
```
## Command Line Interface (CLI)
The GitRepublic CLI provides full access to all features from the command line.
### Installation
```bash
npm install -g gitrepublic-cli
```
### Setup
```bash
# Set your Nostr private key
export NOSTRGIT_SECRET_KEY="nsec1..."
# Configure credential helper and commit hook
gitrep-setup
```
### Getting Help
For complete CLI documentation, run:
```bash
gitrep --help
```
This shows:
- Initial setup instructions
- All git commands
- All API commands
- Repository management
- Publishing Nostr events
- Environment variables
- And much more
### Common CLI Operations
#### Git Operations
```bash
# Clone repository
gitrep clone https://domain.com/api/git/npub1.../repo.git
# Push changes
gitrep push origin main
# Pull changes
gitrep pull origin main
```
#### Repository Management
```bash
# List repositories
gitrep repos list
# Get repository info
gitrep repos get <npub> <repo>
# Update settings
gitrep repos settings <npub> <repo> --visibility public
# Manage maintainers
gitrep repos maintainers <npub> <repo> add <maintainer-npub>
```
#### Publishing Events
```bash
# Publish repository announcement
gitrep publish repo-announcement myrepo --description "My repo"
# Create pull request
gitrep publish pr <owner> <repo> "PR Title" --content "Description"
# Create issue
gitrep publish issue <owner> <repo> "Issue Title" --content "Description"
```
#### Multi-Remote Operations
```bash
# Push to all remotes
gitrep push-all main
# Pull from all remotes and merge
gitrep pull-all --merge
```
### CLI Features
- **Git Wrapper**: Enhanced error messages for GitRepublic operations
- **Credential Helper**: Automatic NIP-98 authentication
- **Commit Signing**: Automatic commit signatures via hook
- **API Access**: Full command-line access to all APIs
- **Multi-Remote Sync**: Push/pull to/from all remotes
## Authentication
### NIP-98 for API
For API access, create NIP-98 authentication events:
1. Create ephemeral event (kind 27235)
2. Add tags: `u` (URL), `method` (HTTP method), `payload` (body hash)
3. Sign with your Nostr private key
4. Base64 encode the event JSON
5. Include in `Authorization: Nostr <base64-event>` header
### CLI Authentication
The CLI handles authentication automatically:
- **Credential Helper**: Generates NIP-98 events for git operations
- **API Commands**: Uses NIP-98 for all API calls
- **No manual setup**: Just set `NOSTRGIT_SECRET_KEY` and run `gitrep-setup`
## Next Steps
- [Tech stack used](./tech-stack.md) - Technical implementation details
- [Specs used](./specs.md) - NIPs and GRASP documentation

128
docs/deployment-modes.md

@ -0,0 +1,128 @@
# Lightweight versus Enterprise Modes
GitRepublic supports two deployment modes with different security and isolation characteristics.
## Lightweight Mode (Single Container) - Default
Lightweight mode is the default deployment option, suitable for most use cases.
### Characteristics
- **Single container**: All users share the same container
- **Shared filesystem**: Repositories stored in shared directory structure
- **Process isolation**: Uses git-http-backend with process isolation
- **Resource limits**: Per-user repository count and disk quota limits
- **Rate limiting**: Per-IP and per-user rate limiting
### Security Features
- **Resource Limits**:
- Maximum repositories per user (default: 100)
- Maximum disk quota per user (default: 10 GB)
- Maximum file size (500 MB)
- Maximum repository size (2 GB)
- **Rate Limiting**:
- Per-IP rate limiting for all operations
- Per-user rate limiting for authenticated operations
- Configurable rate limit windows
- **Path Validation**:
- Strict path validation to prevent traversal attacks
- All file operations validated against repository root
- Git operations scoped to repository directories
- **Audit Logging**:
- Comprehensive logging of all security-relevant events
- User actions, access attempts, permission checks
- Repository operations and ownership transfers
- **git-http-backend Hardening**:
- Timeouts for git operations
- Process isolation
- Scoped access to repository directories
- Input validation and sanitization
### Configuration
Set `ENTERPRISE_MODE=false` or leave unset (default).
### Use Cases
- Personal git hosting
- Small to medium teams
- Development environments
- Single-tenant deployments
## Enterprise Mode (Kubernetes)
Enterprise mode provides maximum isolation and security for multi-tenant deployments.
### Characteristics
- **Container-per-tenant**: Each tenant has their own container
- **Network isolation**: Kubernetes Network Policies
- **Resource quotas**: Per-tenant CPU, memory, and storage limits
- **Separate volumes**: Each tenant has their own PersistentVolume
- **Complete isolation**: Tenants cannot access each other's resources
### Security Features
All lightweight mode features, plus:
- **Process Isolation**:
- Each tenant runs in separate container
- No shared processes or memory
- Complete process isolation
- **Network Isolation**:
- Kubernetes Network Policies
- Tenant-to-tenant communication blocked
- Only necessary network access allowed
- **Resource Quotas**:
- Per-tenant CPU limits
- Per-tenant memory limits
- Per-tenant storage limits
- Enforced by Kubernetes
- **Volume Isolation**:
- Each tenant has dedicated PersistentVolume
- No shared storage access
- Complete filesystem isolation
### Configuration
Set `ENTERPRISE_MODE=true` environment variable.
### Deployment
See `k8s/ENTERPRISE_MODE.md` for complete setup instructions.
### Use Cases
- Multi-tenant SaaS deployments
- Enterprise customers
- High-security requirements
- Regulatory compliance needs
## Security Comparison
| Feature | Lightweight Mode | Enterprise Mode |
|---------|-----------------|-----------------|
| Process Isolation | git-http-backend only | Container-per-tenant |
| Filesystem Isolation | Directory-based | Volume-per-tenant |
| Network Isolation | None | Kubernetes Policies |
| Resource Limits | Per-user quotas | Per-tenant Kubernetes quotas |
| Audit Logging | ✅ | ✅ |
| Path Validation | ✅ | ✅ |
| Rate Limiting | ✅ | ✅ |
## Security Documentation
Security features are implemented in both lightweight and enterprise modes. See the codebase for detailed implementation.
## Next Steps
- [Tech stack used](./tech-stack.md) - Technical implementation
- [Specs used](./specs.md) - Security-related specifications

190
docs/editing-repos.md

@ -0,0 +1,190 @@
# Editing a Repo
This page covers all aspects of editing repositories: branch management, file management, auto-provisioning, file-editing permissions, and event-creation permissions.
## Auto-Provisioning
When you create a repository announcement, GitRepublic automatically:
1. **Polls Nostr relays** for new announcements
2. **Creates a bare git repository** at `/repos/{npub}/{repo-name}.git`
3. **Fetches self-transfer event** for ownership verification
4. **Creates initial commit** with README.md (if provided)
5. **Saves announcement and transfer events** to `nostr/repo-events.jsonl`
6. **Syncs from other remotes** if clone URLs are configured
The repository is ready to use immediately after announcement.
## Branch Management
### Creating Branches
#### Via Web Interface
1. Navigate to your repository
2. Click **Create Branch** button
3. Enter branch name
4. Select source branch
5. Click **Create**
#### Via Git
```bash
git checkout -b feature/new-feature
git push origin feature/new-feature
```
### Viewing Branches
- **Web Interface**: View all branches on the repository page
- **API**: `GET /api/repos/{npub}/{repo}/branches`
- **CLI**: `gitrep repos branches <npub> <repo>`
### Default Branch
The default branch (usually `main`) can be changed via:
- **Web Interface**: Repository settings
- **API**: `POST /api/repos/{npub}/{repo}/default-branch`
## File Management
### Reading Files
#### Via Web Interface
1. Navigate to repository
2. Click **File** tab
3. Browse directory structure
4. Click files to view content
#### Via API
```bash
GET /api/repos/{npub}/{repo}/file?path={file-path}&branch={branch}
```
#### Via CLI
```bash
gitrep file get <npub> <repo> <path> [branch]
```
### Creating/Updating Files
#### Via Web Interface
1. Navigate to repository
2. Click **File** tab
3. Click **Edit** button on a file (or create new)
4. Use the code editor to make changes
5. Enter commit message
6. Select branch
7. Click **Save**
#### Via API
```bash
POST /api/repos/{npub}/{repo}/file
{
"path": "file.txt",
"content": "File content",
"commitMessage": "Add file",
"branch": "main",
"action": "write"
}
```
#### Via CLI
```bash
gitrep file put <npub> <repo> <path> [file] [message] [branch]
```
### Deleting Files
#### Via Web Interface
1. Navigate to file
2. Click **Delete** button
3. Enter commit message
4. Click **Delete**
#### Via API
```bash
POST /api/repos/{npub}/{repo}/file
{
"path": "file.txt",
"commitMessage": "Remove file",
"branch": "main",
"action": "delete"
}
```
#### Via CLI
```bash
gitrep file delete <npub> <repo> <path> [message] [branch]
```
## Permissions
### File Editing Permissions
- **Repository Owner**: Can edit all files
- **Maintainers**: Can edit all files
- **Other Users**: Cannot edit files (read-only)
### Event Creation Permissions
Different events have different permission requirements:
- **Pull Requests**: Anyone can create
- **Issues**: Anyone can create
- **Patches**: Anyone can create
- **Comments**: Anyone can comment
- **Status Updates**: Only owners/maintainers can update PR/issue status
- **Repository Settings**: Only owners/maintainers can update
### Branch Protection
Repository owners can protect branches:
- **Require Pull Requests**: Direct pushes blocked, must use PRs
- **Require Reviews**: PRs need approval before merging
- **Require Status Checks**: Custom checks must pass
Configure via:
- **Web Interface**: Repository settings → Branch Protection
- **API**: `POST /api/repos/{npub}/{repo}/branch-protection`
## Code Editor
The web interface includes a full-featured code editor with:
- **Syntax Highlighting**: Supports many languages
- **Line Numbers**: Easy navigation
- **Word Wrap**: Toggle for long lines
- **Search/Replace**: Find and replace functionality
- **File Browser**: Navigate directory structure
## Commit Signing
Commits can be automatically signed using Nostr keys:
- **CLI**: Automatic signing via commit hook (if configured)
- **Web Interface**: Commits are signed by the server using your NIP-07 key
Commit signatures are stored as Nostr events (kind 1640) and can be verified.
## File Size Limits
- **Maximum file size**: 500 MB per file
- **Maximum repository size**: 2 GB total
These limits prevent abuse and ensure reasonable resource usage.
## Next Steps
- [Managing a repo](./managing-repos.md) - Complete repository management guide
- [REST API and CLI](./api-and-cli.md) - Programmatic access

108
docs/grasp-servers.md

@ -0,0 +1,108 @@
# Working with GRASP Servers
GitRepublic provides minimal GRASP (Git Repository Announcement and Synchronization Protocol) interoperability for seamless compatibility with GRASP servers.
## What is GRASP?
GRASP is a protocol specification for decentralized git hosting that combines git smart HTTP with Nostr relays. GRASP servers provide git repository hosting with Nostr-based announcements and state management.
## GitRepublic GRASP Support
### What We Support
1. **GRASP Server Detection**
- Automatically identifies GRASP servers from repository announcements
- Uses GRASP-01 identification (clone URL pattern + matching `relays` tag)
- Displays GRASP server status in clone URL reachability
2. **Clone URL Reachability**
- Tests and displays reachability status for all clone URLs
- Shows which remotes (including GRASP servers) are accessible
- Indicates server type (Git, GRASP)
3. **Multi-Remote Synchronization**
- When you push, automatically syncs to all remotes listed in announcement
- Includes GRASP servers in sync operations
- Handles sync failures gracefully
4. **Local Pull Command**
- Use `gitrep pull-all --merge` to fetch and merge from all remotes
- Checks reachability first, only pulls from accessible remotes
- Detects conflicts before merging (aborts unless `--allow-conflicts`)
- Works with GRASP servers seamlessly
5. **Standard Git Operations**
- Full compatibility with GRASP servers for clone, push, pull
- Uses standard git smart HTTP protocol
- No special configuration needed
### What We Don't Support (By Design)
- **Full GRASP-01 server compliance**: We're not a full GRASP server
- **GRASP-02 proactive sync**: No server-side hourly pulls (user-controlled via CLI)
- **GRASP-05 archive mode**: Not implemented
## Using GRASP Servers
### Cloning from GRASP Servers
```bash
# Clone from a GRASP server (works just like any git server)
gitrep clone https://grasp.example.com/npub1.../repo.git
```
### Pushing to Multiple Remotes
When your repository has both GitRepublic and GRASP servers in clone URLs:
```bash
# Push to your repo (automatically syncs to all remotes including GRASP)
gitrep push origin main
```
GitRepublic will:
1. Push to the primary server
2. Automatically sync to all other remotes (including GRASP servers)
3. Handle sync failures gracefully
### Pulling from Multiple Remotes
```bash
# Pull from all remotes including GRASP servers
gitrep pull-all --merge
```
This command:
- Checks reachability of all remotes
- Fetches from accessible remotes
- Merges changes into your current branch
- Detects conflicts before merging
### Clone URL Reachability
View which remotes (including GRASP servers) are accessible:
- **Web Interface**: Repository page shows reachability status
- **API**: `GET /api/repos/{npub}/{repo}/clone-urls/reachability`
- **CLI**: `gitrep repos get <npub> <repo>` shows reachability
## GRASP Server Identification
GRASP servers are identified by:
- Clone URL pattern matching GRASP conventions
- Matching `relays` tag in repository announcement
- Server response indicating GRASP support
GitRepublic automatically detects GRASP servers and displays them appropriately.
## Best Practices
1. **Add GRASP servers to clone URLs**: Include GRASP server URLs in your repository announcement
2. **Test reachability**: Check that all remotes (including GRASP) are reachable
3. **Use pull-all**: Regularly pull from all remotes to stay in sync
4. **Handle conflicts**: Use `--allow-conflicts` if you need to proceed despite conflicts
## Next Steps
- [REST API and CLI](./api-and-cli.md) - Multi-remote operations
- [Specs used](./specs.md) - GRASP documentation links

239
docs/managing-repos.md

@ -0,0 +1,239 @@
# Managing a Repo
Complete guide to managing repositories in GitRepublic, including all tabs and sections of the repository interface.
## Repository Header
The repository header displays:
- **Repository name and description**
- **Owner information** (with link to profile)
- **Visibility badge** (public, unlisted, restricted, private)
- **Action menu** (three dots):
- Clone to server (if not already cloned)
- Fork repository
- Transfer ownership (owners only)
- Delete repository (owners only)
## Clone Section
The clone section shows:
- **All clone URLs** for the repository
- **Reachability status** for each URL (✅ reachable, ❌ unreachable)
- **Server type** indicators (Git, GRASP)
- **Copy buttons** for easy URL copying
- **Tor .onion URL** (if configured)
Clone URLs are extracted from the repository announcement and can include:
- This server's URL
- Other GitRepublic instances
- GRASP servers
- Other git hosts (GitHub, GitLab, etc.)
## File Tab
Browse and edit files in the repository.
### Features
- **Directory navigation**: Click folders to browse
- **File viewing**: Click files to view content
- **File editing**: Click "Edit" to modify files
- **File creation**: Click "New File" to create files
- **File deletion**: Click "Delete" to remove files
- **Raw file access**: Click "Raw" for direct file content
- **Syntax highlighting**: Automatic for code files
- **Line numbers**: Easy reference
- **Search**: Find text in files
### Permissions
- **Read**: Anyone can view public repositories
- **Write**: Only owners and maintainers can edit
## Commit Tab
View commit history and details.
### Features
- **Commit list**: Chronological list of all commits
- **Commit details**: Click commits to view:
- Commit message
- Author information
- Files changed
- Diff view
- **Commit verification**: Verify commit signatures (Nostr events)
- **Branch filter**: View commits for specific branches
- **Diff view**: See what changed in each commit
### Commit Verification
Commits can be verified if they have Nostr signatures (kind 1640):
- Shows signature validity
- Displays signer information
- Links to signature event
## PRs Tab
Manage pull requests.
### Features
- **PR list**: View all pull requests
- **PR status**: Open, merged, closed, draft
- **Create PR**: Propose changes from forks
- **PR details**: View PR description, diff, comments
- **Merge PR**: Merge PRs into target branch (maintainers only)
- **Update PR status**: Close, reopen, mark as draft
### Creating Pull Requests
1. Fork the repository (if needed)
2. Make changes in your fork
3. Push changes to a branch
4. Navigate to original repository
5. Click "Create Pull Request"
6. Fill in details and submit
### PR Status Management
- **Open**: Active PR ready for review
- **Merged**: PR has been merged
- **Closed**: PR closed without merging
- **Draft**: Work in progress
## Issues Tab
Track bugs, feature requests, and tasks.
### Features
- **Issue list**: View all issues
- **Issue status**: Open, resolved, closed, draft
- **Create issue**: Report bugs or request features
- **Issue details**: View description, comments, status
- **Update status**: Resolve, close, reopen issues
### Issue Status
- **Open**: Active issue needing attention
- **Resolved**: Issue has been fixed
- **Closed**: Issue closed (duplicate, won't fix, etc.)
- **Draft**: Issue still being written
## Patches Tab
View and manage patches (kind 1617).
### Features
- **Patch list**: View all patches
- **Patch status**: Open, applied, closed, draft
- **Create patch**: Submit patch content directly
- **Apply patch**: Apply patches to repository (maintainers only)
- **Patch series**: View linked patch series
### Patches vs Pull Requests
- **Patches**: For small changes (< 60KB), email-style workflow
- **Pull Requests**: For large changes, branch-based workflow
## Discussions Tab
View discussions and comments.
### Features
- **Discussion list**: View all discussions
- **Comments**: Threaded comments on PRs, issues, highlights
- **Code highlights**: Highlighted code sections with comments
- **Reply**: Reply to comments and highlights
### Discussion Types
- **PR Comments**: Comments on pull requests
- **Issue Comments**: Comments on issues
- **Code Highlights**: Comments on specific code sections
- **General Discussions**: Standalone discussion threads
## Docs Tab
View documentation files.
### Features
- **Documentation list**: View all documentation files
- **Markdown rendering**: Automatic markdown rendering
- **Documentation addresses**: Links to external documentation (naddr format)
Documentation can be:
- **README files**: Automatically rendered
- **Documentation files**: Listed in repository announcement
- **External docs**: Referenced via naddr
## History Tab
View repository history.
### Features
- **Commit timeline**: Visual timeline of commits
- **Branch visualization**: See branch structure
- **Tag markers**: See where tags were created
- **Filter by branch**: View history for specific branches
## Tags Tab
Manage git tags.
### Features
- **Tag list**: View all tags
- **Tag details**: View tag name, commit, message
- **Create tag**: Create new tags
- **Tag releases**: Use tags for version releases
### Creating Tags
#### Via Web Interface
1. Navigate to Tags tab
2. Click "Create Tag"
3. Enter tag name
4. Select commit
5. Add message (optional)
6. Click "Create"
#### Via Git
```bash
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin v1.0.0
```
## Repository Settings
Access repository settings via the repository menu or Settings link.
### Available Settings
- **Description**: Update repository description
- **Visibility**: Change visibility level (public, unlisted, restricted, private)
- **Project Relays**: Configure project relays for event publishing
- **Maintainers**: Add/remove maintainers
- **Clone URLs**: Add/remove clone URLs
- **Ownership Transfer**: Transfer repository to another user
### Visibility Levels
- **Public**: Repository and events published to all relays
- **Unlisted**: Repository public, events only to project relay
- **Restricted**: Repository private, events only to project relay
- **Private**: Repository private, no relay publishing
## Next Steps
- [Search and viewing external git repos](./search-and-external-repos.md) - Finding repositories
- [REST API and CLI](./api-and-cli.md) - Programmatic management

86
docs/nostr-integration.md

@ -0,0 +1,86 @@
# How GitRepublic Integrates Nostr
GitRepublic is built entirely on Nostr, using Nostr events and relays for repository announcements, authentication, and collaboration. This page explains how the git server integrates with Nostr.
## Core Integration Points
### Repository Announcements (NIP-34)
Repositories are announced via Nostr events (kind 30617) that are published to Nostr relays. These announcements contain:
- Repository metadata (name, description, clone URLs)
- Visibility settings (public, unlisted, restricted, private)
- Project relays for event publishing
- Maintainer information
- Clone URLs pointing to git servers
The server automatically polls Nostr relays for new repository announcements and provisions git repositories when found.
### Authentication
GitRepublic uses two Nostr authentication methods:
1. **NIP-07** - Browser extension authentication for web interface
- Users connect via browser extensions (Alby, nos2x, etc.)
- No private keys stored on server
- Secure, user-controlled authentication
2. **NIP-98** - HTTP authentication for git operations
- Ephemeral events (kind 27235) for each git operation
- Includes request URL, method, and payload hash
- Verified by server before allowing operations
- Used for clone, push, pull operations
### Event Publishing
All repository-related events are published to Nostr relays based on repository visibility:
- **Public**: Published to all default relays + project relays
- **Unlisted**: Published only to project relays
- **Restricted**: Published only to project relays
- **Private**: Not published to relays (git-only, stored locally)
Events are always saved to the repository's `nostr/repo-events.jsonl` file for offline papertrail, regardless of visibility.
### Ownership and Permissions
Repository ownership is tracked via Nostr events:
- **Self-transfer events** (kind 1641) prove initial ownership
- **Ownership transfer events** (kind 1641) create a chain of ownership
- **Maintainer information** stored in repository announcements
- All ownership events saved to repository for verification
### Collaboration Events
All collaboration features use Nostr events:
- **Pull Requests** (kind 1618)
- **Pull Request Updates** (kind 1619)
- **Issues** (kind 1621)
- **Status Events** (kinds 1630-1633)
- **Patches** (kind 1617)
- **Highlights** (kind 9802, NIP-84)
- **Comments** (kind 1111, NIP-22)
These events are published to relays (based on visibility) and stored in the repository.
## Event Flow
1. **User creates repository announcement** → Published to Nostr relays
2. **Server polls relays** → Finds announcement → Provisions git repository
3. **User pushes changes** → Server verifies NIP-98 auth → Processes push
4. **Collaboration events created** → Published to relays → Stored in repo
5. **Other users discover events** → Via relays or repository storage
## Benefits of Nostr Integration
- **Decentralized**: No single point of failure
- **Censorship Resistant**: Events stored on multiple relays
- **User Controlled**: Users own their keys and data
- **Interoperable**: Works with any Nostr client
- **Offline Papertrail**: All events saved to repository
## Technical Details
For complete technical details on event kinds, tags, and structure, see [Specs Used](./specs.md).

111
docs/profile-pages.md

@ -0,0 +1,111 @@
# Profile Pages
User profiles in GitRepublic display comprehensive user information, repositories, and payment targets.
## Viewing Profiles
Navigate to a user's profile page:
```
https://{domain}/users/{npub}
```
## Profile Information
Profiles display:
- **User name**: From Nostr profile event (kind 0)
- **About/Bio**: User description
- **Profile picture**: Avatar image
- **Banner image**: Profile banner (if available)
- **Payment targets**: Lightning addresses and payment information (NIP-A3)
- **Repositories**: List of user's repositories
- **Activity**: Recent activity and contributions
## Payment Targets (NIP-A3)
GitRepublic supports payment targets using NIP-A3 (kind 10133) and merges payment information from multiple sources.
### Supported Payment Types
- **Lightning**: Lightning Network addresses (e.g., `user@wallet.example.com`)
- **Bitcoin**: Bitcoin addresses
- **Ethereum**: Ethereum addresses
- **Nano**: Nano addresses
- **Monero**: Monero addresses
- And more (see [NIP-A3 documentation](./NIP-A3.md))
### Payment Target Sources
GitRepublic merges payment information from:
1. **NIP-01 (kind 0)**: Lightning addresses from `lud16` tags or JSON `lud16` field
2. **NIP-A3 (kind 10133)**: All payment targets from `payto` tags
The system:
- Normalizes addresses (lowercase) for deduplication
- Merges lightning addresses from both sources
- Displays all payment targets with `payto://` URIs
- Provides copy buttons for easy sharing
### Creating Payment Target Events
To add payment targets to your profile, publish a kind 10133 event:
```json
{
"kind": 10133,
"content": "",
"tags": [
["payto", "lightning", "user@wallet.example.com"],
["payto", "bitcoin", "bc1qxq66e0t8d7ugdecwnmv58e90tpry23nc84pg9k"]
],
"created_at": 1234567890
}
```
## Repository Listings
User profiles show:
- **All repositories**: Public and user's own repositories
- **Repository status**: Registered, local, verified
- **Repository descriptions**: Quick overview
- **Clone URLs**: Direct access to repositories
## API Access
Fetch user profiles via API:
```bash
GET /api/users/{npub}/profile
```
Response includes:
- Full profile event (kind 0)
- Payment targets array with `payto://` URIs
- Payment event (kind 10133) if available
- Merged lightning addresses
## CLI Access
The GitRepublic CLI can fetch profiles:
```bash
gitrep profile fetch <npub>
```
Automatically merges payment targets and returns `payto://` URIs.
## Profile Event Format
GitRepublic supports both profile event formats:
- **Old format**: JSON in `content` field
- **New format**: Tags-based format (recommended)
Both formats are supported for backward compatibility.
## Next Steps
- [Settings and Dashboard](./settings-and-dashboard.md) - User account management
- [NIP-A3 documentation](./NIP-A3.md) - Complete payment target details

153
docs/repo-operations.md

@ -0,0 +1,153 @@
# Creating, Forking, Transferring, or Cloning a Repo
This page covers all operations for creating, forking, transferring ownership, and cloning repositories in GitRepublic.
## Creating a Repository
### Via Web Interface
1. Navigate to the **Sign Up** page (`/signup`)
2. Connect your NIP-07 browser extension
3. Fill in repository details:
- **Repository Name**: Choose a unique name
- **Description**: Describe your repository
- **Visibility**: Select visibility level (public, unlisted, restricted, private)
- **Project Relays**: Add project relays (required for unlisted/restricted)
- **Clone URLs**: Add existing clone URLs if migrating
- **Maintainers**: Add maintainer npubs (optional)
4. Click **Publish Repository Announcement**
The server will automatically:
- Create a Nostr event announcing your repository
- Provision a bare git repository
- Create a self-transfer event for ownership proof
- Publish events to relays (based on visibility)
- Save events to the repository
### Via CLI
```bash
gitrep publish repo-announcement <repo-name> \
--description "My repository" \
--clone-url "https://domain.com/api/git/npub1.../repo.git" \
--visibility public
```
See `gitrep --help` for complete CLI documentation.
### Repository URL Structure
Your repository will be accessible at:
- Web: `https://{domain}/repos/{your-npub}/{repository-name}`
- Git: `https://{domain}/api/git/{your-npub}/{repository-name}.git` (recommended)
## Forking a Repository
Forking creates your own copy of a repository that you can modify independently.
### Via Web Interface
1. Navigate to the repository you want to fork
2. Click the **Fork** button
3. Enter a name for your fork
4. Click **Fork**
GitRepublic will:
- Clone the repository to your account
- Create a new repository announcement for your fork
- Set you as the owner
- Preserve visibility and project-relay settings from original
- Add a reference to the original repository
### Via CLI
```bash
gitrep repos fork <owner-npub> <repo-name>
```
### Working with Forks
After forking:
- Clone your fork: `git clone https://{domain}/api/git/{your-npub}/{fork-name}.git`
- Make changes and push to your fork
- Create a pull request back to the original repository
## Transferring Ownership
Transfer repository ownership to another user.
### Via Web Interface
1. Navigate to your repository
2. Click **Transfer Ownership** (in repository menu)
3. Enter the new owner's npub
4. Confirm the transfer
The transfer process:
1. Creates an ownership transfer event (kind 1641)
2. Publishes to Nostr relays
3. Saves to repository for verification
4. Notifies new owner when they log in
5. New owner completes transfer by publishing new announcement
### Via CLI
```bash
gitrep repos transfer <npub> <repo> <new-owner-npub>
```
**Important**: Ownership transfers are permanent and create a chain of ownership events. The new owner will have full control.
## Cloning a Repository
### Public Repositories
Anyone can clone public repositories:
```bash
git clone https://{domain}/api/git/{owner-npub}/{repo-name}.git
```
### Private/Restricted Repositories
Private and restricted repositories require authentication:
1. **Install GitRepublic CLI**:
```bash
npm install -g gitrepublic-cli
```
2. **Set your Nostr private key**:
```bash
export NOSTRGIT_SECRET_KEY="nsec1..."
```
3. **Run setup**:
```bash
gitrep-setup
```
4. **Clone the repository**:
```bash
git clone https://{domain}/api/git/{owner-npub}/{repo-name}.git
```
The credential helper will automatically generate NIP-98 authentication tokens.
### Clone URL Paths
GitRepublic supports multiple clone URL paths:
- `/api/git/{npub}/{repo}.git` - Recommended (best compatibility)
- `/repos/{npub}/{repo}.git` - Alternative path
- `/{npub}/{repo}.git` - Root path
All paths work, but `/api/git/` is recommended for best compatibility with commit signing hooks.
## Multi-Remote Synchronization
If a repository has multiple clone URLs configured, GitRepublic automatically syncs changes to all remotes when you push. You can see all clone URLs on the repository page.
## Next Steps
- [Editing a repo](./editing-repos.md) - Learn about branch management and file operations
- [Managing a repo](./managing-repos.md) - Complete repository management guide

110
docs/search-and-external-repos.md

@ -0,0 +1,110 @@
# Search and Viewing External Git Repos
This page covers searching for repositories and viewing external git repositories as read-only.
## Searching Repositories
### Via Web Interface
1. Navigate to the **Search** page (`/search`)
2. Enter search query
3. View results with:
- Repository name and description
- Owner information
- Clone URLs
- Visibility status
### Via API
```bash
GET /api/search?q={query}
```
### Via CLI
```bash
gitrep search <query>
```
### Search Features
- **Name search**: Find repositories by name
- **Description search**: Find by description content
- **Owner search**: Find repositories by owner
- **Tag search**: Find by repository tags
Search queries repositories from Nostr relays and returns matching results.
## Viewing External Git Repos
GitRepublic can display external git repositories as read-only, even if they're not announced on Nostr.
### How It Works
1. **Repository Detection**: If a repository has clone URLs pointing to external git hosts (GitHub, GitLab, etc.)
2. **API Fallback**: GitRepublic attempts to fetch repository data via the external host's API
3. **Read-Only Display**: Repository is displayed with limited functionality:
- View files and directories
- View commit history
- View branches and tags
- **Cannot**: Edit files, create PRs, or push changes
### Supported External Hosts
GitRepublic supports API fallback for:
- **GitHub**: Via GitHub API
- **GitLab**: Via GitLab API
- **Other git hosts**: If they provide compatible APIs
### Limitations
When viewing external repos:
- **Read-only**: No editing or pushing
- **Limited features**: Some features may not be available
- **API dependent**: Requires external host's API to be accessible
- **No local clone**: Repository is not cloned to server
### Clone URLs
External repositories are identified by their clone URLs. If a repository announcement includes clone URLs pointing to external hosts, GitRepublic will attempt to display them.
## Repository Discovery
### Public Repositories
Public repositories are discoverable via:
- **Search**: Search across Nostr relays
- **User profiles**: Browse user's repositories
- **Repository listings**: View all public repositories
### Unlisted Repositories
Unlisted repositories:
- **Not in search results**: Won't appear in general search
- **Accessible if you know the URL**: Can be accessed directly
- **Events only to project relay**: Collaboration events only published to project relay
### Restricted/Private Repositories
Restricted and private repositories:
- **Not discoverable**: Won't appear in search
- **Require authentication**: Must be owner or maintainer to access
- **Limited event publishing**: Events only to project relay (restricted) or not at all (private)
## Clone URL Reachability
GitRepublic tests clone URL reachability and displays status:
- **✅ Reachable**: Server responds and is accessible
- **❌ Unreachable**: Server not accessible or returns error
- **Server type**: Indicates if it's a Git server or GRASP server
View reachability via:
- **Web Interface**: Repository page shows reachability for all clone URLs
- **API**: `GET /api/repos/{npub}/{repo}/clone-urls/reachability`
- **CLI**: `gitrep repos get <npub> <repo>` shows reachability
## Next Steps
- [REST API and CLI](./api-and-cli.md) - Programmatic search and access
- [Profile pages](./profile-pages.md) - Browse user repositories

90
docs/settings-and-dashboard.md

@ -0,0 +1,90 @@
# Settings and Dashboard
User account settings and dashboard features in GitRepublic.
## User Dashboard
Access your dashboard at `/user/dashboard` or via the user menu.
### Dashboard Features
- **Repository overview**: Quick view of your repositories
- **Activity summary**: Recent activity and contributions
- **Pending transfers**: Ownership transfers waiting for your approval
- **Access level**: Your current access level (unlimited, standard, etc.)
- **Quick actions**: Common operations
## User Settings
### Access Level
View your access level:
- **Unlimited**: Full access to all features
- **Standard**: Standard user access
- **Limited**: Restricted access
Access level determines:
- Repository creation limits
- API rate limits
- Feature availability
### Messaging Preferences
Configure messaging and notification preferences:
- **Enable/disable messaging**: Control message forwarding
- **Relay preferences**: Configure preferred relays for messages
- **Notification settings**: Control what you're notified about
Access via:
- **Web Interface**: User settings menu
- **API**: `GET /api/user/messaging-preferences`
- **CLI**: Via API commands
### Git Dashboard
View git-specific information:
- **Repository statistics**: Count, size, activity
- **Clone operations**: Recent clone activity
- **Push operations**: Recent push activity
Access via:
- **Web Interface**: User dashboard
- **API**: `GET /api/user/git-dashboard`
## Repository Settings
Repository-specific settings are managed on the repository page (see [Managing a Repo](./managing-repos.md)).
### Available Settings
- **Description**: Update repository description
- **Visibility**: Change visibility level
- **Project Relays**: Configure project relays
- **Maintainers**: Add/remove maintainers
- **Clone URLs**: Manage clone URLs
- **Branch Protection**: Configure branch protection rules
- **Ownership Transfer**: Transfer repository ownership
## SSH Keys
GitRepublic supports SSH key verification for git operations (optional).
### Verifying SSH Keys
- **API**: `GET /api/user/ssh-keys/verify`
- **Purpose**: Verify SSH keys for git operations
## Environment Variables
For CLI operations, configure these environment variables:
- `NOSTRGIT_SECRET_KEY`: Your Nostr private key (required for CLI)
- `GITREPUBLIC_SERVER`: Default server URL (optional)
- `NOSTR_RELAYS`: Comma-separated relay URLs (optional)
## Next Steps
- [Managing a Repo](./managing-repos.md) - Repository-specific settings
- [REST API and CLI](./api-and-cli.md) - Programmatic access to settings

186
docs/specs.md

@ -0,0 +1,186 @@
# Specs Used
Complete list of Nostr Improvement Proposals (NIPs), GRASP specifications, and custom event kinds used by GitRepublic.
## Standard NIPs
### Core Protocol
- **[NIP-01: Basic Protocol Flow](https://github.com/nostr-protocol/nips/blob/master/01.md)**
- Event structure, signatures, and client-relay communication
- Foundation for all Nostr events
### Authentication & Identity
- **[NIP-07: Browser Extension Authentication](https://github.com/nostr-protocol/nips/blob/master/07.md)**
- `window.nostr` capability for browser extensions
- Primary authentication method for GitRepublic web interface
- Used for signing all repository-related events
- **[NIP-19: bech32-encoded Entities](https://github.com/nostr-protocol/nips/blob/master/19.md)**
- bech32 encoding (npub, nsec, note, nevent, naddr)
- User-friendly display of pubkeys and event references
- Used throughout the UI for repository URLs and search
- **[NIP-98: HTTP Authentication](https://github.com/nostr-protocol/nips/blob/master/98.md)**
- HTTP auth events (kind 27235)
- Authenticates git operations (push, pull, clone)
- Authenticates API requests
- Ephemeral events for each request
### Event Management
- **[NIP-09: Event Deletion](https://github.com/nostr-protocol/nips/blob/master/09.md)**
- Deletion requests (kind 5)
- Used for cleaning up failed operations
- **[NIP-10: Event References](https://github.com/nostr-protocol/nips/blob/master/10.md)**
- Event references (e, p tags)
- Used in patch series for threading patches
- Used in PR updates and comments
- **[NIP-22: Comments](https://github.com/nostr-protocol/nips/blob/master/22.md)**
- Comment events (kind 1111)
- Threaded discussions on PRs, issues, and patches
- NIP-10 reply threading support
### Git Collaboration
- **[NIP-34: Git Repository Announcements](https://github.com/nostr-protocol/nips/blob/master/34.md)**
- Complete git collaboration specification
- **30617**: Repository announcements (replaceable)
- **30618**: Repository state (replaceable, optional)
- **1617**: Patches
- **1618**: Pull requests
- **1619**: Pull request updates
- **1621**: Issues
- **1630**: Status Open
- **1631**: Status Applied/Merged
- **1632**: Status Closed
- **1633**: Status Draft
### Relay & Discovery
- **[NIP-02: Contact List](https://github.com/nostr-protocol/nips/blob/master/02.md)**
- Contact list (kind 3)
- Used for repository filtering
- Fallback for relay discovery
- **[NIP-65: Relay List Metadata](https://github.com/nostr-protocol/nips/blob/master/65.md)**
- Relay list (kind 10002)
- Discovers user's preferred relays for publishing events
- Used to determine which relays to publish to
### Content Features
- **[NIP-84: Highlights](https://github.com/nostr-protocol/nips/blob/master/84.md)**
- Highlight events (kind 9802)
- Code selection and review features
- Extended with file/line tags for code context
### Payment Targets
- **[NIP-A3: Payment Targets](https://github.com/nostr-protocol/nips/blob/master/A3.md)**
- Payment target events (kind 10133)
- `payto://` URI scheme (RFC-8905)
- Supports multiple payment types (Lightning, Bitcoin, Ethereum, etc.)
See [NIP-A3 documentation](./NIP-A3.md) for complete details.
## GRASP Specifications
GitRepublic provides minimal GRASP interoperability:
- **GRASP-01**: Server identification and clone URL patterns
- Detects GRASP servers from repository announcements
- Identifies GRASP servers by URL pattern and relay tags
- **GRASP-02**: Proactive synchronization (not implemented)
- We don't do server-side hourly pulls
- User-controlled via CLI `pull-all` command
- **GRASP-05**: Archive mode (not implemented)
For GRASP documentation, see the GRASP specification repository.
## Custom Event Kinds
GitRepublic uses custom event kinds not defined in any standard NIP:
### Kind 1640: Commit Signature
Cryptographically sign git commits using Nostr keys.
**Tags**:
- `commit`: Final commit hash
- `author`: Author name (first occurrence)
- `author`: Author email (second occurrence)
- `message`: Commit message
- `e`: Optional reference to NIP-98 auth event
**Status**: Custom implementation (may be proposed as NIP in future)
See [Custom Event Kinds](./CustomKinds.md#kind-1640-commit-signature) for complete documentation.
### Kind 1641: Ownership Transfer
Transfer repository ownership from one pubkey to another.
**Tags**:
- `a`: Repository address (30617:pubkey:repo)
- `p`: New owner pubkey
- `d`: Repository name
- `t`: "self-transfer" marker (for initial ownership proof)
**Status**: Custom implementation (non-replaceable to maintain ownership chain)
See [Custom Event Kinds](./CustomKinds.md#kind-1641-ownership-transfer) for complete documentation.
### Kind 30620: Branch Protection
Enforce branch protection rules (require PRs, reviews, status checks).
**Tags**:
- `d`: Repository name
- `a`: Repository identifier
- `branch`: Branch name and protection settings
**Status**: Custom implementation (replaceable)
See [Custom Event Kinds](./CustomKinds.md#30620---branch_protection) for complete documentation.
## Event Kind Reference
| Kind | Name | NIP | Replaceable | Documentation |
|------|------|-----|-------------|---------------|
| 1 | Text Note | NIP-01 | No | [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md) |
| 3 | Contact List | NIP-02 | Yes | [NIP-02](https://github.com/nostr-protocol/nips/blob/master/02.md) |
| 5 | Deletion Request | NIP-09 | No | [NIP-09](https://github.com/nostr-protocol/nips/blob/master/09.md) |
| 1111 | Comment | NIP-22 | No | [NIP-22](https://github.com/nostr-protocol/nips/blob/master/22.md) |
| 1617 | Patch | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 1618 | Pull Request | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 1619 | Pull Request Update | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 1621 | Issue | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 1630 | Status Open | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 1631 | Status Applied | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 1632 | Status Closed | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 1633 | Status Draft | NIP-34 | No | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| **1640** | **Commit Signature** | **Custom** | **No** | **[CustomKinds.md](./CustomKinds.md)** |
| **1641** | **Ownership Transfer** | **Custom** | **No** | **[CustomKinds.md](./CustomKinds.md)** |
| 30617 | Repo Announcement | NIP-34 | Yes | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| 30618 | Repo State | NIP-34 | Yes | [NIP-34](https://github.com/nostr-protocol/nips/blob/master/34.md) |
| **30620** | **Branch Protection** | **Custom** | **Yes** | **[CustomKinds.md](./CustomKinds.md)** |
| 9802 | Highlight | NIP-84 | No | [NIP-84](https://github.com/nostr-protocol/nips/blob/master/84.md) |
| 10002 | Relay List | NIP-65 | Yes | [NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md) |
| 10133 | Payment Targets | NIP-A3 | Yes | [NIP-A3](https://github.com/nostr-protocol/nips/blob/master/A3.md) |
| 27235 | HTTP Auth | NIP-98 | No | [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md) |
## Complete Event Documentation
For complete event structure documentation with JSON examples, see:
- [Custom Event Kinds](./CustomKinds.md) - Custom event documentation
## Next Steps
- [How this git server integrates Nostr](./nostr-integration.md) - Understanding the integration
- [Tech stack used](./tech-stack.md) - Technical implementation

159
docs/tech-stack.md

@ -0,0 +1,159 @@
# Tech Stack Used
Technical implementation details of GitRepublic, including programming languages, frameworks, libraries, and tools.
## Frontend
### Framework
- **SvelteKit**: Full-stack framework with SSR support
- **TypeScript**: Type-safe JavaScript
- **Svelte 5**: Reactive UI framework
### Code Editor
- **CodeMirror 6**: Full-featured code editor
- `@codemirror/autocomplete`: Code autocompletion
- `@codemirror/basic-setup`: Basic editor setup
- `@codemirror/commands`: Editor commands
- `@codemirror/lang-markdown`: Markdown language support
- `@codemirror/language`: Language support infrastructure
- `@codemirror/search`: Search and replace functionality
- `@codemirror/state`: Editor state management
- `@codemirror/view`: Editor view components
### Syntax Highlighting
- **highlight.js**: Syntax highlighting for code blocks
- **CodeMirror language modes**: Language-specific highlighting in editor
### Markdown Processing
- **markdown-it**: Markdown parser and renderer
- **asciidoctor**: AsciiDoc support (via codemirror-asciidoc)
### UI Components
- Custom Svelte components for:
- Repository browser
- Code editor
- Pull request detail view
- Issue management
- File tree navigation
## Backend
### Runtime
- **Node.js**: JavaScript runtime
- **SvelteKit**: Server-side rendering and API routes
### Git Operations
- **git-http-backend**: Git smart HTTP protocol handler
- **simple-git**: Node.js git wrapper for repository operations
### Web Server
- **SvelteKit adapter-node**: Node.js adapter for production
- **Vite**: Build tool and dev server
## Nostr Integration
### Nostr Libraries
- **nostr-tools**: Core Nostr functionality
- Event creation and signing
- NIP-19 encoding/decoding (npub, nsec, naddr, etc.)
- Event verification
- Relay communication utilities
### WebSocket Client
- **ws**: WebSocket client for Nostr relay connections
- Custom **NostrClient**: WebSocket client wrapper for relay operations
### Authentication
- **NIP-07**: Browser extension integration (client-side)
- **NIP-98**: HTTP authentication implementation (server-side)
## Logging
### Logger
- **pino**: Fast, structured JSON logger
- **pino-pretty**: Human-readable log formatting for development
### Logging Features
- Structured logging with context
- Log levels (debug, info, warn, error)
- Audit logging for security events
- Performance logging
Logging is configured via environment variables and uses structured JSON logging with pino.
## Networking
### Tor Support
- **socks**: SOCKS proxy support for Tor connections
- Tor .onion address support for repositories
- Tor routing for Nostr relay connections
### Email
- **nodemailer**: Email sending for notifications (optional)
## Development Tools
### Type Checking
- **TypeScript**: Static type checking
- **svelte-check**: Svelte-specific type checking
### Linting and Formatting
- **ESLint**: JavaScript/TypeScript linting
- **Prettier**: Code formatting
- **@typescript-eslint**: TypeScript-specific ESLint rules
### Build Tools
- **Vite**: Fast build tool and dev server
- **TypeScript Compiler**: Type checking and compilation
## Programming Languages
- **TypeScript**: Primary language for all code
- **JavaScript**: Runtime language (compiled from TypeScript)
- **Svelte**: Component framework (compiled to JavaScript)
## Database/Storage
- **File System**: Git repositories stored on filesystem
- **JSONL Files**: Event storage in `nostr/repo-events.jsonl` format
- **No SQL Database**: Uses filesystem and Nostr relays for data storage
## Security
### Authentication Libraries
- **nostr-tools**: Cryptographic signing and verification
- Custom NIP-98 implementation for HTTP authentication
### Security Features
- Path validation
- Input sanitization
- Rate limiting
- Audit logging
- Process isolation (git-http-backend)
## Deployment
### Containerization
- **Docker**: Container support
- **Docker Compose**: Multi-container orchestration
- **Kubernetes**: Enterprise mode deployment (optional)
### Adapters
- **@sveltejs/adapter-node**: Node.js production adapter
## CLI Tools
The CLI (`gitrepublic-cli`) uses:
- **nostr-tools**: Nostr event handling
- **Node.js**: Runtime environment
- Native **git**: Git operations via git commands
## Version Requirements
- **Node.js**: 18+ (for both web app and CLI)
- **Git**: Required for git operations
- **npm**: Package management
## Next Steps
- [Specs used](./specs.md) - NIPs and GRASP documentation

907
docs/tutorial.md

@ -1,907 +0,0 @@
# GitRepublic Tutorial & Walkthrough
Welcome to GitRepublic! This comprehensive guide will walk you through everything you need to know to get started with decentralized git hosting on Nostr.
## Table of Contents
1. [What is GitRepublic?](#what-is-gitrepublic)
2. [Getting Started](#getting-started)
3. [Creating Your First Repository](#creating-your-first-repository)
4. [Cloning Repositories](#cloning-repositories)
5. [Making Changes and Pushing](#making-changes-and-pushing)
6. [Pull Requests](#pull-requests)
7. [Issues](#issues)
8. [Forking Repositories](#forking-repositories)
9. [Updating Repository Information](#updating-repository-information)
10. [Collaboration Features](#collaboration-features)
11. [Best Practices](#best-practices)
12. [Troubleshooting](#troubleshooting)
---
## What is GitRepublic?
GitRepublic is a decentralized git hosting platform built on Nostr. Unlike traditional git hosting services, GitRepublic:
- **No central authority**: Your repositories are announced on Nostr relays, making them truly decentralized
- **Nostr-based authentication**: Uses NIP-07 (browser extensions) and NIP-98 (HTTP authentication) for secure access
- **Full control**: You own your repositories and can transfer ownership, manage maintainers, and control access
- **Open collaboration**: Create pull requests, issues, and collaborate with others using Nostr events
### Key Concepts
- **NIP-34**: The Nostr Improvement Proposal that defines how repositories are announced and managed
- **NIP-07**: Browser extension authentication (like Alby or nos2x)
- **NIP-98**: HTTP authentication for git operations (clone, push, pull)
- **Repository Announcements**: Nostr events (kind 30617) that announce your repository to the network
- **Ownership Transfer**: Chain of ownership events (kind 1641) that prove repository ownership
---
## Getting Started
### Prerequisites
Before you begin, you'll need:
1. **A Nostr key pair**: You can generate one using a NIP-07 browser extension
2. **NIP-07 Extension**: Install [Alby](https://getalby.com/) or [nos2x](https://github.com/fiatjaf/nos2x) in your browser
3. **Git installed**: Make sure you have git installed on your system
4. **Access to GitRepublic**: Visit the GitRepublic instance you want to use
### Step 1: Install NIP-07 Extension
1. Install a Nostr browser extension:
- **Alby**: [getalby.com](https://getalby.com/)
- **nos2x**: Available for Chrome/Firefox
2. Create or import your Nostr key pair in the extension
3. Make sure the extension is active and unlocked
### Step 2: Connect to GitRepublic
1. Visit the GitRepublic homepage
2. Click the **Login** button in the top right
3. Approve the connection request in your NIP-07 extension
4. You should now see your user badge in the header
---
## Creating Your First Repository
### Using the Web Interface
1. **Navigate to Sign Up**: Click "Sign Up" in the navigation menu
2. **Fill in Repository Details**:
- **Repository Name**: Choose a unique name (e.g., `my-awesome-project`)
- **Description**: Add a description of what your repository does
- **Clone URLs** (optional): If you're migrating from another git host, add the clone URLs here
- **Private** (optional): Check this if you want a private repository
3. **Create Repository**: Click the create button
4. **What Happens Next**:
- GitRepublic creates a Nostr event announcing your repository
- A bare git repository is automatically provisioned
- You'll be redirected to your new repository page
### Repository URL Structure
Your repository will be accessible at:
```
https://{domain}/repos/{your-npub}/{repository-name}
```
For git operations, you can use any of these paths:
```
https://{domain}/api/git/{your-npub}/{repository-name}.git # Recommended
https://{domain}/repos/{your-npub}/{repository-name}.git
https://{domain}/{your-npub}/{repository-name}.git
```
**Note**: The `/api/git/` path is recommended for best compatibility with commit signing hooks.
### Initial Setup
After creating your repository, you can:
1. **Clone it locally**:
```bash
git clone https://{domain}/api/git/{your-npub}/{repository-name}.git
cd {repository-name}
```
2. **Add your first files**:
```bash
echo "# My Awesome Project" > README.md
git add README.md
git commit -m "Initial commit"
git push origin main
```
---
## Cloning Repositories
### Public Repositories
Anyone can clone public repositories without authentication:
```bash
git clone https://{domain}/api/git/{owner-npub}/{repository-name}.git
cd {repository-name}
```
### Private Repositories
Private repositories require authentication. You'll need to set up NIP-98 authentication.
#### Setting Up NIP-98 Authentication
For command-line git operations, you need to install the [GitRepublic CLI](https://github.com/silberengel/gitrepublic-cli) which provides:
1. **Credential Helper**: Automatically generates NIP-98 authentication tokens for git operations
2. **Commit Signing Hook**: Automatically signs commits for GitRepublic repositories
**Quick Setup**:
1. Install the CLI:
```bash
npm install -g gitrepublic-cli
```
2. Set your Nostr private key:
```bash
export NOSTRGIT_SECRET_KEY="nsec1..."
```
3. Run the setup script:
```bash
gitrepublic-setup
```
This automatically configures the credential helper and commit signing hook. See the README for complete setup instructions.
3. **Clone the private repository**:
```bash
git clone https://{domain}/api/git/{owner-npub}/{repository-name}.git
```
When prompted, the credential helper will automatically generate and use a NIP-98 authentication token.
**Note**: For command-line git operations, you'll need to install the [GitRepublic CLI](https://github.com/silberengel/gitrepublic-cli) via `npm install -g gitrepublic-cli` and set up the credential helper. See the README for complete setup instructions.
### Cloning from Multiple Remotes
If a repository has multiple clone URLs configured, GitRepublic will automatically sync changes to all remotes when you push. You can see all clone URLs on the repository page.
For information about GRASP (Git Repository Announcement and Synchronization Protocol) support, including how to work with GRASP servers, see [GRASP.md](./GRASP.md).
---
## Making Changes and Pushing
### Basic Workflow
1. **Make changes to your files**:
```bash
echo "New feature" >> README.md
```
2. **Stage your changes**:
```bash
git add README.md
```
3. **Commit your changes**:
```bash
git commit -m "Add new feature"
```
4. **Push to GitRepublic**:
```bash
git push origin main
```
### Authentication for Push
When you push, GitRepublic will:
1. Verify your NIP-98 authentication token
2. Check that you're the repository owner or a maintainer
3. Verify the repository size limit (2 GB maximum)
4. Process your push
5. Automatically sync to other remotes (if configured)
### Branch Management
#### Creating a New Branch
```bash
git checkout -b feature/new-feature
# Make changes
git add .
git commit -m "Add new feature"
git push origin feature/new-feature
```
#### Viewing Branches
You can view all branches:
- On the repository web page
- Using the API: `GET /api/repos/{npub}/{repo}/branches`
#### Creating Branches via Web UI
1. Navigate to your repository
2. Click "Create Branch"
3. Enter branch name and select the source branch
4. Click "Create"
### Tags
#### Creating a Tag
```bash
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin v1.0.0
```
#### Creating Tags via Web UI
1. Navigate to your repository
2. Click "Create Tag"
3. Enter tag name, select commit, and add a message
4. Click "Create"
---
## Pull Requests
Pull requests (PRs) allow you to propose changes to a repository. They're created as Nostr events (kind 1618) and can be reviewed, commented on, and merged.
### Creating a Pull Request
#### Method 1: Via Web Interface
1. **Fork the repository** (if you don't have write access)
2. **Make your changes** in your fork
3. **Push your changes** to a branch
4. **Navigate to the original repository**
5. **Click "Create Pull Request"**
6. **Fill in the details**:
- Source repository and branch
- Target repository and branch
- Title and description
7. **Submit the PR**
#### Method 2: Using Git and Nostr
1. **Fork and clone the repository**:
```bash
git clone https://{domain}/api/git/{owner-npub}/{repo}.git
cd {repo}
```
2. **Create a feature branch**:
```bash
git checkout -b feature/my-feature
```
3. **Make your changes and push**:
```bash
# Make changes
git add .
git commit -m "Add new feature"
git push origin feature/my-feature
```
4. **Create a PR event** using a Nostr client or the web interface
### Reviewing Pull Requests
1. **Navigate to the repository**
2. **Click on "Pull Requests"**
3. **Select a PR to review**
4. **Review the changes** in the diff view
5. **Add comments** on specific lines or sections
6. **Approve or request changes**
### PR Status
Pull requests can have the following statuses:
- **Open**: The PR is active and ready for review
- **Applied/Merged**: The PR has been merged into the target branch
- **Closed**: The PR was closed without merging
- **Draft**: The PR is still a work in progress
### Merging Pull Requests
Only repository owners and maintainers can merge PRs:
1. **Open the PR detail view** by clicking on a PR
2. **Review the changes** in the diff view
3. **Click "Merge"** button
4. **Select target branch** (default: main)
5. **Optionally add a merge message**
6. **Click "Merge"** to:
- Create a merge commit in the target branch
- Update PR status to "merged" (kind 1631)
- Include merge commit ID in the status event
### Managing PR Status
Repository owners and maintainers can manage PR status:
- **Close PR**: Click "Close" button to mark PR as closed (kind 1632)
- **Reopen PR**: Click "Reopen" button on a closed PR to reopen it (kind 1630)
- **Mark as Draft**: Click "Mark as Draft" to mark PR as draft (kind 1633)
### PR Updates (Kind 1619)
Only the PR author can update their PR:
- PR updates change the tip commit of the PR
- Use this when you've pushed new commits to your branch
- The update creates a kind 1619 event with the new commit ID
---
## Patches
Patches (kind 1617) are an alternative to pull requests for proposing changes to a repository. They're particularly useful for smaller changes or when you want to send code changes directly without creating a full pull request.
### Patches vs. Pull Requests
**When to use Patches:**
- **Small changes**: Each patch event should be under 60KB (per NIP-34 specification)
- **Simple fixes**: Bug fixes, typo corrections, or small feature additions
- **Direct code submission**: When you want to send code changes directly without maintaining a fork
- **Email-style workflow**: Similar to traditional git email-based patch workflows
**When to use Pull Requests:**
- **Large changes**: Changes that exceed 60KB per event
- **Complex features**: Multi-file changes that need discussion and review
- **Ongoing collaboration**: When you need to iterate on changes with maintainers
- **Branch-based workflow**: When you're working with forks and branches
### Key Differences
| Feature | Patches | Pull Requests |
|---------|---------|---------------|
| **Size Limit** | Under 60KB per event | No strict limit |
| **Format** | Git format-patch output | Markdown description + commit reference |
| **Workflow** | Email-style, sequential | Branch-based, iterative |
| **Series Support** | Yes (linked via NIP-10 reply tags) | Yes (via PR updates) |
| **Content** | Full patch content in event | Reference to commits in clone URL |
| **Use Case** | Small, self-contained changes | Large, complex changes |
### Creating a Patch
wPatches are created as Nostr events (kind 1617). The patch content is embedded directly in the event, making it easy to share and review without requiring access to a git repository.
#### Via Web Interface
1. **Navigate to the repository** you want to contribute to
2. **Click the repository menu** (three dots) and select "Create Patch"
3. **Fill in the patch details**:
- **Subject** (optional): A brief title for your patch
- **Patch Content**: Enter your patch content (can be in git format-patch format, but any patch format is acceptable)
4. **Submit the patch** - it will be published as a Nostr event (kind 1617) to the repository's relays
#### Patch Content Format
The patch content can be in any format that describes code changes. Common formats include:
- **Git format-patch output**: Standard git patch format
- **Unified diff format**: `git diff` output
- **Plain text description**: For simple changes, you can describe the changes in plain text
The key advantage of event-based patches is that they're self-contained Nostr events that can be discovered, shared, and reviewed without requiring access to the original git repository.
### Patch Series
For multiple related patches, you can create a patch series:
1. **First patch**: Marked with `t` tag `"root"`
2. **Subsequent patches**: Include NIP-10 `e` reply tags pointing to the previous patch
3. **Patch revisions**: If you need to revise a patch, mark the first revision with `t` tag `"root-revision"` and link to the original root patch
### Patch Status
Like pull requests, patches can have status events:
- **Open**: The patch is active and ready for review
- **Applied/Merged**: The patch has been applied to the repository
- **Closed**: The patch was closed without applying
- **Draft**: The patch is still a work in progress
### Applying Patches
Repository owners and maintainers can apply patches from Nostr events:
1. **Review the patch event** - patches are stored as Nostr events (kind 1617) and can be found on relays
2. **Extract the patch content** from the event content field
3. **Apply the patch** using git (if in git format):
```bash
# If the patch content is in git format-patch format, save it to a file and apply:
echo "<patch-content-from-event>" > patch.patch
git am patch.patch
```
Or manually apply the changes if the patch is in a different format
4. **Create a status event** (kind 1631) marking the patch as applied, referencing the patch event ID
5. **Push the changes** to the repository
The status event creates a permanent record that the patch was applied, linking the patch event to the resulting commits.
---
## Issues
Issues (kind 1621) allow you to track bugs, feature requests, and other tasks related to your repository.
### Creating an Issue
1. **Navigate to your repository**
2. **Click "Issues"** in the repository menu
3. **Click "Create Issue"**
4. **Fill in the details**:
- Title
- Description
- Labels (optional)
5. **Submit the issue**
### Issue Status
Issues can have the following statuses:
- **Open**: The issue is active and needs attention
- **Resolved**: The issue has been fixed or addressed
- **Closed**: The issue was closed (e.g., duplicate, won't fix)
- **Draft**: The issue is still being written
### Managing Issue Status
Repository owners, maintainers, and issue authors can update issue status:
- **Close Issue**: Click "Close" button to mark issue as closed (kind 1632)
- **Resolve Issue**: Click "Resolve" button to mark issue as resolved (kind 1631)
- **Reopen Issue**: Click "Reopen" button on a closed or resolved issue to reopen it (kind 1630)
- **Mark as Draft**: Mark issue as draft (kind 1633)
### Managing Issues
- **Assign issues** to maintainers
- **Add comments** to discuss solutions
- **Link issues to PRs** by referencing them in PR descriptions
- **Close issues** when they're resolved
---
## Forking Repositories
Forking creates your own copy of a repository that you can modify independently.
### How to Fork
1. **Navigate to the repository** you want to fork
2. **Click the "Fork" button**
3. **GitRepublic will**:
- Create a copy of the repository under your account
- Create a new NIP-34 announcement for your fork
- Set you as the owner of the fork
- Add a reference to the original repository
### Working with Forks
After forking:
1. **Clone your fork**:
```bash
git clone https://{domain}/api/git/{your-npub}/{fork-name}.git
```
2. **Make changes** in your fork
3. **Push changes**:
```bash
git push origin main
```
4. **Create a pull request** back to the original repository (if you want to contribute)
### Syncing with Upstream
To keep your fork up to date with the original repository:
1. **Add the original as a remote**:
```bash
git remote add upstream https://{domain}/api/git/{original-npub}/{original-repo}.git
```
2. **Fetch and merge**:
```bash
git fetch upstream
git merge upstream/main
git push origin main
```
---
## Updating Repository Information
To update your repository information, navigate to the signup page with your repository details pre-filled.
### Accessing the Update Form
1. Navigate to your repository page
2. Click "Settings" (if you're the owner or a maintainer)
3. You'll be redirected to the signup page with all current repository information pre-filled
### Privacy Settings
- **Public**: Anyone can view and clone your repository
- **Private**: Only owners and maintainers can access
You can change the privacy setting by checking or unchecking the "Private Repository" option in the update form.
### Maintainer Management
Add maintainers who can:
- Push to the repository
- Merge pull requests
- Manage issues
- Update repository information
**To add a maintainer**:
1. Access the update form (via Settings link)
2. Enter the maintainer's npub in the maintainers field
3. Click "Add Maintainer" to add additional maintainer fields
4. Save changes
**To remove a maintainer**:
1. Access the update form
2. Remove the maintainer's npub from the maintainers field
3. Save changes
### Repository Description
Update your repository description:
1. Access the update form
2. Edit the description field
3. Save changes
### Clone URLs
Add multiple clone URLs to sync your repository to other git hosts:
1. Access the update form
2. Add clone URLs (one per field)
3. Save changes
When you push, GitRepublic will automatically sync to all configured remotes.
### Ownership Transfer
Transfer repository ownership to another user using the transfer workflow:
1. **Initiate Transfer**: On your repository page, click "Transfer Ownership"
2. **Enter New Owner**: Provide the new owner's npub
3. **Sign and Publish**: The transfer event (kind 1641) is signed and published to Nostr relays
4. **Save to Repository**: The transfer event is saved to `nostr/repo-events.jsonl` in your repository for offline papertrail
5. **New Owner Notification**: The new owner will be notified when they log into GitRepublic web
6. **Complete Transfer**: The new owner completes the transfer by publishing a new repository announcement (kind 30617)
7. **Verification**: The new announcement is saved to the repository, and the transfer is complete
**Important**: Ownership transfers are permanent and create a chain of ownership events. The new owner will have full control. Both the transfer event and the new repository announcement are published to relays and saved to `nostr/repo-events.jsonl` in the repository for both online and offline papertrail.
---
## Collaboration Features
### Code Highlights
Highlight specific code sections in pull requests:
1. **Select code** in the PR diff view
2. **Click "Highlight"**
3. **Add a comment** explaining the highlight
4. **Others can comment** on your highlights
### Comments
Comment on:
- Pull requests
- Issues
- Code highlights
- Specific lines in diffs
Comments are threaded and use Nostr events (kind 1111) for persistence.
### Notifications
GitRepublic uses Nostr events for notifications. You can:
- Subscribe to repository events
- Get notified of new PRs, issues, and comments
- Track changes using your Nostr client
---
## Best Practices
### Repository Organization
1. **Use descriptive names**: Choose clear, descriptive repository names
2. **Write good READMEs**: Include installation instructions, usage examples, and contribution guidelines
3. **Use tags for releases**: Tag important versions (e.g., `v1.0.0`)
4. **Keep repositories focused**: One repository per project or component
### Commit Messages
Write clear, descriptive commit messages:
```bash
# Good
git commit -m "Add user authentication feature"
# Better
git commit -m "Add user authentication with NIP-07 support
- Implement NIP-07 browser extension authentication
- Add login/logout functionality
- Update UI to show user badge when logged in"
```
### Branch Strategy
- **main/master**: Production-ready code
- **feature/**: New features
- **bugfix/**: Bug fixes
- **hotfix/**: Urgent production fixes
### Pull Request Guidelines
1. **Keep PRs focused**: One feature or fix per PR
2. **Write clear descriptions**: Explain what and why, not just what
3. **Link related issues**: Reference issues in PR descriptions
4. **Request reviews**: Ask maintainers to review your PRs
5. **Respond to feedback**: Address review comments promptly
### Security
1. **Keep your keys secure**: Never share your nsec (private key)
2. **Use NIP-07 extensions**: Don't enter keys directly in web forms
3. **Review maintainers**: Only add trusted users as maintainers
4. **Monitor your repositories**: Check for unexpected changes
---
## Troubleshooting
### Authentication Issues
**Problem**: Can't push to repository
**Solutions**:
- Verify you're logged in with NIP-07
- Check that you're the owner or a maintainer
- Ensure your NIP-98 authentication is configured correctly
- Check repository privacy settings
### Clone Fails
**Problem**: Can't clone a repository
**Solutions**:
- Verify the repository URL is correct
- Check if the repository is private (requires authentication)
- Ensure you have network access to the GitRepublic instance
- Try cloning with verbose output: `git clone -v {url}`
### Push Fails
**Problem**: Push is rejected
**Solutions**:
- Check repository size limit (2 GB maximum)
- Verify you have write permissions
- Ensure your branch is up to date: `git pull origin main`
- Check for branch protection rules
### Repository Not Found
**Problem**: Repository doesn't appear after creation
**Solutions**:
- Wait a few moments for auto-provisioning
- Refresh the page
- Check that the NIP-34 announcement was published
- Verify you're looking at the correct domain
### Sync Issues
**Problem**: Changes not syncing to other remotes
**Solutions**:
- Verify clone URLs are correct in repository settings
- Check network connectivity to remote git hosts
- Review server logs for sync errors
- Manually push to remotes if needed
---
## User Profiles and Payment Targets
GitRepublic displays full user profiles with support for payment targets using NIP-A3 (kind 10133).
### Viewing User Profiles
1. Navigate to a user's profile page: `/users/{npub}`
2. View their repositories, profile information, and payment targets
3. Profiles support both old JSON format (in content) and new tags format
### Payment Targets (NIP-A3)
Payment targets allow users to specify how they want to receive payments using the `payto://` URI scheme (RFC-8905).
#### Supported Payment Types
- **Lightning**: Lightning Network addresses (e.g., `user@wallet.example.com`)
- **Bitcoin**: Bitcoin addresses
- **Ethereum**: Ethereum addresses
- **Nano**: Nano addresses
- **Monero**: Monero addresses
- And more (see [NIP-A3 documentation](./NIP-A3.md))
#### How Payment Targets Work
GitRepublic merges payment information from multiple sources:
1. **NIP-01 (kind 0)**: Lightning addresses from `lud16` tags or JSON `lud16` field
2. **NIP-A3 (kind 10133)**: All payment targets from `payto` tags
The system:
- Normalizes addresses (lowercase) for deduplication
- Merges lightning addresses from both sources
- Displays all payment targets with `payto://` URIs
- Provides copy buttons for easy sharing
#### Creating Payment Target Events
To add payment targets to your profile, publish a kind 10133 event:
```json
{
"kind": 10133,
"content": "",
"tags": [
["payto", "lightning", "user@wallet.example.com"],
["payto", "bitcoin", "bc1qxq66e0t8d7ugdecwnmv58e90tpry23nc84pg9k"]
],
"created_at": 1234567890
}
```
#### API Access
Fetch user profiles with payment targets via the API:
```bash
GET /api/users/{npub}/profile
```
Response includes:
- Full profile event (kind 0)
- Payment targets array with `payto://` URIs
- Payment event (kind 10133) if available
#### CLI Access
The GitRepublic CLI automatically fetches payment targets when fetching profiles:
```bash
gitrep profile fetch npub1...
```
See [NIP-A3 documentation](./NIP-A3.md) for complete details.
---
## Advanced Topics
### NIP-34 Specification
GitRepublic implements NIP-34 for repository announcements. Key event types:
- **Kind 30617**: Repository announcement
- **Kind 30618**: Repository state
- **Kind 1617**: Git patch
- **Kind 1618**: Pull request
- **Kind 1619**: Pull request update
- **Kind 1621**: Issue
- **Kind 1630-1633**: Status events (open, applied/merged, closed, draft)
- **Kind 1641**: Ownership transfer
See the [NIP-34 documentation](/docs/nip34) for full details.
### GRASP Protocol Support
GitRepublic provides **minimal GRASP (Git Repository Announcement and Synchronization Protocol) interoperability** for seamless compatibility with GRASP servers.
**What is GRASP?**
GRASP is a protocol specification for decentralized git hosting that combines git smart HTTP with Nostr relays. GRASP servers provide git repository hosting with Nostr-based announcements and state management.
**What GitRepublic Supports:**
1. **GRASP Server Detection**: Automatically identifies GRASP servers from repository announcements using GRASP-01 identification (clone URL pattern + matching `relays` tag)
2. **Clone URL Reachability**: Tests and displays reachability status for all clone URLs, showing which remotes (including GRASP servers) are accessible
3. **Multi-Remote Synchronization**: When you push, automatically syncs to all remotes listed in your announcement, including GRASP servers
4. **Local Pull Command**: Use `gitrep pull-all --merge` to fetch and merge from all remotes (including GRASP servers)
- Checks reachability first, only pulls from accessible remotes
- Detects conflicts before merging (aborts unless `--allow-conflicts`)
5. **Standard Git Operations**: Full compatibility with GRASP servers for clone, push, pull using standard git smart HTTP protocol
**What We Don't Support (By Design):**
- Full GRASP-01 server compliance (we're not a full GRASP server)
- GRASP-02 proactive sync (no server-side hourly pulls - user-controlled via CLI)
- GRASP-05 archive mode
**Example: Working with GRASP Servers**
```bash
# Clone from a GRASP server (works just like any git server)
gitrep clone https://grasp.example.com/npub1.../repo.git
# Push to your repo (automatically syncs to all remotes including GRASP)
gitrep push origin main
# Pull from all remotes including GRASP servers
gitrep pull-all --merge
```
### NIP-98 HTTP Authentication
Git operations use NIP-98 for authentication:
1. Client creates an ephemeral event (kind 27235)
2. Event includes request URL, method, and payload hash
3. Client signs event and includes in `Authorization` header
4. Server verifies signature and permissions
### Relay Configuration
GitRepublic uses Nostr relays to:
- Publish repository announcements
- Fetch repository metadata
- Sync pull requests and issues
- Track ownership transfers
Default relays are configured, but you can use custom relays if needed.
---
## Getting Help
- **Documentation**: Check this tutorial and the NIP-34 specification
- **Issues**: Report bugs or request features via GitHub issues (if the instance has a GitHub repo)
- **Community**: Join Nostr communities to discuss GitRepublic
- **Support**: Contact the GitRepublic instance administrator
---
## Conclusion
Congratulations! You now know how to use GitRepublic for decentralized git hosting. Remember:
- GitRepublic is built on Nostr, making it truly decentralized
- You have full control over your repositories
- Collaboration happens through Nostr events
- Security is handled via NIP-07 and NIP-98
Happy coding!

21
k8s/ENTERPRISE_MODE.md

@ -28,9 +28,18 @@ cd k8s
./deploy-tenant.sh npub1abc123... \ ./deploy-tenant.sh npub1abc123... \
--domain git.example.com \ --domain git.example.com \
--storage-class fast-ssd \ --storage-class fast-ssd \
--storage-size 50Gi --storage-size 50Gi \
--subdomain user1 \
--relays wss://relay1.com,wss://relay2.com
``` ```
**Options:**
- `--domain`: Domain for git repositories (default: `git.example.com`)
- `--storage-class`: Kubernetes storage class (default: `standard`)
- `--storage-size`: Volume size per tenant (default: `100Gi`)
- `--subdomain`: Subdomain for tenant (default: first 16 chars of npub)
- `--relays`: Comma-separated Nostr relays (default: `wss://theforest.nostr1.com,wss://nostr.land`)
### Check Status ### Check Status
```bash ```bash
@ -79,10 +88,20 @@ Kubernetes Cluster
| Variable | Description | Default | | Variable | Description | Default |
|----------|-------------|---------| |----------|-------------|---------|
| `ENTERPRISE_MODE` | Enable enterprise mode | `false` | | `ENTERPRISE_MODE` | Enable enterprise mode | `false` |
| `GIT_REPO_ROOT` | Path to store git repositories | `/repos` |
| `GIT_DOMAIN` | Domain for git repositories | `localhost:6543` | | `GIT_DOMAIN` | Domain for git repositories | `localhost:6543` |
| `NOSTR_RELAYS` | Comma-separated Nostr relays | `wss://theforest.nostr1.com,...` | | `NOSTR_RELAYS` | Comma-separated Nostr relays | `wss://theforest.nostr1.com,...` |
| `NOSTR_SEARCH_RELAYS` | Comma-separated search relays | Multiple defaults |
| `MAX_REPOS_PER_USER` | Maximum repositories per user | `100` |
| `MAX_DISK_QUOTA_PER_USER` | Maximum disk quota per user (bytes) | `10737418240` (10GB) |
| `RATE_LIMIT_ENABLED` | Enable rate limiting | `true` |
| `AUDIT_LOGGING_ENABLED` | Enable audit logging | `true` |
| `STORAGE_CLASS` | Kubernetes storage class | `standard` | | `STORAGE_CLASS` | Kubernetes storage class | `standard` |
| `STORAGE_SIZE` | Volume size per tenant | `100Gi` | | `STORAGE_SIZE` | Volume size per tenant | `100Gi` |
| `TOR_SOCKS_PROXY` | Tor SOCKS proxy (optional) | `127.0.0.1:9050` |
| `TOR_ONION_ADDRESS` | Tor .onion address (optional) | Auto-detected |
**Note**: Additional environment variables can be added to `k8s/base/deployment.yaml` in the `env` section. The deployment template currently includes `GIT_REPO_ROOT`, `GIT_DOMAIN`, `NOSTR_RELAYS`, and `ENTERPRISE_MODE` by default.
### Resource Limits (per tenant) ### Resource Limits (per tenant)

1
nostr/commit-signatures.jsonl

@ -82,3 +82,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772002202,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","improving commit signing and verification"]],"content":"Signed commit: improving commit signing and verification","id":"c149ee64445a63b9a471d1866df86d702fe3fead1049a8e3272ea76a25f11094","sig":"f0745d02cb1b2ac012feb5e38cd4917eb9af48338eb13626aedae6ce73025758b2debe6874c5af3a4e252241405fdaa91042a031fa56c4fe0257c978d23babb2"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772002202,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","improving commit signing and verification"]],"content":"Signed commit: improving commit signing and verification","id":"c149ee64445a63b9a471d1866df86d702fe3fead1049a8e3272ea76a25f11094","sig":"f0745d02cb1b2ac012feb5e38cd4917eb9af48338eb13626aedae6ce73025758b2debe6874c5af3a4e252241405fdaa91042a031fa56c4fe0257c978d23babb2"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772003001,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","fix local cloning"]],"content":"Signed commit: fix local cloning","id":"0e7b4f06276988a2caf1c8fa9f6ba4a1cb683033c0714cc88699e3a4bda67d68","sig":"3c46ff9412a72f3ca39d216d6bd2eee7b9f70331fe8c0d557ee8339be4c05d03fe949e3aaef6e29126d4174b9f6d10de9e605273918106b9d40bc81cfaa1d290"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772003001,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","fix local cloning"]],"content":"Signed commit: fix local cloning","id":"0e7b4f06276988a2caf1c8fa9f6ba4a1cb683033c0714cc88699e3a4bda67d68","sig":"3c46ff9412a72f3ca39d216d6bd2eee7b9f70331fe8c0d557ee8339be4c05d03fe949e3aaef6e29126d4174b9f6d10de9e605273918106b9d40bc81cfaa1d290"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772004731,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","increase granularity of repo and event visbility"]],"content":"Signed commit: increase granularity of repo and event visbility","id":"1d96ac54006360066d403209f6893faffec0f8f389ea99af73447a017d5ff03a","sig":"44c53034e91ef444368a5034e3a12024bf893f3e518eef903aecbbb453e612f5f198601daf7b9d8da3bc48ca77ec4d18795f111211c3bc32ed4b6c0707a7a905"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772004731,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","increase granularity of repo and event visbility"]],"content":"Signed commit: increase granularity of repo and event visbility","id":"1d96ac54006360066d403209f6893faffec0f8f389ea99af73447a017d5ff03a","sig":"44c53034e91ef444368a5034e3a12024bf893f3e518eef903aecbbb453e612f5f198601daf7b9d8da3bc48ca77ec4d18795f111211c3bc32ed4b6c0707a7a905"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772005973,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","update the API"]],"content":"Signed commit: update the API","id":"09329cf7eb8c228e87e365b0d7a4d052ddb08b3cf7f75162b2e9b8dd77e917a0","sig":"1a1b40b18dbd744bd4043f0f18d5945ba7d1f738d36bb8457c4ec806832cd1b44ed36417c24d01511fa7fddfa33c376bf24c2fdf478bf1ab015cf4c524aac7e8"}

102
src/routes/api/openapi.json/openapi.json

@ -1544,6 +1544,108 @@
} }
} }
}, },
"/api/repos/{npub}/{repo}/settings": {
"get": {
"summary": "Get repository settings",
"description": "Get repository settings including description, visibility, and project relays",
"tags": ["Repositories"],
"parameters": [
{
"name": "npub",
"in": "path",
"required": true,
"schema": {"type": "string"}
},
{
"name": "repo",
"in": "path",
"required": true,
"schema": {"type": "string"}
}
],
"responses": {
"200": {
"description": "Repository settings",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"owner": {"type": "string"},
"description": {"type": "string"},
"visibility": {
"type": "string",
"enum": ["public", "unlisted", "restricted", "private"]
},
"projectRelays": {
"type": "array",
"items": {"type": "string"}
},
"private": {
"type": "boolean",
"description": "Backward compatibility field (maps to visibility)"
}
}
}
}
}
}
}
},
"post": {
"summary": "Update repository settings",
"description": "Update repository settings. Requires maintainer access. Supports visibility levels: public, unlisted, restricted, private. Unlisted and restricted require project-relay.",
"tags": ["Repositories"],
"security": [{"NIP98": []}],
"parameters": [
{
"name": "npub",
"in": "path",
"required": true,
"schema": {"type": "string"}
},
{
"name": "repo",
"in": "path",
"required": true,
"schema": {"type": "string"}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"description": {"type": "string"},
"visibility": {
"type": "string",
"enum": ["public", "unlisted", "restricted", "private"]
},
"projectRelays": {
"type": "array",
"items": {"type": "string"}
},
"private": {
"type": "boolean",
"description": "Deprecated: Use visibility instead"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Settings updated successfully"
},
"403": {
"description": "Not authorized (requires maintainer access)"
}
}
}
},
"/api/repos/{npub}/{repo}/access": { "/api/repos/{npub}/{repo}/access": {
"get": { "get": {
"summary": "Check repository access", "summary": "Check repository access",

10
src/routes/docs/+page.server.ts

@ -16,21 +16,21 @@ export const load: PageServerLoad = async () => {
// List of paths to try // List of paths to try
const pathsToTry = [ const pathsToTry = [
// Method 1: process.cwd() (works in most cases) // Method 1: process.cwd() (works in most cases)
() => join(process.cwd(), 'docs', 'tutorial.md'), () => join(process.cwd(), 'docs', 'about.md'),
// Method 2: process.cwd() from build directory // Method 2: process.cwd() from build directory
() => join(process.cwd(), '..', 'docs', 'tutorial.md'), () => join(process.cwd(), '..', 'docs', 'about.md'),
// Method 3: import.meta.url - go up from route file to project root // Method 3: import.meta.url - go up from route file to project root
() => { () => {
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
return join(__filename, '..', '..', '..', '..', 'docs', 'tutorial.md'); return join(__filename, '..', '..', '..', '..', 'docs', 'about.md');
}, },
// Method 4: import.meta.url - alternative path calculation // Method 4: import.meta.url - alternative path calculation
() => { () => {
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
return join(__filename, '..', '..', '..', '..', '..', 'docs', 'tutorial.md'); return join(__filename, '..', '..', '..', '..', '..', 'docs', 'about.md');
}, },
// Method 5: Check if running from build directory // Method 5: Check if running from build directory
() => join(process.cwd(), 'build', 'docs', 'tutorial.md'), () => join(process.cwd(), 'build', 'docs', 'about.md'),
]; ];
for (const getPath of pathsToTry) { for (const getPath of pathsToTry) {

94
src/routes/docs/[slug]/+page.server.ts

@ -0,0 +1,94 @@
/**
* Server-side loader for GitRepublic documentation
* Dynamic route that serves any markdown file from the docs directory
*/
import { readFile } from 'fs/promises';
import { join } from 'path';
import { fileURLToPath } from 'url';
import { existsSync } from 'fs';
import { error } from '@sveltejs/kit';
// @ts-ignore - SvelteKit generates this type
import type { PageServerLoad } from './$types';
import logger from '$lib/services/logger.js';
export const load: PageServerLoad = async ({ params }: { params: { slug: string } }) => {
let slug = params.slug;
// Security: Only allow alphanumeric, hyphens, underscores, and dots
if (!/^[a-zA-Z0-9._-]+$/.test(slug)) {
throw error(400, 'Invalid documentation path');
}
// Prevent path traversal
if (slug.includes('..') || slug.includes('/')) {
throw error(400, 'Invalid documentation path');
}
const attemptedPaths: string[] = [];
let lastError: Error | null = null;
// Helper function to try both exact case and lowercase versions
const tryPaths = (basePath: string) => {
const paths: string[] = [];
// Try exact case first
paths.push(join(basePath, `${slug}.md`));
// Try lowercase version (for case-insensitive filesystems)
if (slug !== slug.toLowerCase()) {
paths.push(join(basePath, `${slug.toLowerCase()}.md`));
}
return paths;
};
// List of base paths to try
const basePathsToTry = [
// Method 1: process.cwd() (works in most cases)
() => join(process.cwd(), 'docs'),
// Method 2: process.cwd() from build directory
() => join(process.cwd(), '..', 'docs'),
// Method 3: import.meta.url - go up from route file to project root
() => {
const __filename = fileURLToPath(import.meta.url);
return join(__filename, '..', '..', '..', '..', '..', 'docs');
},
// Method 4: import.meta.url - alternative path calculation
() => {
const __filename = fileURLToPath(import.meta.url);
return join(__filename, '..', '..', '..', '..', '..', '..', 'docs');
},
// Method 5: Check if running from build directory
() => join(process.cwd(), 'build', 'docs'),
];
// Try all combinations of base paths and file name variations
for (const getBasePath of basePathsToTry) {
try {
const basePath = getBasePath();
const paths = tryPaths(basePath);
for (const filePath of paths) {
attemptedPaths.push(filePath);
if (existsSync(filePath)) {
logger.info({ filePath, slug }, 'Found documentation file');
const content = await readFile(filePath, 'utf-8');
return { content, slug };
}
}
} catch (err) {
lastError = err instanceof Error ? err : new Error(String(err));
// Continue to next path
}
}
// All paths failed
logger.error({
error: lastError,
attemptedPaths,
slug,
cwd: process.cwd(),
importMetaUrl: import.meta.url
}, 'Error loading documentation - all paths failed');
throw error(404, `Documentation file "${slug}.md" not found`);
};

226
src/routes/docs/[slug]/+page.svelte

@ -0,0 +1,226 @@
<script lang="ts">
import { page } from '$app/stores';
import { onMount } from 'svelte';
let content = $state('');
let loading = $state(true);
let error = $state<string | null>(null);
onMount(async () => {
try {
const docContent = $page.data.content;
if (docContent) {
const MarkdownIt = (await import('markdown-it')).default;
const hljsModule = await import('highlight.js');
const hljs = hljsModule.default || hljsModule;
const md = new MarkdownIt({
highlight: function (str: string, lang: string): string {
if (lang && hljs.getLanguage(lang)) {
try {
return '<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang }).value +
'</code></pre>';
} catch (err) {
// Fallback to escaped HTML if highlighting fails
}
}
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
}
});
let rendered = md.render(docContent);
// Add IDs to headings for anchor links
rendered = rendered.replace(/<h([1-6])>(.*?)<\/h[1-6]>/g, (match, level, text) => {
const textContent = text.replace(/<[^>]*>/g, '').trim();
const slug = textContent
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
return `<h${level} id="${slug}">${text}</h${level}>`;
});
// Convert relative markdown links to docs routes
rendered = rendered.replace(/<a href="\.\/([^"]+\.md)"/g, (match, file) => {
const slug = file.replace('.md', '');
return `<a href="/docs/${slug}"`;
});
content = rendered;
// Handle anchor links after content is rendered
setTimeout(() => {
if (window.location.hash) {
const id = window.location.hash.substring(1);
const element = document.getElementById(id);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
const markdownContent = document.querySelector('.markdown-content');
if (markdownContent) {
markdownContent.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
if (target.tagName === 'A' && target.getAttribute('href')?.startsWith('#')) {
const id = target.getAttribute('href')?.substring(1);
if (id) {
const element = document.getElementById(id);
if (element) {
e.preventDefault();
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
window.history.pushState(null, '', `#${id}`);
}
}
}
});
}
}, 100);
} else {
error = $page.data.error || 'Failed to load documentation';
}
} catch (err) {
error = err instanceof Error ? err.message : 'Failed to load documentation';
console.error('Error parsing documentation:', err);
} finally {
loading = false;
}
});
</script>
<div class="container">
<header>
<h1>Documentation</h1>
</header>
<main class="docs-content">
{#if loading}
<div class="loading">Loading documentation...</div>
{:else if error}
<div class="error">{error}</div>
{:else}
<div class="markdown-content">
{@html content}
</div>
{/if}
</main>
</div>
<style>
.docs-content {
background: var(--card-bg);
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid var(--border-color);
}
:global(.markdown-content) {
line-height: 1.6;
}
:global(.markdown-content h1) {
font-size: 2rem;
margin-top: 2rem;
margin-bottom: 1rem;
border-bottom: 2px solid var(--border-color);
padding-bottom: 0.5rem;
color: var(--text-primary);
scroll-margin-top: 1rem;
}
:global(.markdown-content h2) {
font-size: 1.5rem;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
color: var(--text-primary);
scroll-margin-top: 1rem;
}
:global(.markdown-content h3) {
font-size: 1.25rem;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
color: var(--text-primary);
scroll-margin-top: 1rem;
}
:global(.markdown-content h4, .markdown-content h5, .markdown-content h6) {
scroll-margin-top: 1rem;
}
:global(.markdown-content) {
scroll-behavior: smooth;
}
:global(.markdown-content code) {
background: var(--bg-secondary);
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.875em;
color: var(--text-primary);
}
:global(.markdown-content pre) {
background: var(--bg-tertiary);
color: var(--text-primary);
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
margin: 1rem 0;
border: 1px solid var(--border-color);
}
:global(.markdown-content pre code) {
background: transparent;
padding: 0;
color: inherit;
}
:global(.markdown-content p) {
margin: 1rem 0;
}
:global(.markdown-content ul, .markdown-content ol) {
margin: 1rem 0;
padding-left: 2rem;
}
:global(.markdown-content li) {
margin: 0.5rem 0;
}
:global(.markdown-content blockquote) {
border-left: 4px solid var(--accent);
padding-left: 1rem;
margin: 1rem 0;
color: var(--text-secondary);
}
:global(.markdown-content table) {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
}
:global(.markdown-content th, .markdown-content td) {
border: 1px solid var(--border-color);
padding: 0.5rem;
text-align: left;
}
:global(.markdown-content th) {
background: var(--bg-secondary);
font-weight: 600;
color: var(--text-primary);
}
:global(.markdown-content td) {
color: var(--text-primary);
}
</style>
Loading…
Cancel
Save