| 14 | | 14 | |
| 15 | ### Goals | 15 | ### Goals |
| 16 | | 16 | |
| 17 | - Host git repositories for a small, fixed set of users (personal / small-team private instance). | 17 | - Host git repositories for users on a self-hosted instance, personal or small-team. |
| 18 | - Browse code, history, diffs, branches, and tags in a UI that is fast on desktop and mobile. | 18 | - Browse code, history, diffs, branches, and tags in a UI that is fast on desktop and mobile. |
| 19 | - Syntax highlighting everywhere code appears, including inside diffs. | 19 | - Syntax highlighting everywhere code appears, including inside diffs and rendered markdown. |
| 20 | - Show whether each commit is cryptographically signed, via SSH or GPG. | 20 | - Show whether each commit is cryptographically signed, via SSH or GPG. |
| 21 | - Organize repositories under nested, user-owned groups. | 21 | - Organize repositories under nested, user-owned groups. |
| | 22 | - **Collaboration.** Issues and pull requests with GitHub-style **scoped labels** |
| | 23 | (`kind: value`, e.g. `type: backend`), assignees, comments, and open/closed state; PRs |
| | 24 | include branch comparison, diff, review, and server-side **merge machinery** (merge commit, |
| | 25 | squash, rebase). Issues and PRs are **toggleable per repository**. |
| | 26 | - **Three-level visibility** (public / internal / private), GitLab-style, with a per-instance |
| | 27 | default; explicit collaborators with read/write/admin. |
| | 28 | - **Accounts.** Optional web **self-registration** (toggleable, with optional hCaptcha / |
| | 29 | reCAPTCHA) alongside admin invites; a full account settings area (change username, email, |
| | 30 | password; manage SSH/GPG keys, API tokens, and profile). |
| 22 | - Re-theme and re-brand without recompiling. | 31 | - Re-theme and re-brand without recompiling. |
| 23 | - One binary, one config file, one data directory. | 32 | - One binary, one config file, one data directory. |
| 24 | | 33 | |
| | 34 | The aim is a **complete forge minus CI** — feature-comparable to Forgejo/Gitea for the areas |
| | 35 | above. When a feature is ambiguous, choose the simpler behaviour. |
| | 36 | |
| 25 | ### Non-goals (do not build these) | 37 | ### Non-goals (do not build these) |
| 26 | | 38 | |
| 27 | - **Issues.** Not planned. | | |
| 28 | - **Pull requests / merge requests.** Not planned. No merge machinery, no review flow. | | |
| 29 | - **Organizations** in the GitHub sense. Repos belong to a user; groups provide the hierarchy. | 39 | - **Organizations** in the GitHub sense. Repos belong to a user; groups provide the hierarchy. |
| 30 | - Web sign-up, public registration, or self-service account creation of any kind. | 40 | - Forks, stars, followers, activity feeds beyond the dashboard, notifications, wikis, |
| 31 | - Forks, stars, followers, activity feeds, notifications, wikis, releases-as-a-feature, | 41 | releases-as-a-feature, git-LFS, webhooks, mirroring, federation. (Not planned for now; may be |
| 32 | git-LFS, webhooks, mirroring, federation. | 42 | revisited, but out of the current "complete minus CI" scope.) |
| 33 | - Being Forgejo/Gitea/GitLab/GitHub. When a feature is ambiguous, choose the simpler behaviour. | | |
| 34 | | 43 | |
| 35 | ### Deferred (design for, do not implement) | 44 | ### Deferred (design for, do not implement) |
| 36 | | 45 | |
| 1164 | - The UI is usable at 380px wide. | 1173 | - The UI is usable at 380px wide. |
| 1165 | - Docker Compose brings up a working instance from the example config. | 1174 | - Docker Compose brings up a working instance from the example config. |
| 1166 | - Every crate has tests; all four cargo checks pass; every commit is conventional. | 1175 | - Every crate has tests; all four cargo checks pass; every commit is conventional. |
| | 1176 | |
| | 1177 | --- |
| | 1178 | |
| | 1179 | ## 18. Collaboration: issues, pull requests, labels |
| | 1180 | |
| | 1181 | Issues and pull requests are enabled **per repository** (`repos.issues_enabled`, |
| | 1182 | `repos.pulls_enabled`, both default true). When disabled, the nav tab and routes for that |
| | 1183 | feature 404 for the repo. Access follows the existing `auth::access` verdict: any viewer with |
| | 1184 | `Read` may open issues and PRs and comment; `Write` (or the author) may edit/close; `Admin` |
| | 1185 | may manage labels and force-merge. |
| | 1186 | |
| | 1187 | ### 18.1 Scoped labels |
| | 1188 | |
| | 1189 | Labels are per-repo, GitHub-style, and may be **scoped**: a label named `type: backend` has |
| | 1190 | scope `type` and value `backend`. Scoped labels are **mutually exclusive within their scope** |
| | 1191 | on a single issue/PR (assigning `type: frontend` removes `type: backend`). Unscoped labels |
| | 1192 | (`bug`, `good first issue`) stack freely. A label carries a name, an optional description, and |
| | 1193 | a colour (`#rrggbb`); the scope is parsed from the first `": "` in the name. |
| | 1194 | |
| | 1195 | Schema: `labels (id, repo_id, name, name_lower, color, description, created_at)` with a unique |
| | 1196 | index on `(repo_id, name_lower)`; join table `issue_labels (issue_id, label_id)`. |
| | 1197 | |
| | 1198 | ### 18.2 Issues |
| | 1199 | |
| | 1200 | An issue belongs to a repo, has an author, a monotonically-per-repo **number** (`#N`), a |
| | 1201 | title, a markdown body, an open/closed state, optional assignee, labels, and threaded comments |
| | 1202 | (markdown). Numbers are allocated from a per-repo counter (`repos.next_iid` bumped in the same |
| | 1203 | transaction) shared with pull requests, so `#N` is unique across both. |
| | 1204 | |
| | 1205 | Schema: `issues (id, repo_id, number, author_id, title, body, state, is_pull, assignee_id, |
| | 1206 | created_at, updated_at, closed_at)`; `comments (id, issue_id, author_id, body, created_at, |
| | 1207 | updated_at)`. Pull requests are rows in `issues` with `is_pull = 1` plus a `pull_requests` |
| | 1208 | side table (see below), so numbering, labels, and comments are shared. |
| | 1209 | |
| | 1210 | Routes: `/-/issues`, `/-/issues/new`, `/-/issues/{n}`; POST endpoints for create, comment, |
| | 1211 | label toggle, assignee, and state change (all CSRF-guarded, gated on `Write`/author). |
| | 1212 | |
| | 1213 | ### 18.3 Pull requests and merge machinery |
| | 1214 | |
| | 1215 | A pull request compares a **head** ref (branch) against a **base** ref in the same repo |
| | 1216 | (cross-repo forks are a non-goal). It shows the commit list and the combined diff (reusing the |
| | 1217 | diff renderer), supports review comments, and can be **merged server-side** by a `Write`+ |
| | 1218 | collaborator when mergeable, via one of three strategies: **merge commit**, **squash**, or |
| | 1219 | **rebase**. Merge is performed with the spawned `git` subprocess (`git merge`/`git |
| | 1220 | merge --squash`/`git rebase` into a work area, then update the base ref), never libgit2 — |
| | 1221 | consistent with §3.1. A PR is auto-closed when its head is merged; conflicts block the merge |
| | 1222 | with a clear message. |
| | 1223 | |
| | 1224 | Schema: `pull_requests (issue_id, base_ref, head_ref, merged_at, merged_by, merge_sha, |
| | 1225 | merge_strategy)`. |
| | 1226 | |
| | 1227 | Routes: `/-/pulls`, `/-/pulls/new?base=…&head=…`, `/-/pulls/{n}` (with `/commits`, `/files` |
| | 1228 | sub-views); POST for create, comment, review, and merge. |
| | 1229 | |
| | 1230 | ## 19. Accounts, self-registration, and settings |
| | 1231 | |
| | 1232 | ### 19.1 Self-registration |
| | 1233 | |
| | 1234 | Optional, off by default, controlled by `instance.allow_registration` (bool). When on, `/register` |
| | 1235 | offers a sign-up form (username, email, password); on success the account is created directly |
| | 1236 | (no invite) and signed in. An optional **captcha** guards the form: `captcha.provider` |
| | 1237 | (`none` | `hcaptcha` | `recaptcha`), `captcha.site_key`, `captcha.secret_key`; when a provider |
| | 1238 | is set, the widget is rendered and the token is server-verified against the provider before the |
| | 1239 | account is created. Admin invites remain available regardless. |
| | 1240 | |
| | 1241 | ### 19.2 Account settings |
| | 1242 | |
| | 1243 | `/settings` expands into sections: **Profile** (display name, pronouns, bio, location, links, |
| | 1244 | avatar — already built); **Account** (change username, email, password — password change |
| | 1245 | re-verifies the current password and invalidates other sessions); **SSH & GPG keys** |
| | 1246 | (list/add/remove, mirroring the CLI `key` commands); **API tokens** (list/create/revoke, using |
| | 1247 | the existing `api_tokens` table; the JWT is shown once on creation). Every mutation is |
| | 1248 | CSRF-guarded and self-service (a user edits only their own account). |