Skip to main content

Using the ducto CLI

The ducto 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. ducto installed with the Supabase extra:
    pip install "ducto[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):
    ducto migrate "postgresql://user:pass@host:5432/db"

The CLI reads .env from your current working directory if available.

Available Commands

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

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

1. Setting pricing — ducto 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
ducto pricing set pricing.json

# Set pricing from a YAML file with a descriptive label
ducto 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 — ducto pricing get

Displays the currently active pricing configuration as formatted JSON.

ducto 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 — ducto pricing list

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

ducto 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 — ducto 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)
ducto pricing activate 1

# Switch back to version 3 (latest)
ducto 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 — ducto pricing validate

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

# Validate a JSON file
ducto pricing validate pricing.json

# Validate a YAML file
ducto 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 — ducto pricing diff

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

ducto 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 — ducto pricing export

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

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

# 2. Edit the file
vim current.json

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

# 4. Publish the new version
ducto 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
ducto pricing validate pricing.new.yaml

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

# 3. Verify the active config
ducto pricing get

# 4. If something goes wrong within minutes, roll back instantly
ducto 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.