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
| Ondersteuningsperiode | 5 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. |
| Beveiligingsfixes | Gratis, 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 melden | security@frem.sh: gecoördineerde openbaarmaking, details op www.frem.sh/security. |
| SBOM | CycloneDX-JSON op cli.frem.sh/cli/SBOM.cdx.json, per versie op cli.frem.sh/cli/v<version>/SBOM.cdx.json. |
| Contractvoorwaarden | Servicevoorwaarden §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 | shHet 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 shEen specifieke versie vastzetten:
curl -sSfL https://cli.frem.sh/cli/install.sh | FREMFORGE_VERSION=0.1.0 shHandmatig downloaden
Wil je liever geen extern script naar sh pipen, dan zijn de binaries ook rechtstreeks bereikbaar. Kies de passende combinatie:
| Besturingssysteem | Architectuur | URL |
|---|---|---|
| macOS | arm64 | https://cli.frem.sh/cli/fremforge-darwin-arm64 |
| macOS | x86_64 | https://cli.frem.sh/cli/fremforge-darwin-x64 |
| Linux | arm64 | https://cli.frem.sh/cli/fremforge-linux-arm64 |
| Linux | x86_64 | https://cli.frem.sh/cli/fremforge-linux-x64 |
| Windows | x86_64 | https://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/fremforgeDe 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.jsonlElke 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 loginDit 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 fileHet 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 whoamiKies 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 --humanDe resultaten eerlijk lezen, want een verificatiehulpmiddel dat zichzelf overschat, is erger dan geen:
partialis 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 gevaloken 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-verifyhaaltwww.frem.sh/.well-known/slsa-trust-root.jsonrechtstreeks 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 appliesZie 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.jsonFragment 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_TOKENGebruikers 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 janeEen 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 | shOver 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.