Customer Administrator Guide
This guide is for customer platform administrators: the people who own router configuration, provider credentials, model groups, caller access, quotas, and routine operational changes for one deployment.
Metrum-hosted production is CLI and docs only. You do not edit
Kubernetes objects. Do not look for a ConfigMap or a Secret, and do not use
kubectl. Metrum applies configuration through the customer CLI; use the base
URL assigned to your deployment.
| Deployment shape | Where config lives | How you change it |
|---|---|---|
| Private managed (Metrum-hosted) | Platform runtime bundle | metrum-genai-smartrouter-fleetctl customer from a release binary package |
| Self-hosted Compose / binary | Files on the host (config.yaml, env.json, license.json, mode 0600) | Edit files, then restart |
| Self-hosted Kubernetes (you operate the cluster) | One Kubernetes Secret with config.yaml, env.json, and license.json mounted under /app/config/ | Update that Secret, then roll the Deployment (Recreate for SQLite PVC). Never put production runtime files in a ConfigMap. See Deploy To Kubernetes. |
Model group names, hostnames, and allow lists are deployment-defined. Call /v1/models with each caller token to confirm what that token may request.
For token format and identity rules, see User Key Generation. For caller config fields, see Caller Tokens. For self-hosted install mechanics, see Installation.
Prepare Initial Configuration
Every deployment needs three protected artifacts:
config.yaml— model groups, provider catalog metadata, routing targets, callers, users, projects, admin auth, and usage settings. Start from the packagedconfig.example.yamlin your release and adapt it to your deployment.env.json— provider API keys and other upstream credentials as environment variable names and values. Keep this file mode0600and out of version control.license.json— Metrum-issued license file mounted at the path configured inserver.license.path.
Before first production traffic:
- Define
users,projects, andproject_membershipsfor every caller you plan to issue. - Define
models.*groups andproviders.*.modelscatalog entries for the upstream routes you will activate. - Choose SQLite or PostgreSQL usage storage according to your installation path (Docker Compose, Binary, Kubernetes).
Validate YAML/JSON syntax locally. On self-hosted installs you may also run:
metrum-genai-smartrouterctl config validate --config /path/to/config.yaml
(metrum-genai-smartrouterctl validates and diffs local config; it does not activate config on a managed hostname by itself.)
How To Update Config (Private Managed)
Do not edit ConfigMaps or Secrets. Metrum applies configuration through the customer CLI. Every mutating command publishes a new runtime bundle and an unsigned workspace manifest. Activation is a signed customer create. Rollback is the previous signed revision, not kubectl rollout undo.
Your operator supplies --profile-ref, --license-ref, and --runtime-bundle-ref. Replace <customer-id> and paths with your deployment values.
Download live config
metrum-genai-smartrouter-fleetctl customer get-config \
--customer-id <customer-id> \
--profile-ref "<profile-ref>" \
--runtime-bundle-ref "<runtime-bundle-ref>" \
--license-ref "<license-ref>" \
--config-out /protected/config.yaml \
--env-out /protected/env.json
Stdout is safe JSON (config_sha256, env_sha256, model_group_count, paths). The files are mode 0600. Pass --force only when you intend to overwrite local copies.
Edit locally, then validate. To activate a full replacement, use customer publish-runtime-bundle with the edited files, then signed customer create. For a YAML mapping merge, use customer update-config --patch-file.
List caller keys
metrum-genai-smartrouter-fleetctl customer list-callers \
--customer-id <customer-id> \
--profile-ref "<profile-ref>" \
--runtime-bundle-ref "<runtime-bundle-ref>" \
--license-ref "<license-ref>"
Stdout is a JSON array of caller records with user, project, membership, allow list, and configured rate/quota fields. The CLI does not group or chart this data; keep the JSON if you want later analysis. Hashes and raw tokens are omitted.
Add a caller key
customer grant-caller generates a token, writes --token-out (mode 0600, refuse overwrite), publishes a bundle that adds the hashed caller row, and writes an unsigned manifest. Distribute the token file once through your approved channel. Sign and customer create to activate.
Revoke or disable a key
metrum-genai-smartrouter-fleetctl customer revoke-caller \
--customer-id <customer-id> \
--profile-ref "<profile-ref>" \
--runtime-bundle-ref "<runtime-bundle-ref>" \
--license-ref "<license-ref>" \
--caller-id <caller-id> \
--status disabled
--status may be disabled (default), rotated, expired, or suspended. The caller row stays in config so reports keep token_id. Sign and customer create to activate.
Check live quota remaining
Quotas are per caller key, not per users[] record. To inspect one user, pass --owner-user (repeatable) so every caller owned by that user is listed.
metrum-genai-smartrouter-fleetctl customer quota-status \
--customer-id <customer-id> \
--admin-basic-file /protected/basic-admin \
--owner-user <user-id>
--admin-basic-file is a mode-0600 username:password file for browser-admin / reports-admin. Ordinary caller tokens cannot read another key's remaining budget. The same HTTP route is GET /admin/reports/api/quota-status.
On self-hosted installs, a single caller can still use GET /v1/usage with that caller's token.
Update quota limits
This changes configured rate / quota / lifetime budgets. It does not reset used day/month/lifetime counters.
metrum-genai-smartrouter-fleetctl customer update-quota \
--customer-id <customer-id> \
--profile-ref "<profile-ref>" \
--runtime-bundle-ref "<runtime-bundle-ref>" \
--license-ref "<license-ref>" \
--owner-user <user-id> \
--day-tokens 40000000 \
--tpm 400000
You may pass --caller-id and/or --owner-user (repeatable). Sign and customer create to activate.
Activate, verify, rollback
metrum-genai-smartrouter-fleetctl customer create \
--customer-id <customer-id> \
--sign-with-key /protected/lifecycle_approval_private_key.b64
metrum-genai-smartrouter-fleetctl customer status \
--customer-id <customer-id> \
--profile-ref "<profile-ref>"
metrum-genai-smartrouter-fleetctl customer smoke \
--customer-id <customer-id> \
--token-file /protected/CALLER_TOKEN.txt \
--model <group-from-v1-models>
Smoke checks /readyz, caller-filtered /v1/models, and one OpenAI Chat completion that returns exact OK for the chosen group.
Greenfield: customer bootstrap chains publish → manifest → sign → deploy → grant caller → sign → deploy → smoke. Your operator supplies the rewrite and bundle-size steps when they apply.
Retire an instance: contact your Metrum operator. Instance deletion is a governed lifecycle action, not a routine self-service flow.
Self-Hosted: First Start And Restart
Docker Compose or binary
- Place reviewed
config.yaml,env.json, andlicense.jsonin the protected paths defined by your install layout. - Start or restart the router service (for example
docker compose restart routeror your systemd unit). - Confirm health:
curl -fsS https://<your-router-host>/readyz
Customer-managed Kubernetes
Follow Deploy To Kubernetes. Production runtime files belong in one Secret (config.yaml, env.json, license.json). Use Recreate when the router uses a single ReadWriteOnce SQLite PVC.
Self-Hosted: Update Configuration And Restart
- Edit
config.yaml(andenv.jsonwhen provider keys change) on the deployment host or through your config-management pipeline. - Validate syntax and deployment-specific checks.
- Apply the change:
- Compose / binary: restart the router process or container.
- Kubernetes: update the runtime Secret and roll the Deployment.
- Re-run smokes:
/readyz,/v1/modelswith a representative caller token, one chat completion, and any client surfaces you rely on (Codex CLI, Claude Code, tools, images).
Keep timestamped backups of the prior config before each change. Roll back by restoring the backup and restarting.
Issue Router API Keys (Caller Tokens)
Router caller tokens are bearer tokens applications use as Authorization: Bearer <token>. The router stores only hashes in config.
Generate a token and caller row
Use router-token-gen from the release binary package (or metrum-genai-smartrouterctl callers generate on the deployment host):
router-token-gen generate \
--owner-user <user-id> \
--project <project-id> \
--env <environment> \
--allow <group>[,<group>...] \
--format json
The output includes:
- the raw token (distribute once to the application owner);
- a public
token_idfor reports; and - a
callerJSON object to merge intoconfig.yamlundercallers:.
Ensure users, projects, and project_memberships exist and are active for that owner/project pair before enabling the caller.
On private managed hosts, prefer customer grant-caller so the hashed row is published into the live bundle. Local generate is a draft only until signed create activates it.
Self-hosted activation
- Append the generated caller row to
callers:inconfig.yaml(or merge with your config controller). - Restart or roll the router.
- Ask the caller to run
/v1/modelsand confirm the allowed groups appear.
Rotate or suspend a token
- Generate a new token and caller row (or set
status: rotated/disabledon the old caller). - Apply config and restart (or
revoke-caller+ signedcreateon managed). - Confirm the old token fails and the new token succeeds on
/v1/models.
Never commit raw tokens, token hashes, or provider keys to git, tickets, or chat.
Operational Checklist After Every Change
-
/readyzreturns 200 -
/v1/modelslists expected groups for each affected caller token - One representative completion succeeds for each client surface you support
- Ordinary caller tokens receive
403on/metricsunless explicitly granted metrics-admin access - Usage or admin reports show expected caller/project labels (when reporting is enabled)
- For managed:
customer quota-statusmatches the intended remaining vs configured limits after a quota change
Where To Go Next
- Enterprise Deployment Patterns — topology and operator contracts
- License-Protected Deployments — license replacement and feature gates
- Request Troubleshooting — 401/403/502 diagnostics with request IDs