Skip to main content

12 - Using the bursar CLI

Every previous notebook drove bursar through Python. In production, publishing and rolling back pricing changes is usually a deploy-pipeline or on-call operation, not a Python script — that's what the CLI is for.

The bursar command-line tool lets you manage your credit pricing configuration directly from the terminal. It connects to your Supabase project via environment variables, validates pricing config files, and supports versioned rollouts with full history tracking.

Prerequisites

Before using the CLI, make sure you have:

  1. bursar installed with the Supabase extra:
    pip install "bursar[supabase]"
  2. Environment variables set:
    export SUPABASE_URL="https://your-project.supabase.co"
    export SUPABASE_SERVICE_ROLE_KEY="your-service-role-key"
  3. Database migrated (tables and RPCs created):
    bursar migrate "postgresql://user:pass@host:5432/db"

The CLI reads .env from your current working directory if available (via python-dotenv) and loads any variables it defines — but variables already set in your shell environment always take precedence over the .env file, so export SUPABASE_URL=... in your shell always wins over a .env entry of the same name.

Available Commands

bursar pricing set <file> [--label <msg>] # Apply new pricing (always creates version)
bursar pricing get # Show current active config
bursar pricing list # List all versions (* = active)
bursar pricing activate <version> # Switch to any historical version
bursar pricing validate <file> # Dry-run validate without applying
bursar pricing diff <v1> <v2> # Show changes between versions
bursar pricing export <version> # Dump a specific version as JSON

Each command is a separate subcommand under bursar pricing. Let's explore each one.

1. Setting pricing — bursar pricing set

The set command reads a JSON or YAML file, validates it, and publishes it as a new version in the database. Previous versions are preserved — nothing is ever overwritten in place.

Always creates a new version. Every call increments the version counter. There is no "upsert" or "skip if exists" — each set is an atomic publish.

# Set pricing from a JSON file
bursar pricing set pricing.json

# Set pricing from a YAML file with a descriptive label
bursar pricing set pricing.prod.yaml --label "deploy-42: reduced haiku pricing"

The --label flag adds a human-readable message to the version, making it easier to identify in list and diff outputs later.

What happens inside:

  1. The file is parsed (JSON or YAML).
  2. The data is validated against two Pydantic schemas:
    • PricingConfigData — ensures all required fields exist and are correctly typed.
    • PricingConfig — validates expression safety (no eval/exec).
  3. An RPC call publishes the config: deactivates the old active row and inserts a new one with version = max(version) + 1.
  4. The full config history is preserved in the credit_pricing_config table.

2. Reading active pricing — bursar pricing get

Displays the currently active pricing configuration as formatted JSON.

bursar pricing get

Example output:

{{
"id": "a1b2c3d4-...",
"config": {{
"version": 1,
"models": {{"_default": "input_tokens * 10 + output_tokens * 30"}},
"tools": {{"_default": "tool_calls * 0"}},
"fixed": {{"batch_job": 100}},
"min_balance": 5000
}},
"version": 1
}}

If no pricing has been configured yet, the command exits with an error.

3. Version history — bursar pricing list

Lists every pricing version that has ever been published, newest first. The active version is marked with *.

bursar pricing list

Example output:

* v3 (id=a1b2c3d4...) deploy-42 2026-06-25T10:30:00
v2 (id=e5f6g7h8...) initial-haiku 2026-06-24T14:00:00
v1 (id=i9j0k1l2...) first-setup 2026-06-23T09:15:00

Each row shows:

  • * if this version is currently active
  • The version number (v3, v2, …)
  • A truncated UUID for direct database queries
  • The label (if provided during set)
  • The creation timestamp

This historical record lets you audit all pricing changes over time.

4. Switching versions — bursar pricing activate

Activates a specific historical version. This is a rollback when you go to an older version, or a restore when you go forward. No new version is created — the existing version's config is re-activated.

# Switch to version 1 (rollback to initial pricing)
bursar pricing activate 1

# Switch back to version 3 (latest)
bursar pricing activate 3

How it works:

  1. Deactivates all configs in the credit_pricing_config table.
  2. Sets active = true on the requested version.
  3. The change is atomic — between steps 1 and 2, there is a brief moment where no config is active, but the RPC runs inside a single transaction.

Why this matters: If a pricing change causes unexpected costs (e.g., a model expression bug that overcharges users), you can roll back to a known-good version instantly — no redeploy needed.

5. Validating configs — bursar pricing validate

Dry-runs validation against a pricing file without publishing it.

# Validate a JSON file
bursar pricing validate pricing.json

# Validate a YAML file
bursar pricing validate pricing.prod.yaml

On success:

Pricing config is valid.

On failure (e.g., invalid expression, missing models field):

Validation failed: 1 validation error for PricingConfigData
models -> dict[str,str]
Field required [type=missing, ...

Use this in CI/CD pipelines to catch pricing errors before deployment.

6. Comparing versions — bursar pricing diff

Shows a unified diff between two pricing versions. Useful for review before activating a rolled-back version.

bursar pricing diff 1 3

Example output:

--- v1
+++ v3
@@ -1,5 +1,6 @@
{{
+ "version": 1,
"models": {{
"_default": "input_tokens * 5 + output_tokens * 15"
}},
+ "plans": {{
+ "free": {{
+ "id": "free",
+ "free_allowance": 5000
+ }}
+ }},
"min_balance": 5000
}}

The diff tells you exactly what changed — model rates, added plans, tool pricing, everything. No guessing.

7. Exporting versions — bursar pricing export

Exports a specific version's config as JSON. Combine with validate for a safe edit-publish workflow:

# 1. Export the active config
bursar pricing export 3 > current.json

# 2. Edit the file
vim current.json

# 3. Validate the edited file
bursar pricing validate current.json

# 4. Publish the new version
bursar pricing set current.json --label "tweaked haiku rates"

This workflow ensures you never accidentally deploy broken pricing.

Putting it together — deployment workflow

Here's the full CI/CD pipeline for pricing updates:

# 1. Validate first
bursar pricing validate pricing.new.yaml

# 2. Publish with a descriptive label
bursar pricing set pricing.new.yaml --label "$(git rev-parse --short HEAD): reduce opus rate"

# 3. Verify the active config
bursar pricing get

# 4. If something goes wrong within minutes, roll back instantly
bursar pricing activate 2

Key design principles:

  • All changes are versioned. No silent overwrites.
  • Labels provide context. Include git commit hashes, deploy IDs, or Jira tickets.
  • Validate before set. Catches syntax and expression errors early.
  • Activate for rollbacks. No redeploy required — instant switch.
  • History never deleted. Full audit trail in credit_pricing_config table.

The CLI turns pricing management from a manual database operation into a safe, scriptable workflow. This closes the loop from Notebook 00: you now know the concepts, the Python API for every feature, and how to operate pricing changes safely in production.