Files
git.stella-ops.org/docs/contracts/sealed-mode.md
master cc69d332e3
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Add unit tests for RabbitMq and Udp transport servers and clients
- Implemented comprehensive unit tests for RabbitMqTransportServer, covering constructor, disposal, connection management, event handlers, and exception handling.
- Added configuration tests for RabbitMqTransportServer to validate SSL, durable queues, auto-recovery, and custom virtual host options.
- Created unit tests for UdpFrameProtocol, including frame parsing and serialization, header size validation, and round-trip data preservation.
- Developed tests for UdpTransportClient, focusing on connection handling, event subscriptions, and exception scenarios.
- Established tests for UdpTransportServer, ensuring proper start/stop behavior, connection state management, and event handling.
- Included tests for UdpTransportOptions to verify default values and modification capabilities.
- Enhanced service registration tests for Udp transport services in the dependency injection container.
2025-12-05 19:01:12 +02:00

6.2 KiB

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.

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

{
  "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.

{
  "anchor_time": "2025-12-05T10:00:00Z",
  "source": "roughtime|rfc3161",
  "format": "roughtime|rfc3161",
  "signature_fingerprint": "sha256:...",
  "token_digest": "sha256:..."
}

StalenessBudget

Defines staleness thresholds.

{
  "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.

{
  "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 <token with airgap:seal scope>

{
  "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 <token with airgap:seal scope>

Response: 200 OK
{
  "sealed": false,
  "last_transition_at": "2025-12-05T10:00:00Z"
}

Get Status

GET /system/airgap/status
Authorization: Bearer <token with airgap:status:read scope>

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 <token with airgap:verify scope>

{
  "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

public sealed class EgressPolicy
{
    public EgressPolicyMode Mode { get; }  // Sealed | Unsealed
    public IReadOnlyList<string> AllowedHosts { get; }
    public bool PermitLoopback { get; }
    public bool PermitPrivateNetworks { get; }
}

EgressRequest / EgressDecision

// 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

{
  "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