AirGap architecture (sealed and offline operation)

Verified against commit 942a18727c (2026-09-26). Re-verify with: grep -rln 'AddAirGapPostureReplica\|PostureReplication' src --include=*.cs | grep -v '__Obsoleted\|Tests' and pwsh ./tools/scripts/test-targeted-xunit.ps1 -Project src/__Libraries/StellaOps.AirGap.Policy/StellaOps.AirGap.Policy.Replication.Tests/StellaOps.AirGap.Policy.Replication.Tests.csproj -Class "*AirGapPostureReplicaTests". src/ is the source of truth; where this page and the code disagree, the code wins.

This page describes how Stella Ops represents and enforces sealed (air-gapped) operation at this commit. There is no AirGap service. The predecessor hosts StellaOps.AirGap.Controller (/system/airgap/{status,seal,unseal,verify}) and StellaOps.AirGap.Time (/api/v1/time/*), their airgap schema and their EF Core persistence were withdrawn under SPRINT_20260722_025 OK-5 and are frozen at src/__Obsoleted/AirGap/. Their contract is archived at docs-archive/modules/airgap-controller/.

1. Principles

2. Components

ComponentPathRole
Environment-state custodiansrc/Platform/StellaOps.Platform.WebService/Endpoints/EnvironmentStateEndpoints.cs, Services/EnvironmentStateStore.csPUT/GET /api/v1/platform/environment-state/{stateClass}/{scope}; registered classes airgap-seal and time-anchor; an unregistered class cannot be written; documents are capped at 256 KiB
Posture interpretersrc/__Libraries/StellaOps.AirGap.Policy/StellaOps.AirGap.Policy/Posture/AirGapSealDocument, AirGapTimeAnchorDocument, the interpreters, AirGapPostureReading, AirGapTimeAnchorProofVerifier
Staleness math…/StellaOps.AirGap.Policy/Time/StalenessBudget, StalenessEvaluation, StalenessCalculator
Egress policy…/StellaOps.AirGap.Policy/IEgressPolicy, EgressPolicyMode (Unsealed advisory, Sealed enforced), allow rules; bound from AirGap:Egress by AddAirGapEgressPolicy
Egress analyzer…/StellaOps.AirGap.Policy.Analyzers/Roslyn analyzer AIRGAP001 (HttpClientUsageAnalyzer) flags raw HttpClient use that bypasses the egress policy
Posture replica…/StellaOps.AirGap.Policy.Replication/AirGapPostureReplica.csAddAirGapPostureReplica; section Catalog:Replication:EnvironmentState (default off)
RFC3161 verificationsrc/OfflineKit/__Libraries/StellaOps.AirGap.Time.Verification/Rfc3161Verifier (signature, chain, OCSP/CRL revocation evidence)
Carrier envelope and trust materialsrc/OfflineKit/__Libraries/StellaOps.AirGap.Bundle/OfflineKitCarrierManifest/Writer/Signing, TrustProfileLoader, TrustSnapshotImporter, retained manifest models
Client-side verificationsrc/OfflineKit/__Libraries/StellaOps.AirGap.Importer/DsseVerifier, RekorOfflineReceiptVerifier, VersionMonotonicityChecker, FileSystemQuarantineService, OfflineVerificationPolicyLoader, Reconciliation/
Enforcement facesrc/Policy/StellaOps.Policy.Engine/Endpoints//system/airgap/{seal,unseal,status,verify}, /system/airgap/staleness/*, /system/airgap/notifications/*

Hosts that compose the posture replica at this commit: Platform, Policy Engine, Notify worker, OfflineKit web, Authority (Airgap/AuthorityPostureReplication.cs), Signals (Hosting/SignalsPostureReplication.cs) and the Vulnerabilities worker (VulnerabilitiesWorkerPostureRegistration.cs).

3. The posture documents

3.1 airgap-seal

AirGapSealDocument (schemaVersion 1): sealed, reason, sealedAt, policyHash, an optional embedded timeAnchor, a default stalenessBudget and optional per-content contentBudgets. stella airgap seal and stella airgap unseal write it (default scope installation, --dry-run, --force, --reason); stella airgap status reads it back.

Write authorization: policy platform.environment_state.airgap-seal.write, satisfied by envstate:airgap-seal:write or the legacy airgap:seal. Read: platform.environment_state.read, satisfied by envstate:read or the legacy airgap:status:read.

3.2 time-anchor

AirGapTimeAnchorDocument (schemaVersion 1, format rfc3161): anchorTime, source, signatureFingerprint, tokenDigest, the proof (the token itself), verifiedAt, revocationChecked and a stalenessBudget. stella airgap time-anchor declare --token <file> --trust-roots <pem> verifies the token with Rfc3161Verifier before it declares the document; stella airgap time-anchor status reads it and, with --trust-roots, re-verifies the stored proof. Write authorization: envstate:time-anchor:write only (no legacy alias).

A consumer does not trust the declared fields on their own: AirGapTimeAnchorProofVerifier re-verifies the stored proof against the public trust bundle registered for the purpose timestamp-authority (Crypto:PublicTrust:CertificateBundles:timestamp-authority). The TimeTokenFormat enum still names Roughtime, but no Roughtime verifier exists at this commit; only RFC3161 tokens are verified.

3.3 Staleness

AirGapPostureReading.EvaluateStaleness(now, contentClass) returns Unknown when no anchor is declared (deliberately distinct from Fresh), Fresh below the warning budget or for a future-dated anchor, Warning at or above warningSeconds, and Breach at or above breachSeconds. The budget is the content-class entry of contentBudgets when present, otherwise the document default (3600 s warning, 7200 s breach). See guides/staleness-and-time.md.

4. Consumers

4.1 OfflineKit import posture gate

OfflineKitImportPostureGate (src/OfflineKit/StellaOps.OfflineKit.WebService/Imports/) reads the posture replica before custody: not declared or unsealed admits, unreadable admits with a logged warning (a replica outage must not strand a sealed estate’s only supply path), sealed requires a trusted time anchor within budget and answers 409 otherwise. The time-anchor trust bundle is mounted on offlinekit-web at /app/etc/offlinekit/time-anchor-trust-roots.pem and bound through appsettings.Production.json (devops/release/bundle/config/etc/offlinekit/, devops/etc/offlinekit/). Detail: ../offlinekit/architecture.md§5.

4.2 Policy Engine enforcement face

The /system/airgap/* paths the withdrawn controller served are mapped today by Policy Engine (SealedModeEndpoints.cs, StalenessEndpoints.cs, AirGapNotificationEndpoints.cs). Its /status answers from the local replica; /seal and /unseal declare posture through Platform; /verify verifies a policy pack or risk-profile bundle and requires airgap:seal. This is Policy Engine’s contract, not the archived controller specification.

4.3 Egress

Hosts register the egress policy with AddAirGapEgressPolicy(configuration, sectionName: "AirGap") or the default section AirGap:Egress. In Sealed mode an outbound request without a matching allow rule raises AirGapEgressBlockedException. Operator-side verification that the container network is closed: guides/sealing-and-egress.md.

5. Offline content exchange

The one supported exchange envelope is the OfflineKit carrier (OfflineKitCarrierManifest, DC-18/DC-21). Export runs write carriers, stella airgap pull --run <id> downloads one and stella airgap import submits it to POST /api/offlinekit/v1/import, which verifies both DSSE envelopes against the operator trust key, applies the posture gate, takes custody and dispatches parts to their owning services. The CLI’s client-side checks (stella offline import --verify-dsse/--verify-rekor, stella verify offline) verify and admit nothing. Withdrawn dialects: the knowledge-snapshot bundle and export-archive validators (src/__Obsoleted/OfflineKit/__Libraries/StellaOps.AirGap.{Bundle,Importer}/) and the HLC job-sync bundle (…/StellaOps.AirGap.Sync/).

The release installer’s offline vulnerability-data seed (devops/release/bundle/install.sh, step 8b; SPRINT_20260925_004 DAY-7) does not use this path: it posts the seed directly to the Vulnerabilities hub’s owner route, authenticating as the stellaops-offlinekit-activator client.

6. Trust material shipped offline

7. Observability

StellaOps.AirGap.Importer/Telemetry/OfflineKitMetrics.cs defines meter StellaOps.AirGap.Importer (offlinekit_import_total, attestation-verify latency and related instruments). At this commit no production host outside the library references it. The controller’s airgap_seal_total / airgap_time_anchor_age_seconds instruments were withdrawn with the host. OfflineKit’s Doctor checks (doctor.offlinekit.posture.not-stored-here and others) are listed in ../offlinekit/architecture.md§2.

8. Tests

9. Design record