Export Center Mirror Bundles

Mirror bundles package StellaOps evidence, policy overlays, and indexes for air-gapped or bandwidth-constrained environments. They are produced by the mirror:full and mirror:delta profiles described in Epic 10 (Export Center) and implemented across Sprints 35-37 (EXPORT-SVC-35-004, EXPORT-SVC-37-001, EXPORT-SVC-37-002). This guide details bundle layouts, delta mechanics, encryption workflow, import procedures, and operational best practices.

Status (verified against commit 942a18727c, 2026-09-26). Mirror production is a profile kind (ExportProfileKind.Mirror = 5, DC-22) executed by MirrorExportRunMaterializer on offlinekit-worker, which writes mirror-<runId>.tar.gz through src/OfflineKit/__Libraries/StellaOps.ExportCenter.Core/MirrorBundle/MirrorBundleBuilder.cs. §2 below is the layout that builder writes. The delta summary (§3) is paths-verified only. The design-era encryption workflow and offline-kit/bin/mirror import tool were removed from this page: see §4 for what exists. When the worker has a carrier signing key, the run also writes an OfflineKit carrier that stella airgap pull / stella airgap import move to another estate.

1. Bundle overview

ProfileContentsTypical use casesDistribution
mirror:fullComplete snapshot of raw evidence, normalized records, indexes, policy snapshots, provenance, signatures.Initial seeding of an air-gapped mirror, disaster recovery drills.Download bundle, optional OCI artifact.
mirror:deltaChanges since a specified base export: added/updated/removed advisories, VEX statements, SBOMs, indexes, manifests.Incremental updates, bandwidth reduction, nightly refreshes.Download bundle, optional OCI artifact.

Raw source data and policy outputs live under separate data/ subdirectories.

2. Filesystem layout

Entries written by MirrorBundleBuilder, in order (paths relative to the archive root; there is no enclosing mirror/ directory):

manifest.yaml
export.json
provenance.json
checksums.txt
README.md
verify-mirror.sh                 # executable; validates checksums.txt
indexes/
  advisories.index.json
  vex.index.json
  sbom.index.json
  findings.index.json
  attestations.index.json
  referrers.index.json
data/
  raw/...                        # raw source records; SBOMs under data/raw/sboms/[<subject>/]
  normalized/...                 # normalized source records
  policy/snapshot.json
  policy/<evaluations>
  consensus/<vex consensus>
  findings/<findings>
  attestations/<attestations>
  referrers/<OCI referrer artifacts>
delta/removed/                   # delta variant only
  advisories.jsonl
  vex.jsonl
  sboms.jsonl

The archive is gzip-compressed PAX tar with a deterministic gzip header. manifest.yaml summarises profile metadata, selectors, counts, sizes and SHA-256 digests; export.json and provenance.json are the JSON manifests (signing: MirrorBundleSigning.cs). There is no signatures/ directory.

Example manifest.yaml:

profile: mirror:full
runId: run-20251029-01
tenant: acme
selectors:
  products:
    - registry.example.com/app:*
  timeWindow:
    from: 2025-10-01T00:00:00Z
    to: 2025-10-29T00:00:00Z
counts:
  advisories: 15234
  vex: 3045
  sboms: 872
  policyEvaluations: 19876
artifacts:
  - path: data/raw/advisories/a0.jsonl.zst
    sha256: 9f4b...
    bytes: 7340021

3. Delta mechanics

Delta bundles reference a previous full or delta run via baseExportId and baseManifestDigest. They contain:

delta/
  changed/
    data/raw/advisories/*.jsonl.zst
    ...
  removed/
    advisories.jsonl        # list of advisory IDs removed
    vex.jsonl
    sboms.jsonl
  manifest.diff.json        # summary of counts, hashes, base export metadata

Example manifest.diff.json (delta):

{
  "baseExportId": "run-20251025-01",
  "baseManifestDigest": "sha256:aa11...",
  "resetBaseline": false,
  "added": {
    "advisories": 43,
    "vex": 12,
    "sboms": 5
  },
  "changed": {
    "advisories": 18,
    "vex": 7
  },
  "removed": {
    "advisories": 2,
    "vex": 0,
    "sboms": 0
  }
}

4. Moving a mirror bundle

Encryption: MirrorBundleBuilder records an encryption block (mode, strict, recipients) in manifest.yaml when a request asks for it, and the envelope library exists (StellaOps.ExportCenter.Core/Encryption/, …/Crypto/Encryption/, pattern in operations/kms-envelope-pattern.md). At this commit MirrorExportRunMaterializer requests no encryption and no production host registers an age or KMS key wrapper, so mirror runs are written unencrypted. There is no offline-kit/bin/mirror import tool.

5. Troubleshooting

SymptomMeaningAction
Run fails before writing a bundle with a refused sourcea sourceUri that is neither an absolute path nor a file:// URIfix the profile scope (../offlinekit/architecture.md§4)
Run completes but writes no carrierno carrier signing key id on the worker (EXPORT_RUN_CARRIER_NOT_WRITTEN)set Export:Carrier:SigningKeyId
verify-mirror.sh reports a checksum mismatchfiles changed after extraction or transfer damagere-download and re-verify; do not import

6. References