GROVE.mdblame
View source
3e3af551# Grove
3e3af552
36387cc3**Monorepo-first hosting platform powered by Mononoke.**
3e3af554
59985bb5Grove is a GitHub-like web platform for hosting, browsing, reviewing, and building code. Built on Meta's Mononoke source control server, it supports both standard Git and Sapling clients.
59985bb6
59985bb7**Live at [grove.host](https://grove.host)**
3e3af558
3e3af559---
3e3af5510
3e3af5511## Architecture
3e3af5512
3e3af5513```
59985bb14 ┌──────────────┐
59985bb15 │ grove.host │ Caddy: auto-TLS + routing
59985bb16 └──────┬───────┘
59985bb17 ┌───────────────┼───────────────┐
59985bb18 ▼ ▼ ▼
59985bb19 ┌─────────────┐ ┌────────────┐ ┌──────────────┐
59985bb20 │ Hub API │ │ Grove API │ │ Grove Web │
59985bb21 │ (identity) │ │ (repos/CI) │ │ (Next.js) │
59985bb22 │ :4000 │ │ :4000 │ │ :3000 │
59985bb23 └─────────────┘ └─────┬──────┘ └──────────────┘
59985bb24 │
59985bb25 ┌─────┴──────┐
59985bb26 │ Bridge │
59985bb27 │ :3100 │
59985bb28 └─────┬──────┘
59985bb29 ┌───────────┴───────────┐
59985bb30 ▼ ▼
59985bb31 ┌──────────────┐ ┌──────────────┐
59985bb32 │ Mononoke │ │ Mononoke │
59985bb33 │ SLAPI :8443 │ │ Git :8080 │
59985bb34 └──────────────┘ └──────────────┘
3e3af5535```
3e3af5536
3e3af5537### Components
3e3af5538
3e3af5539| Component | Tech | Port | Purpose |
3e3af5540|-----------|------|------|---------|
59985bb41| **Caddy** | Go | 80/443 | Reverse proxy, auto-TLS (Let's Encrypt) |
59985bb42| **Hub API** | Node.js/Fastify | 4000 | Users, WebAuthn auth, orgs, JWT issuance |
59985bb43| **Grove API** | Node.js/Fastify | 4000 | Repos, file browsing, diffs, Canopy CI/CD |
59985bb44| **Grove Web** | Next.js 15/React 19 | 3000 | Browser UI |
59985bb45| **Grove Bridge** | Mononoke binary | 3100 | Bridge between API and Mononoke |
59985bb46| **Mononoke SLAPI** | Rust | 8443 | EdenAPI server (Sapling push/pull), mTLS |
59985bb47| **Mononoke Git** | Rust | 8080 | Git HTTP protocol |
59985bb48| **Docker Registry** | registry:2 | 5000 | Local image registry for Canopy CI |
59985bb49
59985bb50### Product Subdomains
59985bb51
59985bb52| Subdomain | Purpose |
59985bb53|-----------|---------|
59985bb54| `grove.host` | Main app — repos, diffs, settings |
59985bb55| `canopy.grove.host` | CI/CD — pipeline runs, build logs |
59985bb56| `ring.grove.host` | Instance logging and monitoring |
59985bb57
59985bb58---
59985bb59
59985bb60## Authentication
59985bb61
59985bb62Grove uses **WebAuthn passkeys** (no passwords). Flow:
59985bb63
59985bb641. User registers/logs in with a passkey (biometric, hardware key, etc.)
59985bb652. Hub API issues a **JWT** (7-day expiry) signed with a shared `JWT_SECRET`
59985bb663. Grove API verifies the JWT on each request
59985bb674. **Personal Access Tokens** (PATs) are supported for CLI and API access
59985bb68
59985bb69Auth endpoints live in **hub-api**. Grove API only verifies tokens.
59985bb70
59985bb71---
59985bb72
59985bb73## Features
59985bb74
59985bb75### Code Hosting
59985bb76- Push/pull via **Git** (`git clone http://grove.host:8080/<repo>`)
59985bb77- Push/pull via **Sapling** (`sl clone https://grove.host:8443/edenapi/<repo>`)
59985bb78- File browser with directory navigation and syntax highlighting
59985bb79- Commit history with pagination
59985bb80- Blame view
59985bb81- Branch listing (Mononoke bookmarks)
59985bb82- README rendering (Markdown + syntax highlighting)
59985bb83- Private repos with org-based access control
59985bb84
59985bb85### Code Review (Diffs)
59985bb86- Create diffs from the web UI (select a branch → opens against default branch)
59985bb87- Conversation view with threaded comments
59985bb88- Changes view with unified diff rendering
59985bb89- Land (merge) diffs via Mononoke pushrebase
59985bb90- Close and reopen diffs
59985bb91- Reviews with approve/request-changes
59985bb92
59985bb93### Canopy CI/CD
59985bb94- YAML pipeline definitions (`.canopy/` directory)
59985bb95- Docker-based step execution
59985bb96- Real-time log streaming via SSE
59985bb97- Push-triggered pipelines (polls Mononoke bookmarks)
59985bb98- Manual pipeline triggers
59985bb99- Pipeline secrets management
59985bb100- Build history and status indicators
59985bb101
59985bb102### Organizations
59985bb103- Create organizations, add/remove members
59985bb104- Org-owned repositories
59985bb105- Access control for private repos
59985bb106
59985bb107### Ring (Instance Logging)
59985bb108- Log ingestion API (JSON + plain text)
59985bb109- Per-repo and global log views
59985bb110- Real-time log polling
59985bb111- Accessible at `ring.grove.host`
59985bb112
59985bb113### CLI (`grove`)
59985bb114- `grove auth login` — Browser-based WebAuthn login
59985bb115- `grove clone <repo>` — Clone a repository
59985bb116- `grove init <name>` — Create and initialize a new repo
59985bb117- `grove repo list` — List repositories
59985bb118- `grove repo create <name>` — Create a new repo
59985bb119- Config stored in `~/.grove/config`
3e3af55120
3e3af55121---
3e3af55122
3e3af55123## Quick Start
3e3af55124
3e3af55125### Prerequisites
3e3af55126
3e3af55127- Docker & Docker Compose
3e3af55128- Node.js 22+ (for local dev)
3e3af55129- Git
3e3af55130
3e3af55131### 1. Build Mononoke Docker Image
3e3af55132
3e3af55133```bash
3e3af55134cd sapling
36387cc135docker build -f ../docker/Dockerfile.mononoke -t grove/mononoke .
3e3af55136```
3e3af55137
59985bb138> First build takes 1-2+ hours (compiles Mononoke from source). Subsequent builds are cached.
3e3af55139
3e3af55140### 2. Import a Repository
3e3af55141
3e3af55142```bash
36387cc143./scripts/import-repo.sh https://github.com/your/repo.git myrepo
3e3af55144```
3e3af55145
3e3af55146### 3. Start Everything
3e3af55147
3e3af55148```bash
36387cc149cd docker
3e3af55150docker compose up
3e3af55151```
3e3af55152
3e3af55153### 4. Access
3e3af55154
3e3af55155- **Web UI**: http://localhost:3000
3e3af55156- **API**: http://localhost:4000
3e3af55157- **Git clone**: `git clone http://localhost:8080/myrepo`
3e3af55158
3e3af55159### Local Development (Without Docker)
3e3af55160
3e3af55161```bash
59985bb162# Hub API
59985bb163cd hub-api && npm install && npm run dev # http://localhost:4001
59985bb164
59985bb165# Grove API
59985bb166cd api && npm install && npm run dev # http://localhost:4000
3e3af55167
3e3af55168# Web UI
59985bb169cd web && npm install && npm run dev # http://localhost:3000
3e3af55170```
3e3af55171
59985bb172### Local Subdomain Dev (For Canopy/Ring Auth)
a33b2b6173
59985bb174Add to `/etc/hosts`:
a33b2b6175
a33b2b6176```text
a33b2b6177127.0.0.1 grove.test
a33b2b6178127.0.0.1 canopy.grove.test
a33b2b6179127.0.0.1 ring.grove.test
a33b2b6180```
a33b2b6181
59985bb182Then `npm run dev:local` and open:
a33b2b6183- Grove: `http://grove.test:3000`
a33b2b6184- Canopy: `http://canopy.grove.test:3000`
a33b2b6185- Ring: `http://ring.grove.test:3000`
a33b2b6186
3e3af55187---
3e3af55188
59985bb189## Project Structure
3e3af55190
3e3af55191```
59985bb192grove/
59985bb193├── GROVE.md # This documentation
59985bb194├── AUDIT.md # Codebase + server audit
59985bb195├── api/ # Grove API server
59985bb196│ └── src/
59985bb197│ ├── server.ts # Fastify entry point
59985bb198│ ├── auth/middleware.ts # JWT verification
59985bb199│ ├── routes/
59985bb200│ │ ├── repos.ts # Repo browsing, CRUD
59985bb201│ │ ├── diffs.ts # Code review (diffs, comments, reviews)
59985bb202│ │ ├── canopy.ts # CI/CD pipeline management
59985bb203│ │ └── ring.ts # Instance log ingestion + viewing
59985bb204│ └── services/
59985bb205│ ├── database.ts # SQLite schema + migrations
59985bb206│ ├── bridge.ts # Mononoke Bridge client
59985bb207│ ├── canopy-runner.ts # Docker-based CI execution
59985bb208│ ├── canopy-poller.ts # Push event polling
59985bb209│ ├── canopy-events.ts # SSE event bus
59985bb210│ └── mononoke-provisioner.ts # Dynamic repo config
59985bb211├── hub-api/ # Hub API server (identity)
59985bb212│ └── src/
59985bb213│ ├── server.ts # Fastify entry point
59985bb214│ └── routes/
59985bb215│ ├── auth.ts # WebAuthn, JWT, PATs
59985bb216│ ├── instances.ts # Instance management
59985bb217│ └── orgs.ts # Organization CRUD
59985bb218├── web/ # Next.js frontend
59985bb219│ ├── app/
59985bb220│ │ ├── layout.tsx, page.tsx # Root layout + dashboard
59985bb221│ │ ├── login/ # WebAuthn login
59985bb222│ │ ├── new/ # Create repository
59985bb223│ │ ├── [owner]/[repo]/ # Repo pages (files, commits, blame, diffs)
59985bb224│ │ ├── canopy/ # CI/CD pages
59985bb225│ │ ├── ring/ # Instance logging pages
59985bb226│ │ └── components/ # UI components
59985bb227│ ├── lib/
59985bb228│ │ ├── api.ts # Typed API client
59985bb229│ │ ├── auth.tsx # Auth context + hooks
59985bb230│ │ └── theme.tsx # Dark/light mode
59985bb231│ └── middleware.ts # Subdomain routing
59985bb232├── cli/ # Grove CLI
59985bb233│ └── src/commands/ # Command modules
59985bb234├── addons/ # Sapling integrations
59985bb235│ ├── isl/ # Interactive Smartlog UI
59985bb236│ ├── isl-server/ # ISL backend
59985bb237│ ├── vscode/ # VS Code extension
59985bb238│ ├── shared/ # Shared utilities
59985bb239│ └── components/ # ISL components library
59985bb240├── docker/ # Development Docker setup
59985bb241│ ├── docker-compose.yml # Dev stack
59985bb242│ └── Dockerfile.* # Build files
59985bb243├── hub/ # Production setup
59985bb244│ ├── docker-compose.yml # Prod stack (8 services + Caddy)
59985bb245│ └── Caddyfile # Reverse proxy config
59985bb246└── scripts/ # Utility scripts
59985bb247 ├── import-repo.sh # Git → Mononoke importer
59985bb248 └── generate-certs.sh # TLS cert generator
3e3af55249```
3e3af55250
3e3af55251---
3e3af55252
59985bb253## Database
59985bb254
59985bb255### Grove API (SQLite: grove.db)
59985bb256
59985bb257| Table | Purpose |
59985bb258|-------|---------|
59985bb259| `users` | Local user records (synced from hub JWT claims) |
59985bb260| `orgs` | Local org records |
59985bb261| `repos` | Repository metadata (owner, name, branches, visibility) |
59985bb262| `diffs` | Code review diffs (title, commits, status) |
59985bb263| `reviews` | Diff reviews (approved/changes requested) |
59985bb264| `comments` | Diff comments (inline or general) |
59985bb265| `pipelines` | CI/CD pipeline definitions |
59985bb266| `pipeline_runs` | Pipeline execution records |
59985bb267| `pipeline_steps` | Individual step records |
59985bb268| `step_logs` | Step output logs |
59985bb269| `canopy_secrets` | Encrypted CI/CD secrets |
59985bb270| `bookmark_state` | Mononoke bookmark tracking |
59985bb271
59985bb272### Hub API (SQLite: hub.db)
59985bb273
59985bb274| Table | Purpose |
59985bb275|-------|---------|
59985bb276| `users` | User accounts |
59985bb277| `credentials` | WebAuthn passkey credentials |
59985bb278| `orgs` | Organizations |
59985bb279| `org_members` | Org membership |
59985bb280| `instances` | Grove instance registrations |
59985bb281| `repos` | Hub-level repo metadata |
59985bb282| `api_tokens` | Personal access tokens |
3e3af55283
3e3af55284---
3e3af55285
59985bb286## Deployment
3e3af55287
59985bb288### Production (grove.host)
3e3af55289
59985bb290```bash
59985bb291# On server at /opt/grove/
59985bb292docker compose up -d
3e3af55293```
3e3af55294
59985bb295Services auto-restart. Caddy handles TLS. Canopy CI rebuilds and deploys on push.
3e3af55296
59985bb297**Backups**: Daily at 4am UTC via `/opt/grove/backup.sh`. 7-day retention.
3e3af55298
59985bb299**Firewall**: UFW enabled. SSH restricted to admin IP. Ports 80, 443, 8080, 8443 open.
3e3af55300
59985bb301### CI/CD Pipeline
3e3af55302
59985bb303The Grove repo itself uses Canopy for CI/CD (self-hosting). Push to `main` triggers:
59985bb3041. Build Docker images for grove-api, grove-web, hub-api
59985bb3052. Push to local registry
59985bb3063. `docker compose pull && up -d`
3e3af55307
3e3af55308---
3e3af55309
3e3af55310## Tech Stack
3e3af55311
3e3af55312| Layer | Technology | Why |
3e3af55313|-------|-----------|-----|
59985bb314| Source control | Mononoke (Rust) | Production-proven at Meta scale, Git-compatible |
59985bb315| API servers | Fastify (Node.js/TypeScript) | Fast, typed, same ecosystem as ISL |
59985bb316| Web UI | Next.js 15 + React 19 + Tailwind 4 | SSR, app router, rapid iteration |
59985bb317| Database | SQLite | Zero-config, embedded, sufficient for current scale |
59985bb318| Auth | WebAuthn passkeys + JWT | Passwordless, phishing-resistant |
59985bb319| Reverse proxy | Caddy | Auto-TLS, simple config |
59985bb320| CI/CD | Docker-based (Canopy) | Native, no external dependency |
59985bb321| Extension | VS Code Extension API | Largest IDE market share |
3e3af55322
3e3af55323---
3e3af55324
3e3af55325## Key Design Decisions
3e3af55326
59985bb3271. **Monorepo-first**: One repo per user/org. Mononoke excels at this.
59985bb3282. **Git protocol for clients**: Standard `git clone`/`git push` works. No custom client needed.
59985bb3293. **Git-based API reads**: Uses bare Git clones + `git ls-tree`/`git show`/`git log` for file browsing. Avoids Mononoke CBOR protocol complexity.
59985bb3304. **Two-API split**: Hub API (identity) is separate from Grove API (repos). Hub issues JWTs; Grove verifies them. Designed for multi-instance deployments.
59985bb3315. **WebAuthn-only auth**: No passwords. Passkeys are phishing-resistant and simpler to implement securely.
59985bb3326. **Canopy CI/CD is native**: Docker-based pipeline execution, integrated into grove-api. No external CI needed.
59985bb3337. **ISL extracted, not forked**: ISL components are reusable. Full ISL integration is a future phase.