Files
git.stella-ops.org/docs/contracts/sealed-install-enforcement.md
StellaOps Bot f6c22854a4
Some checks failed
AOC Guard CI / aoc-verify (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
mock-dev-release / package-mock-release (push) Has been cancelled
feat(api): Add Policy Registry API specification
- Introduced OpenAPI specification for the StellaOps Policy Registry API, covering endpoints for verification policies, policy packs, snapshots, violations, overrides, sealed mode operations, and advisory staleness tracking.
- Defined schemas, parameters, and responses for comprehensive API documentation.

chore(scanner): Add global usings for scanner analyzers

- Created GlobalUsings.cs to simplify namespace usage across analyzer libraries.

feat(scanner): Implement Surface Service Collection Extensions

- Added SurfaceServiceCollectionExtensions for dependency injection registration of surface analysis services.
- Included methods for adding surface analysis, surface collectors, and entry point collectors to the service collection.
2025-12-06 20:52:23 +02:00

426 lines
14 KiB
Markdown

# 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<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:
```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<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
```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 <bundle.tar.gz>
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 |