# Sealed Install Enforcement Contract > **Status:** APPROVED > **Version:** 1.0.0 > **Last Updated:** 2025-12-06 > **Owner:** AirGap Controller Guild > **Unblocks:** TASKRUN-AIRGAP-57-001, TASKRUN-AIRGAP-58-001 ## Overview This contract defines the sealed install enforcement semantics for StellaOps air-gapped deployments. When a pack or task declares `sealed_install: true`, the Task Runner MUST refuse to execute if the environment is not properly sealed. ## Architecture ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Sealed Install Enforcement Flow │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Task Pack │ │ Task Runner │ │ AirGap │ │ │ │ │────>│ │────>│ Controller │ │ │ │ sealed_ │ │ Enforcement │ │ │ │ │ │ install:true │ │ Check │ │ /status │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────────────────────────┐ │ │ │ Decision Matrix │ │ │ │ │ │ │ │ Pack: sealed Env: sealed │ │ │ │ ────────────── ──────────── │ │ │ │ true true → RUN │ │ │ │ true false → DENY │ │ │ │ false true → RUN │ │ │ │ false false → RUN │ │ │ └──────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ## 1. Pack Declaration ### 1.1 Sealed Install Flag Packs declare their sealed requirement in the pack manifest: ```json { "pack_id": "compliance-scan-airgap", "version": "1.0.0", "name": "Air-Gap Compliance Scanner", "sealed_install": true, "sealed_requirements": { "min_bundle_version": "2025.10.0", "max_advisory_staleness_hours": 168, "require_time_anchor": true, "allowed_offline_duration_hours": 720 } } ``` ### 1.2 Sealed Requirements Schema ```json { "type": "object", "properties": { "sealed_install": { "type": "boolean", "default": false, "description": "If true, pack MUST run in sealed environment" }, "sealed_requirements": { "type": "object", "properties": { "min_bundle_version": { "type": "string", "description": "Minimum air-gap bundle version" }, "max_advisory_staleness_hours": { "type": "integer", "minimum": 1, "default": 168, "description": "Maximum age of advisory data in hours" }, "require_time_anchor": { "type": "boolean", "default": true, "description": "Require valid time anchor" }, "allowed_offline_duration_hours": { "type": "integer", "minimum": 1, "default": 720, "description": "Maximum allowed offline duration" }, "require_signature_verification": { "type": "boolean", "default": true, "description": "Require bundle signature verification" } } } } } ``` ## 2. Environment Detection ### 2.1 Sealed Mode Status API The Task Runner queries the AirGap Controller to determine sealed status: ``` GET /api/v1/airgap/status ``` Response: ```json { "sealed": true, "mode": "sealed", "sealed_at": "2025-12-01T00:00:00Z", "sealed_by": "ops-admin@company.com", "bundle_version": "2025.10.0", "bundle_digest": "sha256:abc123...", "last_advisory_update": "2025-12-01T00:00:00Z", "advisory_staleness_hours": 120, "time_anchor": { "timestamp": "2025-12-01T00:00:00Z", "signature": "base64...", "valid": true, "expires_at": "2025-12-31T00:00:00Z" }, "egress_blocked": true, "network_policy": "deny-all" } ``` ### 2.2 Detection Heuristics If the AirGap Controller is unavailable, the Task Runner uses fallback heuristics: | Heuristic | Weight | Indicates | |-----------|--------|-----------| | No external DNS resolution | High | Sealed | | Blocked ports 80, 443 | High | Sealed | | AIRGAP_MODE=sealed env var | High | Sealed | | /etc/stellaops/sealed file exists | Medium | Sealed | | No internet connectivity | Medium | Sealed | | Local-only registry configured | Low | Sealed | Combined heuristic score threshold: **0.7** to consider environment sealed. ## 3. Enforcement Logic ### 3.1 Pre-Execution Check ```csharp public sealed class SealedInstallEnforcer { public async Task EnforceAsync( TaskPack pack, CancellationToken ct = default) { // If pack doesn't require sealed install, allow if (!pack.SealedInstall) { return EnforcementResult.Allowed("Pack does not require sealed install"); } // Get environment sealed status var status = await _airgapController.GetStatusAsync(ct); // Core check: environment must be sealed if (!status.Sealed) { return EnforcementResult.Denied( "SEALED_INSTALL_VIOLATION", "Pack requires sealed environment but environment is not sealed", new SealedInstallViolation { PackId = pack.PackId, RequiredSealed = true, ActualSealed = false, Recommendation = "Activate sealed mode with: stella airgap seal" }); } // Check sealed requirements if (pack.SealedRequirements != null) { var violations = ValidateRequirements(pack.SealedRequirements, status); if (violations.Any()) { return EnforcementResult.Denied( "SEALED_REQUIREMENTS_VIOLATION", "Sealed requirements not met", violations); } } return EnforcementResult.Allowed("Sealed install requirements satisfied"); } private List ValidateRequirements( SealedRequirements requirements, SealedModeStatus status) { var violations = new List(); // Bundle version check if (requirements.MinBundleVersion != null) { if (Version.Parse(status.BundleVersion) < Version.Parse(requirements.MinBundleVersion)) { violations.Add(new RequirementViolation { Requirement = "min_bundle_version", Expected = requirements.MinBundleVersion, Actual = status.BundleVersion, Message = $"Bundle version {status.BundleVersion} < required {requirements.MinBundleVersion}" }); } } // Advisory staleness check if (status.AdvisoryStalenessHours > requirements.MaxAdvisoryStalenessHours) { violations.Add(new RequirementViolation { Requirement = "max_advisory_staleness_hours", Expected = requirements.MaxAdvisoryStalenessHours.ToString(), Actual = status.AdvisoryStalenessHours.ToString(), Message = $"Advisory data is {status.AdvisoryStalenessHours}h old, max allowed is {requirements.MaxAdvisoryStalenessHours}h" }); } // Time anchor check if (requirements.RequireTimeAnchor && (status.TimeAnchor == null || !status.TimeAnchor.Valid)) { violations.Add(new RequirementViolation { Requirement = "require_time_anchor", Expected = "valid time anchor", Actual = status.TimeAnchor?.Valid.ToString() ?? "missing", Message = "Valid time anchor required but not present" }); } return violations; } } ``` ### 3.2 Decision Matrix | Pack `sealed_install` | Environment Sealed | Bundle Valid | Advisories Fresh | Result | |-----------------------|-------------------|--------------|------------------|--------| | `true` | `true` | `true` | `true` | ✅ RUN | | `true` | `true` | `true` | `false` | ⚠️ WARN + RUN (if within grace) | | `true` | `true` | `false` | * | ❌ DENY | | `true` | `false` | * | * | ❌ DENY | | `false` | `true` | * | * | ✅ RUN | | `false` | `false` | * | * | ✅ RUN | ### 3.3 Grace Period Handling For advisory staleness, a grace period can be configured: ```yaml # etc/taskrunner.yaml enforcement: sealed_install: staleness_grace_period_hours: 24 staleness_warning_threshold_hours: 120 deny_on_staleness: true # or false for warn-only ``` ## 4. Refusal Semantics ### 4.1 Error Response When enforcement denies execution: ```json { "error": { "code": "SEALED_INSTALL_VIOLATION", "message": "Pack requires sealed environment but environment is not sealed", "details": { "pack_id": "compliance-scan-airgap", "pack_version": "1.0.0", "sealed_install_required": true, "environment_sealed": false, "violations": [], "recommendation": "Activate sealed mode with: stella airgap seal" } }, "status": "rejected", "rejected_at": "2025-12-06T10:00:00Z" } ``` ### 4.2 CLI Exit Codes | Code | Name | Description | |------|------|-------------| | 40 | `SEALED_INSTALL_VIOLATION` | Pack requires sealed but environment is not | | 41 | `BUNDLE_VERSION_VIOLATION` | Bundle version below minimum | | 42 | `ADVISORY_STALENESS_VIOLATION` | Advisory data too stale | | 43 | `TIME_ANCHOR_VIOLATION` | Time anchor missing or invalid | | 44 | `SIGNATURE_VERIFICATION_VIOLATION` | Bundle signature verification failed | ### 4.3 Audit Logging All enforcement decisions are logged: ```json { "event_type": "sealed_install_enforcement", "timestamp": "2025-12-06T10:00:00Z", "pack_id": "compliance-scan-airgap", "pack_version": "1.0.0", "decision": "denied", "reason": "SEALED_INSTALL_VIOLATION", "environment": { "sealed": false, "bundle_version": null, "advisory_staleness_hours": null }, "user": "task-runner-service", "tenant_id": "550e8400-e29b-41d4-a716-446655440000" } ``` ## 5. Integration Points ### 5.1 Task Runner Integration ```csharp // In TaskRunner execution pipeline public async Task ExecuteAsync(TaskPack pack, TaskContext context) { // Pre-execution enforcement var enforcement = await _sealedInstallEnforcer.EnforceAsync(pack); if (!enforcement.Allowed) { await _auditLogger.LogEnforcementDenialAsync(pack, enforcement); return TaskResult.Rejected(enforcement); } // Continue with execution return await _executor.ExecuteAsync(pack, context); } ``` ### 5.2 CLI Integration ```bash # Check sealed status before running pack $ stella pack run compliance-scan-airgap Error: Sealed install violation Pack 'compliance-scan-airgap' requires a sealed environment. Current environment: Sealed: false To resolve: 1. Import an air-gap bundle: stella airgap import 2. Activate sealed mode: stella airgap seal 3. Verify status: stella airgap status Exit code: 40 ``` ## 6. Configuration ### 6.1 Task Runner Configuration ```yaml # etc/taskrunner.yaml enforcement: sealed_install: enabled: true # Staleness handling staleness_grace_period_hours: 24 staleness_warning_threshold_hours: 120 deny_on_staleness: true # Fallback detection use_heuristic_detection: true heuristic_threshold: 0.7 # Logging log_all_decisions: true audit_retention_days: 365 ``` ### 6.2 Environment Variables | Variable | Description | Default | |----------|-------------|---------| | `AIRGAP_MODE` | Force sealed mode detection | — | | `AIRGAP_CONTROLLER_URL` | AirGap controller endpoint | `http://localhost:8080` | | `SEALED_INSTALL_BYPASS` | Bypass enforcement (dev only) | `false` | ## 7. Tasks Unblocked This contract unblocks: | Task ID | Description | Status | |---------|-------------|--------| | TASKRUN-AIRGAP-57-001 | Sealed install enforcement contract | ✅ UNBLOCKED | | TASKRUN-AIRGAP-58-001 | Sealed install CLI integration | ✅ UNBLOCKED | ## 8. Changelog | Date | Version | Change | |------|---------|--------| | 2025-12-06 | 1.0.0 | Initial contract with enforcement logic, decision matrix, CLI integration |