Naar de hoofdinhoud

fremforge-CLI

De fremforge-CLI is een wrapper van één binary rond de openbare REST API. Dezelfde werkwoorden, hetzelfde autorisatiemodel, dezelfde scopes. Gedistribueerd uitsluitend vanaf PoP’s in de EU; geen npm-register, geen Docker Hub, geen afhankelijkheid van Sigstore Fulcio.

Ondersteuningsperiode, beveiligingsupdates en SBOM

Ondersteuningsperiode5 jaar vanaf de releasedatum van elke minor-versie. Dit is de ondersteuningsperiode in de zin van art. 13, lid 8, van de Cyberweerbaarheidsverordening (Verordening (EU) 2024/2847), en ze is gekoppeld aan de uitgebrachte versie, niet aan je abonnement.
BeveiligingsfixesGratis, altijd, en nooit afhankelijk van een abonnement. Fixes worden vooruit uitgebracht als nieuwe release op dezelfde URL’s, gedekt door dezelfde ondertekende SHA256SUMS. Draai je een vervangen versie? Upgrade om de fix te krijgen.
Een kwetsbaarheid meldensecurity@frem.sh: gecoördineerde openbaarmaking, details op www.frem.sh/security.
SBOMCycloneDX-JSON op cli.frem.sh/cli/SBOM.cdx.json, per versie op cli.frem.sh/cli/v<version>/SBOM.cdx.json.
ContractvoorwaardenServicevoorwaarden §7.5: licentie, garantie en aansprakelijkheid specifiek voor de binary.

De SBOM is kort, en daar gaat het om. De CLI declareert geen enkele npm-afhankelijkheid van derden. Hij heeft twee afhankelijkheden, allebei van ons: @fremverk/audit-chain, dezelfde hashketencode die de API zelf draait (gedeeld in plaats van opnieuw geïmplementeerd, zodat audit-verify niet kan afwijken van wat hij verifieert), en @fremforge/tlog-verify, die opname in het transparantielog controleert voor attest-verify. Geen van beide heeft eigen afhankelijkheden; beide importeren alleen ingebouwde modules van Node, dus het volledige oppervlak van derden van de geleverde binary is de Bun-runtime die bij het compileren wordt ingebed. Vanaf v0.3.1 noemt de SBOM ook de twee eigen pakketten, gemarkeerd als van ons, zodat hij elk onderdeel noemt en niet alleen dat van derden.

De hash van de SBOM staat in SHA256SUMS en valt dus onder dezelfde Ed25519-handtekening als de binaries. Verifieer hem op dezelfde manier (zie §“De Ed25519-handtekening verifiëren” hieronder; dat geldt voor elk bestand dat in het manifest staat, niet alleen voor de binaries):

curl -fsSL https://cli.frem.sh/cli/SBOM.cdx.json -o SBOM.cdx.json
curl -fsSL https://trust.frem.sh/SHA256SUMS -o SHA256SUMS
grep SBOM.cdx.json SHA256SUMS | shasum -a 256 -c -

De CLI is optioneel, met één uitzondering. Elke actie die hij uitvoert, is ook beschikbaar via de REST API, dus als je beleid is om geen binaries van leveranciers te draaien, verlies je geen functionaliteit door hem over te slaan. De uitzondering is audit-verify, die je audit-hashketen op je eigen machine opnieuw berekent. Een API kan dat niet voor je doen, want het hele punt van een hashketen opnieuw berekenen is dat je de partij die hem heeft gemaakt niet hoeft te vertrouwen. De ruwe rijen worden geleverd door GET /orgs/{org}/audit/chain, dus je kunt de berekening ook in je eigen tooling doen. Wat je niet kunt doen, is ons het laten doen en het resultaat verificatie noemen.

Installeren

Eén regel (aanbevolen)

curl -sSfL https://cli.frem.sh/cli/install.sh | sh

Het installatiescript detecteert automatisch je besturingssysteem (Linux of macOS) en architectuur (arm64 of x86_64), downloadt de bijbehorende binary van https://cli.frem.sh/cli/, downloadt de canonieke SHA256SUMS van https://trust.frem.sh/ (een aparte host met aparte schrijfrechten, zodat een compromittering van alleen de binary-host geen overeenkomende hash kan vervalsen), verifieert de SHA-256 en installeert naar ~/.local/bin/fremforge of /usr/local/bin/fremforge.

Kies een andere installatielocatie met FREMFORGE_INSTALL_DIR:

curl -sSfL https://cli.frem.sh/cli/install.sh | FREMFORGE_INSTALL_DIR=/opt/fremforge/bin sh

Een specifieke versie vastzetten:

curl -sSfL https://cli.frem.sh/cli/install.sh | FREMFORGE_VERSION=0.1.0 sh

Handmatig downloaden

Wil je liever geen extern script naar sh pipen, dan zijn de binaries ook rechtstreeks bereikbaar. Kies de passende combinatie:

BesturingssysteemArchitectuurURL
macOSarm64https://cli.frem.sh/cli/fremforge-darwin-arm64
macOSx86_64https://cli.frem.sh/cli/fremforge-darwin-x64
Linuxarm64https://cli.frem.sh/cli/fremforge-linux-arm64
Linuxx86_64https://cli.frem.sh/cli/fremforge-linux-x64
Windowsx86_64https://cli.frem.sh/cli/fremforge-windows-x64.exe

Het architectuurachtervoegsel is x64 (niet x86_64) en arm64; die komen overeen met de --target-tokens van Bun. uname -m meldt x86_64 op Intel/AMD, dus zet dat om naar x64 als je de URL zelf samenstelt (het installatiescript van één regel doet dat voor je).

Verifieer tegen de canonieke hash op trust.frem.sh voordat je de binary uitvoert:

# 0. Map uname -m's x86_64 to the published x64 token
arch=$(uname -m); [ "$arch" = x86_64 ] && arch=x64

# 1. Download the binary
curl -fsSL https://cli.frem.sh/cli/fremforge-$(uname -s | tr A-Z a-z)-$arch \
  -o fremforge

# 2. Download the canonical SHA256SUMS (separate host, separate credential)
curl -fsSL https://trust.frem.sh/SHA256SUMS -o SHA256SUMS

# 3. Verify
sha256sum --check --ignore-missing SHA256SUMS

# 4. Install
chmod +x fremforge
sudo install -m 0755 fremforge /usr/local/bin/fremforge

De Ed25519-handtekening verifiëren (optioneel, aanbevolen)

Naast de SHA-256-controle wordt elke release ondertekend als een in DSSE verpakt in-toto-Statement, onder dezelfde vertrouwensbasis als de SLSA-buildherkomst van fremforge. De verificatie downloadt de publieke vertrouwensbasis van www.frem.sh/.well-known/slsa-trust-root.json en controleert de Ed25519-handtekening op de bytes van SHA256SUMS.

Grijp niet naar slsa-verifier verify-artifact. Dat subcommando heeft geen vlag --trusted-root, en het kan de vertrouwensbasis-JSON van fremforge en een kale Ed25519-DSSE-envelop niet verwerken: het verwacht een bundel in Sigstore-stijl. Deze pagina toonde vroeger zo’n commando; dat had nooit kunnen werken. Verifieer in plaats daarvan rechtstreeks, met tools die je distributie al heeft.

SHA256SUMS.intoto.jsonl is JSON Lines: één DSSE-envelop per gepubliceerde binary. Verifieer elke regel die voor jou van belang is.

# Pull the trust root and the signed envelopes
curl -fsSL https://www.frem.sh/.well-known/slsa-trust-root.json -o slsa-trust-root.json
curl -fsSL https://trust.frem.sh/SHA256SUMS.intoto.jsonl -o SHA256SUMS.intoto.jsonl

# Extract the builder public key: v2, the active key since 2026-09-12. An envelope's
# .signatures[0].keyid names its key; one signed before 2026-09-12 needs ...-v1.
jq -r '.trusted_keys[] | select(.kid=="fremforge-slsa-builder-v2") | .pem' \
  slsa-trust-root.json > builder.pub

# Verify every envelope: rebuild the DSSE pre-authentication encoding and check it
while read -r line; do
  pt=$(printf '%s' "$line" | jq -r .payloadType)
  printf '%s' "$line" | jq -r .payload | base64 -d > payload.bin
  printf '%s' "$line" | jq -r '.signatures[0].sig' | base64 -d > sig.bin
  { printf 'DSSEv1 %s %s %s ' "${#pt}" "$pt" "$(wc -c < payload.bin)"; cat payload.bin; } > pae.bin
  openssl pkeyutl -verify -pubin -inkey builder.pub -rawin -in pae.bin -sigfile sig.bin \
    && jq -r '.subject[] | .name + "  " + .digest.sha256' payload.bin
done < SHA256SUMS.intoto.jsonl

Elke geverifieerde envelop toont elk artefact dat hij dekt en de digest die fremforge daarvoor heeft ondertekend. Vergelijk die digest met de binary die je hebt gedownload: om die vergelijking draait het; de handtekening vertelt je alleen dat de digest van ons is.

De vertrouwensbasis is dezelfde die wordt gebruikt voor de buildherkomst van artefacten die je runner produceert: dezelfde Ed25519-sleutel, dezelfde builderId, hetzelfde verificatierecept. We noemen geen SLSA-niveau; de beveiligingspagina legt uit waarom.

Authenticatie

De CLI ondersteunt drie manieren om inloggegevens mee te geven, in deze volgorde: de vlag --token=…, de omgevingsvariabele FREMFORGE_TOKEN en daarna de opgeslagen login in ~/.config/fremforge/auth.json.

Aanbevolen: inloggen via de browser (niets plakken)

fremforge auth login

Dit opent je browser naar frem.sh, je keurt het apparaat goed, het token komt in ~/.config/fremforge/auth.json (mode 600) en elke volgende aanroep laadt het automatisch. Het is dezelfde OAuth2 + PKCE-stroom die je git push al gebruikt, dus als je het token intrekt op frem.sh/user/settings/applications, trek je de toegang voor git en de CLI in één stap in.

fremforge auth status      # show current login + expiry
fremforge auth logout      # revoke + delete the local file

Het token werkt op zowel de REST API van fremforge (/_app/api/v1/orgs/…) ALS de eigen API van Forgejo (/api/v1/repos/…): Forgejo accepteert zijn eigen OAuth-bearer-tokens rechtstreeks, en de fremforge-API valideert ze via introspectie tegen /api/v1/user van Forgejo. Dezelfde identiteit, beide oppervlakken.

CI en scripts: PAT

Maak voor niet-interactieve situaties (CI-jobs, scripts, agents) een PAT met beperkte scopes aan. Het eenvoudigst gaat dat met de CLI zelf, zodra je één keer interactief bent ingelogd (fremforge auth tokens create --name=… --scopes=…; zie Een PAT met beperkte scopes aanmaken voor CI), of maak er een aan op https://frem.sh/user/settings/applications. Daarna:

export FREMFORGE_TOKEN='<your PAT>'
fremforge whoami

Kies een andere basis-URL met FREMFORGE_URL= (moet HTTPS zijn); de standaard is https://frem.sh.

Commando’s

fremforge auth login                              Browser-based OAuth2+PKCE login.
fremforge auth logout                             Revoke + delete the stored token.
fremforge auth status                             Show current login + expiry.
fremforge whoami                                  Print the user the token authenticates as.
fremforge data-export start <slug>                Queue a full data export for the org.
fremforge data-export list <slug>                 List recent export jobs.
fremforge dependency-updates enable <slug>        Enable hosted Renovate for the org.
fremforge dependency-updates disable <slug>       Disable hosted Renovate for the org.
fremforge dependency-updates run-now <slug>       Trigger an out-of-cycle Renovate run (1/h cap).
fremforge audit-log <slug>                        Fetch the org audit-log slice.
                  [--since=DATE] [--actor=…] [--action=…] [--limit=N]
fremforge audit-verify <slug>                     RECOMPUTE the per-tenant audit hash chain
                  [--human] [--full]              locally, then cross-check our own verdict.
                                                  Exit 0=ok, 2=partial, 3=broken,
                                                  4=anchor_mismatch, 5=local ok but we
                                                  disagreed. --full walks from the chain
                                                  epoch instead of the latest anchor.
fremforge sbom <owner>/<repo> <tag>               Download a release SBOM.
                  [--format=spdx|cyclonedx]
fremforge runs <owner>/<repo>                     List recent CI runs (JSON; --human for a table).
                  [--limit=N] [--status=…] [--event=…]
fremforge runs logs <owner>/<repo> <run-id>       Fetch a run's logs (text/plain, all jobs).
fremforge auth tokens create                      Mint a scoped PAT (printed once).
                  --name=N --scopes=a,b [--ttl-days=N]
fremforge auth tokens list                        List your PATs (--human for a table).
fremforge auth tokens revoke <token-id>           Revoke a PAT.
fremforge findings <type> <slug>                  Security findings. type ∈ secrets |
                  [--status=…] [--repo=…]         dependencies | signed-commits | attestations.
fremforge secrets list <slug>                     List org Actions/CI secret names.
fremforge secrets set <slug> <NAME>               Set a secret (value via FREMFORGE_SECRET_VALUE).
fremforge secrets delete <slug> <NAME>            Delete a secret.
fremforge seats list <slug>                       List org members + seat usage.
fremforge seats add <slug> --email=E              Add a member (--username=, --full-name= optional).
fremforge seats remove <slug> <username>          Remove a member.
fremforge work start <org>/<repo>                 Declare work before the first push (Active work).
                  --title=TEXT [--intent=TEXT]    Prints the branch to create and related work.
                  [--areas=a,b] [--branch=NAME]
fremforge work list <org>/<repo>                  Who is building what in a repository.
                  [--area=AREA]
fremforge version                                 Print the CLI version.

Draai fremforge --help voor de canonieke referentie, of geef --human mee aan elk commando dat het ondersteunt voor uitvoer als tabel.

Uitgewerkte voorbeelden

Je auditketen zelf verifiëren (te gebruiken in CI)

audit-verify vraagt ons niet of je auditlog intact is. Hij downloadt de ruwe rijen, berekent elke hash en elke koppeling opnieuw op je eigen machine, en vergelijkt pas daarna zijn resultaat met het onze. De exitcode is jouw resultaat, niet onze bewering, en als de twee van elkaar verschillen, wordt dat verschil zelf gemeld en faalt het commando.

Dat onderscheid is de reden dat dit commando bestaat. Tot augustus 2026 las het een veld integrity_status uit een API-antwoord en zette dat om naar een exitcode. Dat betekent dat de partij die werd geaudit het antwoord berekende en het “verificatie”-hulpmiddel het doorgaf. Niets wat je kon draaien, zou een gecompromitteerde of oneerlijke API hebben ontmaskerd. Nu wel.

export FREMFORGE_TOKEN='<your PAT, audit:read scope>'

# Human-readable, for terminals
fremforge audit-verify acme --human

# Script-friendly. The exit code reflects the LOCAL walk:
#   0 = ok
#   2 = partial          — some of the chain could not be checked (see below)
#   3 = broken           — a row's content or its link does not match. Investigate.
#   4 = anchor_mismatch  — the WORM-anchored tip is not present in your chain
#   5 = we disagreed     — your walk was clean but our verdict was not
#   6 = bad signature    — the anchor's signature failed against our published key
fremforge audit-verify acme || {
  echo "audit verification failed with exit code $?"
  exit 1
}

# Walk the entire chain from its first row instead of just the tail since the
# last anchor. Slower and much larger; appropriate for a quarterly review.
fremforge audit-verify acme --full --human

De resultaten eerlijk lezen, want een verificatiehulpmiddel dat zichzelf overschat, is erger dan geen:

  • partial is geen geslaagd resultaat. Het betekent dat afkapping een deel van de keten heeft verborgen, zodat de rijen buiten het venster niet geverifieerd zijn. Wij melden in dat geval ok en de CLI doet dat bewust niet.
  • Gewiste en geredigeerde rijen komen bewust niet meer op dezelfde hash uit. AVG-wissing en de payload-opruimer na 90 dagen herschrijven de inhoud van rijen achteraf, zodat die rijen niet meer overeenkomen met hun eigen hash. Ze worden apart geteld (tenant_erased_count, payload_redacted_count) en zijn geen manipulatie. Hun koppelingen worden nog steeds gecontroleerd.
  • Het WORM-anker is ondertekend, en de CLI controleert het tegen een vertrouwensbasis die hij zelf ophaalt. audit-verify haalt www.frem.sh/.well-known/slsa-trust-root.json rechtstreeks op (nooit een sleutel van de API), verifieert de Ed25519-DSSE-handtekening en bevestigt dat de envelop aan dit anker is gebonden en niet aan een ander. Exitcode 6 betekent dat de handtekening is gecontroleerd en is afgekeurd; behandel dat als een incident. NOT CHECKED (exitcode ongewijzigd) betekent dat het anker van vóór het ondertekenen is (2026-08-04) of dat de vertrouwensbasis vanaf je machine niet bereikbaar was. Geen van beide is een fout, en de CLI zegt welke van de twee het is.

Om hetzelfde zonder de binary te doen, lees je de rijen uit GET /orgs/{org}/audit/chain en hash je de canonieke JSON van elke rij met de sleutels in de volgorde action, actor, created_at_iso, fields_json, prev_hash, tenant_id. Houd fields_json byte voor byte gelijk: parse en serialiseer het niet opnieuw, anders komen je digests niet overeen.

Zie integriteit van de auditketen voor de volledige garantie.

Gegevensexport in eigen beheer

# Kick off
fremforge data-export start acme | jq .

# Poll
fremforge data-export list acme | jq '.[] | {id, status, created_at}'

Zie gegevensexport voor het formaat van het artefact, de bewaartermijn en de downloadstroom met ondertekende URL.

Afhankelijkheidsupdates (gehoste Renovate)

fremforge dependency-updates enable acme
fremforge dependency-updates run-now acme   # 1/h rate-limit applies

Zie afhankelijkheidsupdates voor wat er wordt ingeschreven en hoe je dat per repository afbakent.

SBOM downloaden

fremforge sbom acme/api v1.4.0 --format=spdx > sbom.spdx.json
fremforge sbom acme/api v1.4.0 --format=cyclonedx > sbom.cyclonedx.json

Fragment van het auditlog

fremforge audit-log acme --since=2026-05-01 --action=push-protection-override | jq .

CI-runs en logs

Toon recente runs en haal daarna de volledige logs van een run op (alle jobs aan elkaar) via het globale run-ID:

# List the 20 most recent runs as a table
fremforge runs acme/api --human

# Script-friendly: grab the newest run id, then fetch its logs
run_id=$(fremforge runs acme/api --limit=1 | jq -r '.workflow_runs[0].id')
fremforge runs logs acme/api "$run_id"

# Tail just the failing step
fremforge runs logs acme/api "$run_id" | grep -iE "error|failed|exit code" | tail

<run-id> is het globale run-ID dat fremforge runs toont (het veld .workflow_runs[].id), niet het runnummer per workflow in de URL van de webinterface. Het logs-endpoint geeft text/plain terug met alle jobs in één antwoord; je hoeft geen artefact per job te downloaden.

Een PAT met beperkte scopes aanmaken voor CI

Na je eerste fremforge auth login maak je zonder de terminal te verlaten afgeleide tokens met beperkte scopes aan voor CI. Het token wordt één keer getoond; leg het meteen vast:

fremforge auth tokens create --name=ci-readonly --scopes=findings:read,orgs:read --ttl-days=30 \
  | jq -r .token
fremforge auth tokens list --human
fremforge auth tokens revoke <token-id>

Je kunt alleen scopes toekennen die je eigen token al heeft, en een wildcard (*) kan hier niet worden aangemaakt.

CI-secrets beheren

fremforge secrets list acme

# Pass the value via env (preferred — not in shell history or `ps`)
FREMFORGE_SECRET_VALUE="$(cat ./deploy-key)" fremforge secrets set acme DEPLOY_KEY

fremforge secrets delete acme OLD_TOKEN

Gebruikers en leden

fremforge seats list acme --human                       # cap + usage + roster
fremforge seats add acme --email=jane@acme.example --full-name="Jane Doe"
fremforge seats remove acme jane

Een lid toevoegen boven het gecontracteerde maximum aantal gebruikers geeft een 409 met een facturatielink terug: koop eerst extra gebruikers.

Uitvoerformaat

Standaard geeft elk commando JSON op stdout, geschikt voor scripts en pipes. Geef --human mee aan de commando’s die het ondersteunen (audit-verify, attest-verify, release verify, runs, auth tokens list, seats list en work) voor een opgemaakte samenvatting.

De CLI bijwerken

Het installatiescript van één regel is idempotent: opnieuw draaien haalt de nieuwste binary op, verifieert de checksum opnieuw en overschrijft de oude binary ter plekke:

curl -sSfL https://cli.frem.sh/cli/install.sh | sh

Over soevereiniteit

De distributieroute van de CLI is volledig EU-soeverein:

  • Binaries: T Cloud Public OBS (eu-de, Magdeburg/Biere, Duitsland), achter een Bunny-pullzone die beperkt is tot PoP’s in de EU.
  • SHA256SUMS + DSSE-envelop: een aparte T Cloud Public OBS-bucket (trust.frem.sh) met aparte schrijfrechten. Doordat de binary-host en de integriteitshost gescheiden zijn, kan het compromitteren van de ene geen overeenkomst met de andere vervalsen.
  • Vertrouwensbasis: een statisch JSON-bestand dat wordt geleverd vanaf www.frem.sh (dezelfde Bunny-routering uitsluitend via de EU).
  • Geen afhankelijkheid van het npm-register: registry.npmjs.com draait in AWS in de VS; de CLI raakt het nooit aan, niet bij installatie en niet bij gebruik.
  • Geen afhankelijkheid van Docker Hub: om dezelfde reden.
  • Geen Sigstore Fulcio/Rekor: het transparantielog van Sigstore wordt in de VS gehost. fremforge draait zijn eigen Ed25519-ondertekenaar en een statische vertrouwensbasis in plaats van afhankelijk te zijn van Fulcio.

De subverwerkers op het installatiepad staan in het openbare register van subverwerkers en zijn dezelfde die al persoonsgegevens van klanten verwerken volgens bijlage B van de DPA.