Local-first IaC auditor that fuses cost and security findings in a single pass — and writes the fix, not just the flag.
Project description
Cairn
The local-first, cloud-agnostic IaC auditor that fuses cost + security in a single pass — across AWS, Azure, GCP, Kubernetes and on-prem — and writes the fix, not just the flag.
Install · Quick start · Rules · Architecture · Contribute
Cairn scans your Terraform on your machine, applies security, cost, reliability and governance rules in one pass, reconciles the trade-offs between them, and proposes concrete, ready-to-apply fixes.
$ cairn scan ./infra
Cairn found 25 issue(s) in ./infra (6 cost, 6 governance, 3 reliability, 10 security):
1. [CRITICAL/SECURITY] aws_security_group.web (SEC001)
at: infra/main.tf:3
problem: Ingress on port 22 is open to the entire internet (0.0.0.0/0) and covers SSH/RDP.
fix: Restrict cidr_blocks to known ranges, or front the service with a
load balancer or SSM Session Manager instead of exposing it directly.
patch:
cidr_blocks = ["10.0.0.0/8"] # replace with your trusted CIDR
9. [MEDIUM/COST] aws_instance.batch (COST001)
problem: Instance type 'm5.4xlarge' is very large (~$561/month on-demand) and likely over-provisioned.
saves: ~$420.48/month (estimate)
patch:
instance_type = "m5.xlarge"
Trade-offs (cost x risk on the same resource):
⚖ aws_db_instance.main [COST + SECURITY]
Sequence the security fix first, then right-size — resizing an exposed
resource first just makes the breach cheaper to run.
Estimated recoverable spend: ~$1,717.53/month
9 resource(s), 1 file(s), 0.12s. Local-only scan; nothing left this machine.
Why Cairn
Cost lives in one tool (Infracost, Kubecost), security in another (Checkov, Trivy), and nobody reconciles them at the moment of the decision. A change that saves money can widen your attack surface — and no scanner tells you that. Cairn is different by design:
- Fuses cost + security. One pass, one ranked report, explicit trade-offs when both hit the same resource, with estimated dollars next to every cost finding.
- Fixes, not just flags. Every finding carries plain-English remediation and, where safe, a ready-to-apply HCL patch.
- Local-first, by architecture. No SaaS, no upload, no telemetry. Your code, secrets and infrastructure data never leave your machine. LLM explanations are strictly opt-in and bring-your-own-key (or fully local via Ollama).
- Policy-as-code around the scanner. Encode your org's standards — required tags, severity gates, grandfathered ignores — in a reviewable
.cairn.yaml. - Built to operate, eventually. Every action is governed by the Trust Ladder: today read-only with an audit trail; auto-fix PRs and policy-gated remediation come only after trust is earned.
Installation
Requires Python 3.10+.
pip install cairn-iac
Or run from source:
git clone https://github.com/cairn-oss/cairn && cd cairn
pip install -e .
Or via Docker:
docker build -t cairn . && docker run --rm -v "$PWD:/scan" cairn scan /scan
Quick start
# Scan a directory (recursive; .terraform/ and vendored dirs are skipped)
cairn scan ./infra
# Machine formats for CI and code scanning
cairn scan ./infra --format json --output report.json
cairn scan ./infra --format sarif --output report.sarif # GitHub Code Scanning
cairn scan ./infra --format markdown --output report.md # paste into a PR
# Gate CI: exit 1 only on HIGH or CRITICAL (default), or choose your line
cairn scan ./infra --fail-on CRITICAL
cairn scan ./infra --fail-on NEVER # report, never fail
# Draft the fixes as a review-ready proposal (changes nothing)
cairn propose ./infra --output proposal.md
# What does this branch do to the bill? (exit 1 if it breaches your budget)
cairn diff ./infra-main ./infra-branch
# See every rule
cairn rules
Policy-as-code
Drop a .cairn.yaml next to your Terraform:
min_severity: LOW
fail_on: HIGH
required_tags: [Owner, CostCenter]
disabled_rules: [GOV001]
severity_overrides:
COST002: MEDIUM
ignores:
- rule: SEC001
resource: aws_security_group.legacy_*
reason: grandfathered until the Q3 migration
Need a documented exception? Suppress one rule for one resource, inline, with a mandatory reason:
# cairn:ignore SEC001 reason=reachable only through the corporate VPN
See docs/configuration.md for the full reference.
Opt-in AI explanations (BYO-key or fully local)
Cairn never sends anything anywhere by default. If you want tailored explanations, configure a provider and pass --explain:
llm:
provider: ollama # fully local — data still never leaves your machine
model: llama3.1
cairn scan ./infra --explain
openai and anthropic providers use your own API key from the environment (OPENAI_API_KEY / ANTHROPIC_API_KEY). Only the finding itself (rule, resource address, message) is sent — never your files. Details in SECURITY.md.
GitHub Actions
- uses: cairn-oss/cairn@v0
with:
path: ./infra
fail-on: HIGH
Or upload SARIF to Code Scanning — see docs/configuration.md#ci.
What it checks (42 rules across 5 providers)
| Discipline | Examples |
|---|---|
| Security | Open security groups (SSH/RDP → CRITICAL), unencrypted S3/RDS/EBS, public databases and buckets, *:* IAM policies, hardcoded secrets, IMDSv1, plaintext listeners |
| Cost | Oversized EC2/RDS (with $/month estimates), gp2→gp3, orphaned volumes and Elastic IPs, previous-generation instance types, K8s containers without limits |
| Reliability | Databases without backups or deletion protection, buckets without versioning, unpinned images, missing health probes |
| Governance | Untagged resources, org-mandated tags via policy |
| Kubernetes | Privileged containers, root containers, hostPath mounts — manifests and Terraform scan together, in one pass |
| Azure / GCP / vSphere | Network exposure, public storage/DB, encryption, oversizing, tagging — cloud-agnostic and on-prem, filterable with --provider |
Full catalogue with rationale and references: docs/rules.md.
Run cairn providers for live coverage. Terraform for any provider is
parsed; resources from providers without a rule pack are reported as not
scanned (never a misleading "clean"). Coverage detail: docs/coverage.md.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Scan completed; nothing at/above the fail threshold |
| 1 | Findings at/above the fail threshold remain |
| 2 | Cairn could not run (bad path, broken config, usage error) |
Project status & roadmap
Cairn is at v0.5 — Terraform + Kubernetes scanning, propose
(Trust Ladder rung 1), policy packs, pre-merge cost diffing, and the
open-core boundary drawn in code. Shipped and designed releases are in
ROADMAP.md. Architecture and decision records live
in docs/.
Contributing & governance
Rules are single, small Python functions — adding one is a great first PR. Start with CONTRIBUTING.md; propose a rule with the rule proposal template.
Everyone is welcome to contribute; maintainers make the final merge after reviewing each pull request, and only maintainers cut releases. The model — roles, the merge gate, and how production is protected — is in GOVERNANCE.md and docs/merge-and-security-policy.md.
Free vs. paid
Everything in this repository is free forever — enumerated in code and
tested (cairn license prints the list). Team/Enterprise capabilities
(org rollups, SSO, compliance evidence) live in a separate commercial
package and never restrict the core.
License
MIT. The core is free forever — see the project principles in docs/architecture.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cairn_iac-0.6.0.tar.gz.
File metadata
- Download URL: cairn_iac-0.6.0.tar.gz
- Upload date:
- Size: 82.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8e52f58bcf507a880e70ab3b68b63316b5f5cc5fa2237f944a00c374471c471
|
|
| MD5 |
f875646af07f3ea07853f86101eabb81
|
|
| BLAKE2b-256 |
34c7ef77b1317110e5c8dc7f323b976f80c92a1e14c0118cd2d5841134d9400a
|
Provenance
The following attestation bundles were made for cairn_iac-0.6.0.tar.gz:
Publisher:
release.yml on cairn-oss/cairn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cairn_iac-0.6.0.tar.gz -
Subject digest:
c8e52f58bcf507a880e70ab3b68b63316b5f5cc5fa2237f944a00c374471c471 - Sigstore transparency entry: 2190139362
- Sigstore integration time:
-
Permalink:
cairn-oss/cairn@d99cc8ddd7b4ae167666db9bd09dd1814871f555 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/cairn-oss
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d99cc8ddd7b4ae167666db9bd09dd1814871f555 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cairn_iac-0.6.0-py3-none-any.whl.
File metadata
- Download URL: cairn_iac-0.6.0-py3-none-any.whl
- Upload date:
- Size: 74.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7b9358be52620a7035e6c475d08dd452ab8457fd60d8bc8e2de25cb5fd027db
|
|
| MD5 |
d9ea72bf0a0ae074291fc6cfb9c4f1ee
|
|
| BLAKE2b-256 |
918432776fd7b7f91da785212d665cbdcad780fed076f185c3fbc6fc5729fed2
|
Provenance
The following attestation bundles were made for cairn_iac-0.6.0-py3-none-any.whl:
Publisher:
release.yml on cairn-oss/cairn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cairn_iac-0.6.0-py3-none-any.whl -
Subject digest:
c7b9358be52620a7035e6c475d08dd452ab8457fd60d8bc8e2de25cb5fd027db - Sigstore transparency entry: 2190139372
- Sigstore integration time:
-
Permalink:
cairn-oss/cairn@d99cc8ddd7b4ae167666db9bd09dd1814871f555 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/cairn-oss
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d99cc8ddd7b4ae167666db9bd09dd1814871f555 -
Trigger Event:
push
-
Statement type: