🇬🇧 English · 🇫🇷 Français
Pépin finds the pips in your sovereign cloud.
Every command in that recording really runs. It is regenerated at each release
from tools/demo/quickstart.en.tape, and the preflight refuses to tag a version
the GIF does not show. Its French twin is on README.fr.md.
Pépin is a cloud posture scanner (CSPM) for European sovereign clouds (Exoscale, Outscale, Scaleway). It evaluates a tenant's effective configuration against a common reference anchored on SCSL, SecNumCloud 3.2, CIS Controls v8 and ISO/IEC 27001:2022 / 27017, and produces an opposable result: a typed status per control, its exact normative references, and a sealed evidence bundle.
- Sovereign first: each provider is described in its native vocabulary (Net/Subnet/EIM, BSU/OOS, Kapsule…), never by comparison to a non-sovereign hyperscaler.
- Bilingual, language detected: reports, help and errors come out in French or in English, from the whole tool and not half of it. See Language.
- An opposable result, not just findings: every control is
pass/fail/not-applicable(justified) /not-evaluated(with the reason). Apassis asserted only when the data it needs was actually collected — "no finding" is never mistaken for "compliant". - Two sources: live collection via the provider API, or auditing a
Terraform plan (
terraform show -json) — provisioning nothing. - Evidence bundle:
--sealwrites a timestamped bundle (evaluated inventory, assessment, OSCAL 1.1.2, digested manifest) thatpepin verifyre-checks, with optional cosign signature verification.
Released binaries (checksummed, signed, with SLSA provenance), a container image, a GitHub action and a GitLab template are documented in docs/install.md. From source:
# build (Go 1.26+)
go build -o pepin .
# audit a Terraform plan (no resource provisioned)
terraform plan -out tfplan && terraform show -json tfplan > plan.json
./pepin scan scaleway --terraform plan.json
# live collection (credentials via the environment / native provider config)
./pepin scan outscale --live --region eu-west-2
# sealed evidence bundle + verification
./pepin scan scaleway --terraform plan.json --seal ./bundle
./pepin verify ./bundle --pubkey cosign.pubOutput formats: --format table|json|assessment|oscal|sarif.
Exit codes: 0 compliant · 1 non-compliance · 2 error · 3 the scan does not
establish compliance — nothing measured, or the collection could not read the whole
scope, or, with --strict, remaining medium/low gaps · 4 every critical/high
deviation is covered by a valid exemption (CI-friendly). A scan that collected no
resource never returns 0, and neither does one that could not read part of its
scope: an empty result is not a compliant one, a partial one is not either, and an
exempted one is not either.
Every release artefact is covered. One command each, and nothing to trust on faith.
V=v0.3.0
gh release download "$V" --repo stephrobert/pepin
# 1. the checksums are signed, and they cover the binaries AND the SBOM
cosign verify-blob checksums.txt \
--bundle checksums.txt.cosign.bundle \
--certificate-identity-regexp '^https://github\.com/stephrobert/pepin/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
# 2. your files match those signed checksums
sha256sum --check checksums.txt --ignore-missing
# 3. the binary was built by this repository's release workflow (SLSA provenance)
gh attestation verify pepin-linux-amd64 --repo stephrobert/pepinThe SBOM (sbom.cdx.json, CycloneDX) ships with every release. Step 1 covers the
published file; it is also attested against the binaries, which is a different claim
— that this inventory describes that build:
gh attestation verify pepin-linux-amd64 --repo stephrobert/pepin \
--predicate-type https://cyclonedx.org/bom
# feed it to whatever you already use
osv-scanner --sbom sbom.cdx.jsonPépin speaks French and English, and picks one on its own:
--lang=fr|en → PEPIN_LANG → LC_ALL → LANG → fallback: en
The first non-empty source decides, and any locale Pépin does not speak falls
back to English without an error. The choice applies to everything a human
reads — report, verdict, help, errors — and to the parsable formats: json,
sarif, oscal and assessment carry their prose in the resolved language.
What is stable across languages, and what is not. Codes (CLD-NET-1), check
identifiers, severities (critical…), statuses (pass, fail,
not-applicable, not-evaluated), subjects and exit codes never change with
the language: a pipeline keys on those. Titles, messages, remediations and
evidence are prose and do change. Pin PEPIN_LANG in CI if you diff report
text between runs.
Which one is authoritative. French is the reference language of the
normative content: the reference (referentiel/controles.yaml) and the rules
are written in French first, and the English is their maintained translation.
Where a legal or contractual reading is at stake, the French wording of a
control is the one that governs.
- Quickstart — five minutes, no cloud account: a real failure, its fix, and a second scan that says something different.
- Understanding a scan — one real run, read line by line, down to the exit code.
- The assessment model — what
pass,fail,not-applicable,not-evaluatedandexemptedactually assert. - Coverage matrix — what is measurable, per provider and per source. Generated from the reference and the provider descriptors, and verified in CI.
- Detection quality map — what Pépin can prove about its own
verdicts, and what it cannot. Generated from the veracity ledger, the reference tenants
and the canary records; no figure on it is typed in, and the figures are not flattering.
pepin control explain <code>answers the same question for a single control. - Control catalogue — one generated page per control: what it concludes, from which source, and how to fix what it finds.
- Known limitations — the blind spots, named.
- Scope and non-goals — what a Pépin report is not.
- Terraform plan vs live scan — choosing the source, and the real divergences between the two.
- Providers: Scaleway · Outscale · Exoscale — authentication, API calls, minimal read-only permissions, coverage.
- Reference: CLI · Exit codes · Output formats · Normalized inventory.
- Guides: Remediation · Configuring controls — the policy file, and what a relaxation costs · Evidence bundles · Tracing real API calls — what a recording proves, and what it does not · Reference tenants — third-party configurations replayed on every build, and why a fixture cannot do their job · GitHub Actions · GitLab CI.
- Contributing: Adding a control · Adding a provider · Architecture.
- Install · Roadmap — where the effort goes next.
internal/collect: declarative collection engine (YAML specs → normalized model).providers/*.yaml: a provider descriptor (auth, live collection, Terraform mapping, API contract). One provider = three sources in a single YAML file.internal/commonrules/rules/*.rego: common rules (OPA/Rego) that evaluate the normalized model, provider-independently.referentiel/: the control reference (neutral code → severity, SCSL, norm mappings, providers). Source of truth, tested against invented references.internal/assess: builds the opposable assessment (statuses, evidence, provenance) and the sealed bundle (OSCAL, digests, cosign).
See CONTRIBUTING.md to add a provider or a rule, and SECURITY.md for vulnerability disclosure.
Pépin evaluates the configuration of a tenant (the cloud customer's side). The normative mappings it reports (SecNumCloud, ISO, CIS) are indicative correspondences: a Pépin report is not a proof of qualification or certification — those bear on the cloud service provider, not on a tenant scan.
Apache-2.0: see LICENSE and NOTICE.
The mark and its files are documented in docs/brand.md; the name Pépin and the logo are not covered by the Apache 2.0 licence.