# Offline Update Kit (OUK) — Air‑Gap Bundle

<!--
  Build‑time variable injection:
    {{ quota_anon }}   = 33
    {{ quota_token }}  = 333
    {{ dotnet }}       = "10 LTS"
-->

The **Offline Update Kit** packages everything Stella Ops needs to run on a
completely isolated network:

| Component | Contents |
|-----------|----------|
| **Merged vulnerability feeds** | OSV, GHSA plus optional NVD 2.0, CNNVD, CNVD, ENISA, JVN and BDU |
| **Container images** | `stella-ops`, *Zastava* sidecar (x86‑64 & arm64) |
| **Provenance** | Cosign signature, SPDX 2.3 SBOM, in‑toto SLSA attestation |
| **Attested manifest** | `offline-manifest.json` + detached JWS covering bundle metadata, signed during export. |
| **Delta patches** | Daily diff bundles keep size \< 350 MB |

**RU BDU note:** ship the official Russian Trusted Root/Sub CA bundle (`certificates/russian_trusted_bundle.pem`) inside the kit so `feedser:httpClients:source.bdu:trustedRootPaths` can resolve it when the service runs in an air‑gapped network. Drop the most recent `vulxml.zip` alongside the kit if operators need a cold-start cache.

*Scanner core:* C# 12 on **.NET {{ dotnet }}**.  
*Imports are idempotent and atomic — no service downtime.*

---

## 1 · Download & verify

```bash
curl -LO https://get.stella-ops.org/ouk/stella-ops-offline-kit-<DATE>.tgz
curl -LO https://get.stella-ops.org/ouk/stella-ops-offline-kit-<DATE>.tgz.sig
curl -LO https://get.stella-ops.org/ouk/offline-manifest-<DATE>.json
curl -LO https://get.stella-ops.org/ouk/offline-manifest-<DATE>.json.jws

cosign verify-blob \
  --key https://stella-ops.org/keys/cosign.pub \
  --signature stella-ops-offline-kit-<DATE>.tgz.sig \
  stella-ops-offline-kit-<DATE>.tgz
````

Verification prints **OK** and the SHA‑256 digest; cross‑check against the
[changelog](https://git.stella-ops.org/stella-ops/offline-kit/-/releases).

Validate the attested manifest before distribution:

```bash
cosign verify-blob \
  --key https://stella-ops.org/keys/cosign.pub \
  --signature offline-manifest-<DATE>.json.jws \
  offline-manifest-<DATE>.json

jq '.artifacts[] | {name, sha256, size, capturedAt}' offline-manifest-<DATE>.json
```

The manifest enumerates every artefact (`name`, `sha256`, `size`, `capturedAt`) and is signed with the same key registry as Authority revocation bundles. Operators can ship the manifest alongside the tarball so downstream mirrors can re-verify without unpacking the kit.

---

## 2 · Import on the air‑gapped host

```bash
docker compose --env-file .env \
  -f docker-compose.stella-ops.yml \
  exec stella-ops \
  stella admin import-offline-usage-kit stella-ops-offline-kit-<DATE>.tgz
```

* The CLI validates the Cosign signature **before** activation.
* Old feeds are kept until the new bundle is fully verified.
* Import time on a SATA SSD: ≈ 25 s for a 300 MB kit.

---

## 3 · Delta patch workflow

1. **Connected site** fetches `stella-ouk-YYYY‑MM‑DD.delta.tgz`.
2. Transfer via any medium (USB, portable disk).
3. `stella admin import-offline-usage-kit <delta>` applies only changed CVE rows & images.

Daily deltas are **< 30 MB**; weekly roll‑up produces a fresh full kit.

---

## 4 · Quota behaviour offline

The scanner enforces the same fair‑use limits offline:

* **Anonymous:** {{ quota\_anon }} scans per UTC day
* **Free JWT:** {{ quota\_token }} scans per UTC day

Soft reminder at 200 scans; throttle above the ceiling but **never block**.
See the detailed rules in
[`33_333_QUOTA_OVERVIEW.md`](33_333_QUOTA_OVERVIEW.md).

---

## 5 · Troubleshooting

| Symptom                                | Explanation                              | Fix                                   |
| -------------------------------------- | ---------------------------------------- | ------------------------------------- |
| `could not verify SBOM hash`           | Bundle corrupted in transit              | Re‑download / re‑copy                 |
| Import hangs at `Applying feeds…`      | Low disk space in `/var/lib/stella`      | Free ≥ 2 GiB before retry             |
| `quota exceeded` same day after import | Import resets counters at UTC 00:00 only | Wait until next UTC day or load a JWT |

---

## 6 · Related documentation

* **Install guide:** `/install/#air-gapped`
* **Sovereign mode rationale:** `/sovereign/`
* **Security policy:** `/security/#reporting-a-vulnerability`
