Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
sdk-generator-smoke / sdk-smoke (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
4.3 KiB
4.3 KiB
AirGap Controller Scaffold (Draft) — PREP-AIRGAP-CTL-56-001/002/57-001/57-002/58-001
Status: Draft (2025-11-20) Owners: AirGap Controller Guild · Observability Guild · AirGap Time Guild · DevOps Guild Scope: Define the baseline project skeleton, APIs, telemetry, and staleness fields needed to unblock controller tasks 56-001 through 58-001.
1) Project layout
- Project:
src/AirGap/StellaOps.AirGap.Controller(net10.0, minimal API host). - Tests:
tests/AirGap/StellaOps.AirGap.Controller.Testswith xunit + deterministic time provider. - Shared contracts: DTOs under
Endpoints/Contracts, domain state underDomain/AirGapState.cs. - Persistence: in-memory store by default; Mongo store activates when
AirGap:Mongo:ConnectionStringis set. - Tests: Mongo2Go-backed store tests live under
tests/AirGap; seetests/AirGap/README.mdfor OpenSSL shim note.
2) State model
- Persistent document
airgap_state(Mongo):id(constsingleton),tenant_id,sealed(bool),policy_hash,time_anchor(nullable),last_transition_at(UTC),staleness_budget_seconds(int?, optional per bundle),notes.- Index on
{tenant_id}; unique onsingletonwithin tenant.
- In-memory cache with monotonic timestamp to avoid stale reads; cache invalidated on transitions.
Mongo wiring (opt‑in)
- Config section:
"AirGap": {
"Mongo": {
"ConnectionString": "mongodb://localhost:27017",
"Database": "stellaops_airgap",
"Collection": "airgap_state"
}
}
- The DI extension
AddAirGapControllerchooses Mongo whenConnectionStringis present; otherwise falls back to in-memory. - Collection index: unique on
{tenant_id, id}to enforce singleton per tenant.
3) Endpoints (56-002 baseline)
GET /system/airgap/status→ returns current state + staleness summary:{sealed, policy_hash, time_anchor:{source, anchored_at, drift_seconds}, staleness:{age_seconds, warning_seconds, breach_seconds, seconds_remaining}, last_transition_at}.
POST /system/airgap/seal→ body{policy_hash, time_anchor?, staleness_budget_seconds?}; requires Authority scopesairgap:seal+effective:write.POST /system/airgap/unseal→ requiresairgap:seal.- Validation: reject seal if missing
policy_hashor time anchor when platform requires sealed mode.
4) Telemetry (57-002)
- Structured logs:
airgap.sealed,airgap.unsealed,airgap.status.readwith tenant_id, policy_hash, time_anchor_source, drift_seconds. - Metrics (Prometheus/OpenTelemetry): counters
airgap_seal_total,airgap_unseal_total,airgap_startup_blocked_total; gaugesairgap_time_anchor_age_seconds,airgap_staleness_budget_seconds. - Timeline events (Observability stream):
airgap.sealed,airgap.unsealedwith correlation_id.
Startup diagnostics wiring (57-001)
- Config section
AirGap:Startupnow drives sealed-mode startup validation:TenantId(defaultdefault).EgressAllowlist(array; required when sealed).Trust:RootJsonPath,Trust:SnapshotJsonPath,Trust:TimestampJsonPath(all required when sealed; parsed via TUF validator).Rotation:ActiveKeys,Rotation:PendingKeys,Rotation:ApproverIds(base64-encoded keys; dual approval enforced when pending keys exist).
- Failures raise
sealed-startup-blocked:<reason>and incrementairgap_startup_blocked_total{reason}.
5) Staleness & time (58-001)
- Staleness computation:
drift_seconds = now_utc - time_anchor.anchored_at;seconds_remaining = max(0, staleness_budget_seconds - drift_seconds). - Time anchors accept Roughtime or RFC3161 token parsed via AirGap Time component (imported service).
- Status response includes drift and remaining budget; sealed mode refuses to run if budget exceeded.
6) Determinism & offline rules
- No external network calls; time source injected
IClockseeded in tests. - All timestamps RFC3339 UTC; responses sorted properties (serializer config).
7) Open decisions
- Final scopes list (Authority) for status read vs seal/unseal.
- Whether to require dual authorization for
seal(two-man rule) in sealed environments. - Retention/rotation policy for
airgap_stateaudit trail (append-only vs mutation).
8) Handoff
This document satisfies PREP-AIRGAP-CTL-56-001 through 58-001. Update once Authority scopes and time-anchor token format are finalized; then promote to v1 schema doc and wire tests accordingly.