CLI fremforge
La CLI fremforge est un binaire unique qui enveloppe l’API REST publique. Mêmes verbes, même modèle d’autorisation, mêmes scopes. Elle est distribuée exclusivement depuis des PoP européens ; aucune dépendance au registre npm, à Docker Hub ni à Sigstore Fulcio.
Période de support, mises à jour de sécurité et SBOM
| Période de support | 5 ans à compter de la date de publication de chaque version mineure. Il s’agit de la période de support au sens de l’article 13, paragraphe 8, du Cyber Resilience Act (règlement (UE) 2024/2847) ; elle est attachée à la version publiée, et non à votre abonnement. |
| Correctifs de sécurité | Gratuits, toujours, et jamais réservés à une formule. Les correctifs sont livrés vers l’avant, sous forme de nouvelle version aux mêmes URL, couverte par le même SHA256SUMS signé. Vous utilisez une version dépassée ? Mettez à jour pour obtenir le correctif. |
| Signaler une vulnérabilité | security@frem.sh : divulgation coordonnée, détails sur www.frem.sh/security. |
| SBOM | CycloneDX JSON à l’adresse cli.frem.sh/cli/SBOM.cdx.json, et par version à cli.frem.sh/cli/v<version>/SBOM.cdx.json. |
| Conditions contractuelles | CGU §7.5 : licence, garantie et responsabilité propres au binaire. |
Le SBOM est court, et c’est voulu. La CLI ne déclare aucune dépendance npm tierce.
Elle a deux dépendances, toutes deux maison : @fremverk/audit-chain, le même code de chaîne
de hachage que celui qu’exécute l’API (partagé plutôt que réimplémenté, pour que audit-verify ne
puisse pas diverger de ce qu’il vérifie), et @fremforge/tlog-verify, qui vérifie l’inclusion dans
le journal de transparence pour attest-verify. Aucune des deux n’a de dépendance propre ; toutes
deux n’importent que des modules intégrés de Node, si bien que toute la surface tierce du binaire
livré se limite au runtime Bun embarqué à la compilation. Depuis la v0.3.1, le SBOM liste aussi les
deux paquets maison, marqués comme tels, afin de nommer chaque composant et pas seulement le composant tiers.
Le hash du SBOM figure dans SHA256SUMS : il est donc couvert par la même signature Ed25519 que
les binaires. Vérifiez-le de la même manière (voir §« Vérifier la signature Ed25519 »
ci-dessous ; cela s’applique à chaque fichier listé dans le manifeste, pas seulement aux binaires) :
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 -La CLI est facultative, à une exception près. Chaque action qu’elle effectue est disponible via
l’API REST : si votre politique interdit d’exécuter des binaires de fournisseurs, vous ne perdez
aucune fonctionnalité en vous en passant. L’exception est audit-verify, qui recalcule votre chaîne de hachage
d’audit sur votre machine : une API ne peut pas le faire pour vous, puisque tout l’intérêt de
recalculer une chaîne de hachage est de ne pas faire confiance à celui qui l’a produite. Les lignes brutes sont servies
par GET /orgs/{org}/audit/chain, vous pouvez donc faire le calcul avec vos propres
outils ; ce que vous ne pouvez pas faire, c’est nous le confier et appeler le résultat une vérification.
Installation
En une ligne (recommandé)
curl -sSfL https://cli.frem.sh/cli/install.sh | shL’installateur détecte automatiquement votre système (Linux ou macOS) et votre architecture (arm64 ou x86_64), télécharge le binaire correspondant depuis https://cli.frem.sh/cli/, télécharge le SHA256SUMS de référence depuis https://trust.frem.sh/ (un hôte distinct, avec un identifiant d’écriture distinct, de sorte qu’une compromission du seul hôte des binaires ne permet pas de falsifier une correspondance), vérifie le SHA-256 et installe dans ~/.local/bin/fremforge ou /usr/local/bin/fremforge.
Changez l’emplacement d’installation avec FREMFORGE_INSTALL_DIR :
curl -sSfL https://cli.frem.sh/cli/install.sh | FREMFORGE_INSTALL_DIR=/opt/fremforge/bin shÉpinglez une version précise :
curl -sSfL https://cli.frem.sh/cli/install.sh | FREMFORGE_VERSION=0.1.0 shTéléchargement manuel
Si vous préférez ne pas rediriger un script distant vers sh, les binaires sont accessibles directement. Choisissez la bonne combinaison :
| Système | Architecture | 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 |
Le suffixe d’architecture est x64 (et non x86_64) ou arm64 : il correspond aux jetons --target de Bun. uname -m renvoie x86_64 sur Intel/AMD ; remplacez-le donc par x64 si vous construisez l’URL à la main (l’installateur en une ligne le fait pour vous).
Vérifiez le binaire par rapport au hash de référence publié sur trust.frem.sh avant de l’exécuter :
# 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/fremforgeVérifier la signature Ed25519 (facultatif, recommandé)
Au-delà de la correspondance SHA-256, chaque version est signée sous forme de Statement in-toto enveloppé dans un DSSE, sous la même racine de confiance que la provenance de build SLSA de fremforge. Le vérificateur télécharge la racine de confiance publique depuis www.frem.sh/.well-known/slsa-trust-root.json et contrôle la signature Ed25519 des octets de SHA256SUMS.
N’utilisez pas
slsa-verifier verify-artifact. Cette sous-commande n’a pas d’option--trusted-root, et elle ne sait lire ni le JSON de racine de confiance de fremforge ni une enveloppe DSSE Ed25519 brute : elle attend un bundle de type Sigstore. Cette page affichait autrefois une telle commande ; elle n’aurait jamais pu fonctionner. Vérifiez plutôt directement, avec des outils que votre distribution fournit déjà.
SHA256SUMS.intoto.jsonl est au format JSON Lines : une enveloppe DSSE par binaire publié. Vérifiez
chaque ligne qui vous intéresse.
# 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.jsonlChaque enveloppe vérifiée affiche tous les artefacts qu’elle couvre et le condensat que fremforge a signé pour chacun. Comparez ce condensat avec le binaire que vous avez téléchargé : c’est cette comparaison qui compte, la signature vous dit seulement que le condensat vient bien de nous.
La racine de confiance est la même que celle utilisée pour la provenance de build des artefacts produits par votre runner : même clé Ed25519, même builderId, même procédure de vérification. Nous n’annonçons pas de niveau SLSA ; la page sécurité explique pourquoi.
Authentification
La CLI accepte trois modes d’identification, examinés dans cet ordre : l’option --token=…, la variable d’environnement FREMFORGE_TOKEN, puis la connexion enregistrée dans ~/.config/fremforge/auth.json.
Recommandé : connexion par le navigateur (rien à coller)
fremforge auth loginLa commande ouvre votre navigateur sur frem.sh, vous approuvez l’appareil, le jeton est enregistré dans ~/.config/fremforge/auth.json (mode 600) et chaque appel suivant le charge automatiquement. C’est le même parcours OAuth2 + PKCE que celui qu’utilise déjà votre git push : révoquer le jeton sur frem.sh/user/settings/applications révoque donc en une seule fois l’accès git et l’accès CLI.
fremforge auth status # show current login + expiry
fremforge auth logout # revoke + delete the local fileLe jeton fonctionne à la fois sur l’API REST de fremforge (/_app/api/v1/orgs/…) ET sur l’API native de Forgejo (/api/v1/repos/…) : Forgejo accepte nativement ses propres jetons porteurs OAuth, et l’API fremforge les valide par introspection auprès de /api/v1/user de Forgejo. Même identité, sur les deux surfaces.
CI / usage scripté : PAT
Pour les contextes non interactifs (jobs CI, scripts, agents), créez un PAT avec des scopes restreints. Le plus simple est de passer par la CLI elle-même, une fois connecté de façon interactive (fremforge auth tokens create --name=… --scopes=… ; voir Créer un PAT restreint pour la CI), ou d’en créer un sur https://frem.sh/user/settings/applications. Ensuite :
export FREMFORGE_TOKEN='<your PAT>'
fremforge whoamiChangez l’URL de base avec FREMFORGE_URL= (HTTPS obligatoire) ; par défaut, c’est https://frem.sh.
Commandes
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.Lancez fremforge --help pour la référence officielle, ou ajoutez --human à toute commande qui le prend en charge pour obtenir une sortie sous forme de tableau.
Exemples commentés
Vérifiez vous-même votre chaîne d’audit (utilisable en CI)
audit-verify ne nous demande pas si votre journal d’audit est intact. Il télécharge les lignes brutes,
recalcule chaque hash et chaque lien sur votre machine, et seulement ensuite compare son résultat
au nôtre. Le code de sortie est votre résultat, pas notre affirmation ; et si les deux divergent, cette
divergence est elle-même signalée et la commande échoue.
Cette distinction est la raison d’être de la commande. Jusqu’en août 2026, elle lisait un champ
integrity_status dans une réponse de l’API et le traduisait en code de sortie : la partie auditée
calculait la réponse, et l’outil de « vérification » se contentait de la relayer. Rien de ce que vous
pouviez exécuter n’aurait détecté une API compromise ou malhonnête. Désormais, si.
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 --humanLire les résultats honnêtement, car un outil de vérification qui surestime ce qu’il fait est pire que pas d’outil du tout :
partialn’est pas une réussite. Cela signifie qu’une troncature a masqué une partie de la chaîne : les lignes hors de la fenêtre ne sont pas vérifiées. Dans ce cas, nous répondonsok, et la CLI, délibérément, non.- Les lignes effacées et caviardées ne se re-hachent pas, par conception. L’effacement RGPD et le nettoyage des
contenus à 90 jours réécrivent le contenu des lignes après coup ; ces lignes ne correspondent donc plus à leur propre hash.
Elles sont comptées à part (
tenant_erased_count,payload_redacted_count) et ne relèvent pas d’une falsification. Leurs liens, eux, sont toujours vérifiés. - L’ancre WORM est signée, et la CLI la vérifie par rapport à une racine de confiance qu’elle récupère
elle-même.
audit-verifytéléchargewww.frem.sh/.well-known/slsa-trust-root.jsondirectement (jamais une clé fournie par l’API), vérifie la signature DSSE Ed25519 et confirme que l’enveloppe est bien liée à cette ancre et non à une autre. Le code de sortie 6 signifie que la signature a été vérifiée et a échoué ; traitez-le comme un incident.NOT CHECKED(code de sortie inchangé) signifie soit que l’ancre est antérieure à la signature (2026-08-04), soit que la racine de confiance était injoignable depuis votre machine ; aucun des deux n’est un échec, et la CLI précise lequel.
Pour faire la même chose sans le binaire, lisez les lignes via
GET /orgs/{org}/audit/chain et hachez le JSON canonique de chaque ligne, avec les clés
dans l’ordre action, actor, created_at_iso, fields_json, prev_hash, tenant_id. Gardez fields_json
identique à l’octet près : ne le désérialisez pas pour le resérialiser, sinon vos condensats ne correspondront pas.
Consultez intégrité de la chaîne d’audit pour la garantie complète.
Export de données en libre-service
# Kick off
fremforge data-export start acme | jq .
# Poll
fremforge data-export list acme | jq '.[] | {id, status, created_at}'Consultez export de données pour le format de l’archive, la durée de conservation et le téléchargement par URL signée.
Mises à jour de dépendances (Renovate hébergé)
fremforge dependency-updates enable acme
fremforge dependency-updates run-now acme # 1/h rate-limit appliesConsultez mises à jour de dépendances pour savoir ce qui est pris en charge et comment en restreindre la portée dépôt par dépôt.
Téléchargement de SBOM
fremforge sbom acme/api v1.4.0 --format=spdx > sbom.spdx.json
fremforge sbom acme/api v1.4.0 --format=cyclonedx > sbom.cyclonedx.jsonExtrait du journal d’audit
fremforge audit-log acme --since=2026-05-01 --action=push-protection-override | jq .Exécutions CI et journaux
Listez les exécutions récentes, puis récupérez les journaux complets d’une exécution (tous les jobs concaténés) à partir de son identifiant global :
# 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> est l’identifiant global d’exécution affiché par fremforge runs (le champ .workflow_runs[].id), et non le numéro d’exécution propre au workflow qui figure dans l’URL de l’interface web. Le point de terminaison des journaux renvoie du text/plain avec tous les jobs dans une seule réponse ; inutile de télécharger un artefact par job.
Créer un PAT restreint pour la CI
Après votre premier fremforge auth login, créez des jetons enfants aux scopes restreints pour la CI sans quitter le terminal. Le jeton ne s’affiche qu’une fois : récupérez-le immédiatement.
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>Vous ne pouvez accorder que des scopes que votre propre jeton possède déjà, et le joker (*) ne peut pas être attribué ici.
Gérer les secrets CI
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_TOKENUtilisateurs / membres
fremforge seats list acme --human # cap + usage + roster
fremforge seats add acme --email=jane@acme.example --full-name="Jane Doe"
fremforge seats remove acme janeAjouter un membre au-delà du nombre d’utilisateurs prévu au contrat renvoie une erreur 409 avec un lien vers la facturation : achetez d’abord des utilisateurs supplémentaires.
Format de sortie
Par défaut, chaque commande écrit du JSON sur la sortie standard, facile à exploiter dans un script ou un pipe. Ajoutez --human sur les commandes qui le prennent en charge (audit-verify, attest-verify, release verify, runs, auth tokens list, seats list et work) pour obtenir un résumé mis en forme.
Mettre à jour la CLI
L’installateur en une ligne est idempotent : le relancer récupère le dernier binaire, revérifie la somme de contrôle et remplace l’ancien binaire sur place :
curl -sSfL https://cli.frem.sh/cli/install.sh | shNotes sur la souveraineté
Le canal de distribution de la CLI est entièrement souverain européen :
- Binaires : T Cloud Public OBS (eu-de, Magdebourg / Biere, Allemagne), derrière une pull zone Bunny limitée aux PoP européens.
- SHA256SUMS + enveloppe DSSE : un bucket T Cloud Public OBS distinct (
trust.frem.sh), avec un identifiant d’écriture distinct ; séparer l’hôte des binaires de l’hôte d’intégrité signifie que compromettre l’un ne permet pas de falsifier une correspondance avec l’autre. - Racine de confiance : un fichier JSON statique servi depuis
www.frem.sh(même routage Bunny limité à l’UE). - Aucune dépendance au registre npm : registry.npmjs.com tourne sur AWS aux États-Unis ; la CLI n’y accède jamais, ni à l’installation ni à l’usage.
- Aucune dépendance à Docker Hub : pour la même raison.
- Pas de Sigstore Fulcio / Rekor : le journal de transparence de Sigstore est hébergé aux États-Unis. fremforge utilise son propre signataire Ed25519 et une racine de confiance statique plutôt que de dépendre de Fulcio.
Les sous-traitants intervenant sur le chemin d’installation sont listés dans le registre public des sous-traitants ; ce sont les mêmes que ceux qui couvrent déjà les données personnelles des clients au titre de l’annexe B du DPA.