CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
RubHub is a federated git forge written in Rust with a TypeScript/Lit frontend. It uses git itself as the storage backend (no external database) and compiles to a single executable that handles both HTTP and SSH servers.
Key Philosophy: Git as the single source of truth. Everything is stored as JSON files within the git directory structure, enabling federation through git remotes.
Development Commands
Frontend Development (Bun)
bun run dev # Watch mode - rebuilds frontend on changes
bun run build # Production build - minified frontend assets
bun run check # TypeScript type checking
bun run format # Format frontend code with Biome
bun run format:unsafe # Format with unsafe fixes
Backend Development (Rust)
cargo build # Build the Rust binary
cargo build --release # Production build
cargo run # Run the development server
cargo test # Run all tests (integration tests in tests/)
cargo clippy # Lint Rust code
cargo fmt # Format Rust code
Running Tests
# Run all integration tests
cargo test
# Run a specific test
cargo test basic_workflow
# Run with output
cargo test -- --nocapture
Architecture Overview
Monorepo Structure
- Rust backend (
src/): Axum HTTP server + russh SSH server in single executable - Frontend (
frontend/app/): TypeScript + Lit web components, built with Bun - Templates (
templates/): Askama server-side templates - Data directory (
data/): Git repositories and session storage (not in repo)
Backend Architecture (Rust)
Entry Point Flow:
src/main.rs→src/lib.rs::run()→ spawns HTTP and SSH servers- Uses single-threaded Tokio runtime for resilience (intentional design choice)
Key Modules:
http.rs- Axum router and middleware setupssh.rs- SSH server implementation using russhstate.rs- AppConfig and GlobalState (shared between servers)controllers/- MVC-style route handlers (auth, project, user)models/- Data structures (User, Project) with JSON persistenceservices/- Business logic (session, repository, password, validation)extractors/- Custom Axum path extractors for typed URL parametersviews/- Response rendering with theme injection system
Data Persistence Pattern:
- No database: All data stored as JSON files
- Users:
data/git/!{username}.json(special!prefix for metadata) - Projects:
data/git/{username}/!{project_slug}.json - Sessions:
data/sessions/{session_id}.json - Git repos: Bare repositories at
data/git/{username}/{project_slug}
Request Path Extraction:
Custom extractors (PathUser, PathUserProject, etc.) automatically load entities from storage. Uses ~ prefix for user paths: /~{username} → loads User, /~{username}/{project} → loads User + Project. Returns 404 if loading fails.
Access Control:
pub enum AccessType {
None, // No access
Read, // Read-only
Write, // Read + write
Admin, // Full control (owner only)
}
Frontend Architecture
Technology: TypeScript + Lit Web Components + Bun bundler
Build Output: Bun builds frontend/app/app.html → dist/ directory → embedded in Rust binary via rust-embed
Theme Injection System:
- Server renders Askama template with placeholders:
<!--HEAD-->and<!--BODY--> - Compiled
app.htmlcontains theme/styling - Server injects rendered content into theme placeholders
- Result: Server-rendered HTML with client-side interactivity
Client-Side Features:
- Session detection via
session_usercookie (JSON) - Branch switcher dropdown
- Sidebar toggle for mobile
- Progressive enhancement (works without JS)
Configuration
Environment variables (use .env or .env.development):
SITE_URL- Base URL for the site (e.g.,http://localhost:3000)SSH_PUBLIC_URL- Public SSH host for clone URLsDIR_ROOT- Data directory root (defaults to./data/)HTTP_BIND_ADDR- HTTP server bind address (default:127.0.0.1:3000)SSH_BIND_ADDR- SSH server bind address (default:127.0.0.1:2222)
Important Patterns
Adding New Routes
- Add handler in appropriate
controllers/module - Register route in
src/http.rs - Use custom extractors for path parameters (
PathUser,PathUserProject) - Create Askama template in
templates/ - Implement
ThemedRendertrait for response
Working with Git Repositories
Use the services/repository.rs module which wraps gix crate. All repository operations go through this service layer.
Session Management
Sessions are file-based with 30-day expiration:
session_idcookie: HTTP-only, securesession_usercookie: Readable by client JS (JSON with user data)
Validation
Use functions from services/validation.rs:
validate_username()- Strict username rulesvalidate_slug()- URL-safe slugsslugify()- Convert names to slugs
Code Style
Frontend (enforced by Biome):
- Tab indentation
- Double quotes for strings
- Auto-organize imports
Backend (use cargo fmt):
- Standard Rust formatting
- Follow existing patterns in codebase
Testing
Integration tests use a temporary directory and spin up the full server. The with_backend() helper manages lifecycle:
#[tokio::test(flavor = "current_thread")]
async fn my_test() {
with_backend(async {
let client = reqwest::Client::builder()
.cookie_store(true)
.build()
.unwrap();
// Test code here
}).await;
}
Tests use fixed ports (32323 for HTTP, 32324 for SSH) to avoid conflicts.
Build Pipeline
- Frontend: Bun builds HTML/TS/CSS →
dist/directory - Backend: Rust embeds
dist/assets usingrust-embedcrate - Output: Single executable with embedded frontend assets
The rust-embed crate serves assets at runtime from the binary, no external files needed.
Git Workflow
This repo follows a standard git workflow. The default branch is main.