# Portable Evidence Bundles (Sealed/Air-Gapped) > Sprint 160 · Task EVID-OBS-60-001 > Audience: Evidence Locker operators, Air-Gap controllers, incident responders Portable bundles let operators hand off sealed evidence across enclaves without exposing tenant identifiers or internal storage coordinates. The Evidence Locker produces a deterministic archive (`portable-bundle-v1.tgz`) that carries the manifest + signature alongside redacted metadata, checksum manifest, and an offline verification script. ## 1. When to use the portable flow - **Sealed mode exports.** Regulatory or incident response teams that cannot access the primary enclave directly. - **Chain-of-custody transfers.** Moving evidence into offline review systems while keeping the DSSE provenance intact. - **Break-glass rehearsals.** Validating incident response playbooks without exposing internal bundle metadata. Avoid portable bundles for regular intra-enclave automation; the full `bundle.tgz` already carries richer metadata for automated tooling. ## 2. Generating the bundle 1. Seal the evidence bundle as usual (`POST /evidence/snapshot` or via CLI). 2. Request the portable artefact using the new endpoint: ``` GET /evidence/{bundleId}/portable Scope: evidence:read ``` Response headers mirror the standard download (`application/gzip`, `Content-Disposition: attachment; filename="portable-evidence-bundle-{bundleId}.tgz"`). The Evidence Locker caches the portable archive using write-once semantics. Subsequent requests reuse the existing object and the audit log records whether the file was newly created or served from cache. ## 3. Archive layout ``` portable-bundle-v1.tgz ├── manifest.json # Canonical bundle manifest (identical to sealed bundle) ├── signature.json # DSSE signature + optional RFC3161 timestamp (base64 token) ├── bundle.json # Redacted metadata (bundleId, kind, rootHash, timestamps, incidentMetadata) ├── checksums.txt # Merkle root + per-entry SHA-256 digests ├── instructions-portable.txt # Human-readable guidance for sealed transfers └── verify-offline.sh # POSIX shell helper (extract + checksum verify + reminder to run DSSE verification) ``` Redaction rules: - No tenant identifiers, storage keys, descriptions, or free-form metadata. - Incident metadata is retained *only* under the `incidentMetadata` object (`incident.mode`, `incident.changedAt`, etc.). - `portableGeneratedAt` records when the archive was produced so downstream systems can reason about freshness. ## 4. Offline verification workflow 1. Copy `portable-bundle-v1.tgz` into the sealed environment (USB, sneaker-net, etc.). 2. Run the included helper from a POSIX shell: ```sh chmod +x verify-offline.sh ./verify-offline.sh portable-bundle-v1.tgz ``` The script: - extracts the archive into a temporary directory, - validates `checksums.txt` using `sha256sum` (or `shasum -a 256`), and - prints the Merkle root hash from `bundle.json`. 3. Complete provenance verification: - Preferred: `stella evidence verify --bundle portable-bundle-v1.tgz` - Alternative: supply `manifest.json` and `signature.json` to the evidence verifier library. 4. Record the verification output (root hash, timestamp) with the receiving enclave’s evidence locker or incident ticket. > **Note:** The DSSE payload is unchanged from the sealed bundle, so existing verification tooling does not need special handling for portable archives. ## 5. Importing into the receiving enclave 1. Upload the archive to the target Evidence Locker or attach it to the incident record. 2. Store the checksum report generated by `verify-offline.sh` alongside the archive. 3. If downstream automation needs enriched metadata, attach a private note referencing the original bundle’s tenant context—the portable archive intentionally omits it. ## 6. Troubleshooting | Symptom | Likely cause | Remediation | |--------|--------------|-------------| | `verify-offline.sh` reports checksum failures | Transfer corruption | Re-transfer artefact; run `sha256sum portable-bundle-v1.tgz` on both sides and compare. | | `stella evidence verify` cannot reach TSA | Sealed environment lacks TSA connectivity | Verification still succeeds using DSSE signature; capture the missing TSA warning in the import log. | | `/portable` endpoint returns 400 | Bundle not yet sealed or signature missing | Wait for sealing to complete; ensure DSSE signing is enabled. | | `/portable` returns 404 | Bundle not found or tenant mismatch | Confirm DPoP scope and tenant claim; refresh bundle status via `GET /evidence/{id}`. | ## 7. Change management - Portable bundle versioning is encoded in the filename (`portable-bundle-v1.tgz`). When content or script behaviour changes, bump the version and announce in release notes. - Any updates to `verify-offline.sh` must remain POSIX-sh compatible and avoid external dependencies beyond `tar`, `sha256sum`/`shasum`, and standard coreutils. - Remember to update this guide and the bundle packaging dossier (`docs/modules/evidence-locker/bundle-packaging.md`) when fields or workflows change.