I recently started moving my maintained projects from GitHub to Codeberg.GitHub to Codeberg.

On the road, I collected a few notes on the problems I encountered feature wise.

This is in no way meant to complain on Forgejo (or Codeberg), but maybe it will help getting those remaining issues solved, and if so it fills a purpose. Also, please correct if I have missed something, and I’m wrong. For most of these the exists Issues though.

Table of Contents#

  1. Tokens never expire and aren’t repo-scoped
  2. Can’t delete workflow runs from UI
  3. Can’t share workflows across repos
  4. Local reusable workflows are buggy
  5. No keyless artifact signing
  6. Web UI can’t sign merge commits
  7. No SLSA Level 3 supply chain security
  8. No full OpenSSF Scorecard support
  9. No centralized security findings dashboard
  10. No automatic vulnerable dependency blocking
  11. Artifact upload/download actions broken

1. Tokens never expire and aren’t repo-scoped#

GitHub fine-grained tokens:

  • Scope to specific repos
  • Must expire (7 days to 1 year)
  • Very granular permissions

Forgejo tokens:

  • Never expire
  • Apply to all repos you can access

What I did:

  • Manual rotation with calendar reminders
  • Bot accounts with limited access instead of personal tokens
  • Minimal scopes

Security risk from non-expiring tokens. Can’t limit to specific repos.

Issues: #8837, #4992, #2332, #1712


2. Can’t delete workflow runs from UI#

GitHub and GitLab let you delete workflow runs from UI or API. Codeberg lets you delete PR’s with UI, but not workflows. Not great if you accidentally pushed secrets into logs (even if you should rotate those anyway).

GitLabGitHubForgejo
Delete via UIYesYesNo
Delete via APIYesYesNo

Issues: #2184 (has “valuable code” label), #6939 Gitea PR #26275 was closed - too complex.

Workaround: Nothing I can do. Wait for auto-cleanup.


3. Can’t share workflows across repos#

GitHub lets orgs share workflows from a central .github repo. Forgejo only supports local reusable workflows.

Issue: #2436

Workaround: Copied and duplicated workflows to each repo.


4. Local reusable workflows are buggy#

GitHub’s reusable workflows via workflow_call let you define once, call from anywhere. Forgejo has a bug where jobs don’t expand properly - runs-on becomes [], job waits forever.

Issues: #8780, #10448

Workaround: Inlined everything, duplication.


5. No keyless artifact signing#

GitHub + Sigstore = sign artifacts without managing keys. OIDC token proves identity, Fulcio issues ephemeral cert. Done.

Codeberg has no OIDC provider for workflows.

What I did: Cosign with keys, or GPG in GoReleaser. You manage keys yourself.

Issues: #2389, #5034


6. Web UI can’t sign merge commits#

If branch protection requires signed commits, web UI merges fail: Codeberg can’t sign. Affects merge commits, squash merges - anything creating a new commit.

Rebase works - replays existing signed commits, no new commit.

Workaround: Use rebase merge (Settings → Repository → Merge Requests → Fast Forward). For Renovate, set automergeStrategy: rebase. Merge locally.

Must configure per-repo - no org-wide setting.

Issues: #10649, #6438, #1638


7. No SLSA Level 3 supply chain security#

SLSA proves how software was built. SLSA Level 3 means that the signing key is controlled by the platform or another trusted team, so a compromised build can’t forge provenance. Multiple steps would need to happen here - there are multiple paths:

A) if you want to use Codeberg as the trusted provider signing your builds B) if you want to setup something in your own organisation using a custom signing service - For example Tekton Chains and custom Forgejo build runners.

I’m talking about perspective A here, as I’m in the Codeberg end user-perspective in this document.

On GitHub, Sigstore trusts GitHub and will sign your builds. On Codeberg, you pass your signing key as a secret. Your build steps can access it. A malicious dependency could steal it.

What you can get:

LevelStatusHow
L1YesWitness creates in-toto attestations
L2PartialSigned, but self-hosted isn’t “trusted third-party”
L3NoImpossible without platform-controlled signing

Workaround: Use witness for L1-L2 and sigstore.

For L3 ala GitHub we would need:

  1. Forgejo OIDC tokens (#2389)
  2. Sigstore to trust Codeberg as issuer
  3. A SLSA Forgejo generator
  4. Codeberg wanting to offer a signing service

OIDC tokens would enable:

  • L3 with a third-party signing service that trusts Forgejo OIDC (e.g., if Sigstore added Forgejo as a trusted issuer)
  • L3 with Codeberg’s Forgejo + a Codeberg-hosted signing service (you don’t control Codeberg’s OIDC issuer)

If you need L3 for compliance reasons, this is a blocker.

So, suggestions for achieving SLSA Level 3 with Codeberg

If someone involved in Codeberg is reading this…

A practical path would be for Codeberg to offer a signing service for custom runners. In this model, your self-hosted runner builds the software and collects provenance metadata, but instead of signing it locally, the runner sends the provenance to a Codeberg-hosted signing service. This service would verify the request came from a legitimate runner (via OIDC tokens or similar), then sign the provenance with keys stored in Codeberg’s infrastructure that you cannot access. This creates the required trust boundary - you control the build environment, but Codeberg controls the attestation signing.

Alternatively, Codeberg or a trusted related Codeberg entity could offer paid and managed runners similar to GitHub Actions hosted runners, where both the build execution and signing happen on Codeberg-controlled infrastructure. Combined with Sigstore adding Codeberg as a trusted OIDC identity provider for keyless signing, this would provide full L3 compliance. The managed runner approach is more comprehensive but requires significant infrastructure investment from Codeberg, while the signing service approach lets users keep their existing custom runners and only centralizes the trust-critical signing step.

Further reading:

TopicLink
SLSAhttps://slsa.dev/spec/v1.0/levels
Sigstorehttps://docs.sigstore.dev/about/overview/
Fulcio (keyless signing CA)https://docs.sigstore.dev/fulcio/overview/
Tekton Chainshttps://tekton.dev/docs/chains/
GitHub SLSA Generatorhttps://github.com/slsa-framework/slsa-github-generator
Cosign (signing tool)https://docs.sigstore.dev/cosign/overview/
Witnesshttps://witness.dev/docs/welcome/

8. No full OpenSSF Scorecard support#

OpenSSF Scorecard scores your security practices and gives you a badge.

On Codeberg, CLI works locally but API doesn’t index Codeberg repos - and badge service only knows github.com and gitlab.com.

What I did: Run scorecard --local . and store results as artifacts. ~8 of 17 checks work locally.


9. No centralized security findings dashboard#

GitHub’s Security tab shows vulnerability findings from scanners in one place.

Forgejo has no Security tab, no SARIF upload.

What I did: Run trivy/grype, fail builds on HIGH/CRITICAL, review artifacts manually. Scanning works, just no dashboard.


10. No automatic vulnerable dependency blocking#

GitHub blocks PRs that add vulnerable dependencies using its dependency graph with their depedencyanalysis action.

Forgejo has nothing built-in.

What I did: govulncheck + osv-scanner for Go. govulncheck analyzes call graphs - only reports vulns in code you actually call. For other ecosystems: npm audit, pip-audit, cargo audit, or osv-scanner.


11. Artifact upload/download actions broken#

Nitpick, but still did cost me time and confusion. code.forgejo.org mirrors GitHub Actions. But upload-artifact and download-artifact v4+ don’t work - they need GitHub’s Artifact API.

Mirror (broken v4+)Patched (works)
code.forgejo.org/actions/upload-artifactcode.forgejo.org/forgejo/upload-artifact
code.forgejo.org/actions/download-artifactcode.forgejo.org/forgejo/download-artifact

What I did: Use the forgejo/* versions instead of actions/*. Most other mirrors work fine.


Summary#

#FeatureGitHubCodebergWorkaround
1Token expiration/scopingYesNoManual rotation, bot accounts
2Delete workflow runsYesNoWait for auto-cleanup
3Cross-repo workflowsYesNoCopy to each repo
4Reusable workflowsWorksBug #8780Inline
5Keyless signingYesNoCosign with keys
6Signed mergesYesNoRebase
7SLSA L3YesL1-L2 onlyWitness
8OpenSSF ScorecardYesLocal onlyRun locally
9Security dashboardYesNoArtifacts
10Dependencymina blockingYesNogovulncheck
11Artifact actionsN/Av4 mirrors brokenUse forgejo/*

I’m going to start helping out myself by:

  • Translating Forgejo to Swedish
  • Look at how OpenSSF Scorecard support for Forgejo/Codeberg can be improved.