Skip to main content

Branch protection

Branch protection on fremforge follows the upstream Forgejo model: rules attach to branches by glob pattern, enforce review + status + history requirements, and block writes that violate them. fremforge adds org-wide defaults so the same posture applies to every repo without per-repo configuration drift.

Org defaults vs per-repo overrides

Security › Policies › Repository rules sets the rules that apply to every repo in the org. A new repo gets one rule for every pattern in the org’s protected branches list (main, master, release/* by default) when it is created. Saving the defaults applies them to the org’s existing repos at once, and an hourly backfill reconciler runs the same pass as a safety net: it fills in any repo that has zero branch protection, and otherwise changes only the few settings listed under What the org default changes on existing rules — it never overrides a repo admin’s other settings. Two cases rely on that second pass: repos created before org defaults existed, and repos whose default branch was not yet visible when the create-time installer ran (an empty repo you push your own first commit into). Both are protected within the hour without you doing anything. Repos that already have protection but have drifted weaker than the org default are surfaced by the drift detector as an alert rather than being auto-reverted (see below).

Per-repo overrides live on the repo settings page (/<org>/<repo>/settings/branches). Override only when the repo has a genuine policy exception — most teams set the org default once and never touch repo-level rules.

Settings that map directly to Forgejo

  • Require a pull request. Direct pushes to the protected branch are refused (Forgejo’s enable_push: false), so a pull request is the only way in. This is what makes required reviews and checks binding: a direct push skips the pull request they are attached to. On in the fremforge baseline. fremforge’s own automation (dependency updates, AutoFix, the .gitattributes backfill) opens pull requests too, so it keeps working on a PR-only branch.

  • Require pull request reviews before merging. Set a minimum reviewer count (typically 1 or 2). Dismiss stale reviews on push to prevent rubber-stamps from surviving a force-amend.

  • Require status checks to pass before merging. Name the status contexts that must report success. fremforge’s org default requires seven, and requiring only some of them leaves the others unable to block a merge:

    • fremforge/sast — static analysis
    • fremforge/dep-scan — dependency vulnerabilities
    • fremforge/license-scan — licence policy
    • fremforge/image-scan — vulnerabilities and secrets in the repository’s container images and files
    • fremforge/cosign-verify — signature verification of digest-pinned images (informational: it always passes; see Image signatures)
    • fremforge/secret-scan — committed-secret detection
    • fremforge/release-age — holds an automated dependency-update pull request until every version its lockfile resolves has been published for at least your organisation’s release age (3 days unless an owner changed it)

    An eighth, fremforge/iac-scan (infrastructure-as-code misconfiguration, see IaC scanning), posts on every pull request since 17 October 2026 and becomes a required check organisation by organisation, so that no pull request opened before it existed waits for it. Until it is required for your organisation it reports without blocking on its own; you can add it to your required checks yourself at any time.

    The org default requires all seven; if you set required checks by hand, list all seven, and fremforge/iac-scan if you want it to block. fremforge/release-age blocks only automated dependency-update pull requests, and only until their versions reach the release age; on a pull request opened by a person it reports fresh versions without holding the PR. A scanner you switch off for an org or repo is removed from its required checks within the hour, since an off scanner posts nothing. See Re-running scans for what each one covers.

  • Require signed commits. When on, unsigned commits on the protected branch are rejected at push. See Cosign for the commit-signing path with Sigstore short-lived certs.

  • Require linear history. When enabled, fremforge disables merge commits on the repo — merges are squash or rebase only, so the protected branch keeps a linear history.

  • Block force pushes. Refuses --force on the protected branch entirely.

  • Block branch deletion. A protected branch cannot be deleted — this is inherent to having a branch-protection rule attached, not a separate toggle. As long as the branch is protected, delete operations against it are refused.

Settings fremforge adds on top

  • Enforce protection on org owners/admins too. A per-tenant toggle on the Repos admin page — “Enforce protection on org owners/admins too” (Forgejo’s apply_to_admins) — which is on by default. On, a pull request opened by an org owner needs the required approvals like anyone else’s; off, the approval count is waived for pull requests an org owner opens. The toggle governs approvals only. Required status checks apply to every pull request, and an org owner, as a repo admin, can still deliberately merge past them with Forgejo’s force merge (“merge anyway” in the merge box, or force_merge on the merge API). That is the escape hatch for a stuck or mistaken check; it merges a change the checks have not cleared, so use it sparingly. To stop a fremforge check from blocking at all, set its scanner to audit or off for the org or the repo instead. To let only dependency findings that are not reachable through fremforge/dep-scan, there is an opt-in policy: see Reachability: do not block on not-reachable findings, and read its risk first.
  • Require review from code owners. Sets Forgejo’s Block merge on official review requests on the protected branch rules. When a pull request is opened, a CODEOWNERS file (at the repo root, in docs/, .forgejo/ or .gitea/) makes Forgejo request a review from the owners of the changed paths; with this setting on, the pull request cannot merge while any of those requests is still open, so the code owners must review it. Two things to know: it blocks on any open review request to an official reviewer, including one you add by hand; and a code owner who may not approve on the branch (for example a non-owner when approvals are restricted to the Owners team) is not an official reviewer, so their request does not block. Without a CODEOWNERS file, nobody is requested automatically and the setting changes nothing.
  • Status-check allowlist for fremforge-emitted checks. The seven canonical names are listed above. They are held in one place internally, shared between the create-time install and the hourly backfill, so the set a new repo gets and the set the backfill enforces cannot drift apart.
  • Drift detector. A separate daily gap-scan flags any repo whose branch protection is absent or weaker than the org default — weaker meaning fewer required approvals, or a required check the default demands and the repo does not. (The backfill above is hourly and only fills in repos with no protection; this daily scan is what notices a repo that has protection but has been loosened.) This surfaces as a distinct admin Bell alert — “N repos with weakened branch protection” — that links to the Repos admin page. Apart from the settings listed in the next section, it is detect-and-alert only: fremforge does not auto-revert weakened settings, because a repo owner’s explicit settings win. Use the alert to decide whether to follow up with the repo owner.

What the org default changes on existing rules

On a repo that already has its own protection, fremforge leaves the rules alone except for these, each changed on its own without touching any other setting of the rule:

  • The fremforge required checks are added, and removed for a scanner set to off.
  • Enforce protection on org owners/admins too follows the org setting, both ways.
  • Require a pull request, when the org has it on, makes existing rules PR-only too. A rule that names who may push (a push allowlist of users, teams or deploy keys) is left as it is: that is a deliberate choice to let someone push, and the drift detector keeps reporting it.
  • Require review from code owners, when the org has it on, is turned on for existing rules.

The last two only ever tighten. Turning either off at the org level stops applying it to new rules; rules that already have it keep it until a repo admin changes them on the repo’s branch settings.

Only rules on the org’s protected branch patterns are touched, and fremforge never adds a rule for a pattern a repo admin left unprotected on a repo that has rules of its own.

Removed: default repository template

The Repos page used to have a Default repository template field. It was removed on 2026-10-14 because it was saved but never applied: repositories are created in Forgejo, and Forgejo can only generate a new repository from a template, not seed one a member has already created. To start repositories from a template, mark the source repository as a template in its settings and pick it in Forgejo’s New Repository form, or call POST /api/v1/repos/{template_owner}/{template_repo}/generate. The API now refuses a value for default_template_repo with removed-setting.

Setting it up

  1. Open Security › Policies › Repository rules.
  2. Pick the branch glob (typically main or main, master, release/*).
  3. Set the toggles above. Save.
  4. The change is recorded in the audit log as repo-defaults.branch-protection.updated. A retroactive apply to existing zero-protection repos is logged as repo-defaults.branch-protection.retro-applied, and resetting the org defaults to the fremforge baseline is logged as repo-defaults.reset-baseline.

See also

  • Scorecard — branch protection drives the Branch-Protection OpenSSF score.
  • Authentication policy — the broader posture (signed commits, SSH transport, MFA).
  • Cosign — required-signed-commits implementation.