# Sealed Mode Contract (AIRGAP-57) **Contract ID:** `CONTRACT-SEALED-MODE-004` **Version:** 1.0 **Status:** Published **Last Updated:** 2025-12-05 ## Overview This contract defines the sealed-mode operation contract for air-gapped environments. It covers sealing/unsealing state transitions, staleness detection, time anchoring, and egress policy enforcement. ## Implementation References - **Controller:** `src/AirGap/StellaOps.AirGap.Controller/` - **Time:** `src/AirGap/StellaOps.AirGap.Time/` - **Policy:** `src/AirGap/StellaOps.AirGap.Policy/` - **Documentation:** `docs/airgap/sealing-and-egress.md`, `docs/airgap/staleness-and-time.md` ## Data Models ### AirGapState The core sealed-mode state model. ```csharp public sealed record AirGapState { public string Id { get; init; } = "singleton"; public string TenantId { get; init; } = "default"; public bool Sealed { get; init; } = false; public string? PolicyHash { get; init; } = null; public TimeAnchor TimeAnchor { get; init; } = TimeAnchor.Unknown; public DateTimeOffset LastTransitionAt { get; init; } public StalenessBudget StalenessBudget { get; init; } = StalenessBudget.Default; } ``` ### JSON Representation ```json { "id": "singleton", "tenant_id": "default", "sealed": true, "policy_hash": "sha256:...", "time_anchor": { "anchor_time": "2025-12-05T10:00:00Z", "source": "roughtime", "format": "roughtime", "signature_fingerprint": "...", "token_digest": "sha256:..." }, "last_transition_at": "2025-12-05T10:00:00Z", "staleness_budget": { "warning_seconds": 3600, "breach_seconds": 7200 } } ``` ### TimeAnchor Cryptographically verified time reference. ```json { "anchor_time": "2025-12-05T10:00:00Z", "source": "roughtime|rfc3161", "format": "roughtime|rfc3161", "signature_fingerprint": "sha256:...", "token_digest": "sha256:..." } ``` ### StalenessBudget Defines staleness thresholds. ```json { "warning_seconds": 3600, "breach_seconds": 7200 } ``` | Field | Default | Description | |-------|---------|-------------| | `warning_seconds` | 3600 | Warning threshold (1 hour) | | `breach_seconds` | 7200 | Breach threshold (2 hours) | ### StalenessEvaluation Result of staleness calculation. ```json { "age_seconds": 1800, "warning_seconds": 3600, "breach_seconds": 7200, "is_breached": false, "remaining_seconds": 1800 } ``` ## API Endpoints ### Seal Environment ``` POST /system/airgap/seal Content-Type: application/json Authorization: Bearer { "policy_hash": "sha256:...", "time_anchor": { ... }, "staleness_budget": { "warning_seconds": 3600, "breach_seconds": 7200 } } Response: 200 OK { "sealed": true, "last_transition_at": "2025-12-05T10:00:00Z" } ``` ### Unseal Environment ``` POST /system/airgap/unseal Authorization: Bearer Response: 200 OK { "sealed": false, "last_transition_at": "2025-12-05T10:00:00Z" } ``` ### Get Status ``` GET /system/airgap/status Authorization: Bearer Response: 200 OK { "sealed": true, "tenant_id": "default", "staleness": { "age_seconds": 1800, "is_breached": false, "remaining_seconds": 1800 }, "time_anchor": { ... }, "policy_hash": "sha256:..." } ``` ### Verify Bundle ``` POST /system/airgap/verify Content-Type: application/json Authorization: Bearer { "bundle_path": "/path/to/bundle.json", "trust_roots_path": "/path/to/trust-roots.json" } Response: 200 OK { "valid": true, "verification_result": { "dsse_valid": true, "tuf_valid": true, "merkle_valid": true } } ``` ## Egress Policy ### EgressPolicy Model ```csharp public sealed class EgressPolicy { public EgressPolicyMode Mode { get; } // Sealed | Unsealed public IReadOnlyList AllowedHosts { get; } public bool PermitLoopback { get; } public bool PermitPrivateNetworks { get; } } ``` ### EgressRequest / EgressDecision ```json // Request { "component": "excititor", "destination": "https://api.github.com", "intent": "fetch_advisories", "operation": "GET" } // Decision { "allowed": false, "reason": "AIRGAP_EGRESS_BLOCKED", "remediation": "Add api.github.com to allowlist or unseal environment" } ``` ### Enforcement When sealed: - All outbound connections blocked by default - Only allowlisted destinations permitted - Loopback and private networks optionally permitted ## Time Verification ### Roughtime Verification 1. Parse Roughtime response 2. Verify Ed25519 signature against trusted public key 3. Extract anchor time from signed response ### RFC 3161 Verification 1. Parse SignedCms structure 2. Validate TSA certificate chain 3. Extract signing time from timestamp token ## Startup Diagnostics Pre-flight checks when starting in sealed mode: 1. Verify time anchor is present 2. Check staleness budget not breached 3. Validate trust roots are loaded 4. Confirm egress policy is enforced ``` GET /healthz/ready Response: 200 OK (if healthy) Response: 503 Service Unavailable (if sealed mode requirements unmet) ``` ## Telemetry ### Metrics | Metric | Type | Description | |--------|------|-------------| | `airgap_sealed` | gauge | 1 if sealed, 0 if unsealed | | `airgap_anchor_drift_seconds` | gauge | Seconds since time anchor | | `airgap_anchor_expiry_seconds` | gauge | Seconds until staleness breach | | `airgap_seal_total` | counter | Total seal operations | | `airgap_unseal_total` | counter | Total unseal operations | | `airgap_startup_blocked_total` | counter | Blocked startup attempts | ### Structured Logging ```json { "event": "airgap.sealed", "tenant_id": "default", "policy_hash": "sha256:...", "timestamp": "2025-12-05T10:00:00Z" } ``` ## Authority Scopes | Scope | Description | |-------|-------------| | `airgap:seal` | Seal/unseal environment | | `airgap:status:read` | Read sealed status | | `airgap:verify` | Verify bundles | | `airgap:import` | Import bundles | ## Unblocks This contract unblocks the following tasks: - POLICY-AIRGAP-57-001 - POLICY-AIRGAP-57-002 - POLICY-AIRGAP-58-001 ## Related Contracts - [Mirror Bundle Contract](./mirror-bundle.md) - Bundle format for sealed import - [Verification Policy Contract](./verification-policy.md) - Attestation verification