fabrica

hanna/fabrica

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