OfflineKit

The consolidated offline-exchange service. One deployable family, two roles, one database.

It absorbed ExportCenter and Mirror whole, and AirGap’s two posture hosts, at SPRINT_20260722_025 (owner-approved EVL-3). The libraries those families contributed keep their original namespaces on purpose — the lift was byte-identical so no consumer using had to change.

RoleImageServes
offlinekit-webstellaops/offlinekit-webthe whole /api/offlinekit/v1 surface: the export plane and the carrier-custody plane
offlinekit-workerstellaops/offlinekit-workerthe carried background loops (export-run executor, risk-bundle jobs, DevPortal offline); the audit-bundle loop is hosted by offlinekit-web; no HTTP surface

Database: stellaops_offlinekit, one role, migrated forward-only on startup from embedded SQL (ADR-004). Schemas: offlinekit (carrier custody and part activation), export_center (the export engine), catalog_replica + eventing (the tenants and environment-state replicas).

Router name: offlinekit. One process publishes one HELLO name, which is why the predecessor’s exportcenter name died with it and every bare /v1/exports path had to be re-prefixed.

The two planes, and why the distinction matters

The host serves two things that look alike and are not:

Capabilities

CapabilityWhere it lives
Export profiles, runs, artifacts, distributionsStellaOps.ExportCenter.Application/Api/, .Core/Persistence/
Assurance exports (CRA, NIS2 SoA, DORA RoI).Application/AssuranceExports/, .Adapters.Cra, .Adapters.Nis2
Audit bundles, incidents, lineage, exception reports.Application/{AuditBundle,Incident,Lineage,ExceptionReport}/
Risk bundles.RiskBundles, .WorkerRuntime/RiskBundleWorker.cs
Attestation and promotion attestation.Application/Attestation/
Mirror bundle assemblyStellaOps.Mirror.Creator, .Application/Mirror/MirrorProfilePlanner.cs
Carrier custody: import, status, manifest, validateStellaOps.OfflineKit.WebService/Imports/, Endpoints/
Generic part activation…/Imports/OfflineKitPartActivation.cs, OfflineKitPartExtractor.cs
Sealed-posture readsStellaOps.AirGap.Policy.Replication over this host’s own replica
Time-anchor verificationStellaOps.AirGap.Time.Verification

The part-activation seam

OfflineKit owns custody; it never owns another domain’s content. An adapter implementing IOfflineKitPartActivator is keyed on the (domain, mediaType, generation) triple that the manifest validates and the parts ledger keys on, and it owns exactly three things: its own payload type, its own bounded schema, and the call to its owner API. It never touches the ledger — an activator that could would be a second custody authority.

The registry refuses to compose when two activators claim one triple. That is deliberate: two owners each believing they activate the same signed content is not a runtime condition to recover from, and whichever the container resolved last would silently win.

A declared part that no registered activator claims is recorded failed with PART_ACTIVATOR_NOT_REGISTERED while the carrier stays verified. Custody and activation are separate verdicts; collapsing them would make a missing adapter look like an untrusted bundle.

Reference adapter: ScannerArtifactBomPartActivator. The Vulnerabilities/VEX adapter is OK-7V and is not built.

Where the detail still lives

This family’s own pages:

These dossiers are retained rather than merged — they hold format, protocol and signing detail that is still current, behind a pointer stub for the topology:

Operator-facing: docs/OFFLINE_KIT.md.

Known gaps, stated rather than implied

Re-measured 2026-09-14 at the close of SPRINT_20260722_025; the earlier entries here (no export-run executor, no owned-state measurer, predecessor database still present) are all false at HEAD and are removed rather than annotated.