CloudCost CLI for Terraform cost estimates and GitHub pull request cost automation.
Project description
CloudCost AI
CloudCost AI is a developer-first FinOps MVP. It has two mechanisms:
- An AI spend proxy powered by LiteLLM.
- A GitHub App webhook that estimates Terraform cost deltas in pull requests with Infracost.
The landing page is served from index.html; the working backend is in backend/app.
Get Started
The first-run path is modeled around a local CLI estimate, then GitHub automation:
.\.venv\Scripts\python -m pip install -e .
cloudcost go
Run cloudcost go from a Terraform project directory. It runs the local estimate, then opens the GitHub App manifest install flow with the required webhook, events, and permissions already filled in. If a team only wants the local estimate, cloudcost still works by itself. Power users can also run cloudcost setup, cloudcost doctor, cloudcost connect-github, or cloudcost analyze --plan tfplan.json directly.
When the package is published, users should install it with:
pipx install cloudcost-cli
cloudcost go
Publishing notes are in docs/publishing-pypi.md.
The signed-in dashboard sidebar links to /get-started, a docs-style first-run guide with macOS, Windows, and Linux command tabs that update the CLI install, setup, Terraform, CI, and GitHub steps. The left docs rail links to styled HTML docs for CloudCost CLI, VPS install, and pricing API reference.
Cloudflare Pages Landing Page
For a free public early-access page, deploy the static pages plus the Pages Functions:
functions/api/waitlist.jsfunctions/api/auth/*functions/api/dashboard/summary.jspackage.json
Use npm run build and set the Cloudflare Pages output directory to dist; the build script copies index.html, assets/, docs/, /login, /signup, /dashboard, /get-started, and /tester-dashboard.
Set DATABASE_URL, AUTH_SECRET, RESEND_API_KEY, and FROM_EMAIL in Cloudflare Pages environment variables. The Functions store early-access emails, users, OTPs, and browser sessions in Neon, and send Resend confirmations without running the Python backend.
See docs/cloudflare-pages-waitlist.md.
One-Click VPS Path
For a customer-facing install, use the VPS bundle instead of the local debug path.
Prerequisites:
- Ubuntu VPS with ports
80and443open - A domain pointed at the VPS
- Docker, or approval for the bootstrap script to install Docker
- A pricing mode:
- Recommended: CloudCost hosted pricing key
- Advanced: Infracost API key for syncing a self-hosted pricing database
Run:
git clone YOUR_REPO_URL cloudcost-ai
cd cloudcost-ai
bash scripts/bootstrap-vps.sh
The script generates local secrets, creates .env, starts Caddy HTTPS, backend, LiteLLM, and Postgres. If the customer chooses strict self-hosted pricing, it also initializes and runs the local Infracost pricing API. It then prints:
- Dashboard:
https://YOUR_DOMAIN/dashboard - Tester runbook:
https://YOUR_DOMAIN/tester-dashboard - GitHub install page:
https://YOUR_DOMAIN/install/github - Webhook URL:
https://YOUR_DOMAIN/api/github/webhook
Open the GitHub install page and click Install on GitHub. GitHub creates a customer-owned GitHub App from CloudCost's manifest and redirects back with the App ID, webhook secret, and private key. CloudCost stores those on the VPS automatically.
After setup, run:
bash scripts/doctor-vps.sh
This path is the intended customer install path. The longer local setup below remains for development and debugging.
Architecture
Browser
-> FastAPI backend
-> static landing page
-> waitlist endpoint
-> GitHub webhook endpoint
-> LiteLLM admin wrapper
-> metadata-only usage ingest
LLM clients
-> LiteLLM proxy :4000
-> OpenAI / Anthropic / other providers
-> metadata-only callback
-> FastAPI /api/llm/usage
GitHub pull_request webhook
-> HMAC validation
-> installation access token
-> locate Terraform plan JSON
-> Infracost Plan JSON API
-> upsert PR comment
Quick Start
- Create local environment config:
Copy-Item .env.example .env
- Fill
.envwith:
DATABASE_URL, preferably a pooled Neon Postgres URL withsslmode=requireGITHUB_APP_IDorGITHUB_CLIENT_IDGITHUB_PRIVATE_KEY_PATHGITHUB_WEBHOOK_SECRETINFRACOST_API_KEYLITELLM_MASTER_KEY- at least one provider key, such as
OPENAI_API_KEY
Put the downloaded GitHub App private key at secrets/github-app-private-key.pem.
Docker Compose mounts ./secrets into the backend container as /app/secrets.
DATABASE_URL is shared by LiteLLM and the backend. LiteLLM uses it for virtual keys and spend/auth state; the backend creates cloudcost_waitlist, cloudcost_usage_events, cloudcost_users, cloudcost_email_otps, and cloudcost_sessions tables for app data. Set APP_DATABASE_URL if you want those app tables in a separate database.
The waitlist endpoint accepts only an email address, stores one row per email, and uses data/waitlist.jsonl only when no Postgres URL is configured. Set SMTP_HOST and SMTP_FROM_EMAIL if you want waitlist signups to receive a confirmation email.
For app signups, set AUTH_SECRET to a long random value. New accounts verify a mail OTP before /dashboard creates a session.
- Start the stack:
docker compose up --build
If Docker Desktop is not installed, run the backend and LiteLLM locally instead:
.\.venv\Scripts\python -m pip install -e ".[dev]"
.\.venv\Scripts\python -m pip install "litellm[proxy]" prisma
.\scripts\start-litellm-local.ps1
In a second terminal:
.\.venv\Scripts\python -m uvicorn backend.app.main:app --reload --port 8000
For the local no-Docker path, set LITELLM_BASE_URL=http://127.0.0.1:4000 in .env.
- Open:
- Landing page:
http://localhost:8000 - Sign in:
http://localhost:8000/login - Sign up:
http://localhost:8000/signup - Dashboard:
http://localhost:8000/dashboard - Get started:
http://localhost:8000/get-started - Tester runbook:
http://localhost:8000/tester-dashboard - Backend health:
http://localhost:8000/healthz - Mechanism map:
http://localhost:8000/api/mechanism - LiteLLM proxy:
http://localhost:4000
GitHub App Setup
Register a GitHub App with:
- Webhook URL:
https://YOUR_DOMAIN/api/github/webhook - Webhook secret: same value as
GITHUB_WEBHOOK_SECRET - Subscribe to only
pull_request - Repository permissions:
- Contents: read
- Pull requests: read and write
- Issues: write
- Metadata: read
The backend handles opened, reopened, synchronize, and ready_for_review pull request actions.
Terraform Plan JSON
The MVP looks for one of these files in the pull request files first, then in the repository tree at the PR head SHA:
plan.jsontfplan.jsonterraform-plan.jsoninfracost-plan.json- files ending in
.tfplan.jsonor.plan.json
Generate a compatible file with Terraform:
terraform init
terraform plan -out tfplan.binary
terraform show -json tfplan.binary > plan.json
When found, the backend runs the local Infracost CLI against the plan JSON, parses diffTotalMonthlyCost, and creates or updates one CloudCost AI pull request comment. In the default self-hosted setup, the plan JSON stays inside your backend runtime; the CLI only talks to your Cloud Pricing API for price lookups.
Self-Hosted Infracost
The default project mode is:
INFRACOST_MODE=cli
INFRACOST_CLI_PATH=infracost
INFRACOST_PRICING_API_ENDPOINT=http://127.0.0.1:4001
That means CloudCost AI does not call the hosted Infracost Plan JSON API. Instead:
- You self-host the Infracost Cloud Pricing API.
- The backend runs
infracost breakdown --path plan.json --format json. - The Infracost CLI requests prices from
INFRACOST_PRICING_API_ENDPOINT. - The backend comments the result on the pull request.
For local Windows development, install the Infracost CLI and make sure infracost is on PATH. For Docker builds, the backend image installs the CLI automatically.
See /docs/pricing-api.html for the full setup path.
LiteLLM Spend Proxy
LiteLLM requires DATABASE_URL for persistent virtual keys, budgets, spend logs, and auth state. With Neon, use the pooled connection string from the Neon dashboard:
DATABASE_URL=postgresql://USER:PASSWORD@HOST/neondb?sslmode=require&channel_binding=require
Create a virtual key through CloudCost AI:
Invoke-RestMethod -Method Post http://localhost:8000/api/llm/keys `
-ContentType 'application/json' `
-Body '{
"user_id": "ada@example.com",
"team_id": "platform",
"models": ["gpt-5-mini"],
"max_budget": 10,
"budget_duration": "30d",
"metadata": {"service": "billing"}
}'
Use the returned key against LiteLLM:
$env:OPENAI_API_KEY = "sk-returned-virtual-key"
Point OpenAI-compatible clients at:
http://localhost:4000/v1
On the VPS bundle, Caddy exposes LiteLLM through the same HTTPS origin:
https://YOUR_DOMAIN/llm/v1
LiteLLM enforces the budgets and forwards metadata-only usage records to /api/llm/usage. The custom callback deliberately omits prompts, messages, responses, choices, and completions.
Local Development
Install dependencies:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
Run tests:
pytest
Run the backend without Docker:
uvicorn backend.app.main:app --reload --port 8000
Important Limits
This is an MVP scaffold, not a production billing system yet.
- Real GitHub, Infracost, and LLM provider credentials are required for end-to-end live calls.
- The JSONL stores in
data/are for local validation; replace them with a database before production. - For production GitHub traffic, put the backend behind HTTPS and keep the webhook secret/private key out of the repo.
- For production LiteLLM, pin image versions, rotate master keys, and use a managed Postgres database.
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 cloudcost_cli-0.1.0.tar.gz.
File metadata
- Download URL: cloudcost_cli-0.1.0.tar.gz
- Upload date:
- Size: 40.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85b7161440642dcba641bbb9851c04ac0b9457186221a6f1e89b91f65b7d5054
|
|
| MD5 |
3211f276724f4fcc7b3f0985e384e15e
|
|
| BLAKE2b-256 |
abe821c63e30be1317707a30c9f79afb0ebe0e12b278e8d9b3221195c419649c
|
File details
Details for the file cloudcost_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cloudcost_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 39.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6575840fa3c7e94620a6643cdb5783bcb7c2de1088d5d085fca4d9f163711cb4
|
|
| MD5 |
611187ca3bf0a2dba8aa1aa5077e015c
|
|
| BLAKE2b-256 |
9284dd3f41b9b8508bdacd2d2bfb5da8d339d6b1a11ce00a0266f74e25a17626
|