Sealed Install Enforcement Contract
Status: SUPERSEDED — NOT IMPLEMENTED AS WRITTEN Version: 1.0.0 (historical) Last Updated: 2025-12-06 (last reconciled against source 2026-05-30) Owner: AirGap Controller Guild Originally Unblocked: TASKRUN-AIRGAP-57-001, TASKRUN-AIRGAP-58-001 (both in the now-deleted TaskRunner service)
[!WARNING] Reconciliation note (2026-05-30). This contract was authored against a
TaskRunnerservice that has since been deleted from the monorepo. There is nosrc/TaskRunner/directory, andStellaOps.Auth.Abstractions/StellaOpsScopes.cs(lines 755-758) records that thetaskrunner:*scopes were “REMOVED (service deleted, constants kept for DB/migration backward compat).” The archived sprintdocs-archive/implplan/SPRINT_0157_0001_0001_taskrunner_i.mdshows tasks TASKRUN-AIRGAP-57-001/58-001 were marked DONE insidesrc/TaskRunner/StellaOps.TaskRunner, but that code no longer ships.Concretely, none of the following exist anywhere in
src/: asealed_installpack-manifest flag, aSealedInstallEnforcerclass, aSealedRequirements/SealedInstallViolationtype, or the error codes / exit codes / env vars described below. The pseudo-code, JSON schemas, decision matrices, and CLI flows in this document are aspirational / historical and do not reflect shipped behaviour.What does ship today (see §8):
- AirGap Controller —
GET/POST /system/airgap/{status,seal,unseal,verify}(src/AirGap/StellaOps.AirGap.Controller), gated on the three catalogued scopesairgap:status:read/airgap:seal/airgap:import(/verifysharesairgap:status:read).- Policy Engine sealed-mode —
SealedModeService(src/Policy/StellaOps.Policy.Engine/AirGap/, contractCONTRACT-SEALED-MODE-004), the real fail-closed enforcement gated onIEgressPolicy.IsSealed.stella airgapCLI —export,import,diff,status,jobssubcommands (src/Cli/StellaOps.Cli/Commands/AirGapCommandGroup.cs). There is nostella airgap seal/unsealcommand and nostella pack runcommand.The remainder of this document (§§1-7) is retained for historical context. Treat it as a DRAFT design that was never landed in its specified form; consult §8 for the implemented surface. For the authoritative, reconciled sealed-mode contract, see Sealed Mode (
CONTRACT-SEALED-MODE-004).
Overview
This contract was intended to define sealed-install enforcement semantics for StellaOps air-gapped deployments: when a pack or task declared sealed_install: true, a Task Runner would refuse to execute if the environment was not properly sealed.
[!NOTE] NOT IMPLEMENTED. No
sealed_installflag is honoured by any shipping service. The Task Runner that would have enforced it has been removed. See §8 for the sealed-mode enforcement that actually ships (Policy EngineSealedModeService+ AirGap Controller egress policy).
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 (DRAFT — not implemented)
[!WARNING] The
sealed_installmanifest flag andsealed_requirementsblock below do not exist in any shipping pack manifest schema. A repo-wide search forsealed_install/SealedInstallinsrc/returns no matches. This section is a never-landed design.
1.1 Sealed Install Flag
Packs declare their sealed requirement in the pack manifest:
{
"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
{
"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 (DRAFT — see §8 for the real API)
2.1 Sealed Mode Status API
[!WARNING] INCORRECT — the path, scope, and response shape below do not match the implementation. The real endpoint is
GET /system/airgap/status(not/api/v1/airgap/status), it requires theairgap:status:readscope, and it returnsAirGapStatusResponse— see §8.1 for the actual contract. None of the fieldsmode,sealed_at,sealed_by,bundle_version,bundle_digest,last_advisory_update,advisory_staleness_hours,egress_blocked, ornetwork_policyexist in the shipping response. Air-gap staleness is expressed in seconds against aStalenessBudget(Warning/Breach), not in “advisory staleness hours”, and is tracked per content kind viaContentStaleness.
The original (unimplemented) design had the Task Runner query the AirGap Controller as follows:
GET /api/v1/airgap/status # NOT IMPLEMENTED — real path is GET /system/airgap/status
Response (speculative shape — does not match AirGapStatusResponse):
{
"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
[!WARNING] NOT IMPLEMENTED. There is no heuristic sealed-mode detector. Sealed state is determined deterministically: the AirGap Controller persists explicit seal/unseal state per tenant (
AirGapState.Sealed), and the Policy Engine readsIEgressPolicy.IsSealed(true iffEgressPolicyMode.Sealed). There is no DNS/port-probing fallback, noAIRGAP_MODEenv var consumed by this path, no/etc/stellaops/sealedsentinel file, and no “0.7 heuristic score” anywhere insrc/.
The original design proposed the following fallback heuristics (none of which ship):
| 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 (DRAFT — illustrative pseudo-code only)
[!WARNING] NOT IMPLEMENTED. The
SealedInstallEnforcer,EnforcementResult,SealedRequirements,SealedInstallViolation, andRequirementViolationtypes below do not exist insrc/. No service performs amin_bundle_version/max_advisory_staleness_hours/require_time_anchorpre-execution check against a pack flag. The closest shipping enforcement isSealedModeService.EnforceBundleImportAsync(src/Policy/StellaOps.Policy.Engine/AirGap/SealedModeService.cs), which gates bundle import (not pack execution) onIEgressPolicy.IsSealedand time-anchor staleness, returningSealedModeEnforcementResult(Allowed, Reason, Remediation). The pseudo-code below is retained only to convey the original intent.
3.1 Pre-Execution Check
// ILLUSTRATIVE / NOT IMPLEMENTED — no such type exists in src/
public sealed class SealedInstallEnforcer
{
public async Task<EnforcementResult> 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<RequirementViolation> ValidateRequirements(
SealedRequirements requirements,
SealedModeStatus status)
{
var violations = new List<RequirementViolation>();
// 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:
# 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 (DRAFT — codes below do not exist)
[!WARNING] NOT IMPLEMENTED. The error code
SEALED_INSTALL_VIOLATION(andSEALED_REQUIREMENTS_VIOLATION,BUNDLE_VERSION_VIOLATION,ADVISORY_STALENESS_VIOLATION,TIME_ANCHOR_VIOLATION,SIGNATURE_VERIFICATION_VIOLATION) and the CLI exit codes 40-44 below appear nowhere insrc/. The shipping sealed-mode error codes are theSealedModeErrorCodes.*constants used bySealedModeService(e.g. staleness-breach / staleness-warning); the AirGap Controllerverifyendpoint returns aVerifyResponse(Valid, Reason)rather than a coded error envelope.
4.1 Error Response
When enforcement denies execution (speculative shape — not emitted by any service):
{
"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:
{
"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 (DRAFT — TaskRunner deleted)
[!WARNING] NOT IMPLEMENTED. The
src/TaskRunner/service that hosted this integration has been removed from the monorepo, so the execution-pipeline hook below has no host. The CLI commands shown (stella pack run …, “Activate sealed mode:stella airgap seal”) do not exist — the realstella airgapgroup exposes onlyexport,import,diff,status, andjobs(src/Cli/StellaOps.Cli/Commands/AirGapCommandGroup.cs); seal/unseal are HTTP-only operations on the AirGap Controller.
5.1 Task Runner Integration
// NOT IMPLEMENTED — TaskRunner service removed; no such pipeline exists
// In TaskRunner execution pipeline
public async Task<TaskResult> 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
# 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 <bundle.tar.gz>
2. Activate sealed mode: stella airgap seal
3. Verify status: stella airgap status
Exit code: 40
6. Configuration (DRAFT — keys/vars below not honoured)
[!WARNING] NOT IMPLEMENTED. The
etc/taskrunner.yamlenforcement.sealed_install.*keys and the env varsAIRGAP_MODE,AIRGAP_CONTROLLER_URL, andSEALED_INSTALL_BYPASSare not consumed by any shipping service (noSEALED_INSTALL_BYPASSor sealed-install enforcement config reader exists insrc/). Real sealed-mode behaviour is configured through the egress policy (EgressPolicy.Options,EgressPolicyMode) and the AirGap Controller startup options (AirGapStartupOptions); staleness is configured viaStalenessBudget(Warning/Breach seconds) on the seal request, not via hour-based grace periods.
6.1 Task Runner Configuration
# 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 (historical — service since deleted)
This contract originally unblocked the tasks below. They were marked DONE inside the src/TaskRunner/StellaOps.TaskRunner service (see docs-archive/implplan/SPRINT_0157_0001_0001_taskrunner_i.md), which has since been removed from the monorepo. The delivered code therefore no longer ships.
| Task ID | Description | Status |
|---|---|---|
| TASKRUN-AIRGAP-57-001 | Sealed install enforcement | ❌ Removed with TaskRunner service |
| TASKRUN-AIRGAP-58-001 | Sealed install CLI integration | ❌ Removed with TaskRunner service |
8. Actual Implementation (as of 2026-05-30)
This section documents the sealed-mode enforcement that actually ships, replacing the aspirational §§1-7 above.
8.1 AirGap Controller HTTP API
Source: src/AirGap/StellaOps.AirGap.Controller/Endpoints/AirGapEndpoints.cs. All routes are under the /system/airgap group and resolve the tenant from the stellaops:tenant/tid claim or x-tenant-id header.
| Method & Path | Scope (policy) | Handler / Contract |
|---|---|---|
GET /system/airgap/status | airgap:status:read (AirGapPolicies.StatusRead) | Returns AirGapStatusResponse |
POST /system/airgap/seal | airgap:seal (AirGapPolicies.Seal) | Body SealRequest; returns AirGapStatusResponse |
POST /system/airgap/unseal | airgap:seal (AirGapPolicies.Seal) | Returns AirGapStatusResponse |
POST /system/airgap/verify | airgap:status:read (AirGapPolicies.Verify) | Body VerifyRequest; returns VerifyResponse(Valid, Reason) |
Canonical scope constants live in StellaOps.Auth.Abstractions/StellaOpsScopes.cs: AirgapStatusRead = "airgap:status:read", AirgapSeal = "airgap:seal", AirgapImport = "airgap:import". There is no airgap:verify scope: the AirGapPolicies.Verify policy asserts AirgapStatusRead (Program.cs:59-69), because verification is a read-only integrity check.
AirGapStatusResponse(Endpoints/Contracts/AirGapStatusResponse.cs):
{
"tenantId": "default",
"sealed": true,
"policyHash": "sha256:…",
"timeAnchor": {
"anchorTime": "2025-12-01T00:00:00Z",
"source": "roughtime",
"format": "…",
"signatureFingerprint": "…",
"tokenDigest": "…"
},
"staleness": {
"ageSeconds": 432000,
"warningSeconds": 3600,
"breachSeconds": 7200,
"isWarning": false,
"isBreach": true
},
"driftSeconds": 432000,
"driftBaselineSeconds": 0,
"secondsRemaining": 0,
"contentStaleness": {
"advisories": { "ageSeconds": 0, "secondsRemaining": 7200, "isWarning": false, "isBreach": false }
},
"lastTransitionAt": "2025-12-01T00:00:00Z",
"evaluatedAt": "2025-12-06T10:00:00Z"
}
SealRequest(Endpoints/Contracts/SealRequest.cs) carries a required policyHash, an optional timeAnchor (StellaOps.AirGap.Time.Models.TimeAnchor), an optional stalenessBudget (StalenessBudget(WarningSeconds, BreachSeconds), default 3600/7200), and an optional per-content contentBudgets map.
8.2 Policy Engine sealed-mode (CONTRACT-SEALED-MODE-004)
Source: src/Policy/StellaOps.Policy.Engine/AirGap/ (SealedModeService, SealedModeModels, ISealedModeService). This is the real fail-closed enforcement primitive:
IsSealeddelegates toIEgressPolicy.IsSealed(src/AirGap/StellaOps.AirGap.Policy/), which istrueiffEgressPolicyMode.Sealed.SealAsync/UnsealAsyncpersist aPolicyPackSealedStateper tenant (policy hash, time anchor, staleness budget, last-transition timestamp).EvaluateStalenessAsynccompares wall clock againstTimeAnchor.AnchorTimeusingStalenessBudget(Warning/Breach seconds) — there is no hour-based “advisory staleness” concept.EnforceBundleImportAsyncreturnsSealedModeEnforcementResult(Allowed, Reason, Remediation): allows when not sealed; in sealed mode blocks bundle import on a staleness breach (SealedModeErrorCodes.StalenessBreach) and warns onSealedModeErrorCodes.StalenessWarning. Note this gates bundle import, not pack/task execution.VerifyBundleAsyncreturns DSSE/TUF/Merkle validity viaIPolicyPackBundleTrustVerifier, refusing to mark a bundle valid when no verifier is configured.
8.3 CLI surface
Source: src/Cli/StellaOps.Cli/Commands/AirGapCommandGroup.cs. The stella airgap group exposes:
| Command | Purpose |
|---|---|
stella airgap export | Export a sealed knowledge snapshot (advisories/VEX/policies/trust-roots, optional DSSE sign, time anchor). |
stella airgap import <bundle> | Import a snapshot; --verify-only, --force, --trust-policy, --max-age-hours, --quarantine-on-failure. |
stella airgap diff <base> <target> | Compare two knowledge snapshots. |
stella airgap status | Show current air-gap state and staleness (text/json). |
stella airgap jobs {export,import,list} | Manage HLC job-sync bundles for offline scenarios. |
There is no stella airgap seal / stella airgap unseal command and no stella pack run command. Sealing/unsealing is performed through the AirGap Controller HTTP API (§8.1).
9. Changelog
| Date | Version | Change |
|---|---|---|
| 2025-12-06 | 1.0.0 | Initial contract with enforcement logic, decision matrix, CLI integration. |
| 2026-05-30 | 1.0.0 (reconciled) | Source reconciliation: marked SUPERSEDED / NOT IMPLEMENTED. TaskRunner service deleted; no sealed_install flag, SealedInstallEnforcer, or coded exit codes exist. Corrected status-API path/scope/shape and CLI surface; added §8 documenting the shipping AirGap Controller API, Policy Engine SealedModeService, and stella airgap CLI. |
Related Contracts
- Sealed Mode (
CONTRACT-SEALED-MODE-004) — the authoritative, reconciled sealed-mode contract (AirGap Controller API, egress policy, staleness, time anchoring). Read this instead for current behaviour. - Mirror Bundle Contract — bundle format for sealed import.
- Verification Policy Contract — attestation verification.
