Expand 13 hidden lines 14 14 15 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 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 20 - Show whether each commit is cryptographically signed, via SSH or GPG. 21 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 31 - Re-theme and re-brand without recompiling. 23 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 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 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. 31 - - Forks, stars, followers, activity feeds, notifications, wikis, releases-as-a-feature, 32 - git-LFS, webhooks, mirroring, federation. 33 - - Being Forgejo/Gitea/GitLab/GitHub. When a feature is ambiguous, choose the simpler behaviour. 40 + - Forks, stars, followers, activity feeds beyond the dashboard, notifications, wikis, 41 + releases-as-a-feature, git-LFS, webhooks, mirroring, federation. (Not planned for now; may be 42 + revisited, but out of the current "complete minus CI" scope.) 34 43 35 44 ### Deferred (design for, do not implement) 36 45 Expand 1127 hidden lines 1164 1173 - The UI is usable at 380px wide. 1165 1174 - Docker Compose brings up a working instance from the example config. 1166 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).