956ea42a
Add git-collab-server design spec
a73x 2026-03-30 16:52
Spec for a minimal self-contained git hosting server ("Gerrit lite"):
axum HTTP (web UI + smart HTTP clone), russh SSH (key-based push/pull),
askama templates, multi-repo hosting, sidebar nav web UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
diff --git a/docs/superpowers/specs/2026-03-30-git-collab-server-design.md b/docs/superpowers/specs/2026-03-30-git-collab-server-design.md new file mode 100644 index 0000000..cc5868c --- /dev/null +++ b/docs/superpowers/specs/2026-03-30-git-collab-server-design.md @@ -0,0 +1,204 @@ # git-collab-server Design A minimal, self-contained git hosting server — "Gerrit lite" — companion to the git-collab CLI. ## Goals - Web UI for read-only browsing of code and collab objects (patches, reviews, issues) - HTTPS read-only clone (anonymous, no auth) - SSH read/write clone (key-based auth) - Single binary, zero external dependencies - Multi-repo hosting (serve a directory of repos) ## Non-goals - Web-based write actions (reviews, comments via browser) - HTTPS push - User accounts, permissions, or access control beyond SSH key auth - Syntax highlighting in v1 ## Architecture ### Binary `git-collab-server` — a second `[[bin]]` in the existing Cargo workspace. Shares the `git-collab` lib crate to read patches, issues, reviews, events without duplication. ### Source Layout ``` src/ server/ main.rs ← entry point: parse config, start HTTP + SSH config.rs ← parse collab-server.toml http/ mod.rs ← axum router setup repo_list.rs ← GET / — list repos repo.rs ← GET /:repo/* — repo pages git_http.rs ← git smart HTTP protocol (clone) templates/ ← askama HTML templates ssh/ mod.rs ← russh server setup auth.rs ← authorized_keys parsing + key verification session.rs ← exec handler (git-upload-pack / git-receive-pack) ``` ### Dependencies New dependencies (server-only, gated behind `[[bin]]`): ```toml axum = "0.8" tokio = { version = "1", features = ["full"] } askama = "0.12" russh = "0.44" russh-keys = "0.44" toml = "0.8" tracing = "0.1" tracing-subscriber = "0.3" ``` ## Configuration File: `collab-server.toml`, passed via `--config` flag. ```toml repos_dir = "/srv/git" http_bind = "0.0.0.0:8080" ssh_bind = "0.0.0.0:2222" authorized_keys = "/etc/git-collab-server/authorized_keys" site_title = "my git server" # optional ``` ## HTTP Layer Two responsibilities on the same axum router. ### Web UI Routes ``` GET / → repo list GET /:repo → repo overview (recent commits, open patches, open issues) GET /:repo/commits → full commit log GET /:repo/tree → file tree at HEAD GET /:repo/tree/:ref/*path → browse directory GET /:repo/blob/:ref/*path → file contents GET /:repo/diff/:oid → single commit diff GET /:repo/patches → patch list GET /:repo/patches/:id → patch detail (revisions, reviews, inline comments) GET /:repo/issues → issue list GET /:repo/issues/:id → issue detail ``` ### Git Smart HTTP (Clone Only) ``` GET /:repo.git/info/refs?service=git-upload-pack POST /:repo.git/git-upload-pack ``` Spawns `git upload-pack --stateless-rpc` as a subprocess. No auth required. No push over HTTP. ### Rendering Askama templates compiled at build time. Each route reads collab data via the existing lib (`patch::list`, `issue::list`, etc.) and git data via `git2`. ## SSH Layer russh-based embedded SSH server. ### Auth - Public key only. No passwords. - Keys checked against the `authorized_keys` file. Custom parser (simple format: `ssh-ed25519 <base64-key> <comment>`, one per line). - `#` lines and blanks ignored. - File reloaded on SIGHUP — no restart needed to add/remove users. ### Session Exec Only two commands allowed, anything else is rejected: ``` git-upload-pack '/path/to/repo' ← clone/fetch git-receive-pack '/path/to/repo' ← push ``` Repo path argument validated against `repos_dir` to prevent path traversal — must resolve to a subdirectory of the configured repos root. ### Host Key - Ed25519 key generated at first startup, saved to `<config_dir>/server_host_key`. - Fingerprint printed on startup for operator verification. ### Push Semantics Push lands commits on branches as normal git. Collab refs (`refs/collab/`) are pushed via `git-collab sync` as usual — the server treats them as normal refs with no special handling. ## Web UI Design Sidebar navigation (Gerrit-style layout). ### Page Layout ``` ┌─────────────────────────────────────────────┐ │ site_title repo_name │ ├────────┬────────────────────────────────────┤ │ │ │ │ commits│ (main content area) │ │ tree │ │ │ ────── │ │ │ patches│ │ │ issues │ │ │ │ │ ├────────┴────────────────────────────────────┤ │ git-collab-server │ └─────────────────────────────────────────────┘ ``` - Sidebar sections: commits, tree, (divider), patches (open count badge), issues (open count badge) - Active section highlighted - Repo list page (`/`) has no sidebar — simple list with repo name, last commit date, description ### Styling - Single embedded `<style>` in base template - Dark theme, monospace for code, system fonts for nav - No external CSS/JS CDN, no JavaScript dependencies - Diffs rendered as colored `<pre>` blocks (green/red lines) - File contents as plain `<pre>` (no syntax highlighting in v1) ## Repo Discovery - Scans `repos_dir` for directories containing a `HEAD` file (bare repos) or `.git` subdirectory (non-bare) - Re-scans on each request (cheap readdir, no watcher) - Repo name = directory name, minus `.git` suffix if present ## Startup Sequence 1. Parse CLI args (`--config collab-server.toml`) via clap 2. Load and validate config (`repos_dir` must exist) 3. Generate or load SSH host key 4. Print host key fingerprint 5. Start HTTP and SSH servers concurrently via `tokio::select!` 6. Log: `HTTP listening on ..., SSH listening on ..., serving N repos from /path` ## Error Handling - Invalid repo in URL → 404 - Git errors → 500 with simple error template - SSH bad command → channel close with error message - Bad config at startup → exit with clear message to stderr ## Logging `tracing` crate for structured logs: - Startup info (bind addresses, repo count, host key fingerprint) - SSH auth events (accepted/rejected + key fingerprint) - HTTP request log ## Auth Model Two separate concerns: - **SSH access:** Server-level `authorized_keys` file controls who can connect and push/pull over SSH. - **Collab event signing:** Per-repo `.git/collab/trusted-keys` controls whose collab events (patches, reviews, issues) are trusted. This is unchanged from the CLI tool.