From 8877f792469d84c4de0d60b890a38e93a132a83f Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 20 Feb 2026 22:54:10 +0100 Subject: [PATCH] bug-fixes and doc updates Nostr-Signature: d089915a2d9a9d46ba25d2d3c1cb4608a2b658ecc4260f17e73efa4ccc63a28d 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 3d447f05a55704d45ed843b7cc5fa16e49f3da0e452b1523392aefbb7a2ae3e79400a763df5705db8e38abc89e9a89480ab2c529890b531b171c4e980520d9b8 --- README.md | 661 ++++++++---------- docker-compose.yml | 2 + docs/tutorial.md | 4 +- k8s/ENTERPRISE_MODE.md | 221 ++++++ k8s/README.md | 42 +- k8s/base/deployment.yaml | 4 +- k8s/base/ingress.yaml | 31 + k8s/delete-tenant.sh | 59 ++ k8s/deploy-tenant.sh | 138 ++++ nostr/commit-signatures.jsonl | 1 + src/lib/config.ts | 14 + src/lib/services/git/repo-manager.ts | 8 +- src/routes/api/git/[...path]/+server.ts | 31 +- .../repos/[npub]/[repo]/prs/merge/+server.ts | 1 + src/routes/repos/[npub]/[repo]/+page.svelte | 3 +- 15 files changed, 834 insertions(+), 386 deletions(-) create mode 100644 k8s/ENTERPRISE_MODE.md create mode 100644 k8s/base/ingress.yaml create mode 100755 k8s/delete-tenant.sh create mode 100755 k8s/deploy-tenant.sh diff --git a/README.md b/README.md index c5b837b..dba11f2 100644 --- a/README.md +++ b/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. -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 @@ -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 - **Download Repository**: Download repositories as ZIP archives - **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 - **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) - ⚠️ **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 -- **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) +### Installation -### 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 - - 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 +# Start development server +npm run dev +``` -- **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 +#### For CLI (Command Line) +```bash +# Install globally +npm install -g gitrepublic-cli -- **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 +# Set your Nostr private key +export NOSTRGIT_SECRET_KEY="nsec1..." + +# 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 ### Repository Creation Flow 1. **User Creates Announcement**: - - User visits `/signup` and connects NIP-07 extension - - Enters repository name, description, and optional clone URLs + - **Web**: Visit `/signup` and connect NIP-07 extension + - **CLI**: Run `gitrep publish repo-announcement ` + - **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 @@ -126,13 +193,15 @@ These are not part of any NIP but are used by this application: ### Git Operations Flow 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` - For private repos, verifies NIP-98 authentication - Proxies request to `git-http-backend` which serves the repository 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: - `u` tag: Request URL - `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 1. **Current Owner Initiates Transfer**: + - **Web**: Use transfer UI in repository settings + - **CLI**: `gitrep transfer ` + - **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 @@ -171,68 +243,49 @@ These are not part of any NIP but are used by this application: - Updates current owner for all permission checks - Maintainers remain valid (checked against current owner) -### Pull Requests & Issues Flow - -1. **Creating a PR/Issue**: - - User creates a kind 1618 (PR) or 1621 (Issue) event - - Includes repository identifier in tags - - Publishes to Nostr relays - -2. **Status Management**: - - Owner/maintainer creates status events (kind 1630-1633) - - Links to PR/Issue via event references - - Status changes: open → applied/closed/draft - - **PR Merging**: Creates merge commit and publishes status event (kind 1631) with merge commit ID - - **PR Updates**: PR author can update PR tip commit using kind 1619 events - - **Issue Management**: Owners, maintainers, and issue authors can update issue status - -3. **Highlights & Comments**: - - User selects code in PR diff view - - Creates kind 9802 highlight event with code selection metadata - - Users can comment on highlights using kind 1111 events - - Comments are threaded using `A`, `K`, `P` tags (root) and `a`, `k`, `p` tags (parent) - -### Forking Flow - -1. **User Forks Repository**: - - User clicks "Fork" button on repository page - - Server: - - Clones original repository - - Creates new repository at `/repos/{user-npub}/{fork-name}.git` - - Creates new NIP-34 announcement for fork - - Creates self-transfer event for fork ownership - - Publishes both to Nostr relays - -2. **Fork Identification**: - - Fork announcement includes reference to original repository - - UI displays "Forked from" badge - -### Private Repository Access - -1. **Privacy Setting**: - - Repository announcement includes `private` tag (or `t` tag with value `private`) - - Server marks repository as private - -2. **Access Control**: - - 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 +## 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) + +### 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 @@ -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 - **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 -### Lightweight Mode (Single Container) +### 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) - **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. ## 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_DOMAIN`: Domain for git repositories (default: `localhost:6543`) - `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. -- `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**: - ``` - HiddenServiceDir /var/lib/tor/gitrepublic - HiddenServicePort 80 127.0.0.1:6543 - ``` +- `NOSTR_SEARCH_RELAYS`: Comma-separated list of Nostr relays for searching (default: includes multiple relays) -3. **Restart Tor**: - ```bash - sudo systemctl restart tor - ``` +### 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. -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! +### Tor Support +- `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 - -- `SECURITY_MODE`: `lightweight` (single container) or `enterprise` (Kubernetes) (default: `lightweight`) - `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) - `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_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 +## Usage Examples -### Creating a Repository +### Web Interface +#### Creating a Repository 1. Go to `/signup` 2. Connect your NIP-07 extension 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. -### Git Command Line Setup - -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. - -**Quick Setup:** - -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: - - Finds the scripts (works with npm install or git clone) - - Configures git credential helper - - Installs commit signing hook - - Checks if `NOSTRGIT_SECRET_KEY` is set - -**Important Notes:** -- 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:** -- Full API access: `gitrepublic repos list`, `gitrepublic file get`, etc. -- Server configuration: `gitrepublic config server` -- JSON output support: `gitrepublic --json repos get ` - -For complete setup instructions, API commands, and troubleshooting, see the [GitRepublic CLI README](https://github.com/your-org/gitrepublic-cli). - -### Cloning a 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 -```bash -# Using GitRepublic API endpoint (recommended for commit signing detection) -git clone https://{domain}/api/git/{npub}/{repo-name}.git +#### 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 -# Or using repos endpoint -git clone https://{domain}/repos/{npub}/{repo-name}.git +### Command Line Interface -# Direct path (also works, but may conflict with GRASP servers) -git clone https://{domain}/{npub}/{repo-name}.git -``` +#### Cloning a Repository +```bash +# Using GitRepublic CLI (recommended) +gitrep 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 and to distinguish from GRASP servers. All three paths work for cloning, but `/api/git/` is recommended for best compatibility. +# Or using standard git (requires credential helper setup) +git clone https://{domain}/api/git/{npub}/{repo-name}.git +``` -### Pushing to 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 -# Add remote (use /api/git/ or /repos/ path for best compatibility) -git remote add origin https://{domain}/api/git/{npub}/{repo-name}.git +# Using GitRepublic CLI (automatic authentication) +gitrep push origin main -# Push (requires credential helper setup) +# 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. -### Viewing Repositories +#### API Commands +```bash +# List repositories +gitrep repos list -- 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 +# Get repository details +gitrep repos get -### Managing Repositories +# Push to all remotes +gitrep push-all main -- **Settings**: Visit `/repos/{npub}/{repo}/settings` to manage privacy, maintainers, and description -- **Forking**: Click "Fork" button on repository page -- **Transfer Ownership**: Use the transfer API endpoint or create a kind 1641 event manually +# Publish repository announcement +gitrep publish repo-announcement +``` -## Security Features +For complete CLI documentation, see [gitrepublic-cli/README.md](./gitrepublic-cli/README.md). -### Lightweight Mode (Single Container) -- **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 +### API Access -### Enterprise Mode (Kubernetes) -- **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 +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` -### Security Considerations +#### Authentication +All write operations require NIP-98 HTTP authentication: -- **Path Traversal Protection**: All file paths are validated and sanitized -- **Input Validation**: Commit messages, author info, and file paths are validated -- **Size Limits**: 2 GB per repository, 500 MB per file -- **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 +```bash +# Example: Create a file (requires NIP-98 auth) +curl -X POST https://your-domain.com/api/repos/{npub}/{repo}/file \ + -H "Authorization: Nostr " \ + -H "Content-Type: application/json" \ + -d '{"path": "test.txt", "content": "Hello World"}' +``` -See `docs/SECURITY.md` and `docs/SECURITY_IMPLEMENTATION.md` for detailed information. +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) -## License +### Setup -[Add your license here] +```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) +``` -## 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 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index c945e8d..ccb1fc9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,8 @@ services: # - "6543" # Internal only, accessed via reverse proxy container environment: - 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_DOMAIN=${GIT_DOMAIN:-gitrepublic.imwald.eu} # Set to your domain for production (without https://) - NOSTR_RELAYS=${NOSTR_RELAYS:-wss://theforest.nostr1.com} diff --git a/docs/tutorial.md b/docs/tutorial.md index d397dd7..152a4a5 100644 --- a/docs/tutorial.md +++ b/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 -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 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. -**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 diff --git a/k8s/ENTERPRISE_MODE.md b/k8s/ENTERPRISE_MODE.md new file mode 100644 index 0000000..c954720 --- /dev/null +++ b/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- + +# View logs +kubectl logs -n gitrepublic-tenant- -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- \ + --source-pvc gitrepublic-repos +``` + +## Monitoring + +### View Resource Usage + +```bash +# CPU and memory usage +kubectl top pods -n gitrepublic-tenant- + +# Storage usage +kubectl describe pvc gitrepublic-repos -n gitrepublic-tenant- +``` + +### Logs + +```bash +# Application logs +kubectl logs -n gitrepublic-tenant- -l app=gitrepublic + +# All resources in namespace +kubectl get all -n gitrepublic-tenant- +``` + +## Troubleshooting + +### Pod Not Starting + +```bash +# Check pod status +kubectl describe pod -n gitrepublic-tenant- -l app=gitrepublic + +# Check events +kubectl get events -n gitrepublic-tenant- --sort-by='.lastTimestamp' +``` + +### Volume Issues + +```bash +# Check PVC status +kubectl describe pvc gitrepublic-repos -n gitrepublic-tenant- + +# Check storage class +kubectl get storageclass +``` + +### Network Issues + +```bash +# Test connectivity from pod +kubectl exec -n gitrepublic-tenant- -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 diff --git a/k8s/README.md b/k8s/README.md index c412eb7..bbba68b 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -36,13 +36,40 @@ k8s/ ## 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. -### 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**: ```bash @@ -50,8 +77,10 @@ export TENANT_ID="npub1abc123..." export GIT_DOMAIN="git.example.com" export NOSTR_RELAYS="wss://relay1.com,wss://relay2.com" 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/resource-quota.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/service.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: - Watches for new repository announcements @@ -69,7 +99,7 @@ Create a Kubernetes operator that: - Manages tenant lifecycle - Handles scaling and resource allocation -#### Option 3: Helm Chart +#### Option 4: Helm Chart Package as Helm chart for easier deployment: ```bash diff --git a/k8s/base/deployment.yaml b/k8s/base/deployment.yaml index bd75a43..cdf0e22 100644 --- a/k8s/base/deployment.yaml +++ b/k8s/base/deployment.yaml @@ -44,8 +44,8 @@ spec: value: "${NOSTR_RELAYS}" - name: PORT value: "6543" - - name: SECURITY_MODE - value: "enterprise" # Use enterprise mode in K8s + - name: ENTERPRISE_MODE + value: "true" # Enable enterprise mode in K8s volumeMounts: - name: repos mountPath: /repos diff --git a/k8s/base/ingress.yaml b/k8s/base/ingress.yaml new file mode 100644 index 0000000..727f8ed --- /dev/null +++ b/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 diff --git a/k8s/delete-tenant.sh b/k8s/delete-tenant.sh new file mode 100755 index 0000000..4e72e3b --- /dev/null +++ b/k8s/delete-tenant.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Delete a tenant from Kubernetes +# Usage: ./delete-tenant.sh + +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 " + 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}" diff --git a/k8s/deploy-tenant.sh b/k8s/deploy-tenant.sh new file mode 100755 index 0000000..f1b9f2e --- /dev/null +++ b/k8s/deploy-tenant.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# Deploy a tenant to Kubernetes in enterprise mode +# Usage: ./deploy-tenant.sh [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 [--domain ] [--storage-class ] [--storage-size ] [--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}" diff --git a/nostr/commit-signatures.jsonl b/nostr/commit-signatures.jsonl index 200be9e..addffda 100644 --- a/nostr/commit-signatures.jsonl +++ b/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":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":1771623058,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"29ded44ac53cd8c924310728e3bac34070a2daa62418f3d79a386fdc84d93afe","sig":"a60a145c2f234e4bad593423ebc2a39ab12ec7a78c343b46348e6dbeac809be9b61e42219c9d569cc0902788bb59af3c36ffd0de1aa56d9d52611575d2ac797f"} diff --git a/src/lib/config.ts b/src/lib/config.ts index 998d717..095db1e 100644 --- a/src/lib/config.ts +++ b/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) * Returns a deduplicated list with user relays first, then defaults diff --git a/src/lib/services/git/repo-manager.ts b/src/lib/services/git/repo-manager.ts index 14025fc..c4c79e8 100644 --- a/src/lib/services/git/repo-manager.ts +++ b/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); // Get current branch name - const currentBranch = await git.revParse(['--abbrev-ref', 'HEAD']); + const currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']); if (!currentBranch) { 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 try { - await git.revParse([`refs/remotes/${remoteBranch}`]); + await git.revparse([`refs/remotes/${remoteBranch}`]); } catch { // Remote branch doesn't exist yet - safe to push (first push) return true; } // Get local and remote commit SHAs - const localSha = await git.revParse(['HEAD']); - const remoteSha = await git.revParse([`refs/remotes/${remoteBranch}`]); + const localSha = await git.revparse(['HEAD']); + const remoteSha = await git.revparse([`refs/remotes/${remoteBranch}`]); // If they're the same, it's safe (no-op) if (localSha === remoteSha) { diff --git a/src/routes/api/git/[...path]/+server.ts b/src/routes/api/git/[...path]/+server.ts index cc3dbe4..d93e8cb 100644 --- a/src/routes/api/git/[...path]/+server.ts +++ b/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 // Git requires WWW-Authenticate header on ALL 401 responses, otherwise it won't retry 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, headers: { 'WWW-Authenticate': 'Basic realm="GitRepublic"', @@ -822,23 +822,30 @@ export const POST: RequestHandler = async ({ params, url, request }) => { // Check protection for all branches being pushed for (const targetBranch of pushedBranches) { - const protectionCheck = await branchProtectionService.canPushToBranch( - authResult.pubkey || '', - currentOwnerPubkey, - repoName, - targetBranch, - isMaintainer - ); + try { + const protectionCheck = await branchProtectionService.canPushToBranch( + authResult.pubkey || '', + currentOwnerPubkey, + repoName, + 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) { - // 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 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'); } } diff --git a/src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts b/src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts index 84a4b68..dc6c663 100644 --- a/src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/prs/merge/+server.ts @@ -3,6 +3,7 @@ */ import { json } from '@sveltejs/kit'; +// @ts-ignore - SvelteKit generates this type import type { RequestHandler } from './$types'; import { withRepoValidation } from '$lib/utils/api-handlers.js'; import type { RepoRequestContext } from '$lib/utils/api-context.js'; diff --git a/src/routes/repos/[npub]/[repo]/+page.svelte b/src/routes/repos/[npub]/[repo]/+page.svelte index dd397d9..7dd2bc1 100644 --- a/src/routes/repos/[npub]/[repo]/+page.svelte +++ b/src/routes/repos/[npub]/[repo]/+page.svelte @@ -4057,7 +4057,7 @@ {repo} {repoOwnerPubkey} isMaintainer={isMaintainer} - userPubkeyHex={userPubkeyHex} + userPubkeyHex={userPubkeyHex ?? undefined} onStatusUpdate={loadPRs} /> @@ -5374,7 +5374,6 @@ margin-bottom: 1rem; padding: 0.75rem; } - } .non-maintainer-notice { font-size: 0.7rem;