Add determinism tests for verdict artifact generation and update SHA256 sums script
- Implemented comprehensive tests for verdict artifact generation to ensure deterministic outputs across various scenarios, including identical inputs, parallel execution, and change ordering. - Created helper methods for generating sample verdict inputs and computing canonical hashes. - Added tests to validate the stability of canonical hashes, proof spine ordering, and summary statistics. - Introduced a new PowerShell script to update SHA256 sums for files, ensuring accurate hash generation and file integrity checks.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# AirGap Docs Index
|
||||
|
||||
- Time anchors & staleness: `time-anchor-scaffold.md`, `staleness-and-time.md`, `time-config-sample.json`, `time-api.md`, `time-anchor-verification-gap.md`.
|
||||
- Importer scaffolds: `importer-scaffold.md`, `bundle-repositories.md`.
|
||||
- Controller/diagnostics: `controller-scaffold.md`, `sealed-startup-diagnostics.md`.
|
||||
- Time anchors & staleness: `staleness-and-time.md`, `time-config-sample.json`, `time-api.md`, `time-anchor-verification-gap.md`.
|
||||
- Import pipeline: `importer.md`, `bundle-repositories.md`.
|
||||
- Controller/diagnostics: `controller.md`, `sealed-startup-diagnostics.md`.
|
||||
- Portable evidence flows: `portable-evidence.md`.
|
||||
|
||||
Use these as the front door for AirGap module work; update alongside code changes.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# Placeholder hashes; replace with real asset sums when inputs arrive
|
||||
@@ -296,7 +296,7 @@ src/Authority/
|
||||
|
||||
| Document | Sprint | Updates |
|
||||
|----------|--------|---------|
|
||||
| `docs/airgap/importer-scaffold.md` | 0338 | Add monotonicity, quarantine sections |
|
||||
| `docs/airgap/importer.md` | 0338 | Monotonicity + quarantine reference |
|
||||
| `docs/airgap/runbooks/quarantine-investigation.md` | 0338 | New runbook |
|
||||
| `docs/modules/cli/commands/offline.md` | 0339 | New command reference |
|
||||
| `docs/modules/cli/guides/airgap.md` | 0339 | Update with CLI examples |
|
||||
@@ -336,4 +336,4 @@ src/Authority/
|
||||
- [14-Dec-2025 Offline and Air-Gap Technical Reference](../product-advisories/14-Dec-2025%20-%20Offline%20and%20Air-Gap%20Technical%20Reference.md)
|
||||
- [Air-Gap Mode Playbook](./airgap-mode.md)
|
||||
- [Offline Kit Documentation](../24_OFFLINE_KIT.md)
|
||||
- [Importer Scaffold](./importer-scaffold.md)
|
||||
- [Importer](./importer.md)
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# 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.
|
||||
100
docs/airgap/controller.md
Normal file
100
docs/airgap/controller.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# AirGap Controller
|
||||
|
||||
The AirGap Controller is the tenant-scoped state keeper for sealed-mode operation. It records whether an installation is sealed, what policy hash is active, which time anchor is in force, and what staleness budgets apply.
|
||||
|
||||
For workflow context, start at `docs/airgap/overview.md` and `docs/airgap/airgap-mode.md`.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
- Maintain the current AirGap state per tenant (sealed/unsealed, policy hash, time anchor, staleness budgets).
|
||||
- Provide a deterministic, auditable status snapshot for operators and automation.
|
||||
- Enforce sealed/unsealed transitions via Authority scopes.
|
||||
- Emit telemetry signals suitable for dashboards and forensics timelines.
|
||||
|
||||
Non-goals:
|
||||
|
||||
- Bundle signature validation and import staging (owned by the importer; see `docs/airgap/importer.md`).
|
||||
- Cryptographic signing (Signer/Attestor).
|
||||
|
||||
## API
|
||||
|
||||
Base route group: `/system/airgap` (requires authorization).
|
||||
|
||||
### `GET /system/airgap/status`
|
||||
|
||||
Required scope: `airgap:status:read`
|
||||
|
||||
Response: `AirGapStatusResponse` (current state + staleness evaluation).
|
||||
|
||||
Notes:
|
||||
|
||||
- Tenant routing uses `x-tenant-id` (defaults to `default` if absent).
|
||||
- `driftSeconds` and `secondsRemaining` are derived from the active time anchor and staleness budget evaluation.
|
||||
- `contentStaleness` contains per-category staleness evaluations (clients should treat keys as case-insensitive).
|
||||
|
||||
### `POST /system/airgap/seal`
|
||||
|
||||
Required scope: `airgap:seal`
|
||||
|
||||
Body: `SealRequest`
|
||||
|
||||
- `policyHash` (required): binds the sealed state to a specific policy revision.
|
||||
- `timeAnchor` (optional): time anchor record (from the AirGap Time service).
|
||||
- `stalenessBudget` (optional): default staleness budget.
|
||||
- `contentBudgets` (optional): per-category staleness budgets (e.g., `advisories`, `vex`, `scanner`).
|
||||
|
||||
Behavior:
|
||||
|
||||
- Rejects requests missing `policyHash` (`400 { \"error\": \"policy_hash_required\" }`).
|
||||
- Records the sealed state and returns an updated status snapshot.
|
||||
|
||||
### `POST /system/airgap/unseal`
|
||||
|
||||
Required scope: `airgap:seal`
|
||||
|
||||
Behavior:
|
||||
|
||||
- Clears the sealed state and returns an updated status snapshot.
|
||||
- Staleness is returned as `Unknown` after unseal (clients should treat this as "not applicable").
|
||||
|
||||
### `POST /system/airgap/verify`
|
||||
|
||||
Required scope: `airgap:verify`
|
||||
|
||||
Purpose: verify replay / bundle verification requests against the currently active AirGap state.
|
||||
|
||||
## State model (per tenant)
|
||||
|
||||
Canonical fields captured by the controller (see `src/AirGap/StellaOps.AirGap.Controller`):
|
||||
|
||||
- `tenantId`
|
||||
- `sealed`
|
||||
- `policyHash` (nullable)
|
||||
- `timeAnchor` (`TimeAnchor`, may be `Unknown`)
|
||||
- `stalenessBudget` (`StalenessBudget`)
|
||||
- `contentBudgets` (`Dictionary<string, StalenessBudget>`)
|
||||
- `driftBaselineSeconds` (baseline used to keep drift evaluation stable across transitions)
|
||||
- `lastTransitionAt` (UTC)
|
||||
|
||||
Determinism requirements:
|
||||
|
||||
- Use UTC timestamps only.
|
||||
- Use ordinal comparisons for keys and stable serialization settings for JSON responses.
|
||||
- Never infer state from wall-clock behavior other than the injected `TimeProvider`.
|
||||
|
||||
## Telemetry
|
||||
|
||||
The controller emits:
|
||||
|
||||
- Structured logs: `airgap.status.read`, `airgap.sealed`, `airgap.unsealed`, `airgap.verify` (include `tenant_id`, `policy_hash`, and drift/staleness).
|
||||
- Metrics: `airgap_seal_total`, `airgap_unseal_total`, `airgap_status_read_total`, and gauges for drift/budget/remaining seconds.
|
||||
- Timeline events (optional): `airgap.sealed`, `airgap.unsealed`, `airgap.staleness.warning`, `airgap.staleness.breach`.
|
||||
|
||||
## References
|
||||
|
||||
- `docs/airgap/overview.md`
|
||||
- `docs/airgap/sealed-startup-diagnostics.md`
|
||||
- `docs/airgap/staleness-and-time.md`
|
||||
- `docs/airgap/time-api.md`
|
||||
- `docs/airgap/importer.md`
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
# AirGap Importer Scaffold (prep for AIRGAP-IMP-56-001/56-002/58-002)
|
||||
|
||||
## Scope for prep
|
||||
- Provide minimal project and test scaffolds so downstream implementation can wire DSSE, TUF, Merkle validation, and audit logging without redoing structure.
|
||||
- Capture trust-root inputs required (bundle path, signing keys, allowed algorithms, validity window).
|
||||
|
||||
## What landed (2025-11-20)
|
||||
- New project: `src/AirGap/StellaOps.AirGap.Importer/StellaOps.AirGap.Importer.csproj` (net10.0, deterministic-only dependencies).
|
||||
- Planning layer: `BundleImportPlanner` emits deterministic plan steps and early validation reasons (`bundle-path-required`, `trust-roots-required`, `invalid-trust-window`).
|
||||
- Contracts: `TrustRootConfig` record carries root bundle path, trusted key fingerprints, allowed algorithms, and optional validity window.
|
||||
- Validation shape: `BundleValidationResult` centralises success/failure reasons for replay/capture.
|
||||
- Tests: `tests/AirGap/StellaOps.AirGap.Importer.Tests` validate planner behavior without external feeds.
|
||||
|
||||
## Updates (2025-11-20)
|
||||
- Added DSSE verifier (RSA-PSS/SHA256) with PAE encoding + trusted key fingerprint checks.
|
||||
- Added TUF metadata validator (root/snapshot/timestamp) with hash consistency guard.
|
||||
- Added deterministic Merkle root calculator for bundle object staging.
|
||||
- Expanded tests for DSSE, TUF, Merkle helpers.
|
||||
- Added trust store + root rotation policy (dual approval) and import validator that coordinates DSSE/TUF/Merkle/rotation checks.
|
||||
|
||||
## Updates (2025-12-15)
|
||||
- Added monotonicity enforcement primitives under `src/AirGap/StellaOps.AirGap.Importer/Versioning/` (`BundleVersion`, `IVersionMonotonicityChecker`, `IBundleVersionStore`).
|
||||
- Added file-based quarantine service under `src/AirGap/StellaOps.AirGap.Importer/Quarantine/` (`IQuarantineService`, `FileSystemQuarantineService`, `QuarantineOptions`).
|
||||
- Updated `ImportValidator` to include monotonicity checks, force-activate support (requires reason), and quarantine on validation failures.
|
||||
- Added Postgres-backed bundle version tracking in `src/AirGap/StellaOps.AirGap.Storage.Postgres/Repositories/PostgresBundleVersionStore.cs` and registration via `src/AirGap/StellaOps.AirGap.Storage.Postgres/ServiceCollectionExtensions.cs`.
|
||||
- Updated tests in `tests/AirGap/StellaOps.AirGap.Importer.Tests` to cover versioning/quarantine and the new import validator behavior.
|
||||
|
||||
## Next implementation hooks
|
||||
- Replace placeholder plan with actual DSSE + TUF verifiers; keep step ordering stable.
|
||||
- Feed trust roots from sealed-mode config and Evidence Locker bundles (once available) before allowing imports.
|
||||
- Record audit trail for each plan step (success/failure) and a Merkle root of staged content.
|
||||
|
||||
## Determinism/air-gap posture
|
||||
- No network dependencies; BCL + `Microsoft.Extensions.*` only.
|
||||
- Tests use cached local NuGet feed (`local-nugets/`).
|
||||
- Plan steps are ordered list; do not reorder without bumping downstream replay expectations.
|
||||
|
||||
## How to consume
|
||||
```bash
|
||||
# run tests offline once feed is hydrated
|
||||
DOTNET_NOLOGO=1 dotnet test tests/AirGap/StellaOps.AirGap.Importer.Tests/StellaOps.AirGap.Importer.Tests.csproj --no-build
|
||||
```
|
||||
|
||||
## Owners
|
||||
- AirGap Importer Guild / Security Guild (per sprint 0510).
|
||||
77
docs/airgap/importer.md
Normal file
77
docs/airgap/importer.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# AirGap Importer
|
||||
|
||||
The AirGap Importer verifies and ingests offline bundles (mirror, bootstrap, evidence kits) into a sealed or constrained deployment. It fails closed by default: imports are rejected when verification fails, and failures are diagnosable offline.
|
||||
|
||||
This document describes importer behavior and its key building blocks. For bundle formats and operational workflow, see `docs/airgap/offline-bundle-format.md`, `docs/airgap/mirror-bundles.md`, and `docs/airgap/operations.md`.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
- Verify bundle integrity and authenticity (DSSE signatures; optional TUF metadata where applicable).
|
||||
- Enforce monotonicity (prevent version rollback unless explicitly force-activated with a recorded reason).
|
||||
- Stage verified content into deterministic layouts (catalog + item repository + object-store paths).
|
||||
- Quarantine failed bundles for forensic analysis with deterministic logs and metadata.
|
||||
- Emit an audit trail for every dry-run and import attempt (success or failure).
|
||||
|
||||
## Verification pipeline (conceptual)
|
||||
|
||||
1. **Plan**: build an ordered list of validation/ingest steps for the bundle (`BundleImportPlanner`).
|
||||
2. **Validate signatures**: verify DSSE envelopes and trusted key fingerprints.
|
||||
3. **Validate metadata** (when present): verify TUF root/snapshot/timestamp consistency against trust roots.
|
||||
4. **Compute deterministic roots**: compute a Merkle root over staged bundle items (stable ordering).
|
||||
5. **Check monotonicity**: ensure the incoming bundle version is newer than the currently active version.
|
||||
6. **Quarantine on failure**: preserve the bundle + verification log and emit a stable failure reason code.
|
||||
7. **Commit**: write catalog/item entries and activation record; emit audit/timeline events.
|
||||
|
||||
The step order must remain stable; if steps change, treat it as a contract change and update CLI/UI guidance.
|
||||
|
||||
## Quarantine
|
||||
|
||||
When verification fails, the importer quarantines the bundle with enough information to debug offline.
|
||||
|
||||
Typical structure:
|
||||
|
||||
- `/updates/quarantine/<tenantId>/<timestamp>-<reason>-<id>/`
|
||||
- `bundle.tar.zst` (original)
|
||||
- `manifest.json` (if extracted)
|
||||
- `verification.log` (deterministic, no machine-specific paths)
|
||||
- `failure-reason.txt` (human-readable)
|
||||
- `quarantine.json` (structured metadata: tenant, reason, timestamps, sizes, hashes)
|
||||
|
||||
Operational expectations:
|
||||
|
||||
- Quarantine is bounded: enforce per-tenant quota + TTL cleanup.
|
||||
- Listing is deterministic: sort by `quarantined_at` then `quarantine_id` (ordinal).
|
||||
|
||||
## Version monotonicity
|
||||
|
||||
Rollback resistance is enforced via:
|
||||
|
||||
- A per-tenant version store (`IBundleVersionStore`) backed by Postgres in production.
|
||||
- A monotonicity checker (`IVersionMonotonicityChecker`) that compares incoming bundle versions to the active version.
|
||||
- Optional force-activate path requiring a human reason, stored alongside the activation record.
|
||||
|
||||
## Storage model
|
||||
|
||||
The importer writes deterministic metadata that other components can query:
|
||||
|
||||
- **Bundle catalog**: (tenant, bundle_id, digest, imported_at_utc, content paths).
|
||||
- **Bundle items**: (tenant, bundle_id, path, digest, size).
|
||||
|
||||
For the logical schema and deterministic ordering rules, see `docs/airgap/bundle-repositories.md`.
|
||||
|
||||
## Telemetry and auditing
|
||||
|
||||
Minimum signals:
|
||||
|
||||
- Counters: imports attempted/succeeded/failed, dry-runs, quarantines created, monotonicity failures, force-activations.
|
||||
- Structured logs with stable reason codes (e.g., `dsse_signature_invalid`, `tuf_root_invalid`, `merkle_mismatch`, `version_rollback_blocked`).
|
||||
- Audit emission: include tenant, bundle_id, digest, operator identity, and whether sealed mode was active.
|
||||
|
||||
## References
|
||||
|
||||
- `docs/airgap/offline-bundle-format.md`
|
||||
- `docs/airgap/mirror-bundles.md`
|
||||
- `docs/airgap/bundle-repositories.md`
|
||||
- `docs/airgap/operations.md`
|
||||
- `docs/airgap/controller.md`
|
||||
|
||||
@@ -29,4 +29,4 @@ Display a top-of-console banner when `sealed=true`:
|
||||
- `docs/airgap/airgap-mode.md` — deeper policy shapes per mode.
|
||||
- `docs/airgap/bundle-repositories.md` — mirror/bootstrap bundle structure.
|
||||
- `docs/airgap/staleness-and-time.md` — time anchors and staleness checks.
|
||||
- `docs/airgap/controller-scaffold.md` / `importer-scaffold.md` — implementation scaffolds.
|
||||
- `docs/airgap/controller.md` / `docs/airgap/importer.md` — controller + importer references.
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# Controller Scaffold Prep — PREP-AIRGAP-CTL-56-001 / 56-002
|
||||
|
||||
Status: Draft (2025-11-20)
|
||||
Owners: AirGap Controller Guild · DevOps Guild
|
||||
Scope: Provide the controller scaffold + status API contract so AIRGAP-CTL-56-001/56-002 can proceed.
|
||||
|
||||
## Deliverables included
|
||||
- Service scaffold described in `docs/airgap/controller-scaffold.md` (project layout, DI wiring, config keys, auth scopes).
|
||||
- Baseline status/seal endpoints sketch:
|
||||
- `GET /system/airgap/status` → `{sealed, policy_hash?, staleness_seconds?, time_anchor_id?, bundle_id?}`
|
||||
- `POST /system/airgap/seal` (body: `{policy_hash, reason}`) → returns new state; requires `airgap:seal` scope.
|
||||
- Determinism & offline posture: no external calls; state persisted via `airgap_state` store; timestamps UTC; subject ordering deterministic.
|
||||
|
||||
## Next steps for implementation
|
||||
- Generate controller project under `src/AirGap/StellaOps.AirGap.Controller` per scaffold.
|
||||
- Wire Authority scope checks (`airgap:seal`, `airgap:status:read`).
|
||||
- Add sealed-mode guard middleware and timeline events per `docs/airgap/sealed-startup-diagnostics.md` once integrated.
|
||||
|
||||
## Handoff
|
||||
Use this prep doc to satisfy PREP-AIRGAP-CTL-56-001 and PREP-AIRGAP-CTL-56-002. Update if scope changes; otherwise move tasks to DONE.
|
||||
@@ -1,25 +0,0 @@
|
||||
# Staleness & Drift Prep — PREP-AIRGAP-CTL-58-001-BLOCKED-ON-57-002
|
||||
|
||||
Status: Draft (2025-11-20)
|
||||
Owners: AirGap Controller Guild · AirGap Time Guild
|
||||
Scope: Capture the staleness/drift requirements for controller status once seal/unseal telemetry (57-002) is available.
|
||||
|
||||
## Inputs
|
||||
- Time anchor ingestion from Time service (Roughtime/RFC3161) via `time_anchor_id`, `drift_seconds`, `staleness_budget_seconds`.
|
||||
- Bundle metadata from importer (bundle_id, manifest hash, generated_at).
|
||||
|
||||
## Proposed status enrichments
|
||||
- Add fields to `GET /system/airgap/status`:
|
||||
- `staleness_seconds_remaining`
|
||||
- `bundle_id`
|
||||
- `time_anchor_id`
|
||||
- `drift_seconds`
|
||||
- Compute `staleness_seconds_remaining = staleness_budget_seconds - drift_seconds` (floor at 0).
|
||||
- Determinism: calculations purely from stored numbers; no wall-clock calls beyond persisted anchor timestamps.
|
||||
|
||||
## Observability
|
||||
- Metrics: `airgap_staleness_seconds{tenant}` (gauge), `airgap_drift_seconds{tenant}`.
|
||||
- Timeline events emitted when budgets breached: `airgap.staleness.threshold`.
|
||||
|
||||
## Handoff
|
||||
Use this prep note to satisfy PREP-AIRGAP-CTL-58-001. After integrating sealed-startup telemetry and time anchor verification, implement the above fields and metrics, then mark the implementation task DOING.
|
||||
@@ -1,41 +0,0 @@
|
||||
# AirGap Time Anchor Scaffold (prep for AIRGAP-TIME-57-001)
|
||||
|
||||
## Scope for prep
|
||||
- Provide a deterministic parsing surface for signed time tokens (Roughtime, RFC3161) so staleness calculations and telemetry wiring can start without full crypto yet.
|
||||
|
||||
## What landed (2025-11-20)
|
||||
- New project: `src/AirGap/StellaOps.AirGap.Time/StellaOps.AirGap.Time.csproj` (net10.0), BCL-only.
|
||||
- Model: `TimeAnchor` canonical record (anchor time, source, format, signature fingerprint placeholder, token digest).
|
||||
- Parser: `TimeTokenParser` with deterministic SHA-256 digest derivation and structured success/failure reasons.
|
||||
- Result envelope: `TimeAnchorValidationResult` and `TimeTokenFormat` enum.
|
||||
- Tests: `tests/AirGap/StellaOps.AirGap.Time.Tests` cover empty-token failure and digest production for Roughtime tokens.
|
||||
|
||||
## Updates (2025-11-20)
|
||||
- Added staleness calculator (`StalenessCalculator`) and budgets/evaluation models to derive warning/breach states deterministically.
|
||||
- Added `TimeAnchorLoader` to ingest hex-encoded tokens from fixtures; sample tokens placed under `src/AirGap/StellaOps.AirGap.Time/fixtures/`.
|
||||
- Added `TimeStatusService` + `InMemoryTimeAnchorStore` for per-tenant anchor/budget status + staleness; tests in `TimeStatusServiceTests`.
|
||||
- Added verification pipeline (`TimeVerificationService`) with stub Roughtime/RFC3161 verifiers requiring trust roots; loader now verifies using trust roots.
|
||||
- Added API surface `/api/v1/time/status` (plus POST `/api/v1/time/anchor`) via `TimeStatusController` and web host wiring.
|
||||
- Added sealed startup hook (`StartupValidationExtensions`) to block app start when anchor missing/stale; uses budgets and returns structured reasons.
|
||||
- Upgraded Roughtime verifier to real Ed25519 signature check + RFC3161 verifier using SignedCms; failures now return `roughtime-*` / `rfc3161-*` reasons.
|
||||
- Added config binding (`AirGap:*`) for tenant and staleness budgets; startup validation pulls from config.
|
||||
- Added config sample at `docs/airgap/time-config-sample.json` for sealed-mode deployments.
|
||||
- Documented endpoints and payloads at `docs/airgap/time-api.md`.
|
||||
- Health check: `/healthz/ready` reports degraded/healthy based on staleness; consumers should scrape for sealed-mode readiness.
|
||||
|
||||
## Next implementation hooks
|
||||
- Plug real Roughtime and RFC3161 decoders, verifying against trust roots supplied via sealed-mode config.
|
||||
- Persist `TimeAnchor` rows under controller/importer once schema is final; emit telemetry counters/alerts.
|
||||
- Replace placeholder signature fingerprint with actual signer fingerprint post-verification.
|
||||
|
||||
## Determinism/air-gap posture
|
||||
- Parser avoids wall-clock; anchor time derived deterministically from token digest until real parser is wired.
|
||||
- No network calls; uses cached NuGet (`local-nugets/`) for tests.
|
||||
|
||||
## How to consume
|
||||
```bash
|
||||
DOTNET_NOLOGO=1 dotnet test tests/AirGap/StellaOps.AirGap.Time.Tests/StellaOps.AirGap.Time.Tests.csproj --no-build
|
||||
```
|
||||
|
||||
## Owners
|
||||
- AirGap Time Guild (per sprint 0510).
|
||||
Reference in New Issue
Block a user