Feature Specification: Component forgejo

Created: 2026-10-07

Status: Implemented

Input: Reconstructed from the code of core/components/dev/forgejo/ and the dev group at 14504f80d, pull requests #1383, #1466, #1475, #1547, #1583, #1589, #1595, #1852, #1956, #1992 and #1994.

This spec describes one component of the dev group. The family spec ../spec.md describes what all components share.

User Scenarios & Testing (mandatory)

User Story 1 - Contributors use the forge over HTTPS (Priority: P1)

A contributor opens the forge in a browser, clones and pushes over HTTPS, and gets mail notifications. The component runs Forgejo with MariaDB behind nginx, which terminates TLS with the certificate of the ssl resource. The forge publishes a security.txt, so that a security researcher finds the contact for a report.

Why this priority: The forge holds the source code and the CI of the project.

Independent Test: A VM test runs the component on one node, with stand-ins for the contract results and the relay, and plain HTTP in place of TLS. It checks the domain and the public URL in links, that nothing listens on other addresses, that the repositories are in MariaDB, that security.txt is served without a challenge, and that a notification reaches the relay with the smtp result. A unit test of the dev group pins the domain, the listen addresses, the sender and the security.txt. No check covers TLS, HSTS or the backup.

Acceptance Scenarios:

  1. Given the forge machine, When it deploys, Then Forgejo listens only on loopback and nginx serves the forge on its domain.
  2. Given the ssl request, When it resolves, Then the certificate covers the forge domain and the extra names of the deployment, in the dev group the git-pages sites.
  3. Given the smtp result, When Forgejo sends a notification, Then it uses STARTTLS with that result and the configured sender.

User Story 2 - A proof-of-work gate protects the expensive views (Priority: P1)

Scrapers must stay off the expensive HTML views. A browser without an Anubis cookie gets a proof-of-work challenge. Git clients, CI runners and raw file reads run no JavaScript, so their paths pass without a challenge.

Why this priority: Without the gate, scraper load makes the forge slow. Without the allow rules, the gate breaks clones and CI.

Independent Test: The VM test sends browser requests without a cookie. Raw files, Git refs and tar and zip archives pass. Bundle archives and file views get a challenge.

Acceptance Scenarios:

  1. Given a browser without a cookie, When it requests a machine path, Then the request reaches Forgejo. When it requests a file view, Then it gets a challenge.

User Story 3 - CI tools rerun jobs and find runs by commit (Priority: P2)

CI tooling must rerun a failed Actions run or job through the API, and must find runs by commit fast. Forgejo supplies the rerun API. The component patches the Forgejo package to add the index that finds runs by commit, and to stop idle runners that all select the same waiting job.

Why this priority: The forge works without it, but CI recovery is slower.

Independent Test: No check covers the patches.

Acceptance Scenarios:

  1. Given the package, When a client asks to rerun one job, Then Forgejo reruns that job.

Edge Cases

  • A patch that does not apply to a new version of Forgejo fails the build, so that a maintainer reviews the patches.
  • The Anubis signing key comes from a secret, so the cookies of visitors stay valid after a restart.
  • An upstream Anubis deny rule wins over the allow list, because the allow rules come after the default rules.
  • Static assets bypass Anubis.
  • Long requests get a long proxy timeout, so that a slow view or a large push does not fail at the proxy.
  • The database dump and the file copy of the backup are from different times. Stop Forgejo before a restore and start it after both restores.
  • An offline runner stays registered for one week.

Requirements (mandatory)

Functional Requirements

  • FR-001: The component MUST declare the Forgejo package and the shared options, without a default subdomain. The deployment MUST give the domain as a fully-qualified name.
  • FR-002: When the component is disabled, it MUST make no requests.
  • FR-003: The component MUST request a certificate for its domain plus the extra names of the deployment, by default none. See git-pages.md.
  • FR-004: The component MUST request public HTTP and HTTPS ports from firewall.
  • FR-005: The component MUST request from secrets the Anubis signing key, readable only by root, and the database and mail passwords, readable by Forgejo.
  • FR-006: The component MUST request a mail credential from smtp with a sender that has no default, and MUST configure the mailer from the result.
  • FR-007: The component MUST serve an RFC 9116 security.txt and its public key, with the configured contact and policy, a fixed expiry, the preferred language and the canonical URL. The contact, the policy and the key MUST have no default. The node MUST render the file from data, because a plan holds only data.
  • FR-008: The component MUST import its NixOS modules through nixos-module, and MUST NOT import git-pages.
  • FR-009: Forgejo MUST listen only on loopback, MUST enable LFS, MUST cap the item count of API responses, and MUST use the local MariaDB with the password from the secrets contract.
  • FR-010: The root URL of Forgejo MUST be the HTTPS URL of its domain.
  • FR-011: The node MUST size the MariaDB buffer pool to the forge data, and MUST cap the journal.
  • FR-012: nginx MUST listen only on the addresses of the network resource, MUST send dynamic requests through Anubis and static assets to Forgejo, and MUST add HSTS.
  • FR-013: The access log MUST NOT hold the RPC requests of the runners, because they are frequent and tell the operator nothing.
  • FR-014: Anubis MUST listen only locally, MUST keep its key in a credential and its state on disk, and MUST expose metrics.
  • FR-015: Anubis MUST let raw files, media, archives, release downloads, the Git and LFS protocols and the Actions RPC pass without a challenge, after the default rules.
  • FR-016: The package MUST have the job rerun API, MUST add the commit index on runs, and MUST let a runner skip a waiting job that another runner is about to get.
  • FR-017: With a backup bucket from the S3 contract, the component MUST back up a consistent database dump and the state of Forgejo without caches, indexes, temporary files and logs. Without it, it MUST configure no backup.

Key Entities

  • Anubis gate: The proof-of-work gate between nginx and Forgejo, with its allow list.
  • Offsite backup: The database dump and the file copy in the backup bucket.
  • Forge machine: The dev roster entry that runs this component and no CI jobs. Its host config also imports git-pages.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: The VM test passes, and access logs hold no runner RPC lines.
  • SC-002: The dev group evaluates with the smtp request satisfied by the external relay.

Assumptions

  • The dev group has no maddy. The external relay supplies the relay password.
  • Contributors reach the forge from the internet, so the ports are public in every environment.

Known Deviations

  • The package carries local patches for what upstream does not have: the commit index on runs and the job selection of runners.
  • The offsite backup is off. The dev group has no garage that makes the key, and the forge node does not use OpenBao, where the backup reads its secrets (#29).