55 lines
3.9 KiB
Markdown
55 lines
3.9 KiB
Markdown
# 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.Tests` with xunit + deterministic time provider.
|
|
- Shared contracts: DTOs under `Endpoints/Contracts`, domain state under `Domain/AirGapState.cs`.
|
|
- Persistence: in-memory state store only (no external DB dependency). Postgres-backed persistence will follow in a later sprint.
|
|
- Tests: run entirely in-memory; no Mongo/OpenSSL shims required.
|
|
|
|
## 2) State model
|
|
- In-memory state record per tenant: `id` (const `singleton`), `tenant_id`, `sealed` (bool), `policy_hash`, `time_anchor` (nullable), `last_transition_at` (UTC), `staleness_budget_seconds` (int?, optional per bundle), `notes`.
|
|
- In-memory cache with monotonic timestamp to avoid stale reads; cache invalidated on transitions.
|
|
- Persistence roadmap: swap in a Postgres-backed store with equivalent singleton and tenant scoping; Mongo wiring has been removed.
|
|
|
|
## 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 scopes `airgap:seal` + `effective:write`.
|
|
- `POST /system/airgap/unseal` -> requires `airgap:seal`.
|
|
- Validation: reject seal if missing `policy_hash` or time anchor when platform requires sealed mode.
|
|
|
|
## 4) Telemetry (57-002)
|
|
- Structured logs: `airgap.sealed`, `airgap.unsealed`, `airgap.status.read` with tenant_id, policy_hash, time_anchor_source, drift_seconds.
|
|
- Metrics (Prometheus/OpenTelemetry): counters `airgap_seal_total`, `airgap_unseal_total`, `airgap_startup_blocked_total`; gauges `airgap_time_anchor_age_seconds`, `airgap_staleness_budget_seconds`.
|
|
- Timeline events (Observability stream): `airgap.sealed`, `airgap.unsealed` with correlation_id.
|
|
|
|
### Startup diagnostics wiring (57-001)
|
|
- Config section `AirGap:Startup` now drives sealed-mode startup validation:
|
|
- `TenantId` (default `default`).
|
|
- `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 increment `airgap_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 `IClock` seeded 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_state` audit 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.
|