# 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: ```json { "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: ```json { "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: ```json { "payloadType": "application/vnd.stellaops.bundle.manifest+json", "payload": "", "signatures": [ { "keyid": "signing-key-001", "sig": "" } ] } ``` ## Creation ### API Endpoint ```http GET /v1/alerts/{alertId}/bundle Authorization: Bearer Response: application/gzip Content-Disposition: attachment; filename="alert-123.stella.bundle.tgz" ``` ### Programmatic ```csharp var packager = services.GetRequiredService(); 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 ```http POST /v1/alerts/{alertId}/bundle/verify Content-Type: application/json { "bundle_hash": "sha256:abc123...", "signature": "" } Response: { "is_valid": true, "hash_valid": true, "chain_valid": true, "signature_valid": true, "verified_at": "2024-12-15T10:00:00Z" } ``` ### Programmatic ```csharp 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 ```bash # 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: ```yaml 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](../../export-center/registry-compatibility.md) 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: ```bash 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](../../../contracts/function-map-v1.md) 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 | ## Related Documentation - [Evidence Bundle Envelope](./evidence-bundle-envelope.md) - [DSSE Signing Guide](./dsse-signing.md) - [Offline Kit Guide](../OFFLINE_KIT.md) - [API Reference](../api/evidence-decision-api.openapi.yaml)