10 KiB
10 KiB
component_architecture_airgap.md - Stella Ops AirGap (2025Q4)
Air-gapped deployment controller, importer, and time anchor services.
Scope. Implementation-ready architecture for AirGap: the controller, importer, and time anchor subsystems enabling StellaOps operation in disconnected/air-gapped environments with sealed-mode state management.
0) Mission & boundaries
Mission. Enable fully offline, air-gapped operation of StellaOps with sealed-mode state management, bundle-based updates, and cryptographic time anchors for staleness detection.
Boundaries.
- AirGap does not connect to external networks in sealed mode.
- AirGap does not generate vulnerability data. It imports pre-packaged bundles.
- Bundle verification is mandatory. Unsigned or tampered bundles are rejected.
- Time anchors are cryptographically verified using Roughtime or RFC3161.
1) Solution & project layout
src/AirGap/
├─ StellaOps.AirGap.Controller/ # Seal/unseal state machine, status APIs
│ ├─ Services/
│ │ ├─ ISealingController.cs # Sealing state interface
│ │ ├─ SealingController.cs # State machine implementation
│ │ └─ StatusService.cs # Health and status endpoints
│ └─ Models/
│ ├─ SealState.cs # sealed | unsealed | transitioning
│ └─ SealTransition.cs # Transition metadata
│
├─ StellaOps.AirGap.Importer/ # Bundle verification and import
│ ├─ Services/
│ │ ├─ IBundleVerifier.cs # DSSE/TUF verification
│ │ ├─ BundleVerifier.cs # Verification implementation
│ │ ├─ ICatalogImporter.cs # Catalog update interface
│ │ └─ CatalogImporter.cs # Import orchestration
│ └─ Models/
│ ├─ ImportBundle.cs # Bundle metadata
│ └─ ImportResult.cs # Import outcome
│
├─ StellaOps.AirGap.Time/ # Time anchor verification
│ ├─ Services/
│ │ ├─ ITimeAnchorService.cs # Time anchor interface
│ │ ├─ RoughtimeAnchor.cs # Roughtime implementation
│ │ └─ Rfc3161Anchor.cs # RFC3161 timestamp
│ └─ Models/
│ ├─ TimeAnchor.cs # Anchor record
│ └─ StalenessResult.cs # Staleness calculation
│
├─ StellaOps.AirGap.Policy/ # Air-gap specific policy rules
│ ├─ StellaOps.AirGap.Policy/
│ ├─ StellaOps.AirGap.Policy.Analyzers/
│ └─ StellaOps.AirGap.Policy.Tests/
│
├─ __Libraries/
│ ├─ StellaOps.AirGap.Bundle/ # Bundle format and parsing
│ └─ StellaOps.AirGap.Persistence/ # State persistence
│
├─ __Tests/ # Test projects
│
├─ scripts/ # Operational scripts
│
└─ AGENTS.md # Guild charter
2) External dependencies
- PostgreSQL - State persistence, import history
- Authority - Scope enforcement (
airgap:seal,airgap:status:read) - Cryptography - Bundle signature verification
- Object storage - Bundle staging and quarantine
3) Contracts & data model
3.1 Seal State
public enum SealState
{
Unsealed, // Normal operation, network allowed
Transitioning, // Sealing or unsealing in progress
Sealed // Air-gapped, no network egress
}
public sealed record SealStatus
{
public required SealState State { get; init; }
public required DateTimeOffset LastTransition { get; init; }
public required string TransitionedBy { get; init; }
public DateTimeOffset? SealedSince { get; init; }
public TimeAnchor? LastTimeAnchor { get; init; }
}
3.2 Import Bundle
{
"bundleId": "airgap-2025-01-15-abc123",
"bundleType": "advisory-update",
"version": "2025.01.15.001",
"createdAt": "2025-01-15T10:30:00Z",
"contents": [
{
"type": "concelier-snapshot",
"digest": "sha256:abc123...",
"path": "data/concelier-2025-01-15.tar.zst"
},
{
"type": "trivy-db",
"digest": "sha256:def456...",
"path": "data/trivy-db-2025-01-15.tar.gz"
}
],
"signature": {
"keyId": "sha256:sigkey...",
"algorithm": "ecdsa-p256",
"value": "base64..."
},
"timeAnchor": {
"source": "roughtime",
"timestamp": "2025-01-15T10:25:00Z",
"proof": "base64..."
}
}
3.3 Time Anchor
public sealed record TimeAnchor
{
public required string Source { get; init; } // roughtime, rfc3161
public required DateTimeOffset Timestamp { get; init; }
public required byte[] Proof { get; init; } // Cryptographic proof
public required string[] Servers { get; init; } // Servers used
public required bool Verified { get; init; }
}
public sealed record StalenessResult
{
public required TimeSpan Age { get; init; }
public required bool IsStale { get; init; }
public required TimeSpan StalenessThreshold { get; init; }
public string? Warning { get; init; }
}
4) REST API (Controller + Importer)
All under /api/v1/airgap. Auth: OpTok with airgap scopes.
Controller APIs
GET /status → { state, lastTransition, timeAnchor }
POST /seal → { transitionId, status: "transitioning" }
POST /unseal → { transitionId, status: "transitioning" }
GET /transitions/{id} → { transition details }
Importer APIs
POST /bundles/upload multipart → { bundleId, status: "pending" }
POST /bundles/{id}/verify → { valid: bool, details }
POST /bundles/{id}/import → { importId, status: "importing" }
GET /bundles/{id}/status → { status, progress, errors }
GET /bundles → { bundles: BundleSummary[] }
Time APIs
GET /time/anchor → { anchor: TimeAnchor, staleness }
POST /time/anchor { source, proof } → { anchor, verified }
GET /time/staleness → { staleness: StalenessResult }
5) Configuration (YAML)
AirGap:
Controller:
InitialState: "unsealed"
TransitionTimeoutSeconds: 300
RequireApproval: true
Importer:
StagingPath: "/data/airgap/staging"
QuarantinePath: "/data/airgap/quarantine"
MaxBundleSizeMb: 10240
TrustRoots:
- "sha256:abc123..." # StellaOps signing key
AllowedBundleTypes:
- "advisory-update"
- "trivy-db"
- "policy-pack"
Time:
StalenessThresholdHours: 168 # 7 days
RoughtimeServers:
- "roughtime.cloudflare.com"
- "roughtime.google.com"
Rfc3161Servers:
- "http://timestamp.digicert.com"
RequireMultipleServers: true
MinServerQuorum: 2
Quarantine:
TtlDays: 30
MaxQuotaMb: 51200
Postgres:
ConnectionString: "Host=postgres;Database=airgap;..."
6) Sealing State Machine
┌──────────────┐
│ Unsealed │
└──────┬───────┘
│ seal()
▼
┌──────────────┐
│ Transitioning│
└──────┬───────┘
│ complete
▼
┌──────────────┐
│ Sealed │
└──────┬───────┘
│ unseal()
▼
┌──────────────┐
│ Transitioning│
└──────┬───────┘
│ complete
▼
┌──────────────┐
│ Unsealed │
└──────────────┘
Transition Actions
Seal transition:
- Verify pending work is complete
- Capture final time anchor
- Disable network egress
- Update state to Sealed
Unseal transition:
- Verify network connectivity
- Refresh time anchor
- Enable network egress
- Update state to Unsealed
7) Bundle Import Flow
1. Upload bundle to staging
└─ Validate manifest structure
2. Verify bundle
├─ Check signature against trust roots
├─ Verify content digests
└─ Validate time anchor
3. Import bundle
├─ Extract contents
├─ Update target catalogs (Concelier, Trivy, etc.)
└─ Record import in history
4. Cleanup or quarantine
├─ Success: Remove from staging
└─ Failure: Move to quarantine with TTL
8) Security & compliance
- Signature verification: All bundles must be signed
- Trust roots: Configurable trust root keys
- Quarantine: Failed imports isolated with TTL
- Audit trail: All imports and state changes logged
- Scope enforcement: Authority scopes for all operations
- Rollback prevention: Version monotonicity enforced
9) Performance targets
- Seal/unseal transition: < 30s
- Bundle verification: < 10s for 1GB bundle
- Bundle import: < 60s for typical advisory update
- Time anchor verification: < 5s
10) Observability
Metrics:
airgap.state{state=sealed|unsealed|transitioning}airgap.bundles.imported_total{type,result}airgap.bundles.quarantined_total{reason}airgap.time.staleness_secondsairgap.time.anchor_age_seconds
Tracing: Spans for transitions, imports, verifications.
11) Testing matrix
- Seal/unseal tests: State machine transitions
- Bundle tests: Verification and import flows
- Quarantine tests: Failed import handling
- Time tests: Staleness calculations, anchor verification
- Integration tests: Full offline workflow simulation
Related Documentation
- Evidence reconciliation:
./evidence-reconciliation.md - Exporter coordination:
./exporter-cli-coordination.md - Mirror DSSE plan:
./mirror-dsse-plan.md - Offline Kit:
../../24_OFFLINE_KIT.md - Time anchor schema:
../../airgap/time-anchor-schema.md