Configuration
Config file format, agent overrides, capture settings, storage, and merge rules.
TokenJam reads its config from ~/.config/tj/config.toml (created by tj onboard). All settings can be overridden via the CLI, the REST API, or the web UI. The file is the source of truth and is regenerated when you save.
Example
# ~/.config/tj/config.toml (generated by tj onboard)
[defaults.budget]
daily_usd = 10.00
[agents.my-email-agent]
description = "Personal email management agent"
[agents.my-email-agent.budget]
daily_usd = 5.00
session_usd = 1.00
[[agents.my-email-agent.sensitive_actions]]
name = "send_email"
severity = "critical"
[agents.my-email-agent.drift]
enabled = true
baseline_sessions = 10
token_threshold = 2.0
[capture]
prompts = false
completions = false
tool_outputs = false
[storage]
path = "~/.config/tj/telemetry.duckdb"
retention_days = 90
Merge rules
Budget limits merge per-field: each agent inherits [defaults.budget] unless overridden. Setting daily_usd on one agent doesn’t reset session_usd to nothing.
Plan tier and framing
TokenJam knows two distinct budget concepts. Don’t conflate them.
[defaults.budget]/[agents.<id>.budget]— per-agent alert thresholds (daily_usd,session_usd) checked on every span.[budget.<provider>]— per-provider config: your declared plan tier, and an optional cycle ceiling used by thebudget-projectionanalyzer.
tj onboard prompts for your plan tier and writes it to [budget.<provider>] plan. You can set it non-interactively with tj onboard --plan <tier>, or edit it by hand:
[budget.anthropic]
plan = "max_5x" # api | pro | max_5x | max_20x | plus | team | enterprise | local
# usd = 200 # optional cycle ceiling — only used by budget-projection
[budget.openai]
plan = "plus" # api | plus | team | enterprise
The plan tier drives how dollar figures are rendered everywhere — the CLI, the REST API, and the web UI all read one shared framing rule rather than deciding on their own:
| Plan tier | How figures are shown |
|---|---|
API (api) | Dollar costs verbatim. |
Subscription (pro, max_5x, max_20x, plus, team, enterprise) | Token-share framing — ”% of cycle” and “implied API value”, never a spend claim. |
Local (local) | Tokens only. No dollars. |
| Unknown | Dollars suppressed with a hint to re-run tj onboard --reconfigure. |
This is why a Max-plan user sees quota-share language while an API user sees dollars: the plan tier, not the command, decides the framing.
Capture settings
By default, prompt and completion text are not stored. Flip [capture] flags to record them locally. Useful for debugging, but the data only ever exists on your machine.
| Flag | What it records |
|---|---|
prompts | The user/system prompt sent to the model. |
completions | The model’s response text. |
tool_outputs | The raw return value of each tool call. |
When capture.* is false, only metadata (tokens, cost, latency, span structure) is stored.
Custom model rate overrides
TokenJam ships a packaged model rate table (tokenjam/pricing/models.toml, USD per million tokens) that prices most public models with zero configuration. These are model token rates only — the numbers used to turn token counts into a cost estimate. They are not a product plan.
Inspect the resolved table any time:
tj pricing list # every (provider, model) with input/output/cache rates
tj pricing list --model claude # filter by substring
tj pricing list --json
The source column tells you whether each row came from the packaged table (packaged) or a local override (override).
The packaged table can’t know your situation — negotiated rates, open-weight models hosted elsewhere, or a self-hosted endpoint. Override rates locally, no PR and no package edit needed. There are two places to put an override, and two ways to key it.
Where overrides live (later wins):
- A
[pricing]section in your main config (tj.toml/.tj/config.toml/~/.config/tj/config.toml) — the project-local home. - A standalone file at
~/.config/tj/pricing.toml, or any path in theTJ_PRICING_FILEenvironment variable.
The project-local [pricing] section wins over the standalone file, which wins over the packaged table.
Two ways to key a rate:
Provider-keyed corrects a rate for a specific provider and model:
[pricing.anthropic]
"claude-haiku-4-5" = { input_per_mtok = 0.80, output_per_mtok = 4.00, cache_read_per_mtok = 0.08, cache_write_per_mtok = 1.00 }
Model-keyed pins a rate to a bare model name regardless of which provider TokenJam inferred. Reach for this when a model’s provider resolves to unknown (open-weight or unattributed traffic). It lives under a reserved models section:
[pricing.models]
"llama-3.3-70b" = { input_per_mtok = 0.59, output_per_mtok = 0.79 }
"claude-haiku-4-5" = { input_per_mtok = 0.50, output_per_mtok = 2.50 } # your negotiated rate
In the standalone ~/.config/tj/pricing.toml, the reserved section is simply [models] and provider-keyed entries stay at the top level:
[models]
"llama-3.3-70b" = { input_per_mtok = 0.59, output_per_mtok = 0.79 }
[anthropic]
"claude-haiku-4-5" = { input_per_mtok = 0.50, output_per_mtok = 2.50 }
Only input_per_mtok and output_per_mtok are required; the cache rates default to 0.0. Dated variants (claude-haiku-4-5-20251001) are priced off the base-name entry automatically.
Lookup order (first match wins): model-keyed override, then provider-keyed override, then the packaged table, then a $0.50 input / $2.00 output flat default (logged once) when nothing matches.
The rate table is cached for the process lifetime, so restart the daemon (tj stop then tj serve) to pick up an edit.
Storage
DuckDB is the only datastore. The file lives at [storage].path and is opened read-write by tj serve and read-only by the MCP server and CLI. retention_days controls automatic pruning of old spans.
Discovery order
tj looks for config in this order:
--config <path>CLI flagTJ_CONFIGenvironment variable./tj.config.toml(current directory)~/.config/tj/config.toml(default, written bytj onboard)
The first match wins.
Verification
Run tj doctor to check that your config is well-formed and matches what the daemon is using:
tj doctor
doctor reports missing fields, invalid types, conflicting overrides, and whether the daemon picked up your most recent edits.