Zum Hauptinhalt springen

Export-Schema

Diese Seite ist das Schema für den vollständigen Datenexport: was das Bundle enthält und welche Form jeder Datensatz hat. Wie du einen Export startest, ihn prüfst und welche Grenzen gelten, steht auf der Seite zum Datenexport. §9 des Auftragsverarbeitungsvertrags verweist hierher.

Aufbau des Bundles

export-<your-org-slug>-<YYYY-MM-DDTHH-MM-SS-mmmZ>.tar.gz
├── VERIFY.md                          — step-by-step verification recipe
├── manifest.json                      — every file below, SHA-256 and size
├── manifest.json.intoto.jsonl         — DSSE-signed in-toto Statement over manifest.json
├── audit.ndjson                       — your org's audit events, last 90 days
├── access-log.ndjson                  — your registry access log (package and container downloads, uploads and deletes; CI cache hits are in pulled-versions.ndjson)
├── bundles/
│   ├── <repo>.bundle                  — git bundle: full history, every ref
│   └── <repo>.empty                   — instead of a bundle, for a repository with no commits (zero bytes)
├── findings/                          — scan findings, see "Scan findings, policy, attestations and SBOMs" below
├── policy/                            — package policy, verdicts, exceptions and scanner settings
├── attestations/                      — build provenance and release attestations, with their signatures
├── sboms/                             — repository and container-image SBOMs, with an index
└── metadata/
    ├── packages.ndjson                — package inventory, see "Packages" below
    ├── packages-files.ndjson          — every file of every package version
    ├── packages-container-tags.ndjson — container image tag → manifest digest
    ├── components.ndjson              — third-party component inventory, see "Component inventory" below
    ├── deployments.ndjson             — deployments your CI/CD reported, see "Deployments" below
    ├── pulled-versions.ndjson         — package versions pulled per day and consumer, see "Pulled versions" below
    └── <repo>/
        ├── repo.ndjson                — repository settings, description, topics
        ├── issues.ndjson              — every issue, open and closed
        ├── pulls.ndjson               — every pull request, open and closed
        ├── comments.ndjson            — issue and pull-request comments
        ├── labels.ndjson
        ├── milestones.ndjson          — open and closed
        ├── releases.ndjson            — release metadata; see "Release assets" below
        └── lfs-manifest.ndjson        — LFS object inventory, see "LFS content" below

In Repository-Namen werden Zeichen außerhalb von a-z 0-9 . _ - durch _ ersetzt.

Verwendete Standards:

  • Repositorys: das native Format git bundle von Git. git clone <bundle> stellt das Repository vollständig wieder her, einschließlich Historie, Branches und Tags. Ein Repository ohne Commits hat nichts zu bündeln und erscheint stattdessen als leere Datei <repo>.empty.
  • Issues, Pull Requests und die übrigen Dateien in metadata/: NDJSON, ein JSON-Objekt pro Zeile, in derselben Form, in der die Forgejo-API jedes Objekt zurückgibt.
  • Audit-Log: NDJSON, ein Ereignis pro Zeile (id, action, actor, fields_json, created_at). Dieselbe Form wie der interne Audit-Stream von fremforge.
  • Registry-Zugriffslog: NDJSON, älteste zuerst, ein Ereignis pro Zeile in der Form, die GET /orgs/{slug}/access-log zurückgibt, mit Client-IPs und Logins so, wie sie erfasst wurden. Es umfasst alles, was innerhalb der Aufbewahrungsfrist deines Audit-Logs noch gespeichert ist. Siehe Registry-Zugriffslog.
  • Integrität: manifest.json führt jede Datei mit ihrem SHA-256-Hash und ihrer Größe auf. Das Manifest ist mit dem Ed25519-Builder-Schlüssel von fremforge DSSE-signiert. Die Signatur liegt in manifest.json.intoto.jsonl und lässt sich mit openssl, jq, python3 und curl gegen die öffentliche Vertrauensbasis unter https://www.frem.sh/.well-known/slsa-trust-root.json prüfen, ganz ohne Werkzeuge von fremforge. Die vollständige Anleitung steht in VERIFY.md im Bundle und in §„Das signierte Manifest prüfen“ auf der Seite zum Datenexport. Es ist dieselbe Vertrauensbasis, die für SLSA-Build-Attestierungen veröffentlicht wird: ein Schlüssel, zwei Prädikattypen, keine Abhängigkeit von Sigstore.

Keine proprietären Formate. Jedes Artefakt im Bundle lässt sich mit gängigen Open-Source-Werkzeugen lesen. Eine CLI von fremforge ist nicht nötig.

Pakete

metadata/packages.ndjson ist das Inventar der Paket-Registry deiner Organisation: eine Zeile pro Paketversion, mit ihrem type (container, npm, maven, pypi, generic, …), name und version, so wie die Paket-API von Forgejo sie auflistet.

metadata/packages-files.ndjson führt jede Datei jeder dieser Versionen auf, eine Zeile pro Datei:

FeldBedeutung
owner, type, name, versiondie Paketversion, zu der die Datei gehört
file_id, file_nameKennung und Name der Datei in der Registry
sizeGröße in Bytes
sha256SHA-256 der Datei; zusätzlich sha512, wo die Registry einen erfasst
download_pathPfad auf frem.sh, der genau diese Datei mit einem API-Token (Authorization: token …) herunterlädt; die Registry kann mit einer Weiterleitung zum Objektspeicher antworten. null für Registry-Typen, deren Datei-URL sich nicht allein aus dem Inventar ableiten lässt (zum Beispiel Maven); verwende für diese web_path
web_pathder Download-Link der Datei auf ihrer Paketseite, für einen angemeldeten Browser

Bei Container-Images ist eine Paketversion ein Tag (oder, bei einem Manifest ohne Tag wie einer Plattform eines Multi-Arch-Images, der Digest selbst). metadata/packages-container-tags.ndjson ordnet jedem Tag seinen Manifest-Digest zu (owner, name, tag, manifest_digest), den docker pull <image>@sha256:… und andere OCI-Werkzeuge verwenden. Die Layer und die Konfiguration des Images erscheinen in packages-files.ndjson als Dateien, die nach ihrem Digest benannt sind; download_path zeigt auf die Endpunkte /v2/…/blobs/… und /v2/…/manifests/… der Registry.

Eine heruntergeladene Datei gegen das Inventar prüfen:

row=$(jq -c 'select(.name=="@acme/lib" and .version=="1.2.0")' metadata/packages-files.ndjson)
curl -sSL -H "Authorization: token $TOKEN" -o pkg.bin "https://frem.sh$(jq -r .download_path <<<"$row")"
echo "$(jq -r .sha256 <<<"$row")  pkg.bin" | sha256sum -c -

Das Inventar ist immer im Bundle. Die Paketdateien selbst kommen nur auf Wunsch, als separates Archiv: siehe §„Paketinhalte“.

Komponenteninventar

metadata/components.ndjson ist das Inventar der Komponenten von Drittanbietern deiner Organisation: eine Zeile pro Komponente und Ort, an dem sie verwendet wird oder wurde, dieselben Zeilen, die GET /orgs/{slug}/components/usages liefert. Beendete Verwendungen werden 180 Tage aufbewahrt und sind enthalten, sodass die Historie mit dir geht.

FeldBedeutung
purl, ecosystem, name, versiondie Komponente, als Package-URL und ihre Bestandteile
licence, licence_sourceder Lizenzausdruck und woher er gelesen wurde
subject_kind, subject_name, subject_refwo sie verwendet wird: das Repository, Paket oder Image und die Ref oder der Digest
image_tagsbei einem Image die Tags, die darauf zeigen; sonst leer
source_repodas Repository, das sie gebaut hat, soweit bekannt
is_directtrue für eine direkte Abhängigkeit, false für eine transitive, null, wo die SBOM es nicht angibt
environment, environment_sourcedie Umgebungsklasse (sandbox, development, test oder production) und wie sie gesetzt wurde
is_current, first_seen_at, last_seen_at, removed_atob die Verwendung aktuell ist und wann sie zuerst und zuletzt gesehen und entfernt wurde

Deployments

metadata/deployments.ndjson enthält jedes Deployment, das deine CI/CD mit POST /orgs/{slug}/repos/{repo}/deployments gemeldet hat, eine Zeile pro Deployment, aufsteigend nach Kennung.

FeldBedeutung
iddie Kennung des Deployments
repository, environment, environment_classdas Repository (owner/name), der von dir vergebene Umgebungsname und seine Klasse (sandbox, development, test, staging oder production)
ref, sha, task, descriptionRef und Commit, die deployt wurden, die Aufgabe (deploy, sofern du keine andere angegeben hast) und deine Beschreibung
image_digests, packagesdie Image-Digests und Package-URLs, die das Deployment nennt
stateder letzte Status: pending, queued, in_progress, success, failure, error oder inactive
creator_kind, creator, run_idwer es gemeldet hat (job, robot, user oder token), dessen Name und bei einem Job der Actions-Run
created_at, updated_atwann es gemeldet und zuletzt geändert wurde
statusesjeder Status, neueste zuerst: state, description, log_url, environment_url, creator_kind, creator, created_at

Gezogene Versionen

metadata/pulled-versions.ndjson ist der Pull-Zensus: eine Zeile pro UTC-Tag, Paketversion und Verbraucher, für die Tage, die innerhalb der Aufbewahrungsfrist deines Zugriffslogs noch gespeichert sind. Seit dem 13.10.2026 ist er die einzige Aufzeichnung der Downloads, die ein CI-Job über den Abhängigkeits-Proxy gemacht hat und die aus dessen Cache bedient wurden; diese stehen nicht in access-log.ndjson. Es sind die Zeilen, die GET /orgs/{slug}/components/pulled aufsummiert; die Advisories, die dieser Endpunkt hinzufügt, sind nicht in der Datei.

FeldBedeutung
dayder UTC-Tag, YYYY-MM-DD
originregistry für deine eigenen Pakete, proxy für den Abhängigkeits-Proxy
ecosystem, name, versionPakettyp (npm, pypi, maven, nuget, go, container, …), Name und Version
consumerwer es gezogen hat: kind (job, robot, bot, user oder anonymous), name (das Repository des Jobs, der Name des Robots oder der Login; null bei anonym), id, bei einem Robot sein login und bei einem Job run_ids, die Actions-Runs, die es an diesem Tag gezogen haben (neueste zuerst, höchstens 100; leer vor dem 13.10.2026)
pullserfolgreiche Downloads an diesem Tag
first_seen_at, last_seen_atder erste und der letzte davon, UTC mit Mikrosekunden

Scan-Befunde, Richtlinie, Attestierungen und SBOMs

Jeder Export enthält die Sicherheitsdaten, die fremforge über deine Organisation führt, als NDJSON (ein JSON-Objekt pro Zeile), wie jede andere Datei vom signierten Manifest abgedeckt. Jede Zeile ist der vollständige Datensatz, den fremforge speichert, ohne die interne Mandanten-ID und Speicherpfade; verworfene Befunde sind enthalten, mit Angabe, wer sie verworfen hat, warum und bis wann. Zeilen, die ein Registry-Paket betreffen, tragen ein Objekt package (type, owner, name, version, file, blob_sha256).

findings/
├── sast.ndjson                        — static analysis (rule, file, line, severity)
├── dependencies.ndjson                — dependency vulnerabilities, with CVSS and EPSS where known
├── dependency-dismissals.ndjson       — repository-wide dependency dismissals
├── licences.ndjson                    — licence findings, each with its licence policy decision
├── container-images.ndjson            — container image vulnerabilities
├── container-image-dismissals.ndjson
├── secrets.ndjson                     — rule, file, line and fingerprint; the secret value is never stored
├── malware-uploads.ndjson             — malware found in uploads
├── malware-dependencies.ndjson        — known-malicious dependencies
├── malware-container-layers.ndjson    — per image manifest, each layer's malware verdict
├── packages.ndjson                    — vulnerabilities found in your registry packages
├── package-licences.ndjson            — declared and detected licences of your registry packages
└── sha-pinning.ndjson                 — workflow actions not pinned to a commit
policy/
├── package-policy.ndjson              — your package policy (mode, thresholds, licence lists, minimum age)
├── package-policy-rules.ndjson        — allow and deny rules
├── package-policy-approvers.ndjson
├── package-policy-verdicts.ndjson     — the current verdict for each package file, with reasons
├── package-gate-decisions.ndjson      — downloads blocked, or that would have been blocked in shadow mode
├── exceptions.ndjson                  — policy exceptions, who requested and who approved them
├── malware-overrides.ndjson
├── package-access-packages.ndjson     — per-package access: each configured package (mode, inheritance)
├── package-access-grants.ndjson       — per-package access grants (teams, Actions repositories)
├── package-protection-rules.ndjson    — protected package names
├── scan-overrides.ndjson              — per-repository scanner mode overrides
└── settings/                          — each scanner's org settings (sast, dependencies, licences, container-images, secrets),
                                         and licence-repo-overrides.ndjson: per-repository licence policy overrides
                                         and package-access.ndjson: the per-package access default
                                         and retention.ndjson: one line per evidence retention class, with what you set,
                                         what applies, the default, the bounds and whether a legal hold is active
attestations/
├── build-provenance.ndjson            — one line per build provenance attestation
├── build-provenance/<id>.intoto.jsonl — its DSSE envelope (the Ed25519 signature)
├── build-provenance/<id>.sigstore.json — its keyless Sigstore bundle, where one was made
├── releases.ndjson                    — release attestations: subjects, the frozen release manifest, status, and envelope_path
├── sbom-ingest.ndjson                 — attestations uploaded together with an SBOM
└── sbom-ingest/<sbom id>.intoto.jsonl
sboms/
├── index.ndjson                       — one line per SBOM, see below
├── repos/<id>.cdx.json                — a repository SBOM, CycloneDX (<id>.spdx.json for SPDX)
└── images/<id>.cdx.json               — a container image SBOM
  • Signaturen reisen mit den Attestierungen. Jeder Umschlag lässt sich gegen dieselbe Vertrauensbasis prüfen wie das Bundle, mit dem Skript aus §„Das signierte Manifest prüfen“, das du auf den Umschlag statt auf manifest.json.intoto.jsonl richtest (lass die Prüfungen von manifestSha256 und predicateType weg, die nur für Export-Bundles gelten). Die Sigstore-Bundles sind gegen fremforges eigenes Fulcio und die eigene Zeitstempelstelle signiert, nicht gegen das öffentliche Sigstore; siehe SLSA-Provenienz.
  • Eine fehlende Signatur wird gezeigt, nicht versteckt. Eine Zeile mit "envelope_status": "missing" ist eine Attestierung, deren Signaturobjekt beim Export nicht mehr im Speicher lag. Jeder andere Speicherfehler lässt den Export fehlschlagen.
  • Container-Layer. Das Malware-Urteil eines Layers gehört zu seinem Inhalt, daher wird ein Layer, den ein anderes Image mitnutzt, mit seinem Urteil unter jedem deiner Manifeste aufgeführt, das ihn verwendet. Images, die über den Abhängigkeits-Proxy gezogen wurden, sind nur aufgeführt, solange deine Organisation das ClamAV-Scanning von Proxy-Abrufen eingeschaltet hat; ist es aus (der Standard), werden sie nicht mit ClamAV gescannt und haben keine Layer-Urteile.
  • Release-Attestierungen gibt es für Repositorys mit unveränderlichen Releases. Ein Release, dessen status nicht signed ist, hat keinen Umschlag (envelope_path ist null).
  • SBOMs. sboms/ enthält jede SBOM, die fremforge für deine Repositorys (Release- und Default-Branch-Builds, ob hochgeladen oder vom zentralen Scan erstellt) und für deine Container-Images (das Inventar der Registry pro Manifest) speichert, als das Dokument selbst. Ein Repository-Build hat meist sowohl ein CycloneDX- als auch ein SPDX-Dokument; siehe SBOMs. Jede Zeile von sboms/index.ndjson ist der gespeicherte SBOM-Datensatz (id, subject_kind repo oder image, format, repo_full_name oder artifact_name, release_tag, image_tags, manifest_digest, created_at, sha256_digest und der Rest des gespeicherten Datensatzes, ohne Speicherpfade) plus drei Felder zur Datei in diesem Bundle: document_path, document_sha256 und document_status. Letzteres ist ok, wenn das Dokument mit dem beim Speichern erfassten SHA-256 übereinstimmt, digest_mismatch, wenn nicht (die Bytes werden trotzdem exportiert, und beide Digests stehen in der Zeile), und missing, wenn das Dokument nicht mehr im Speicher lag. Jeder andere Speicherfehler lässt den Export fehlschlagen.
  • Paket-SBOMs sind nicht in sboms/. Die SBOM jeder Datei eines Registry-Pakets liegt neben der Datei im optionalen Archiv der Paketinhalte.