Files
git.stella-ops.org/docs/modules/airgap/guides/offline-bundle-format.md
2026-01-28 02:30:48 +02:00

10 KiB

Offline Bundle Format (.stella.bundle.tgz)

Sprint: SPRINT_3603_0001_0001
Module: ExportCenter

This document describes the .stella.bundle.tgz format for portable, signed, verifiable evidence packages.

Overview

The offline bundle is a self-contained archive containing all evidence and artifacts needed for offline triage of security findings. Bundles are:

  • Portable: Single file that can be transferred to air-gapped environments
  • Signed: DSSE-signed manifest for authenticity verification
  • Verifiable: Content-addressable with SHA-256 hashes for integrity
  • Complete: Contains all data needed for offline decision-making

File Format

{alert-id}.stella.bundle.tgz
├── manifest.json                    # Bundle manifest (DSSE-signed)
├── metadata/
│   ├── alert.json                   # Alert metadata snapshot
│   └── generation-info.json         # Bundle generation metadata
├── evidence/
│   ├── reachability-proof.json      # Call-graph reachability evidence
│   ├── callstack.json               # Exploitability call stacks
│   └── provenance.json              # Build provenance attestations
├── vex/
│   ├── decisions.ndjson             # VEX decision history (NDJSON)
│   └── current-status.json          # Current VEX status
├── sbom/
│   ├── current.cdx.json             # Current SBOM slice (CycloneDX)
│   └── baseline.cdx.json            # Baseline SBOM for diff
├── diff/
│   └── sbom-delta.json              # SBOM delta changes
└── attestations/
    ├── bundle.dsse.json             # DSSE envelope for bundle
    └── evidence.dsse.json           # Evidence attestation chain

Manifest Schema

The manifest.json file follows this schema:

{
  "bundle_format_version": "1.0.0",
  "bundle_id": "abc123def456...",
  "alert_id": "alert-789",
  "created_at": "2024-12-15T10:00:00Z",
  "created_by": "user@example.com",
  "stellaops_version": "1.5.0",
  "entries": [
    {
      "path": "metadata/alert.json",
      "hash": "sha256:...",
      "size": 1234,
      "content_type": "application/json"
    }
  ],
  "root_hash": "sha256:...",
  "signature": {
    "algorithm": "ES256",
    "key_id": "signing-key-001",
    "value": "..."
  }
}

Manifest Fields

Field Type Required Description
bundle_format_version string Yes Format version (semver)
bundle_id string Yes Unique bundle identifier
alert_id string Yes Source alert identifier
created_at ISO 8601 Yes Bundle creation timestamp (UTC)
created_by string Yes Actor who created the bundle
stellaops_version string Yes StellaOps version that created bundle
entries array Yes List of content entries with hashes
root_hash string Yes Merkle root of all entry hashes
signature object No DSSE signature (if signed)

Entry Schema

Each entry in the manifest:

{
  "path": "evidence/reachability-proof.json",
  "hash": "sha256:abc123...",
  "size": 2048,
  "content_type": "application/json",
  "compression": null
}

DSSE Signing

Bundles support DSSE (Dead Simple Signing Envelope) signing:

{
  "payloadType": "application/vnd.stellaops.bundle.manifest+json",
  "payload": "<base64-encoded manifest>",
  "signatures": [
    {
      "keyid": "signing-key-001",
      "sig": "<base64-encoded signature>"
    }
  ]
}

Creation

API Endpoint

GET /v1/alerts/{alertId}/bundle
Authorization: Bearer <token>

Response: application/gzip
Content-Disposition: attachment; filename="alert-123.stella.bundle.tgz"

Programmatic

var packager = services.GetRequiredService<IOfflineBundlePackager>();

var result = await packager.CreateBundleAsync(new BundleRequest
{
    AlertId = "alert-123",
    ActorId = "user@example.com",
    IncludeVexHistory = true,
    IncludeSbomSlice = true
});

// result.Content contains the tarball stream
// result.ManifestHash contains the verification hash

Verification

API Endpoint

POST /v1/alerts/{alertId}/bundle/verify
Content-Type: application/json

{
  "bundle_hash": "sha256:abc123...",
  "signature": "<optional DSSE signature>"
}

Response:
{
  "is_valid": true,
  "hash_valid": true,
  "chain_valid": true,
  "signature_valid": true,
  "verified_at": "2024-12-15T10:00:00Z"
}

Programmatic

var verification = await packager.VerifyBundleAsync(
    bundlePath: "/path/to/bundle.stella.bundle.tgz",
    expectedHash: "sha256:abc123...");

if (!verification.IsValid)
{
    Console.WriteLine($"Verification failed: {string.Join(", ", verification.Errors)}");
}

CLI Usage

# Export bundle
stellaops alert bundle export --alert-id alert-123 --output ./bundles/

# Verify bundle
stellaops alert bundle verify --file ./bundles/alert-123.stella.bundle.tgz

# Import bundle (air-gapped instance)
stellaops alert bundle import --file ./bundles/alert-123.stella.bundle.tgz

OCI Referrer Artifacts

Mirror bundles automatically include OCI referrer artifacts (SBOMs, attestations, signatures) discovered from container registries. These artifacts are stored under a dedicated referrers/ directory keyed by subject image digest.

Referrer Directory Structure

bundle.stella.bundle.tgz
├── ...existing structure...
├── referrers/
│   └── sha256-abc123.../                # Subject image digest
│       ├── sha256-def456.json           # CycloneDX SBOM
│       ├── sha256-ghi789.json           # in-toto attestation
│       └── sha256-jkl012.json           # VEX statement
└── indexes/
    ├── referrers.index.json             # Referrer artifact index
    └── attestations.index.json          # Attestation cross-reference

Manifest Referrers Section

The bundle manifest includes a referrers section documenting all discovered artifacts:

referrers:
  subjects:
    - subject: "sha256:abc123..."
      artifacts:
        - digest: "sha256:def456..."
          artifactType: "application/vnd.cyclonedx+json"
          mediaType: "application/vnd.oci.image.manifest.v1+json"
          size: 12345
          path: "referrers/sha256-abc123.../sha256-def456.json"
          sha256: "def456789..."
          category: "sbom"
          annotations:
            org.opencontainers.image.created: "2026-01-27T10:00:00Z"
        - digest: "sha256:ghi789..."
          artifactType: "application/vnd.in-toto+json"
          mediaType: "application/vnd.oci.image.manifest.v1+json"
          size: 8192
          path: "referrers/sha256-abc123.../sha256-ghi789.json"
          sha256: "ghi789abc..."
          category: "attestation"

Referrer Validation

The ImportValidator verifies referrer artifacts during bundle import:

Validation Severity Description
ReferrerMissing Error Declared artifact not found in bundle
ReferrerChecksumMismatch Error SHA-256 doesn't match declared value
ReferrerSizeMismatch Error Size doesn't match declared value
OrphanedReferrer Warning File exists in referrers/ but not declared

Artifact Types

Artifact Type Category Description
application/vnd.cyclonedx+json sbom CycloneDX SBOM
application/vnd.spdx+json sbom SPDX SBOM
application/vnd.openvex+json vex OpenVEX statement
application/vnd.csaf+json vex CSAF advisory
application/vnd.in-toto+json attestation in-toto attestation
application/vnd.dsse.envelope+json attestation DSSE envelope
application/vnd.slsa.provenance+json attestation SLSA provenance
application/vnd.stella.rva+json attestation RVA attestation

Registry Compatibility

Referrer discovery supports both OCI 1.1 native API and fallback tag-based discovery:

  • OCI 1.1+: Uses native /v2/{repo}/referrers/{digest} endpoint
  • OCI 1.0 (fallback): Discovers via sha256-{digest}.* tag pattern

See Registry Compatibility Matrix for per-registry details.

Function Map Artifacts

Bundles can include runtime linkage verification artifacts. These are stored in dedicated subdirectories:

bundle.stella.bundle.tgz
├── ...existing structure...
├── function-maps/
│   ├── {service}-function-map.json
│   └── {service}-function-map.dsse.json
├── observations/
│   └── {date-label}-observations.ndjson
└── verification/
    ├── verification-report.json
    └── verification-report.dsse.json

Artifact Types

Artifact Type Media Type Description
function-map application/vnd.stella.function-map+json Function map predicate
function-map.dsse application/vnd.dsse+json DSSE-signed function map
observations application/x-ndjson Runtime observations (NDJSON)
verification-report application/vnd.stella.verification-report+json Verification result
verification-report.dsse application/vnd.dsse+json DSSE-signed verification report

Offline Verification Workflow

In air-gapped environments:

  1. Export the bundle with function map and observations included
  2. Transfer to the air-gapped instance
  3. Run offline verification:
    stella function-map verify \
      --function-map ./function-maps/my-service-function-map.json \
      --offline --observations ./observations/2026-01-23-observations.ndjson
    

See Function Map V1 Contract for the predicate schema specification.


Security Considerations

  1. Hash Verification: Always verify bundle hash before processing
  2. Signature Validation: Verify DSSE signature if present
  3. Content Validation: Validate JSON schemas after extraction
  4. Size Limits: Enforce maximum bundle size limits (default: 100MB)
  5. Path Traversal: Tarball extraction must prevent path traversal attacks

Versioning

Format Version Changes Min StellaOps Version
1.0.0 Initial format 1.0.0