Shapes and workspaces
Why metro works in shapes, and sandbox versus production.
metro has two ways to describe a change: shapes, which are checked offline, and ops, which read your tenant and write a plan. Workspace packs decide which billing intents metro knows about. And every live call names a tenant.
Shapes
A shape is a reviewed, local contract for one kind of change, in JSON or YAML. metro validates it before any Metronome client loads, so a malformed payload never reaches the API.
metro shape list
metro shape explain free-credit-trial
metro validate --shape free-credit-trial --file trial.json --json
metro package preview --shape free-credit-trial --file trial.json --sandbox --jsonA shape encodes the traps for you. free-credit-trial, for example, enforces commit_duration.unit: PERIODS, a string duration.value, starting_at_offset and proration, "so agents never hand-craft this payload".
Each shape has a maturity and blocked modes. Today every shape is local-validated: explain, validate and preview work; apply through the shape is blocked. Live writes go through ops instead. The full list is on Shapes; the rules they check are on Validators.
Ops
An op is a live command with a preflight and an apply: metro customer create, metro contract move-plan, metro ledger-adjustment and the rest. Preflight reads the tenant, so it can check things a shape can't: that an alias is free, that a balance exists, that a contract doesn't overlap. See the safety contract.
| Shape preview | Op preflight | |
|---|---|---|
| Talks to Metronome | No | Reads only |
| Output | A payload plan and a sha256: hash | A plan file with exact requests and a payloadHash |
| Can apply | Not in this release | Yes, with --confirm-hash and a typed token |
Workspace packs
metro is split in two. metro-core knows the Metronome API, the shapes and the evidence gates. A workspace pack adds billing intents on top, like "move a customer to another package". The public build ships the generic pack.
metro workspace list
metro workspace current
metro plan listmetro plan list shows the pack's intents, and metro plan <intent> --sandbox --json maps one to its best shape, the paths it rejects, and the commands to run. The generic pack binds to your own catalog through metro catalog bind; it has no hardcoded IDs.
The active pack resolves in this order: metro workspace use, then METRO_WORKSPACE_PACK, then the session, then the profile, then ~/.metro/config.json, then the built-in default.
Sandbox and production
Sandbox and production are two Metronome tenants behind one base URL, https://api.metronome.com. The key picks the tenant.
| Sandbox | Production | |
|---|---|---|
| Key | METRONOME_SANDBOX_KEY | METRONOME_PROD_KEY |
| Flag | --sandbox | --prod |
| Typed token for a write | --confirm-live-apply=I_UNDERSTAND_THIS_WILL_WRITE_LIVE_METRONOME_SANDBOX | --confirm-prod-apply=I_UNDERSTAND_BILLING_SIDE_EFFECTS |
| When | Always first | After the sandbox readback passes, with a typed yes for that change |
metro refuses a sandbox key equal to the production key, in either direction. A plan's hash covers its tenant, so a sandbox plan can't be applied to production.
Tenant snapshots
metro catalog bind --sandbox --write saves your IDs to ~/.metro/profiles.json, and a versioned model to ~/.metro/snapshots/sandbox.json. metro tenant snapshot diff --sandbox --markdown shows what changed since the last bind, and --against=production compares the two tenants.