Canonical Model

Provider-neutral domain entities, typed values, and SQLite storage.

Collectors normalize provider payloads into a canonical model before analysis. Domain structs hold typed fields and source IDs, not raw SDK objects.

Typed values

Type Description
Money Minor units + ISO currency code
Percentage Basis points
Bytes, Duration Standard units
Timestamp RFC3339 UTC
IDs Provider, account, resource, snapshot identifiers

Core entities

Entity Contents
Accounts, regions, resources Compute, volumes, databases, NAT, network
Tags, relationships Includes TargetMissing for broken refs
Costs Attribution method, confidence, billing interval
Metrics Raw series and derived utilization signals
Snapshots Complete or partial; per-service coverage
Analysis runs Findings, evidence, recommendations, rule outcomes

Data quality

Each record tags DataQuality:

Value Meaning
observed From a provider API
derived Computed from observed data
estimated Heuristic; labeled as such
unavailable Signal absent
stale Outside freshness window

Storage

SQLite via modernc.org/sqlite (pure Go, CGO_ENABLED=0):

  • Embedded forward-only migrations
  • Schema version in schema_meta
  • Atomic transactions for snapshots and analysis runs
  • Idempotent save when external_key is set

Path: {DataDir}/cloudopt.db

Fixtures

YAML under testdata/fixtures/ loads the same schema as live collectors. Use import-fixture or --offline on collect.

Format spec: testdata/fixtures/README.md.

Partial snapshots

When a region or service fails independently, collectors mark the snapshot partial and record coverage metadata. analyze rejects partial snapshots unless you pass --allow-partial-snapshot.