Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- 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.
6.2 KiB
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
- Parse Roughtime response
- Verify Ed25519 signature against trusted public key
- Extract anchor time from signed response
RFC 3161 Verification
- Parse SignedCms structure
- Validate TSA certificate chain
- Extract signing time from timestamp token
Startup Diagnostics
Pre-flight checks when starting in sealed mode:
- Verify time anchor is present
- Check staleness budget not breached
- Validate trust roots are loaded
- 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
Related Contracts
- Mirror Bundle Contract - Bundle format for sealed import
- Verification Policy Contract - Attestation verification