{"id":51183066,"url":"https://github.com/berkeleybop/budget-enforcer","last_synced_at":"2026-06-27T08:02:40.027Z","repository":{"id":362799540,"uuid":"1121975382","full_name":"berkeleybop/budget-enforcer","owner":"berkeleybop","description":"The \"official\" budget enforcer for BBOP Vertex AI (GCP).","archived":false,"fork":false,"pushed_at":"2026-06-05T23:58:34.000Z","size":180,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-06T01:20:31.581Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/berkeleybop.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-23T22:12:21.000Z","updated_at":"2026-06-05T23:58:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/berkeleybop/budget-enforcer","commit_stats":null,"previous_names":["berkeleybop/budget-enforcer"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/berkeleybop/budget-enforcer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berkeleybop%2Fbudget-enforcer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berkeleybop%2Fbudget-enforcer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berkeleybop%2Fbudget-enforcer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berkeleybop%2Fbudget-enforcer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/berkeleybop","download_url":"https://codeload.github.com/berkeleybop/budget-enforcer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berkeleybop%2Fbudget-enforcer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34845749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-27T08:02:39.136Z","updated_at":"2026-06-27T08:02:40.019Z","avatar_url":"https://github.com/berkeleybop.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# budget-enforcer\n\nAutomatic budget enforcement for Vertex AI on GCP. When spending exceeds\na configured threshold, this service disables the API consumer service\naccount's keys, immediately halting Vertex AI API calls.\n\n## How we work with this repo\n\nThis system is designed to be operated via\n[Claude Code](https://claude.com/claude-code). The typical workflow is:\n\n1. **You** make decisions (budget amount, which models to enable, when\n   to deploy)\n2. **Claude** reads `CLAUDE.md` for project context, uses Terraform to\n   make changes, and runs `gcloud` commands for operations and recovery\n3. **The docs** exist so both you and Claude understand what's happening\n   — you can verify Claude's work, and Claude can reason about the\n   system\n\nYou can also operate everything manually — all commands are documented\nin `docs/SOP.md`. But the intended path is collaborative: you describe\nwhat you want, Claude executes it.\n\n## Budget limit\n\nThe monthly spending limit is set via the `monthly_budget_amount` variable\nin `terraform/terraform.tfvars` (default: **$100**). When actual spend\nreaches this amount, the service automatically disables the consumer\nservice account's keys, immediately stopping all Vertex AI API calls.\n\nTo change it, edit `terraform.tfvars` and re-apply:\n\n```bash\ncd terraform/\n# Edit terraform.tfvars: monthly_budget_amount = 200\nterraform apply\n```\n\nA flux estimator checks spend every 5 minutes using API call counts,\ncatching runaway spend much faster than GCP Billing (which lags\n12-24 hours). See \"How it works\" below for details.\n\n## How it works\n\nThis service runs on Cloud Run and has **two independent ways** to\ndetect overspend. Both do the same thing when triggered: disable the\nconsumer service account's JSON keys so applications can no longer\ncall Vertex AI.\n\n### Why two mechanisms?\n\nGCP only tells you what you've spent **12-24 hours after the fact**.\nIf your budget is $5 and you're burning $30/hour on Claude Opus, you\ncould spend $360-720 before GCP even notices. So we have:\n\n1. **The billing path** — waits for GCP's official spend numbers (accurate\n   but slow)\n2. **The flux estimator** — counts your API calls in real time and\n   *estimates* what they probably cost (fast but approximate)\n\nThink of it like a gas gauge: the billing path is like checking your\ncredit card statement (accurate, delayed), while the flux estimator is\nlike watching the pump's dollar counter tick up (real-time, estimated).\n\n### The billing path (accurate, 12-24h delay)\n\n```\nGCP Billing ──► Pub/Sub topic ──► This Cloud Run service ──► Disables keys\n  \"You spent $5\"                    (POST /)\n```\n\nThis is the original mechanism. GCP Billing periodically checks actual\nspend against your budget. When it crosses 100%, it sends a message\nthrough Pub/Sub to this service. Reliable, but the 12-24h lag means\noverspend can be significant.\n\n### The flux estimator (approximate, ~5 min delay)\n\n```\nCloud Scheduler ──► This Cloud Run service ──► Cloud Monitoring\n  (every 5 min)      (GET /check-usage)          \"How many API calls\n                                                   in the last 48h?\"\n                           │\n                           ▼\n                     estimated_spend = call_count × cost_per_call\n                           │\n                     if estimated_spend \u003e= budget × tolerance:\n                           │\n                           ▼\n                     Disable keys\n```\n\nEvery 5 minutes, Cloud Scheduler pokes the `/check-usage` endpoint.\nThat endpoint asks Cloud Monitoring: \"how many Vertex AI API calls\nhappened in the last 48 hours?\" It then multiplies that count by a\nconservative cost-per-call estimate to get an approximate dollar amount.\n\n### Configuration\n\nThere's only one budget number to set: `monthly_budget_amount` in your\n`terraform.tfvars`. Terraform automatically passes this to both\nenforcement paths.\n\nThe flux estimator has a few extra knobs (all optional, defaults work):\n\n| Setting | What it does | Default |\n|---|---|---|\n| `enforcement_tolerance` | How strict to be (0.8 = cut off early, 1.2 = allow some overshoot) | 1.0 |\n| `flux_window_hours` | How far back to look at usage data | 48 |\n| `cost_per_call_fallback` | Per-call cost if token metrics unavailable | $0.30 |\n\nPer-model pricing (Opus vs Haiku vs Gemini, etc.) and prompt cache\ndiscounts are built into `main.py`. A 10% regional premium is applied\nby default for non-global endpoints like us-east5. The pricing table\nshould be checked periodically when new model versions are released —\nsee [`CLAUDE.md`](CLAUDE.md) for sources and what to check.\n\n## Architecture\n\nThree service accounts, each with a distinct role:\n\n| Identity | Role | Purpose |\n|---|---|---|\n| **Personal (Owner)** | Owner | IAM policy bindings on Cloud Run |\n| **Admin SA** | Editor, SA Key Admin, etc. | Runs this Cloud Run service; disables consumer keys |\n| **Consumer SA** | Vertex AI User | Used by applications; gets its keys disabled |\n\n## Setup\n\n### Quick start (new project)\n\n1. Complete the manual prerequisites in [`docs/MANUAL_STEPS.md`](docs/MANUAL_STEPS.md)\n2. Copy and fill in your variables:\n   ```bash\n   cd terraform/\n   cp terraform.tfvars.example terraform.tfvars\n   # Edit terraform.tfvars with your project details\n   ```\n3. Build and push the container:\n   ```bash\n   gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/budget-enforcer .\n   ```\n4. Deploy:\n   ```bash\n   terraform init\n   terraform plan    # Review what will be created\n   terraform apply\n   ```\n   \u003e **Note:** State is stored locally on your machine (no shared backend).\n   \u003e See `terraform/backend.tf` for details on state management and how\n   \u003e to take over an existing deployment.\n5. Create the consumer SA JSON key (see `docs/MANUAL_STEPS.md` step 3)\n6. Distribute the key and Claude Code env config to developers:\n   ```bash\n   terraform output claude_code_env_snippet\n   ```\n\n### Existing deployment\n\nSee [`docs/SOP.md`](docs/SOP.md) for manual deployment steps, recovery\nprocedures, and troubleshooting.\n\n## Repository structure\n\n```\nmain.py                         # Cloud Run service (Flask)\nDockerfile                      # Container definition\nrequirements.txt                # Python dependencies\nCLAUDE.md                       # Project guide for Claude Code and developers\nterraform/\n  main.tf                       # All GCP resources\n  variables.tf                  # Input variables (self-documenting)\n  outputs.tf                    # Operational outputs + Claude Code config\n  versions.tf                   # Provider version constraints\n  backend.tf                    # State management docs\n  terraform.tfvars.example      # Template for your variables\ndocs/\n  SOP.md                        # Full operational runbook\n  MANUAL_STEPS.md               # Steps that cannot be automated\n```\n\n### CLAUDE.md\n\n[`CLAUDE.md`](CLAUDE.md) is automatically loaded by\n[Claude Code](https://claude.com/claude-code) for project context. It's\nalso useful as a human onboarding document — it covers:\n\n- Key concepts and common pitfalls (three-identity model, billing lag)\n- How the two enforcement mechanisms work\n- Terraform workflow and state management\n- Secrets policy\n- Pricing table maintenance checklist (what to check and when)\n- Common tasks as a quick-reference\n\n## Recovery\n\nWhen keys are disabled by the budget-enforcer:\n\n```bash\n# Re-enable the consumer SA key\ngcloud iam service-accounts keys enable KEY_ID \\\n  --iam-account=CONSUMER_SA_EMAIL\n```\n\nSee [`docs/SOP.md`](docs/SOP.md) for full recovery procedures including\nbudget reset, IAM binding restoration, and end-to-end verification.\n\n## License\n\nBSD 3-Clause. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberkeleybop%2Fbudget-enforcer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fberkeleybop%2Fbudget-enforcer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberkeleybop%2Fbudget-enforcer/lists"}