OfflineKit

Verified against commit 942a18727c (2026-09-26). Re-verify with: grep -n 'Map[A-Za-z]*Endpoints\|MapGet\|MapPost\|MapGroup' src/OfflineKit/StellaOps.OfflineKit.WebService/Program.cs, ls src/OfflineKit/__Libraries/StellaOps.OfflineKit.Persistence/Migrations/ and grep -n 'offlinekit' devops/release/bundle/docker-compose.yml.

The estate-wide service, database, compose and bundle wiring is generated in the service inventory; this dossier describes the module and defers to that inventory for the service list.

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

It absorbed the Export Center service (hosts frozen at src/__Obsoleted/ExportCenter/) and the Mirror creator at SPRINT_20260722_025 (owner-approved EVL-3), and it holds the surviving AirGap exchange libraries. The AirGap posture hosts were withdrawn rather than absorbed: posture custody moved to Platform (platform.environment_state); see AirGap. The libraries the predecessors 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 (export plane and carrier-custody plane), /doctor/offlinekit/checks, /health/ready, /openapi/v1.json; hosts the audit-bundle loop
offlinekit-workerstellaops/offlinekit-workerthe export-run executor, risk-bundle jobs and the DevPortal-offline job; HTTP only for /health/liveness and /health/readiness
offlinekit-exportcenter-initstellaops/offlinekit-web (one-shot)creates /var/lib/stella/exportcenter on the shared export volume and hands it to uid 10001; both roles wait for it to complete

Database: stellaops_offlinekit, one role, connection only from STELLAOPS_POSTGRES_OFFLINEKIT_CONNECTION (fail closed), migrated forward-only on startup from embedded SQL (ADR-004). Schemas: offlinekit (carrier custody, part activation, migration ledger), export_center (the export engine), export_center_app (tenant-isolation functions), catalog_replica + eventing (the tenants and environment-state replicas).

Volumes: offlinekit-data (import custody under /var/lib/stella/offlinekit-import) and exportcenter-object-store-data (export runs and bundles under /var/lib/stella/exportcenter).

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 was re-prefixed under /api/offlinekit.

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/, .Application/RunExecution/, .Core/Persistence/
Assurance exports (CRA, NIS2 SoA, NIS2 effectiveness, DORA).Application/AssuranceExports/, .Application/Nis2Soa/, .Adapters.Cra, .Adapters.Nis2, .Adapters.StandardsMapping
Audit bundles, incidents, lineage, exception reports, simulation exports.Application/{AuditBundle,Incident,Lineage,ExceptionReport,SimulationExport}/
Risk bundles.RiskBundles, .Application/RiskBundle/, .WorkerRuntime
Attestation and promotion attestation.Application/Attestation/
Mirror bundle production (a profile kind, DC-22).Core/MirrorBundle/MirrorBundleBuilder.cs, .Application/RunExecution/MirrorExportRunMaterializer.cs, .Application/Mirror/
DevPortal offline bundle.Core/DevPortalOffline/, job on offlinekit-worker
Carrier custody: import, status, manifest, validate, part re-activationStellaOps.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 old StellaOps.Mirror.Creator library (project StellaOps.Mirror.Creator.Core) and its two shell scripts were withdrawn on 2026-09-26 to src/__Obsoleted/OfflineKit/__Libraries/StellaOps.Mirror.Creator/. Its only service failed closed and nothing outside its own tests used it. Mirror bundles are the mirror export profile kind.

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. 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.

Registered adapters: ScannerArtifactBomPartActivator (Scanner artifact BOMs) and VulnerabilityMirrorSeedPartActivator (Vulnerabilities mirror seeds, OK-7V). Both authenticate as the stellaops-offlinekit-activator client; details in architecture.md§5.

Changes recorded 2026-09-14 to 2026-09-26

Where the detail lives

Retained format records, behind pointer stubs for the retired topology:

Operator-facing: docs/OFFLINE_KIT.md.

Known gaps, stated rather than implied