fabrica

hanna/fabrica

docs: add README

bf1c5f1 · hanna committed on 2026-07-26

Project overview, feature list, quick start (Docker + Nix), configuration,
CLI table, architecture, and development notes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
1 files changed · +141 −0UnifiedSplit
README.md +141 −0
@@ -0,0 +1,141 @@
1# fabrica
2
3*fabrica* (Latin: **forge**) is a self-hosted git server — a single Rust binary that serves
4your repositories over **HTTPS** (read) and **SSH** (read + write) behind a fast, themeable,
5htmx-driven web UI. Private by default, no sign-up required.
6
7It aims to be a **complete forge minus CI**, feature-comparable to Forgejo/Gitea: code
8browsing, nested groups, three-level visibility, issues and pull requests with server-side
9merge machinery, and a full account-settings area — all from one binary, one config file, and
10one data directory.
11
12## Features
13
14- **Code browsing** — files, history, diffs, branches, and tags, with syntax highlighting
15 everywhere code appears (including inside diffs and rendered markdown) and per-commit
16 signature verification (SSH and GPG).
17- **Nested groups** — organize repositories under arbitrarily (up to 5 levels) nested,
18 user-owned groups. Renames and moves are metadata-only.
19- **Three-level visibility** — public / internal / private, with a per-instance default and
20 explicit collaborators (read / write / admin). Private repos 404 rather than 403, never
21 leaking their existence.
22- **Issues & pull requests** — toggleable per repository, with GitHub-style **scoped labels**
23 (`kind: value`), assignees, comments, locking, and open/closed state. PRs include branch
24 comparison, diff review, and server-side **merge / squash / rebase**.
25- **Accounts** — optional web self-registration (toggleable, with optional hCaptcha/reCAPTCHA)
26 alongside admin invites; account settings for username, email, password, SSH/GPG keys, API
27 tokens, and profile.
28- **Admin dashboard** — instance stats, user/repo/group management, sign-up invites, and
29 config overrides.
30- **Themeable, no build step** — server-rendered [`maud`](https://maud.lambda.xyz/) templates
31 enhanced with vendored htmx; every interactive element works without JavaScript. Themes are
32 single CSS files of `--fb-*` custom properties; drop one in and `SIGHUP` to load it.
33- **JSON API** under `/api/v1`, and a CLI that operates directly on the store (no IPC with a
34 running server).
35
36**Out of scope:** orgs, forks, stars, followers, notifications, wikis, releases-as-a-feature,
37git-LFS, webhooks, mirroring, federation. **CI is deferred** — the seams exist (inert behind
38`ui.show_runs = false`) but the runner is not built yet.
39
40## Requirements
41
42The `git` binary (**≥ 2.41**) must be on `PATH` at runtime — fabrica shells out to it for pack
43transport (`upload-pack` / `receive-pack`) and maintenance, while using libgit2 in-process for
44browsing. Every deployment path below guarantees it; verify with `fabrica doctor`.
45
46## Quick start
47
48### Docker / Compose
49
50```sh
51docker compose up
52```
53
54Builds the image and starts fabrica on `:8080` (HTTP) and `:2222` (SSH) with named volumes for
55data and repos. SQLite is the default database; add `--profile postgres` and point
56`FABRICA__DATABASE__URL` at it to use Postgres.
57
58### From source (Nix)
59
60The toolchain (Rust nightly via fenix) is pinned in the flake:
61
62```sh
63nix develop # enter the dev shell
64just check # fmt + check + clippy + test (the quality gate)
65cargo run -- serve # serve with the built-in defaults
66```
67
68`serve` runs in the foreground (correct for systemd/Docker) and applies pending migrations on
69startup. Create the first administrator:
70
71```sh
72cargo run -- user add alice alice@example.com --admin
73```
74
75Then open <http://localhost:8080>.
76
77## Configuration
78
79Copy [`fabrica.example.toml`](fabrica.example.toml), edit it, and point the binary at it with
80`--config <path>` (or place it at `/etc/fabrica/fabrica.toml` or under `$XDG_CONFIG_HOME/fabrica/`).
81Every key can be overridden by an environment variable `FABRICA__<SECTION>__<KEY>` (double
82underscores nest), and any `*_file` key reads its value from a file — for agenix, systemd-creds,
83or Docker secrets. Unknown keys are a hard error.
84
85```sh
86fabrica config check # validate
87fabrica config show # print the merged config, secrets redacted
88```
89
90On first run fabrica generates its HS256 secret and SSH host key. See
91[`docs/configuration.md`](docs/configuration.md) and [`docs/deployment.md`](docs/deployment.md)
92for details — including the reverse-proxy rule that pack routes **must not be buffered**.
93
94## CLI
95
96Every subcommand touches the store and filesystem directly; there is no running-server IPC.
97
98| Command | Purpose |
99|---|---|
100| `fabrica serve` | Run the HTTP + SSH server (foreground) |
101| `fabrica user` | Manage accounts (add, passwd, admin, verify, disable, del) |
102| `fabrica repo` | Manage repositories (add, rename, visibility, collaborator, archive, path…) |
103| `fabrica group` | Manage groups |
104| `fabrica key` / `token` | Manage SSH/GPG keys and API tokens |
105| `fabrica config` | Validate / show the effective configuration |
106| `fabrica migrate` | Apply database migrations |
107| `fabrica doctor` | Check the runtime environment (git on PATH, etc.) |
108
109## Architecture
110
111A single root binary (`src/main.rs`) dispatches into library crates under `crates/`, with a
112strictly one-way dependency direction:
113
114```
115model ← store / git / auth ← web / api / ssh / cli
116```
117
118- `model` (domain types, no I/O) · `config` (figment TOML+env) · `store` (sqlx, SQLite **and**
119 Postgres over one portable schema) · `git` (libgit2 reads + `git` subprocess pack transport)
120- `highlight` (tree-sitter) · `auth` (argon2id, sessions, JWT) · `mail` (lettre)
121- `ssh` (russh) · `web` (axum + maud + htmx) · `api` (`/api/v1` JSON) · `cli` (clap)
122
123Repositories are stored on disk by ULID (`{repo_dir}/{id[0..2]}/{id}.git`); the database is the
124only name→path authority, so renames and group moves are metadata-only. See
125[`spec.md`](spec.md) for the full design contract and [`docs/decisions.md`](docs/decisions.md)
126for the running log of design decisions and deviations.
127
128## Development
129
130```sh
131just check # cargo fmt --check + check + clippy -D warnings + test
132nix flake check # the superset: clippy, fmt, test, deny, doc
133```
134
135The workspace forbids `unsafe_code` and denies `unwrap`/`panic`/`todo` outside tests. `cargo
136test` alone suffices with no external services (Postgres tests are feature-gated). Commits
137follow Conventional Commits, scoped by crate/area.
138
139## License
140
141[MPL-2.0](LICENSE).