Files
git.stella-ops.org/docs/modules/airgap/architecture.md
StellaOps Bot e6c47c8f50 save progress
2025-12-28 23:49:56 +02:00

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:

  1. Verify pending work is complete
  2. Capture final time anchor
  3. Disable network egress
  4. Update state to Sealed

Unseal transition:

  1. Verify network connectivity
  2. Refresh time anchor
  3. Enable network egress
  4. 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_seconds
  • airgap.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

  • 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