Skip to main content

Using fremforge alongside GitHub while you migrate

A migration rarely happens in one weekend. While your code and CI still live on GitHub, you can already put fremforge’s scanning, SBOM inventory and findings to work. This page covers the two parts that work today:

  1. Mirror your GitHub repositories into fremforge, so fremforge scans them.
  2. Upload SBOMs from your GitHub Actions jobs, and read the findings over the API.

Both are steps on the way, not the destination. Pull requests, merge gates and CI run where the code lives, so they arrive when you move the repository itself with the GitHub migration guide.

Publishing packages to fremforge’s registries from external CI is not covered here. See Packages for the supported ways to authenticate.

1. Mirror the repository into fremforge

A pull mirror keeps a read-only copy of a GitHub repository in your fremforge organisation, synced on a schedule.

  1. Go to frem.sh/repo/migrate (or + → New Migration) and pick GitHub.
  2. Paste the repository’s HTTPS URL. For a private repository, give a GitHub token that can read the repository’s contents, and nothing more. A fine-grained token limited to that repository with Contents: read is enough.
  3. Tick This repository will be a mirror before you click Migrate. Without it the import is a one-time copy.
  4. Choose the target organisation. The interval defaults to 8 hours; you can lower it to 10 minutes under the repository’s Settings → Mirror Settings.

Details, limits and the list of allowed source hosts are on Repository mirroring.

What fremforge does with a mirror

When a sync moves the default branch, fremforge treats it like a push to the default branch: it scans the new head for vulnerable dependencies, licences and code issues (SAST), and records an SBOM of the repository. Each repository is scanned at most once an hour, and the latest head wins.

Findings appear in the admin UI under /<org>/_admin/code-security, and over the Findings API.

What a mirror does not give you:

  • No pull-request scans and no merge gates. Pull requests stay on GitHub and are not mirrored, so fremforge cannot post statuses on them.
  • No Actions runs. The mirror is read-only; workflows run where you push.
  • A delay. The copy is as fresh as the last sync.

2. Upload SBOMs from GitHub Actions

If your build produces artefacts that the repository scan does not see, such as a container image or a bundled binary, upload their SBOM from the job that builds them.

Create a token

In fremforge, go to Admin → API tokens and mint a token with the sboms:write scope (add findings:read if the same job reads findings, see below). The token starts with ffp_. Give it an expiry, and store it in GitHub as an Actions secret, for example FREMFORGE_API_TOKEN. Store your organisation’s slug as a variable, for example FREMFORGE_ORG.

Upload step

- name: Generate and upload SBOM
  env:
    FREMFORGE_API_TOKEN: ${{ secrets.FREMFORGE_API_TOKEN }}
    ORG: ${{ vars.FREMFORGE_ORG }}
  run: |
    syft dir:. --output cyclonedx-json=sbom.json
    # The digest is the SHA-256 of the SBOM file itself; the server recomputes it.
    DIGEST=$(sha256sum sbom.json | cut -d' ' -f1)
    curl -sS --fail-with-body \
      -H "Authorization: Bearer ${FREMFORGE_API_TOKEN}" \
      -F "sbom=@sbom.json" \
      -F "artifact_name=myapp" \
      -F "artifact_version=${GITHUB_SHA}" \
      -F "format=cyclonedx-json" \
      -F "sha256_digest=${DIGEST}" \
      -F "git_ref=${GITHUB_REF}" \
      -F "commit_sha=${GITHUB_SHA}" \
      "https://frem.sh/_app/api/v1/orgs/${ORG}/sboms"

The request is multipart/form-data to POST /_app/api/v1/orgs/{org}/sboms:

FieldRequiredValue
sbomyesThe SBOM file, at most 5 MiB.
artifact_nameyesLetters, digits, ., _, / and -, up to 255 characters.
artifact_versionyesSame character set, up to 255 characters.
formatyescyclonedx-json or spdx-json. The file must actually be that format.
sha256_digestyesLowercase hex SHA-256 of the SBOM file. A mismatch is refused with 400 digest-mismatch.
git_refnoA full ref such as refs/heads/main.
commit_shano7 to 64 lowercase hex characters.
repo_full_namenoowner/repo. Set it to the mirror’s name in fremforge to tie the SBOM to that repository.
attestationnoAn in-toto attestation bundle (JSONL).

A successful upload answers 201 with the SBOM’s id. The SBOM then appears under Admin → Security → SBOMs. See SBOMs for what is stored and how it is shown.

3. Read findings from GitHub Actions

With a token carrying findings:read, a GitHub Actions job can read the findings fremforge holds for your mirrored repositories, for example to fail a release job while an unresolved critical dependency finding exists:

- name: Check fremforge findings
  env:
    FREMFORGE_API_TOKEN: ${{ secrets.FREMFORGE_API_TOKEN }}
    ORG: ${{ vars.FREMFORGE_ORG }}
    REPO: ${{ vars.FREMFORGE_ORG }}/backend   # the mirror's name in fremforge
  run: |
    curl -sS --fail-with-body \
      -H "Authorization: Bearer ${FREMFORGE_API_TOKEN}" \
      "https://frem.sh/_app/api/v1/orgs/${ORG}/code-security/deps/findings?limit=200" \
      > deps.json
    CRITICAL=$(jq --arg r "$REPO" '[.findings[] | select(.repo_full_name == $r and .severity == "critical" and .state == "open")] | length' deps.json)
    echo "open critical dependency findings: ${CRITICAL}"
    test "${CRITICAL}" -eq 0

Results are paginated with limit (up to 200) and offset; the response says has_more when there is another page. The other scanners use the same envelope (with their own fields and severity scales) under code-security/sast, code-security/images, code-security/licenses and code-security/secrets. The Findings API page has the full reference, including dismissals.

Next step: move the repository

Once the team is used to fremforge’s findings, move the repository and its workflows for real with the GitHub migration guide. After the move, pushes and pull requests are scanned as they happen, merge gates apply, and CI runs on fremforge’s runners.