Risk Bundles (Air-Gap)
Audience: operators and release engineers running Stella Ops in offline or air-gapped environments.
Purpose: build, verify, and import deterministic, signed risk-intelligence bundles without network connectivity.
Risk bundles package vulnerability-risk intelligence (CISA KEV, FIRST EPSS, OpenSSF OSV) into deterministic, signed archives. Once verified against their manifest and signatures, they can be imported into a sealed Stella Ops deployment with no external feed access — preserving the platform’s offline-first, supply-chain-integrity posture.
Bundle Structure
A risk bundle is a gzip-compressed tar archive (risk-bundle.tar.gz) with the following structure:
risk-bundle.tar.gz
├── manifests/
│ └── provider-manifest.json # Bundle metadata and provider entries
├── providers/
│ ├── cisa-kev/
│ │ └── snapshot # CISA Known Exploited Vulnerabilities JSON
│ ├── first-epss/
│ │ └── snapshot # FIRST EPSS scores CSV/JSON
│ └── osv/ # (optional) OpenSSF OSV bulk JSON
│ └── snapshot
└── signatures/
└── provider-manifest.dsse # DSSE envelope for manifest
Provider Manifest
The provider-manifest.json contains bundle metadata and per-provider entries:
{
"version": "1.0.0",
"bundleId": "risk-bundle-20241211-120000",
"createdAt": "2024-12-11T12:00:00Z",
"inputsHash": "sha256:abc123...",
"providers": [
{
"providerId": "cisa-kev",
"digest": "sha256:def456...",
"snapshotDate": "2024-12-11T00:00:00Z",
"optional": false
},
{
"providerId": "first-epss",
"digest": "sha256:789abc...",
"snapshotDate": "2024-12-11T00:00:00Z",
"optional": true
}
]
}
| Field | Description |
|---|---|
version | Manifest schema version (currently 1.0.0) |
bundleId | Unique identifier for this bundle |
createdAt | ISO-8601 UTC timestamp of bundle creation |
inputsHash | SHA-256 hash of concatenated provider digests (deterministic ordering) |
providers[] | Array of provider entries sorted by providerId |
Provider Entry Fields
| Field | Description |
|---|---|
providerId | Provider identifier (cisa-kev, first-epss, osv) |
digest | SHA-256 hash of snapshot file (sha256:<hex>) |
snapshotDate | ISO-8601 timestamp of provider data snapshot |
optional | Whether the provider is required for bundle validity |
Provider Catalog
| Provider | Source | Coverage | Refresh | Required |
|---|---|---|---|---|
cisa-kev | CISA Known Exploited Vulnerabilities | Exploited CVEs with KEV flag | Daily | Yes |
first-epss | FIRST EPSS scores | Exploitation probability per CVE | Daily | No |
osv | OpenSSF OSV | OSS advisories with affected ranges | Weekly | No (opt-in) |
Building Risk Bundles
Using the Export Worker
The Export Center worker can build risk bundles via the stella export risk-bundle job:
# Build bundle with default providers (CISA KEV + EPSS)
stella export risk-bundle --output /path/to/output
# Include OSV providers (larger bundle)
stella export risk-bundle --output /path/to/output --include-osv
# Build with a specific bundle ID
stella export risk-bundle --output /path/to/output --bundle-id "custom-bundle-id"
Using the CI Build Script
For CI pipelines and deterministic testing, use the shell scripts:
# Build fixture bundle for CI testing (deterministic)
ops/devops/risk-bundle/build-bundle.sh --output /tmp/bundle --fixtures-only
# Build with OSV
ops/devops/risk-bundle/build-bundle.sh --output /tmp/bundle --fixtures-only --include-osv
# Build with a custom bundle ID
ops/devops/risk-bundle/build-bundle.sh --output /tmp/bundle --fixtures-only --bundle-id "ci-test-bundle"
Build Script Options
| Option | Description |
|---|---|
--output <dir> | Output directory for bundle artifacts (required) |
--fixtures-only | Use fixture data instead of live provider downloads |
--include-osv | Include OSV providers (increases bundle size) |
--bundle-id <id> | Custom bundle ID (default: auto-generated with timestamp) |
Build Outputs
After building, the output directory contains:
output/
├── risk-bundle.tar.gz # The bundle archive
├── risk-bundle.tar.gz.sha256 # SHA-256 checksum
└── manifest.json # Copy of provider-manifest.json
Verifying Risk Bundles
Using the CLI
# Basic verification
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz
# With detached signature
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --signature-path ./bundle.sig
# Check Sigstore Rekor transparency log
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --check-rekor
# JSON output for automation
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --json
# Verbose output with warnings
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --verbose
CLI Options
| Option | Description |
|---|---|
--bundle-path, -b | Path to risk bundle file (required) |
--signature-path, -s | Path to detached signature file |
--check-rekor | Verify transparency log entry in Sigstore Rekor |
--json | Output results as JSON |
--tenant | Tenant context for verification |
--verbose | Show detailed output including warnings |
Using the Verification Script
For offline/air-gap verification without the CLI:
# Basic verification
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz
# With detached signature
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz --signature /path/to/bundle.sig
# Strict mode (warnings are errors)
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz --strict
# JSON output
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz --json
Verification Steps
The verification process performs these checks:
- Archive integrity — bundle is a valid tar.gz archive.
- Structure validation — required files present (
manifests/provider-manifest.json). - Manifest parsing — valid JSON with required fields (
bundleId,version,providers). - Provider hash verification — each provider snapshot matches its declared digest.
- Mandatory provider check —
cisa-kevmust be present and valid. - DSSE signature validation — manifest signature verified (if present).
- Detached signature — bundle archive signature verified (if provided).
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Bundle is valid |
| 1 | Bundle is invalid or verification failed |
| 2 | Input error (missing file, bad arguments) |
JSON Output Format
{
"valid": true,
"bundleId": "risk-bundle-20241211-120000",
"version": "1.0.0",
"providerCount": 2,
"mandatoryProviderFound": true,
"errorCount": 0,
"warningCount": 1,
"errors": [],
"warnings": ["Optional provider not found: osv"]
}
Importing Risk Bundles
Prerequisites
- Verify the bundle before import (see above).
- Ensure the target system has sufficient storage.
- Back up existing provider data if replacing.
Import Steps
- Transfer the bundle to the air-gapped environment via approved media.
- Verify the bundle using the CLI or verification script.
- Extract to staging:
mkdir -p /staging/risk-bundle tar -xzf risk-bundle.tar.gz -C /staging/risk-bundle - Validate provider data:
# Verify individual provider hashes sha256sum /staging/risk-bundle/providers/cisa-kev/snapshot sha256sum /staging/risk-bundle/providers/first-epss/snapshot - Import into Concelier:
stella concelier import-risk-bundle --path /staging/risk-bundle
Error Handling
| Error | Cause | Resolution |
|---|---|---|
| “Bundle is not a valid tar.gz archive” | Corrupted download/transfer | Re-download and verify checksum |
| “Missing required file: manifests/provider-manifest.json” | Incomplete bundle | Rebuild bundle |
| “Missing mandatory provider: cisa-kev” | KEV snapshot missing | Rebuild with valid provider data |
| “Hash mismatch: cisa-kev” | Corrupted provider data | Re-download provider snapshot |
| “DSSE signature validation failed” | Tampered manifest | Investigate chain of custody |
CI/CD Integration
Gitea Workflow
The risk-bundle-ci workflow:
- Build job — compiles the RiskBundles library, runs tests, builds a fixture bundle.
- Offline kit job — packages the bundle for offline kit distribution.
- Publish checksums job — publishes checksums to the artifact store (main branch only).
# Trigger manually or on push to relevant paths
on:
push:
paths:
- 'src/ExportCenter/StellaOps.ExportCenter.RiskBundles/**'
- 'ops/devops/risk-bundle/**'
workflow_dispatch:
inputs:
include_osv:
type: boolean
default: false
Offline Kit Integration
Risk bundles are included in the Offline Update Kit:
offline-kit/
└── risk-bundles/
├── risk-bundle.tar.gz
├── risk-bundle.tar.gz.sha256
├── manifest.json
├── checksums.txt
└── kit-manifest.json
The kit-manifest.json provides metadata for offline kit consumers:
{
"component": "risk-bundle",
"version": "20241211-120000",
"files": [
{"path": "risk-bundle.tar.gz", "checksum_file": "risk-bundle.tar.gz.sha256"},
{"path": "manifest.json", "checksum_file": "manifest.json.sha256"}
],
"verification": {
"checksums": "checksums.txt",
"signature": "risk-bundle.tar.gz.sig"
}
}
Signing and Trust
DSSE Manifest Signature
The signatures/provider-manifest.dsse file contains a Dead Simple Signing Envelope (DSSE):
{
"payloadType": "application/vnd.stellaops.risk-bundle.manifest+json",
"payload": "<base64-encoded-manifest>",
"signatures": [
{
"keyid": "risk-bundle-signing-key",
"sig": "<signature>"
}
]
}
Offline Trust Roots
For air-gapped verification, include the public keys in the bundle:
signatures/
├── provider-manifest.dsse
└── pubkeys/
└── <tenant>.pem
Sigstore/Rekor Integration
When --check-rekor is specified, verification queries the Sigstore Rekor transparency log to confirm the bundle was published to the public ledger. In sealed mode this step is skipped unless an offline Rekor checkpoint is supplied.
Determinism Checklist
Risk bundles are designed for reproducible builds:
- [x] Fixed timestamps for tar entries (
--mtime="@<epoch>") - [x] Sorted file ordering (
--sort=name) - [x] Numeric owner/group (
--owner=0 --group=0 --numeric-owner) - [x] Deterministic gzip compression (
gzip -n) - [x] Providers sorted by
providerIdin manifest - [x] Files sorted lexicographically in bundle
- [x] UTF-8 canonical paths
- [x] ISO-8601 UTC timestamps
Troubleshooting
Q: Bundle verification fails with “jq not available”.
A: The verification script uses jq for JSON parsing. Install it, or use the CLI (stella risk bundle verify), which has built-in JSON support.
Q: Hash mismatch after transfer.
A: Binary transfers can corrupt files. Use checksums:
# On source system
sha256sum risk-bundle.tar.gz > checksum.txt
# On target system
sha256sum -c checksum.txt
Q: “Optional provider not found” warning.
A: This is informational. Optional providers (EPSS, OSV) enhance risk analysis but aren’t required. Use --strict if you want to enforce their presence.
Q: DSSE signature validation fails in air-gap.
A: Ensure the offline trust root is configured:
stella config set risk-bundle.trust-root /path/to/pubkey.pem
Related Documentation
- Offline Update Kit — complete offline kit documentation
- Mirror Bundles — OCI artifact bundles for air-gap
- EPSS Bundles — EPSS-specific bundle workflows
- Risk-Bundle Provider Matrix — detailed provider specifications
- Export Center Architecture — export service design
