Metadata-Version: 2.4
Name: oneport-costwatch
Version: 0.3.0
Summary: AI infrastructure cost gate — reads your IaC, no cloud credentials needed
Author-email: Oneport <eng@oneport.co.in>
License-Expression: MIT
Project-URL: Homepage, https://www.oneport.co.in
Project-URL: Repository, https://github.com/oneport/oneport-costwatch
Project-URL: Issues, https://github.com/oneport/oneport-costwatch/issues
Project-URL: Changelog, https://github.com/oneport/oneport-costwatch/blob/main/CHANGELOG.md
Keywords: finops,cost-optimization,terraform,iac,ai,gemini,infracost
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.1.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: oneport-account>=0.1.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0.0
Requires-Dist: rich>=13.0.0
Provides-Extra: claude
Requires-Dist: anthropic>=0.25.0; extra == "claude"
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Requires-Dist: mypy>=1.10.0; extra == "dev"
Requires-Dist: types-pyyaml; extra == "dev"
Dynamic: license-file

# Oneport Costwatch

**An AI infrastructure-cost gate that reads your Infrastructure-as-Code — no cloud credentials needed.**

Point it at a repo full of Terraform / Docker Compose and it tells you what's
about to cost you money and *how to make it cheaper* — before you `apply`.
It works entirely from the IaC you already have. It never asks for AWS/GCP
keys, never touches your account, and never leaves your machine except for one
call to the model of your choice.

```
$ oneport-costwatch analyze infra/

Oneport Costwatch
  Parsed 4 resource(s); 4 priced. Approx provisioned cost: ~$1,947/mo
  Potential savings: ~$791/mo across 4 finding(s)

🔴 CRITICAL aws_instance.api  save ~$364/mo  (dev-no-shutdown)
   Development instances are running 24/7 and can be shut down nights/weekends.
   - environment = dev, count = 2  →  + add a shutdown schedule (Instance Scheduler)
   infra/main.tf:4

🟠 HIGH aws_instance.batch_worker  save ~$300/mo  (always-on)
   A batch worker running 24/7 on on-demand is wasteful for a bursty workload.
   - environment = staging  →  + AWS Batch on Spot, or a schedule
   infra/main.tf:28
   ...
```

---

## Why not just Infracost?

[Infracost](https://www.infracost.io/) is great at one thing: putting a **price
tag** on your Terraform. Costwatch is a different job — it's the **judgment
layer** on top of the price tag:

| | Infracost | Oneport Costwatch |
|---|---|---|
| Price a Terraform plan | ✅ | ✅ (rough, bundled table) |
| Multi-source IaC (Compose too) | Terraform-focused | ✅ Terraform **+** Docker Compose |
| *"This is 4× oversized, here's the right size"* | ❌ | ✅ (the wedge) |
| Spot/serverless/shutdown/autoscaling advice | ❌ | ✅ |
| PR gate on cost **increase** | partial | ✅ sticky comment + exit code |
| Needs cloud credentials | no | **no** |

Costwatch's estimate is deliberately approximate — if you want a penny-accurate
plan cost, run Infracost too. Costwatch is here to catch the *waste*.

---

## Install

```bash
pip install oneport-costwatch
```

**0-BYOK — no API keys, ever.** The AI pass runs on OnePort's own managed models,
metered against your account; you never bring a model key. Get a token at
[oneport.co.in](https://www.oneport.co.in) → Console → API Tokens, then log in once:

```bash
onp account login <op_live_...>
```

Prefer no account? `onp costwatch price` gives a deterministic estimate with no login.

Deterministic pricing (`oneport-costwatch price`) needs **no login at all**.

---

## Usage

```bash
# Analyze a directory of IaC (default: current dir)
oneport-costwatch analyze infra/

# Most accurate source: a Terraform plan you already generated
terraform show -json plan.tfplan > plan.json
oneport-costwatch analyze infra/ --plan plan.json

# Deterministic estimate only — no model call, no key
oneport-costwatch price infra/

# JSON for CI / dashboards
oneport-costwatch analyze infra/ -f json

# SARIF for GitHub Code Scanning
oneport-costwatch analyze infra/ -f sarif > costwatch.sarif

# Only show the big stuff
oneport-costwatch analyze infra/ -s high
```

### Make it a CI gate

By default `analyze` reports and exits 0 — it never fails your build. Opt into a gate with either (or both) of:

```bash
# Fail when any finding is at/above a severity
oneport-costwatch analyze infra/ --fail-on high

# Fail when total monthly cost exceeds a hard budget (USD/month)
oneport-costwatch analyze infra/ --budget 5000
```

`--min-severity` controls what's **shown**; `--fail-on` controls what **fails CI** — they're independent. On a `--post` PR run, `--budget` caps the *added* cost the change is responsible for.

### Waivers — accept a cost on the record

Approve a specific finding without turning the check off (which would also hide the next wasteful one). Waivers live in `.oneport/costwatch-waivers.yml`:

```yaml
waivers:
  - resource: "aws_instance.ml_trainer"   # glob on the resource address
    reason: "GPU box is intentionally always-on for the training queue"
    expires: "2026-12-31"
  - file: "envs/dev/*.tf"                  # ...or match by file
    category: dev-no-shutdown
    reason: "dev is torn down nightly by a separate job"
```

A waived finding is still shown (marked `[WAIVED]`) and still exported to SARIF as a `suppressions` entry, but it never trips `--fail-on`. Expired waivers are ignored, so the finding re-blocks — an approval can't outlive its reason.

### Cost diff on pull requests (the gate)

```bash
oneport-costwatch analyze https://github.com/org/repo/pull/42 --post
```

Costwatch fetches the **base** and **head** versions of the changed IaC files,
prices both, and — **only if the PR raises provisioned cost past the threshold** —
posts a sticky comment explaining *"this PR adds ~$340/mo, here's a cheaper
config."* Re-runs update the same comment (hidden HTML marker; the PR is the
state store — no server, no database). Add `--fail-on-increase` to turn it into
a hard CI gate. Needs `GITHUB_TOKEN`.

See [`examples/workflows/`](examples/workflows/) for a ready-to-drop GitHub
Actions workflow.

---

## Teach it your rules

Costwatch reads `.oneport/guidelines.md` (path configurable) and honours it on
every run. For example:

```markdown
# Cost guidelines
- prod must stay on-demand — never suggest spot for prod resources
- our batch tier is allowed to run large instances overnight
```

A guideline-driven decision is tagged `team-guideline` in the output, and
Costwatch will *not* raise a finding a guideline forbids.

---

## What it parses

- **Terraform** — `.tf` files (HCL) and `terraform show -json` plan output.
  Extracts `aws_instance`, `aws_db_instance`, `aws_ebs_volume`,
  `aws_elasticache_cluster`, `aws_autoscaling_group`, `google_compute_instance`,
  and more — instance types, storage, replicas, counts, env tags.
- **Docker Compose** — services, `image`, `deploy.replicas`, resource limits.

Unknown instance types are still reported (just without a dollar estimate).

---

## Configuration

Optional `.oneport-costwatch.yml` in your repo root:

```yaml
model: gemini-flash-latest
output:
  format: inline        # inline | json | github | sarif
  min_severity: info    # info | warning | high | critical  (display only)
fail_on: high           # block CI at/above this severity (unset = no gate)
budget: 5000            # block CI when monthly cost exceeds this USD cap (unset = no gate)
ignore_paths:
  - .terraform
  - modules/vendored
guidelines_path: .oneport/guidelines.md
post_threshold: 25      # only --post-comment when a PR adds ≥ $25/mo
```

Env vars: `COSTWATCH_MODEL`, `COSTWATCH_FORMAT`, `GITHUB_TOKEN`.

---

## Privacy

Serverless and low-trust by design — see [PRIVACY.md](PRIVACY.md). In short:
**no cloud credentials, read-only, runs on your machine.** The only thing that
leaves your box is the parsed resource inventory, sent to the OnePort managed
proxy for the waste analysis — never your Terraform state or cloud keys.

## License

MIT — see [LICENSE](LICENSE).
