Rule Engine

Deterministic optimization rules, evaluators, and finding fingerprints.

Typed evaluators run against canonical snapshots. Rules come from a versioned YAML manifest. No embedded scripts or expression language.

Rule manifest

Default: internal/application/rules/default_rules.yaml

Each rule specifies:

  • ID, version, category, severity
  • Applicability and thresholds
  • Required signals (inventory, costs, metrics, pricing)
  • Remediation text
  • Evaluator name

Override path: rules_manifest_path in config, or COA_RULES_MANIFEST.

Built-in evaluators

Evaluator Finds
stopped_instance_storage_cost Stopped instances still billing storage
unattached_block_volume Detached EBS volumes
stale_volume_snapshot Snapshots past retention threshold
missing_cost_allocation_tags Spend without required tags
ec2_downsize_candidate Oversized EC2 from utilization
ec2_idle_instance Sustained low utilization
ebs_volume_type_optimize gp2 → gp3 migration
rds_downsize_candidate Oversized RDS
nat_gateway_low_utilization NAT with minimal traffic

Outcomes (per rule, per snapshot)

Outcome Meaning
passed No finding
failed Finding written
suppressed Matched; filtered by suppressions.yaml
not_evaluated Required signal missing
error Unknown evaluator name

Panics inside an evaluator mark the rule not_evaluated. They do not crash the process.

Finding fields

  • Severity, category, resource IDs
  • Evidence (stored on the analysis run)
  • Assumptions, confidence
  • Recommendation (from rule remediation)
  • SHA-256 fingerprint: rule_id + rule_version + sorted resource IDs

Suppressions

config/suppressions.yaml:

suppressions:
  - fingerprint: "<finding fingerprint>"
    reason: "Accepted until decommission"
    expires_at: "2026-12-31T23:59:59Z"
  - rule_id: compute.stopped_instance_storage_cost
    resource_id: res-i-stopped
    reason: "Decommission scheduled Q3"

Expired entries are skipped.

CLI output

cloudopt analyze --json

Stdout JSON includes the full analysis object plus rule counts. Text mode prints passed / failed / suppressed / not_evaluated / error totals.