Browse Source

bug-fixes and doc updates

Nostr-Signature: d089915a2d9a9d46ba25d2d3c1cb4608a2b658ecc4260f17e73efa4ccc63a28d 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 3d447f05a55704d45ed843b7cc5fa16e49f3da0e452b1523392aefbb7a2ae3e79400a763df5705db8e38abc89e9a89480ab2c529890b531b171c4e980520d9b8
main
Silberengel 3 weeks ago
parent
commit
8877f79246
  1. 655
      README.md
  2. 2
      docker-compose.yml
  3. 4
      docs/tutorial.md
  4. 221
      k8s/ENTERPRISE_MODE.md
  5. 42
      k8s/README.md
  6. 4
      k8s/base/deployment.yaml
  7. 31
      k8s/base/ingress.yaml
  8. 59
      k8s/delete-tenant.sh
  9. 138
      k8s/deploy-tenant.sh
  10. 1
      nostr/commit-signatures.jsonl
  11. 14
      src/lib/config.ts
  12. 8
      src/lib/services/git/repo-manager.ts
  13. 33
      src/routes/api/git/[...path]/+server.ts
  14. 1
      src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts
  15. 3
      src/routes/repos/[npub]/[repo]/+page.svelte

655
README.md

@ -1,8 +1,58 @@
# gitrepublic-web # GitRepublic
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.
See [ARCHITECTURE_FAQ.md](./docs/ARCHITECTURE_FAQ.md) for answers to common architecture questions. ## Command Line Interface (CLI)
**The GitRepublic CLI is published and available via npm:**
```bash
npm install -g gitrepublic-cli
```
The CLI provides:
- **Git wrapper** with enhanced error messages for GitRepublic operations
- **Credential helper** for automatic NIP-98 authentication
- **Commit signing hook** that automatically signs commits using Nostr keys
- **Full API access** from the command line
### Quick Start with CLI
```bash
# Install
npm install -g gitrepublic-cli
# Set your Nostr private key
export NOSTRGIT_SECRET_KEY="nsec1..."
# Setup (configures credential helper and commit hook)
gitrep-setup
# Use gitrep (or gitrepublic) for git operations
gitrep clone https://your-domain.com/api/git/npub1.../repo.git
gitrep push origin main
# 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.
For complete CLI documentation, see [gitrepublic-cli/README.md](./gitrepublic-cli/README.md).
---
## Overview
GitRepublic consists of three main components:
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
3. **REST API** - Complete API for programmatic access (see `/api/openapi.json` for full documentation)
All three interfaces use the same underlying Nostr-based authentication and repository management system.
## Features ## Features
@ -43,7 +93,6 @@ See [ARCHITECTURE_FAQ.md](./docs/ARCHITECTURE_FAQ.md) for answers to common arch
- **Raw File View**: Direct access to raw file content - **Raw File View**: Direct access to raw file content
- **Download Repository**: Download repositories as ZIP archives - **Download Repository**: Download repositories as ZIP archives
- **OpenGraph Metadata**: Rich social media previews with repository images and banners - **OpenGraph Metadata**: Rich social media previews with repository images and banners
- **Universal Git Dashboard**: Aggregate and view issues and pull requests from all configured git platforms (GitHub, GitLab, Gitea, etc.) in one place
### Security & Validation ### Security & Validation
- **Path Traversal Protection**: Validates and sanitizes file paths - **Path Traversal Protection**: Validates and sanitizes file paths
@ -56,57 +105,75 @@ See [ARCHITECTURE_FAQ.md](./docs/ARCHITECTURE_FAQ.md) for answers to common arch
- **Git Operations**: Uses NIP-98 HTTP authentication (ephemeral signed events) - **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. - ⚠ **Security Note**: Never send private keys (nsec) in API requests. Use NIP-07 for web UI or NIP-98 for git operations.
## Nostr Event Kinds Used ## Getting Started
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). ### 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
### Standard NIP Event Kinds ### Installation
- **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)
### Custom Event Kinds #### For Web Server
```bash
# Clone the repository
git clone https://github.com/silberengel/gitrepublic-web.git
cd gitrepublic-web
These are not part of any NIP but are used by this application: # Install dependencies
npm install
- **1640** (`COMMIT_SIGNATURE`): Git commit signature events # Start development server
- Used to cryptographically sign git commits using Nostr keys npm run dev
- 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) #### For CLI (Command Line)
- Transfers ownership from one pubkey to another ```bash
- Self-transfers (owner → owner) used for initial ownership proof # Install globally
- Non-replaceable to maintain immutable chain of ownership npm install -g gitrepublic-cli
- 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) # Set your Nostr private key
- Allows requiring pull requests, reviewers, status checks for protected branches export NOSTRGIT_SECRET_KEY="nsec1..."
- 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 # Setup credential helper and commit hook
gitrep-setup
```
### Quick Start
#### Web Interface
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
```bash
# Clone a repository
gitrep clone https://your-domain.com/api/git/npub1.../repo.git
# Make changes and push
git add .
git commit -m "Update README"
gitrep push origin main
```
#### API Access
All API endpoints are documented in OpenAPI format at `/api/openapi.json`. Use NIP-98 authentication for all write operations.
Example:
```bash
curl -X GET https://your-domain.com/api/repos/list
```
## How It Works ## How It Works
### Repository Creation Flow ### Repository Creation Flow
1. **User Creates Announcement**: 1. **User Creates Announcement**:
- User visits `/signup` and connects NIP-07 extension - **Web**: Visit `/signup` and connect NIP-07 extension
- Enters repository name, description, and optional clone URLs - **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 - System automatically creates a self-transfer event (kind 1641) for initial ownership proof
- Both announcement and self-transfer are published to Nostr relays - Both announcement and self-transfer are published to Nostr relays
@ -126,13 +193,15 @@ These are not part of any NIP but are used by this application:
### Git Operations Flow ### Git Operations Flow
1. **Clone/Fetch**: 1. **Clone/Fetch**:
- User runs `git clone https://{domain}/api/git/{npub}/{repo}.git` (or `/repos/` path) - **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` - Server handles GET requests to `info/refs?service=git-upload-pack`
- For private repos, verifies NIP-98 authentication - For private repos, verifies NIP-98 authentication
- Proxies request to `git-http-backend` which serves the repository - Proxies request to `git-http-backend` which serves the repository
2. **Push**: 2. **Push**:
- User configures git with NIP-98 authentication - **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: - Before push, client creates a NIP-98 event (kind 27235) with:
- `u` tag: Request URL - `u` tag: Request URL
- `method` tag: HTTP method (POST) - `method` tag: HTTP method (POST)
@ -153,6 +222,9 @@ These are not part of any NIP but are used by this application:
### Ownership Transfer Flow ### Ownership Transfer Flow
1. **Current Owner Initiates Transfer**: 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: - Owner creates a kind 1641 event with:
- `a` tag: Repository identifier (`30617:{owner}:{repo}`) - `a` tag: Repository identifier (`30617:{owner}:{repo}`)
- `p` tag: New owner pubkey - `p` tag: New owner pubkey
@ -171,68 +243,49 @@ These are not part of any NIP but are used by this application:
- Updates current owner for all permission checks - Updates current owner for all permission checks
- Maintainers remain valid (checked against current owner) - Maintainers remain valid (checked against current owner)
### Pull Requests & Issues Flow ## Nostr Event Kinds Used
1. **Creating a PR/Issue**: 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).
- User creates a kind 1618 (PR) or 1621 (Issue) event
- Includes repository identifier in tags ### Standard NIP Event Kinds
- Publishes to Nostr relays - **30617** (`REPO_ANNOUNCEMENT`): Repository announcements (NIP-34)
- **30618** (`REPO_STATE`): Repository state announcements (NIP-34, optional)
2. **Status Management**: - **1617** (`PATCH`): Git patches (NIP-34)
- Owner/maintainer creates status events (kind 1630-1633) - **1618** (`PULL_REQUEST`): Pull request events (NIP-34)
- Links to PR/Issue via event references - **1619** (`PULL_REQUEST_UPDATE`): Pull request updates (NIP-34)
- Status changes: open → applied/closed/draft - **1621** (`ISSUE`): Issue events (NIP-34)
- **PR Merging**: Creates merge commit and publishes status event (kind 1631) with merge commit ID - **1630** (`STATUS_OPEN`): Open status (NIP-34)
- **PR Updates**: PR author can update PR tip commit using kind 1619 events - **1631** (`STATUS_APPLIED`): Applied/merged status (NIP-34)
- **Issue Management**: Owners, maintainers, and issue authors can update issue status - **1632** (`STATUS_CLOSED`): Closed status (NIP-34)
- **1633** (`STATUS_DRAFT`): Draft status (NIP-34)
3. **Highlights & Comments**: - **9802** (`HIGHLIGHT`): NIP-84 highlight events for code selections
- User selects code in PR diff view - **1111** (`COMMENT`): NIP-22 comment events for threaded discussions
- Creates kind 9802 highlight event with code selection metadata - **27235** (`NIP98_AUTH`): NIP-98 HTTP authentication events
- Users can comment on highlights using kind 1111 events - **3**: Contact list (NIP-02, for relay discovery)
- Comments are threaded using `A`, `K`, `P` tags (root) and `a`, `k`, `p` tags (parent) - **10002**: Relay list metadata (NIP-65, for relay discovery)
- **24**: Public message (NIP-24, for relay write proof)
### Forking Flow - **5**: Event deletion request (NIP-09)
1. **User Forks Repository**: ### Custom Event Kinds
- User clicks "Fork" button on repository page
- Server: These are not part of any NIP but are used by this application:
- Clones original repository
- Creates new repository at `/repos/{user-npub}/{fork-name}.git` - **1640** (`COMMIT_SIGNATURE`): Git commit signature events
- Creates new NIP-34 announcement for fork - Used to cryptographically sign git commits using Nostr keys
- Creates self-transfer event for fork ownership - Tags: `commit` (hash), `author` (name, email), `message` (commit message), `e` (NIP-98 auth event reference, optional)
- Publishes both to Nostr relays - See [docs/NIP_COMPLIANCE.md](./docs/NIP_COMPLIANCE.md#1640---commit_signature) for complete example
2. **Fork Identification**: - **1641** (`OWNERSHIP_TRANSFER`): Repository ownership transfer events (non-replaceable)
- Fork announcement includes reference to original repository - Transfers ownership from one pubkey to another
- UI displays "Forked from" badge - Self-transfers (owner → owner) used for initial ownership proof
- Non-replaceable to maintain immutable chain of ownership
### Private Repository Access - 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
1. **Privacy Setting**:
- Repository announcement includes `private` tag (or `t` tag with value `private`) - **30620** (`BRANCH_PROTECTION`): Branch protection rules (replaceable)
- Server marks repository as private - Allows requiring pull requests, reviewers, status checks for protected branches
- Tags: `d` (repo name), `a` (repo identifier), `branch` (branch name and protection settings)
2. **Access Control**: - See [docs/NIP_COMPLIANCE.md](./docs/NIP_COMPLIANCE.md#30620---branch_protection) for complete example
- All API endpoints check privacy status
- For private repos, requires NIP-98 authentication
- Verifies user is current owner or listed maintainer
- Returns 403 if unauthorized
### Relay Write Proof
Instead of traditional rate limiting, users must prove they can write to at least one default Nostr relay:
1. **Proof Mechanism**:
- User publishes a NIP-98 event (kind 27235) to a default relay
- Event must be within 60 seconds (per NIP-98 spec)
- Server verifies event exists on relay
- Alternative: User publishes kind 24 public message (5-minute window)
2. **Verification**:
- Server queries relay for the proof event
- Validates timestamp and signature
- Grants access if proof is valid
## Architecture ## Architecture
@ -258,187 +311,53 @@ Instead of traditional rate limiting, users must prove they can write to at leas
- **HighlightsService**: Manages NIP-84 highlights and NIP-22 comments - **HighlightsService**: Manages NIP-84 highlights and NIP-22 comments
- **RelayWriteProof**: Verifies user can write to Nostr relays - **RelayWriteProof**: Verifies user can write to Nostr relays
## 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)
```
## 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.
## Security Features ## Security Features
### Lightweight Mode (Single Container) ### Lightweight Mode (Single Container) - Default
- **Resource Limits**: Per-user repository count and disk quota limits - **Resource Limits**: Per-user repository count and disk quota limits
- **Rate Limiting**: Per-IP and per-user rate limiting for all operations - **Rate Limiting**: Per-IP and per-user rate limiting for all operations
- **Audit Logging**: Comprehensive logging of all security-relevant events - **Audit Logging**: Comprehensive logging of all security-relevant events
- **Path Validation**: Strict path validation to prevent traversal attacks - **Path Validation**: Strict path validation to prevent traversal attacks
- **git-http-backend Hardening**: Timeouts, process isolation, scoped access - **git-http-backend Hardening**: Timeouts, process isolation, scoped access
- **Mode**: Set `ENTERPRISE_MODE=false` or leave unset (default)
### Enterprise Mode (Kubernetes) ### Enterprise Mode (Kubernetes)
- **Process Isolation**: Container-per-tenant architecture - **Process Isolation**: Container-per-tenant architecture
- **Network Isolation**: Kubernetes Network Policies - **Network Isolation**: Kubernetes Network Policies
- **Resource Quotas**: Per-tenant CPU, memory, and storage limits - **Resource Quotas**: Per-tenant CPU, memory, and storage limits
- **Separate Volumes**: Each tenant has their own PersistentVolume - **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. See `docs/SECURITY.md` and `docs/SECURITY_IMPLEMENTATION.md` for detailed information.
## Environment Variables ## Environment Variables
- `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. See [Git Command Line Setup](#git-command-line-setup) above. **Note**: Install the [GitRepublic CLI](https://github.com/your-org/gitrepublic-cli) package to use this. ### Core Configuration
- `ENTERPRISE_MODE`: Enable enterprise mode with Kubernetes (default: `false`). When `true`, expects container-per-tenant architecture. See `k8s/ENTERPRISE_MODE.md` for details.
- `GIT_REPO_ROOT`: Path to store git repositories (default: `/repos`) - `GIT_REPO_ROOT`: Path to store git repositories (default: `/repos`)
- `GIT_DOMAIN`: Domain for git repositories (default: `localhost:6543`) - `GIT_DOMAIN`: Domain for git repositories (default: `localhost:6543`)
- `NOSTR_RELAYS`: Comma-separated list of Nostr relays (default: `wss://theforest.nostr1.com`) - `NOSTR_RELAYS`: Comma-separated list of Nostr relays (default: `wss://theforest.nostr1.com`)
- `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. - `NOSTR_SEARCH_RELAYS`: Comma-separated list of Nostr relays for searching (default: includes multiple relays)
- `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.
### Tor Hidden Service Setup
To provide `.onion` addresses for all repositories, you need to set up a Tor hidden service:
1. **Install and configure Tor**:
```bash
# On Debian/Ubuntu
sudo apt-get install tor
# Edit Tor configuration
sudo nano /etc/tor/torrc
```
2. **Add hidden service configuration**: ### Git Operations
``` - `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.
HiddenServiceDir /var/lib/tor/gitrepublic
HiddenServicePort 80 127.0.0.1:6543
```
3. **Restart Tor**: ### Tor Support
```bash - `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.
sudo systemctl restart tor - `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.
```
4. **Get your .onion address**:
```bash
sudo cat /var/lib/tor/gitrepublic/hostname
```
5. **Set environment variable** (optional, if hostname file is in a different location):
```bash
export TOR_ONION_ADDRESS=your-onion-address.onion
```
The server will automatically:
- Detect the `.onion` address from the hostname file or environment variable
- Add a `.onion` clone URL to every repository announcement
- Make repositories accessible via Tor even if the server is only on localhost
**Note**: The `.onion` address works even if your server is only accessible on `localhost` - Tor will handle the routing!
### Security Configuration ### Security Configuration
- `SECURITY_MODE`: `lightweight` (single container) or `enterprise` (Kubernetes) (default: `lightweight`)
- `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`)
- `RATE_LIMIT_WINDOW_MS`: Rate limit window in milliseconds (default: `60000` = 1 minute)
- `RATE_LIMIT_GIT_MAX`: Max git operations per window (default: `60`)
- `RATE_LIMIT_API_MAX`: Max API requests per window (default: `120`)
- `RATE_LIMIT_FILE_MAX`: Max file operations per window (default: `30`)
- `RATE_LIMIT_SEARCH_MAX`: Max search requests per window (default: `20`)
- `AUDIT_LOGGING_ENABLED`: Enable audit logging (default: `true`) - `AUDIT_LOGGING_ENABLED`: Enable audit logging (default: `true`)
- `AUDIT_LOG_FILE`: Optional file path for audit logs (default: console only)
- If set, logs are written to files with daily rotation (e.g., `audit-2024-01-01.log`)
- Example: `/var/log/gitrepublic/audit.log` → creates `audit-2024-01-01.log`, `audit-2024-01-02.log`, etc.
- `AUDIT_LOG_RETENTION_DAYS`: Number of days to keep audit log files (default: `90`)
- Old log files are automatically deleted after this period
- Set to `0` to disable automatic cleanup
### Git HTTP Backend Setup
The server uses `git-http-backend` for git operations. Ensure it's installed:
```bash
# On Debian/Ubuntu
sudo apt-get install git
# Verify installation
which git-http-backend
```
The server will automatically locate `git-http-backend` in common locations. ## Usage Examples
## Usage ### Web Interface
### Creating a Repository
#### Creating a Repository
1. Go to `/signup` 1. Go to `/signup`
2. Connect your NIP-07 extension 2. Connect your NIP-07 extension
3. Enter repository name and description 3. Enter repository name and description
@ -448,133 +367,159 @@ The server will automatically locate `git-http-backend` in common locations.
The server will automatically provision the repository. The server will automatically provision the repository.
### Git Command Line Setup #### Viewing Repositories
- Go to `/` to see all public repositories
To use git from the command line with GitRepublic, install the [GitRepublic CLI](https://github.com/your-org/gitrepublic-cli) tools. This lightweight package provides the credential helper and commit signing hook. - Go to `/repos/{npub}/{repo}` to view a specific repository
- Go to `/users/{npub}` to view a user's repositories
**Quick Setup:** - Go to `/search` to search for repositories
1. **Install via npm** (recommended):
```bash
npm install -g gitrepublic-cli
```
Or clone from GitHub:
```bash
git clone https://github.com/your-org/gitrepublic-cli.git
cd gitrepublic-cli
npm install
```
2. **Set your Nostr private key**:
```bash
export NOSTRGIT_SECRET_KEY="nsec1..."
# Or add to ~/.bashrc or ~/.zshrc for persistence
echo 'export NOSTRGIT_SECRET_KEY="nsec1..."' >> ~/.bashrc
```
3. **Run automatic setup**:
```bash
# Setup everything automatically
gitrepublic-setup
# Or with options:
gitrepublic-setup --domain your-domain.com # Configure for specific domain
gitrepublic-setup --global-hook # Install hook globally
```
The setup script automatically: #### Managing Repositories
- Finds the scripts (works with npm install or git clone) - **Settings**: Visit `/repos/{npub}/{repo}/settings` to manage privacy, maintainers, and description
- Configures git credential helper - **Forking**: Click "Fork" button on repository page
- Installs commit signing hook - **Transfer Ownership**: Use the transfer UI in repository settings
- Checks if `NOSTRGIT_SECRET_KEY` is set
**Important Notes:** ### Command Line Interface
- The `NOSTRGIT_SECRET_KEY` must match the repository owner or you must have maintainer permissions
- The credential helper generates fresh NIP-98 tokens for each request (per-request authentication)
- The commit signing hook only signs commits for GitRepublic repositories (detects `/api/git/npub` or `/repos/npub` URL patterns)
- Never commit your private key to version control
**CLI Features:** #### Cloning a Repository
- Full API access: `gitrepublic repos list`, `gitrepublic file get`, etc. ```bash
- Server configuration: `gitrepublic config server` # Using GitRepublic CLI (recommended)
- JSON output support: `gitrepublic --json repos get <npub> <repo>` gitrep clone https://{domain}/api/git/{npub}/{repo-name}.git
For complete setup instructions, API commands, and troubleshooting, see the [GitRepublic CLI README](https://github.com/your-org/gitrepublic-cli). # Or using standard git (requires credential helper setup)
git clone https://{domain}/api/git/{npub}/{repo-name}.git
```
### Cloning a Repository **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 ```bash
# Using GitRepublic API endpoint (recommended for commit signing detection) # Using GitRepublic CLI (automatic authentication)
git clone https://{domain}/api/git/{npub}/{repo-name}.git gitrep push origin main
# Or using repos endpoint
git clone https://{domain}/repos/{npub}/{repo-name}.git
# Direct path (also works, but may conflict with GRASP servers) # Or using standard git (requires credential helper)
git clone https://{domain}/{npub}/{repo-name}.git git push origin main
``` ```
**Note**: Use `/api/git/` or `/repos/` paths to ensure proper detection by the commit signing hook and to distinguish from GRASP servers. All three paths work for cloning, but `/api/git/` is recommended for best compatibility. The credential helper will automatically generate NIP-98 authentication tokens for push operations. The commit signing hook will automatically sign commits for GitRepublic repositories.
### Pushing to a Repository
#### API Commands
```bash ```bash
# Add remote (use /api/git/ or /repos/ path for best compatibility) # List repositories
git remote add origin https://{domain}/api/git/{npub}/{repo-name}.git gitrep repos list
# Push (requires credential helper setup) # Get repository details
git push origin main gitrep repos get <npub> <repo>
# Push to all remotes
gitrep push-all main
# Publish repository announcement
gitrep publish repo-announcement <repo-name>
``` ```
The credential helper will automatically generate NIP-98 authentication tokens for push operations. The commit signing hook will automatically sign commits for GitRepublic repositories. For complete CLI documentation, see [gitrepublic-cli/README.md](./gitrepublic-cli/README.md).
### Viewing Repositories ### API Access
- Go to `/` to see all public repositories All API endpoints are documented in OpenAPI format. Access the API documentation at:
- Go to `/repos/{npub}/{repo}` to view a specific repository - **Development**: `http://localhost:5173/api/openapi.json`
- Go to `/users/{npub}` to view a user's repositories - **Production**: `https://your-domain.com/api/openapi.json`
- Go to `/search` to search for repositories
### Managing Repositories #### Authentication
All write operations require NIP-98 HTTP authentication:
- **Settings**: Visit `/repos/{npub}/{repo}/settings` to manage privacy, maintainers, and description ```bash
- **Forking**: Click "Fork" button on repository page # Example: Create a file (requires NIP-98 auth)
- **Transfer Ownership**: Use the transfer API endpoint or create a kind 1641 event manually 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"}'
```
## Security Features The CLI handles authentication automatically. For manual API access, see the [NIP-98 specification](https://github.com/nostr-protocol/nips/blob/master/98.md).
### Lightweight Mode (Single Container) ## Development
- **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
### Enterprise Mode (Kubernetes) ### Prerequisites
- **Process Isolation**: Container-per-tenant architecture - Node.js 18+
- **Network Isolation**: Kubernetes Network Policies - Git with `git-http-backend` installed
- **Resource Quotas**: Per-tenant CPU, memory, and storage limits - NIP-07 browser extension (for web UI)
- **Separate Volumes**: Each tenant has their own PersistentVolume
### Security Considerations ### Setup
- **Path Traversal Protection**: All file paths are validated and sanitized ```bash
- **Input Validation**: Commit messages, author info, and file paths are validated npm install # Installs dependencies for both web and CLI (workspace)
- **Size Limits**: 2 GB per repository, 500 MB per file npm run dev
- **Authentication**: All write operations require NIP-98 authentication ```
- **Authorization**: Ownership and maintainer checks for all operations
- **Private Repositories**: Access restricted to owners and maintainers
- **Resource Limits**: Per-user repository count and disk quota limits (configurable)
- **Rate Limiting**: Per-IP and per-user rate limiting (configurable)
- **Audit Logging**: All security-relevant events are logged
See `docs/SECURITY.md` and `docs/SECURITY_IMPLEMENTATION.md` for detailed information. **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.
## License ### Project Structure
[Add your license here] ```
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)
```
## Contributing ## Additional Documentation
[Add contribution guidelines here] - [Architecture FAQ](./docs/ARCHITECTURE_FAQ.md) - Answers to common architecture questions
- [NIP Compliance](./docs/NIP_COMPLIANCE.md) - Complete event kind reference with JSON examples
- [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

2
docker-compose.yml

@ -16,6 +16,8 @@ services:
# - "6543" # Internal only, accessed via reverse proxy container # - "6543" # Internal only, accessed via reverse proxy container
environment: environment:
- NODE_ENV=production - NODE_ENV=production
# ENTERPRISE_MODE: Not set (defaults to false) - this is lightweight mode
# For enterprise mode with Kubernetes, see k8s/ENTERPRISE_MODE.md
- GIT_REPO_ROOT=/repos - GIT_REPO_ROOT=/repos
- GIT_DOMAIN=${GIT_DOMAIN:-gitrepublic.imwald.eu} # Set to your domain for production (without https://) - GIT_DOMAIN=${GIT_DOMAIN:-gitrepublic.imwald.eu} # Set to your domain for production (without https://)
- NOSTR_RELAYS=${NOSTR_RELAYS:-wss://theforest.nostr1.com} - NOSTR_RELAYS=${NOSTR_RELAYS:-wss://theforest.nostr1.com}

4
docs/tutorial.md

@ -140,7 +140,7 @@ Private repositories require authentication. You'll need to set up NIP-98 authen
#### Setting Up NIP-98 Authentication #### Setting Up NIP-98 Authentication
For command-line git operations, you need to install the [GitRepublic CLI](https://github.com/your-org/gitrepublic-cli) which provides: 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 1. **Credential Helper**: Automatically generates NIP-98 authentication tokens for git operations
2. **Commit Signing Hook**: Automatically signs commits for GitRepublic repositories 2. **Commit Signing Hook**: Automatically signs commits for GitRepublic repositories
@ -171,7 +171,7 @@ This automatically configures the credential helper and commit signing hook. See
When prompted, the credential helper will automatically generate and use a NIP-98 authentication token. 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/your-org/gitrepublic-cli) and set up the credential helper. See the README for complete setup instructions. **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 ### Cloning from Multiple Remotes

221
k8s/ENTERPRISE_MODE.md

@ -0,0 +1,221 @@
# Enterprise Mode Setup Guide
Enterprise mode provides complete isolation between tenants using Kubernetes. Each tenant (user/npub) gets their own container, namespace, and persistent volume.
## Quick Start
### Prerequisites
1. **Kubernetes cluster** (minikube, kind, or production cluster)
2. **kubectl** configured to access your cluster
3. **envsubst** (usually comes with `gettext` package)
4. **Ingress controller** (nginx-ingress recommended)
### Enable Enterprise Mode
Set the `ENTERPRISE_MODE` environment variable to `true`:
```bash
export ENTERPRISE_MODE=true
```
**Default**: `false` (lightweight mode - single container)
### Deploy a Tenant
```bash
cd k8s
./deploy-tenant.sh npub1abc123... \
--domain git.example.com \
--storage-class fast-ssd \
--storage-size 50Gi
```
### Check Status
```bash
# List all tenant namespaces
kubectl get namespaces | grep gitrepublic-tenant
# Check tenant resources
kubectl get all -n gitrepublic-tenant-<npub>
# View logs
kubectl logs -n gitrepublic-tenant-<npub> -l app=gitrepublic -f
```
### Delete a Tenant
```bash
./delete-tenant.sh npub1abc123...
```
## Architecture
In Enterprise Mode:
```
Kubernetes Cluster
├── Namespace: gitrepublic-tenant-npub1
│ ├── Deployment (1 pod)
│ │ └── Container: gitrepublic-web
│ ├── Service (ClusterIP)
│ ├── PersistentVolumeClaim (100Gi)
│ ├── ResourceQuota
│ ├── NetworkPolicy
│ └── Ingress
├── Namespace: gitrepublic-tenant-npub2
│ └── (same structure)
└── Namespace: gitrepublic-tenant-npub3
└── (same structure)
```
## Configuration
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `ENTERPRISE_MODE` | Enable enterprise mode | `false` |
| `GIT_DOMAIN` | Domain for git repositories | `localhost:6543` |
| `NOSTR_RELAYS` | Comma-separated Nostr relays | `wss://theforest.nostr1.com,...` |
| `STORAGE_CLASS` | Kubernetes storage class | `standard` |
| `STORAGE_SIZE` | Volume size per tenant | `100Gi` |
### Resource Limits (per tenant)
Default limits in `resource-quota.yaml`:
- **CPU**: 2 requests, 4 limits
- **Memory**: 2Gi requests, 4Gi limits
- **Storage**: 100Gi requests, 200Gi limits
- **Pods**: 2 max
Adjust in `k8s/base/resource-quota.yaml` as needed.
## Ingress Configuration
Each tenant can have:
- **Subdomain**: `user1.git.example.com`
- **Path-based**: `git.example.com/npub1abc123...`
Update `k8s/base/ingress.yaml` for your routing strategy.
### SSL/TLS
To enable HTTPS, uncomment the TLS section in `ingress.yaml` and configure cert-manager:
```yaml
tls:
- hosts:
- ${TENANT_SUBDOMAIN}.${GIT_DOMAIN}
secretName: gitrepublic-tls
```
## Network Isolation
Network policies prevent:
- Inter-tenant communication
- Unauthorized ingress
- Unnecessary egress
Only allows:
- Ingress from ingress controller
- Egress to Nostr relays (WSS on port 443)
- DNS queries
## Storage
Each tenant gets:
- **Own PersistentVolume**: Complete isolation
- **Size limits**: Configurable per tenant
- **Storage class**: Can use fast SSD for performance
### Backup
Use Kubernetes VolumeSnapshots:
```bash
kubectl create volumesnapshot gitrepublic-snapshot-$(date +%Y%m%d) \
--namespace gitrepublic-tenant-<npub> \
--source-pvc gitrepublic-repos
```
## Monitoring
### View Resource Usage
```bash
# CPU and memory usage
kubectl top pods -n gitrepublic-tenant-<npub>
# Storage usage
kubectl describe pvc gitrepublic-repos -n gitrepublic-tenant-<npub>
```
### Logs
```bash
# Application logs
kubectl logs -n gitrepublic-tenant-<npub> -l app=gitrepublic
# All resources in namespace
kubectl get all -n gitrepublic-tenant-<npub>
```
## Troubleshooting
### Pod Not Starting
```bash
# Check pod status
kubectl describe pod -n gitrepublic-tenant-<npub> -l app=gitrepublic
# Check events
kubectl get events -n gitrepublic-tenant-<npub> --sort-by='.lastTimestamp'
```
### Volume Issues
```bash
# Check PVC status
kubectl describe pvc gitrepublic-repos -n gitrepublic-tenant-<npub>
# Check storage class
kubectl get storageclass
```
### Network Issues
```bash
# Test connectivity from pod
kubectl exec -n gitrepublic-tenant-<npub> -l app=gitrepublic -- curl -I https://theforest.nostr1.com
```
## Migration from Lightweight Mode
1. **Backup repositories** from lightweight mode
2. **Deploy tenant** in enterprise mode
3. **Restore data** to new volume
4. **Update DNS** to point to new ingress
5. **Verify** all operations work
6. **Decommission** old lightweight container
## Cost Considerations
Enterprise mode uses more resources:
- **Per-tenant overhead**: ~500MB RAM, 0.5 CPU per tenant
- **Storage**: Separate volumes (can't share unused space)
- **Network**: More complex routing
**Recommendation**: Use enterprise mode for:
- High-value tenants
- Security-sensitive deployments
- Compliance requirements
- Large-scale deployments
Use lightweight mode for:
- Development/testing
- Small deployments
- Cost-sensitive scenarios

42
k8s/README.md

@ -36,13 +36,40 @@ k8s/
## Usage ## Usage
### Single Container (Lightweight) ### Single Container (Lightweight) - Default Mode
Use the existing `docker-compose.yml` or `Dockerfile`. Security improvements are application-level and work automatically. Use the existing `docker-compose.yml` or `Dockerfile`. Security improvements are application-level and work automatically.
### Kubernetes (Enterprise) **Environment Variable**: `ENTERPRISE_MODE` is not set (defaults to `false`)
#### Option 1: Manual Deployment ### Kubernetes (Enterprise Mode)
**Enable Enterprise Mode**: Set `ENTERPRISE_MODE=true` environment variable
Enterprise mode provides:
- Container-per-tenant isolation
- Separate volumes per tenant
- Network isolation
- Resource quotas
- Process isolation
#### Option 1: Using Deployment Script (Recommended)
Use the provided deployment script:
```bash
# Deploy a tenant
./k8s/deploy-tenant.sh npub1abc123... \
--domain git.example.com \
--storage-class fast-ssd \
--storage-size 50Gi \
--subdomain user1
# Delete a tenant
./k8s/delete-tenant.sh npub1abc123...
```
#### Option 2: Manual Deployment
1. **Create namespace for tenant**: 1. **Create namespace for tenant**:
```bash ```bash
@ -50,8 +77,10 @@ export TENANT_ID="npub1abc123..."
export GIT_DOMAIN="git.example.com" export GIT_DOMAIN="git.example.com"
export NOSTR_RELAYS="wss://relay1.com,wss://relay2.com" export NOSTR_RELAYS="wss://relay1.com,wss://relay2.com"
export STORAGE_CLASS="fast-ssd" export STORAGE_CLASS="fast-ssd"
export STORAGE_SIZE="100Gi"
export TENANT_SUBDOMAIN="user1"
# Replace variables in templatesa # Replace variables in templates
envsubst < k8s/base/namespace.yaml | kubectl apply -f - envsubst < k8s/base/namespace.yaml | kubectl apply -f -
envsubst < k8s/base/resource-quota.yaml | kubectl apply -f - envsubst < k8s/base/resource-quota.yaml | kubectl apply -f -
envsubst < k8s/base/limit-range.yaml | kubectl apply -f - envsubst < k8s/base/limit-range.yaml | kubectl apply -f -
@ -59,9 +88,10 @@ envsubst < k8s/base/pvc.yaml | kubectl apply -f -
envsubst < k8s/base/deployment.yaml | kubectl apply -f - envsubst < k8s/base/deployment.yaml | kubectl apply -f -
envsubst < k8s/base/service.yaml | kubectl apply -f - envsubst < k8s/base/service.yaml | kubectl apply -f -
envsubst < k8s/base/network-policy.yaml | kubectl apply -f - envsubst < k8s/base/network-policy.yaml | kubectl apply -f -
envsubst < k8s/base/ingress.yaml | kubectl apply -f -
``` ```
#### Option 2: Operator Pattern (Recommended) #### Option 3: Operator Pattern (Advanced)
Create a Kubernetes operator that: Create a Kubernetes operator that:
- Watches for new repository announcements - Watches for new repository announcements
@ -69,7 +99,7 @@ Create a Kubernetes operator that:
- Manages tenant lifecycle - Manages tenant lifecycle
- Handles scaling and resource allocation - Handles scaling and resource allocation
#### Option 3: Helm Chart #### Option 4: Helm Chart
Package as Helm chart for easier deployment: Package as Helm chart for easier deployment:
```bash ```bash

4
k8s/base/deployment.yaml

@ -44,8 +44,8 @@ spec:
value: "${NOSTR_RELAYS}" value: "${NOSTR_RELAYS}"
- name: PORT - name: PORT
value: "6543" value: "6543"
- name: SECURITY_MODE - name: ENTERPRISE_MODE
value: "enterprise" # Use enterprise mode in K8s value: "true" # Enable enterprise mode in K8s
volumeMounts: volumeMounts:
- name: repos - name: repos
mountPath: /repos mountPath: /repos

31
k8s/base/ingress.yaml

@ -0,0 +1,31 @@
# Ingress for gitrepublic tenant
# Routes external traffic to the tenant's service
# Each tenant can have their own subdomain or path-based routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitrepublic-ingress
namespace: gitrepublic-tenant-${TENANT_ID}
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
# Optional: SSL/TLS configuration
# cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
rules:
- host: ${TENANT_SUBDOMAIN}.${GIT_DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitrepublic
port:
number: 80
# Optional: TLS configuration
# tls:
# - hosts:
# - ${TENANT_SUBDOMAIN}.${GIT_DOMAIN}
# secretName: gitrepublic-tls

59
k8s/delete-tenant.sh

@ -0,0 +1,59 @@
#!/bin/bash
# Delete a tenant from Kubernetes
# Usage: ./delete-tenant.sh <tenant-npub>
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if tenant ID is provided
if [ -z "$1" ]; then
echo -e "${RED}Error: Tenant ID (npub) is required${NC}"
echo "Usage: $0 <tenant-npub>"
exit 1
fi
TENANT_ID="$1"
# Validate tenant ID format (basic check)
if [[ ! "$TENANT_ID" =~ ^npub1[a-z0-9]+$ ]]; then
echo -e "${YELLOW}Warning: Tenant ID doesn't look like a valid npub format${NC}"
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
NAMESPACE="gitrepublic-tenant-${TENANT_ID}"
# Check if namespace exists
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
echo -e "${RED}Error: Namespace ${NAMESPACE} does not exist${NC}"
exit 1
fi
echo -e "${YELLOW}WARNING: This will delete the entire tenant namespace and all resources!${NC}"
echo " Namespace: ${NAMESPACE}"
echo " This includes:"
echo " - All pods and containers"
echo " - Persistent volumes (data will be lost unless backed up)"
echo " - All configuration"
echo ""
read -p "Are you sure you want to delete this tenant? (yes/NO) " -r
echo
if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then
echo "Cancelled."
exit 0
fi
echo -e "${GREEN}Deleting tenant namespace: ${NAMESPACE}${NC}"
kubectl delete namespace "$NAMESPACE" --wait=true
echo ""
echo -e "${GREEN}✓ Tenant deleted successfully!${NC}"

138
k8s/deploy-tenant.sh

@ -0,0 +1,138 @@
#!/bin/bash
# Deploy a tenant to Kubernetes in enterprise mode
# Usage: ./deploy-tenant.sh <tenant-npub> [options]
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if tenant ID is provided
if [ -z "$1" ]; then
echo -e "${RED}Error: Tenant ID (npub) is required${NC}"
echo "Usage: $0 <tenant-npub> [--domain <domain>] [--storage-class <class>] [--storage-size <size>] [--subdomain <subdomain>]"
echo ""
echo "Example:"
echo " $0 npub1abc123... --domain git.example.com --storage-class fast-ssd --storage-size 50Gi"
exit 1
fi
TENANT_ID="$1"
shift
# Default values
GIT_DOMAIN="${GIT_DOMAIN:-git.example.com}"
STORAGE_CLASS="${STORAGE_CLASS:-standard}"
STORAGE_SIZE="${STORAGE_SIZE:-100Gi}"
TENANT_SUBDOMAIN="${TENANT_SUBDOMAIN:-${TENANT_ID:0:16}}"
NOSTR_RELAYS="${NOSTR_RELAYS:-wss://theforest.nostr1.com,wss://nostr.land}"
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--domain)
GIT_DOMAIN="$2"
shift 2
;;
--storage-class)
STORAGE_CLASS="$2"
shift 2
;;
--storage-size)
STORAGE_SIZE="$2"
shift 2
;;
--subdomain)
TENANT_SUBDOMAIN="$2"
shift 2
;;
--relays)
NOSTR_RELAYS="$2"
shift 2
;;
*)
echo -e "${RED}Unknown option: $1${NC}"
exit 1
;;
esac
done
# Validate tenant ID format (basic check)
if [[ ! "$TENANT_ID" =~ ^npub1[a-z0-9]+$ ]]; then
echo -e "${YELLOW}Warning: Tenant ID doesn't look like a valid npub format${NC}"
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
echo -e "${GREEN}Deploying tenant: ${TENANT_ID}${NC}"
echo " Domain: ${GIT_DOMAIN}"
echo " Subdomain: ${TENANT_SUBDOMAIN}"
echo " Storage Class: ${STORAGE_CLASS}"
echo " Storage Size: ${STORAGE_SIZE}"
echo ""
# Export variables for envsubst
export TENANT_ID
export GIT_DOMAIN
export STORAGE_CLASS
export STORAGE_SIZE
export TENANT_SUBDOMAIN
export NOSTR_RELAYS
# Check if kubectl is available
if ! command -v kubectl &> /dev/null; then
echo -e "${RED}Error: kubectl is not installed or not in PATH${NC}"
exit 1
fi
# Check if we can connect to cluster
if ! kubectl cluster-info &> /dev/null; then
echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}"
exit 1
fi
# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="${SCRIPT_DIR}/base"
echo -e "${GREEN}Creating namespace...${NC}"
envsubst < "${BASE_DIR}/namespace.yaml" | kubectl apply -f -
echo -e "${GREEN}Creating resource quota...${NC}"
envsubst < "${BASE_DIR}/resource-quota.yaml" | kubectl apply -f -
echo -e "${GREEN}Creating limit range...${NC}"
envsubst < "${BASE_DIR}/limit-range.yaml" | kubectl apply -f -
echo -e "${GREEN}Creating persistent volume claim...${NC}"
envsubst < "${BASE_DIR}/pvc.yaml" | kubectl apply -f -
echo -e "${GREEN}Creating deployment...${NC}"
envsubst < "${BASE_DIR}/deployment.yaml" | kubectl apply -f -
echo -e "${GREEN}Creating service...${NC}"
envsubst < "${BASE_DIR}/service.yaml" | kubectl apply -f -
echo -e "${GREEN}Creating network policy...${NC}"
envsubst < "${BASE_DIR}/network-policy.yaml" | kubectl apply -f -
echo -e "${GREEN}Creating ingress...${NC}"
envsubst < "${BASE_DIR}/ingress.yaml" | kubectl apply -f -
echo ""
echo -e "${GREEN}✓ Tenant deployed successfully!${NC}"
echo ""
echo "To check status:"
echo " kubectl get all -n gitrepublic-tenant-${TENANT_ID}"
echo ""
echo "To view logs:"
echo " kubectl logs -n gitrepublic-tenant-${TENANT_ID} -l app=gitrepublic"
echo ""
echo "To delete tenant:"
echo " kubectl delete namespace gitrepublic-tenant-${TENANT_ID}"

1
nostr/commit-signatures.jsonl

@ -30,3 +30,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771619895,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","update docs"]],"content":"Signed commit: update docs","id":"82efc8b4dbac67dec5e02ebd46e504d7a6a3bbe7a53963984c3c4cbf6ac52a3b","sig":"5f5643be35aa997558ac79e99aa70f680a0e449bd1027afd83d65b2d7a1eee5f65d23d0d89b069e6118add1e78a3becd33d47f1d2fd82c6f86d9d12e14a5bc2e"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771619895,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","update docs"]],"content":"Signed commit: update docs","id":"82efc8b4dbac67dec5e02ebd46e504d7a6a3bbe7a53963984c3c4cbf6ac52a3b","sig":"5f5643be35aa997558ac79e99aa70f680a0e449bd1027afd83d65b2d7a1eee5f65d23d0d89b069e6118add1e78a3becd33d47f1d2fd82c6f86d9d12e14a5bc2e"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771622212,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","finish implementing nip-34"]],"content":"Signed commit: finish implementing nip-34","id":"e036526abc826e4435a562f1f334e594577d78a7a50a02cb78f8e5565ea68872","sig":"12642202ef028dfbac68ce53e9cf9f7a64ce3242d2dd995fd0b4c4014c9aa2b18891b72dc281fa5aadacd636646ebd8d2b69fd29bf36407658dff9725b779be5"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771622212,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","finish implementing nip-34"]],"content":"Signed commit: finish implementing nip-34","id":"e036526abc826e4435a562f1f334e594577d78a7a50a02cb78f8e5565ea68872","sig":"12642202ef028dfbac68ce53e9cf9f7a64ce3242d2dd995fd0b4c4014c9aa2b18891b72dc281fa5aadacd636646ebd8d2b69fd29bf36407658dff9725b779be5"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771622823,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","finish missing events"]],"content":"Signed commit: finish missing events","id":"964e4a35978748cbbc5127daad5c8a0724b6df6f4342c5b0940dd16bc2e8262d","sig":"f44401709ba7a5fdf988f149033d7aa9bd838e8d6b86cd6357d738e69fab405d0df70394910ceebdb22501518583280499320424a5e7b4999e91d1fb6a2b07b8"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771622823,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","finish missing events"]],"content":"Signed commit: finish missing events","id":"964e4a35978748cbbc5127daad5c8a0724b6df6f4342c5b0940dd16bc2e8262d","sig":"f44401709ba7a5fdf988f149033d7aa9bd838e8d6b86cd6357d738e69fab405d0df70394910ceebdb22501518583280499320424a5e7b4999e91d1fb6a2b07b8"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771623058,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"29ded44ac53cd8c924310728e3bac34070a2daa62418f3d79a386fdc84d93afe","sig":"a60a145c2f234e4bad593423ebc2a39ab12ec7a78c343b46348e6dbeac809be9b61e42219c9d569cc0902788bb59af3c36ffd0de1aa56d9d52611575d2ac797f"}

14
src/lib/config.ts

@ -93,6 +93,20 @@ export function isOnionAddress(url: string): boolean {
} }
} }
/**
* Enterprise mode configuration
* When enabled, the system expects to run in Kubernetes with container-per-tenant architecture
* Default: false (lightweight mode - single container)
* Can be overridden by ENTERPRISE_MODE env var (set to "true" to enable)
*/
export const ENTERPRISE_MODE =
typeof process !== 'undefined' && process.env?.ENTERPRISE_MODE === 'true';
/**
* Security mode string (for logging/debugging)
*/
export const SECURITY_MODE = ENTERPRISE_MODE ? 'enterprise' : 'lightweight';
/** /**
* Combine default relays with user's relays (from kind 10002) * Combine default relays with user's relays (from kind 10002)
* Returns a deduplicated list with user relays first, then defaults * Returns a deduplicated list with user relays first, then defaults

8
src/lib/services/git/repo-manager.ts

@ -455,7 +455,7 @@ Your commits will all be signed by your Nostr keys and saved to the event files
const git = simpleGit(repoPath); const git = simpleGit(repoPath);
// Get current branch name // Get current branch name
const currentBranch = await git.revParse(['--abbrev-ref', 'HEAD']); const currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
if (!currentBranch) { if (!currentBranch) {
return false; // Can't determine current branch return false; // Can't determine current branch
} }
@ -468,15 +468,15 @@ Your commits will all be signed by your Nostr keys and saved to the event files
// Check if remote branch exists // Check if remote branch exists
try { try {
await git.revParse([`refs/remotes/${remoteBranch}`]); await git.revparse([`refs/remotes/${remoteBranch}`]);
} catch { } catch {
// Remote branch doesn't exist yet - safe to push (first push) // Remote branch doesn't exist yet - safe to push (first push)
return true; return true;
} }
// Get local and remote commit SHAs // Get local and remote commit SHAs
const localSha = await git.revParse(['HEAD']); const localSha = await git.revparse(['HEAD']);
const remoteSha = await git.revParse([`refs/remotes/${remoteBranch}`]); const remoteSha = await git.revparse([`refs/remotes/${remoteBranch}`]);
// If they're the same, it's safe (no-op) // If they're the same, it's safe (no-op)
if (localSha === remoteSha) { if (localSha === remoteSha) {

33
src/routes/api/git/[...path]/+server.ts

@ -655,7 +655,7 @@ export const POST: RequestHandler = async ({ params, url, request }) => {
// This ensures git calls the credential helper proactively // This ensures git calls the credential helper proactively
// Git requires WWW-Authenticate header on ALL 401 responses, otherwise it won't retry // Git requires WWW-Authenticate header on ALL 401 responses, otherwise it won't retry
if (!rawAuthHeader) { if (!rawAuthHeader) {
return new Response('Authentication required. Please configure the git credential helper. See https://github.com/your-org/gitrepublic-cli for setup instructions.', { return new Response('Authentication required. Please configure the git credential helper. Install via: npm install -g gitrepublic-cli\nSee https://github.com/silberengel/gitrepublic-cli for setup instructions.', {
status: 401, status: 401,
headers: { headers: {
'WWW-Authenticate': 'Basic realm="GitRepublic"', 'WWW-Authenticate': 'Basic realm="GitRepublic"',
@ -822,23 +822,30 @@ export const POST: RequestHandler = async ({ params, url, request }) => {
// Check protection for all branches being pushed // Check protection for all branches being pushed
for (const targetBranch of pushedBranches) { for (const targetBranch of pushedBranches) {
const protectionCheck = await branchProtectionService.canPushToBranch( try {
authResult.pubkey || '', const protectionCheck = await branchProtectionService.canPushToBranch(
currentOwnerPubkey, authResult.pubkey || '',
repoName, currentOwnerPubkey,
targetBranch, repoName,
isMaintainer targetBranch,
); isMaintainer
);
if (!protectionCheck.allowed) {
return error(403, protectionCheck.reason || `Branch '${targetBranch}' is protected`); if (!protectionCheck.allowed) {
return error(403, protectionCheck.reason || `Branch '${targetBranch}' is protected`);
}
} catch (error) {
// If we can't check protection, log but don't block (fail open for now)
// Security: Sanitize error messages
const sanitizedError = sanitizeError(error);
logger.warn({ error: sanitizedError, npub, repoName, targetBranch }, 'Failed to check branch protection');
} }
} }
} catch (error) { } catch (error) {
// If we can't check protection, log but don't block (fail open for now) // If we can't extract branches or check protection, log but don't block (fail open for now)
// Security: Sanitize error messages // Security: Sanitize error messages
const sanitizedError = sanitizeError(error); const sanitizedError = sanitizeError(error);
logger.warn({ error: sanitizedError, npub, repoName, targetBranch }, 'Failed to check branch protection'); logger.warn({ error: sanitizedError, npub, repoName }, 'Failed to check branch protection');
} }
} }

1
src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts

@ -3,6 +3,7 @@
*/ */
import { json } from '@sveltejs/kit'; import { json } from '@sveltejs/kit';
// @ts-ignore - SvelteKit generates this type
import type { RequestHandler } from './$types'; import type { RequestHandler } from './$types';
import { withRepoValidation } from '$lib/utils/api-handlers.js'; import { withRepoValidation } from '$lib/utils/api-handlers.js';
import type { RepoRequestContext } from '$lib/utils/api-context.js'; import type { RepoRequestContext } from '$lib/utils/api-context.js';

3
src/routes/repos/[npub]/[repo]/+page.svelte

@ -4057,7 +4057,7 @@
{repo} {repo}
{repoOwnerPubkey} {repoOwnerPubkey}
isMaintainer={isMaintainer} isMaintainer={isMaintainer}
userPubkeyHex={userPubkeyHex} userPubkeyHex={userPubkeyHex ?? undefined}
onStatusUpdate={loadPRs} onStatusUpdate={loadPRs}
/> />
<button onclick={() => selectedPR = null} class="back-btn">← Back to PR List</button> <button onclick={() => selectedPR = null} class="back-btn">← Back to PR List</button>
@ -5374,7 +5374,6 @@
margin-bottom: 1rem; margin-bottom: 1rem;
padding: 0.75rem; padding: 0.75rem;
} }
}
.non-maintainer-notice { .non-maintainer-notice {
font-size: 0.7rem; font-size: 0.7rem;

Loading…
Cancel
Save