Tweak guide
Tweak stores each bot clone's configuration in one place. A project (bot) defines its settings and their defaults in a schema; each clone keeps only what it changes; at startup, and at the start of each work cycle, the bot fetches its merged config.
How it fits together
project (one bot, e.g. refill-bot)
├─ schema which settings exist, their types, rules and DEFAULT values
└─ clones one per deployment (site12-01, site12-02, …)
└─ values ONLY what this clone changes from the defaults
config a bot receives = schema defaults, overlaid with the clone's values
- Change the schema to add, remove or rename a setting, or to change a default for everyone.
- Edit a clone when one deployment needs something different.
- Bulk edit when several clones need the same change, or each needs its own.
Secrets (API keys, tokens, passwords) don't belong in Tweak. They stay in the secret manager.
Getting started
- Connect: paste your
tweak_live_…key at the top right. It's kept in this browser tab only and cleared when the tab closes. - Create a project: + New in the sidebar. Give it an ID (lowercase letters, digits,
._-), a name, and a schema; a starter schema is filled in for you. - Create a clone: + New clone on the Clones tab. Leave the values empty to use every default, or set only what differs.
- Copy the environment ID shown after creating the clone. It goes in that deployment's
.envasTWEAK_ENV_ID, and it's how the bot fetches its config.
Editing a clone
Click a clone in the Clones tab. Each setting from the schema is listed under its group:
| Column | Meaning |
|---|---|
| Setting | Human name, path (e.g. /schedule/startAt) and description. |
| Type | The type, its rules (format, min/max, list limits) and the default. |
| Value | What this clone gets. The input matches the type: text, number, true/false, a dropdown for fixed choices, or JSON for lists and objects. |
| From | clone means this clone sets its own value; default means it follows the schema. |
- Save stores the value for this clone only.
- Reset removes the clone's value, so it follows the default again, including future changes to that default.
- What the bot receives shows the merged config exactly as the bot gets it.
- Rotate ID and Delete are for leaked IDs and retired clones; see If an environment ID leaks.
- Advanced: raw merge patch applies a JSON patch: objects merge, lists replace,
nullresets a value to its default.
If someone else edited the clone after you opened it, your save is refused with "clone changed since you read it". Click Reload, check their change, and try again. Nothing is overwritten silently.
Bulk edits
- Same change for several clones: tick the clones, write one patch (e.g.
{"schedule": {"endAt": "20:00"}}). - Different change per clone: one patch per clone, e.g.
{"site12-01": {"patch": {"queues": ["am"]}}, "site12-02": {"patch": {"queues": ["pm"]}}}. The template button fills in the selected clones.
Preview shows every clone's before and after. Apply unlocks only after a preview of that exact edit. It's all or nothing: if one clone would fail, none are changed.
Want the same value on every clone? Change the schema default instead. A bulk edit copies the value into each clone, and those clones then stop following the default.
Changing the schema
- Open the Schema tab and edit the JSON.
- Click Preview changes. It lists:
- Values that will be deleted: clone values for settings you removed.
- Clones that won't match: values that break a new rule. The change still goes through; fix those clones afterwards.
- Click Apply. Keep the list of deleted values it shows: it's the only record of them.
| Goal | How |
|---|---|
| Add a setting | Add it with a default. Every clone gets the default; nothing else changes. |
| Change a default for everyone | Edit its default. Clones that set their own value keep it. |
| Remove a setting | Delete it and preview: clone values for it will be deleted. |
| Rename a setting | Add the new one → copy values with a bulk edit → remove the old one. The last preview should show nothing you care about being deleted. |
| Bring back a removed setting | Re-add it. Clones start from the default; restore old values from the saved list with a bulk edit. |
Writing a schema
Schemas use JSON Schema. The top level is an object, and each setting lives under properties:
{
"type": "object",
"properties": {
"schedule": {
"type": "object",
"title": "Schedule",
"description": "When the bot works, in pharmacy local time.",
"properties": {
"startAt": {
"type": "string", "pattern": "^\\d{2}:\\d{2}$", "default": "08:00",
"title": "Start time", "description": "HH:MM"
}
}
},
"queues": { "type": "array", "items": { "type": "string" }, "minItems": 1,
"default": ["main"], "title": "Work queues" },
"llmModel": { "enum": ["sonnet", "opus"], "default": "sonnet", "title": "LLM model" }
}
}
| Keyword | Use |
|---|---|
type | string, integer, number, boolean, array, object |
default | The value every clone gets unless it sets its own. Must satisfy the setting's own rules. |
title, description | Human name and help text, shown here and to LLMs. |
enum | A fixed list of allowed values (shown as a dropdown). |
pattern | A regular expression a string must match. |
minimum, maximum, multipleOf | Number limits. |
minItems, maxItems, uniqueItems, items | List rules. |
internal, admin | Reserved flags, true/false, on a field or a group (covering everything inside). Planned: internal = visible only to internal TJM staff; admin = only admins can change it. Shown as tags now, not enforced yet. |
additionalProperties | For maps with free-form keys, e.g. {"type": "number"} for per-queue weights. |
Tweak's own rules: the top level must be "type": "object"; bad defaults are rejected when you save; objects with properties are closed, so an unknown key (a typo like queue for queues) is rejected instead of silently ignored.
Badges and fingerprints
| Badge | Meaning |
|---|---|
| valid / invalid | Whether the clone's values match the current schema. An invalid clone still gets its config, with the errors listed. |
| applied | The bot has fetched its current config. |
| not yet | The config changed since the bot last fetched. It picks up the change at its next work cycle. |
| never fetched | No bot has used this clone's environment ID yet. |
The config fingerprint identifies the exact config a clone receives. Clones with the same fingerprint have identical config. Bots log it with every result, so a result can be traced back to the config that produced it.
Versions (v1, v2…) count edits and protect against two people overwriting each other. Fingerprints describe content. A schema edit that doesn't affect a clone (a label, say) keeps its fingerprint the same.
Connecting a bot
Set on each deployment's machine:
TWEAK_URL=https://<tweak-host> TWEAK_ENV_ID=tweak_env_… # the clone's environment ID
The bot calls GET /api/v1/config with Authorization: Bearer $TWEAK_ENV_ID at the start of each work cycle. When nothing changed it gets an empty 304, so this is cheap, and changes apply within one cycle without a restart. Copy the ready-made loader from docs/recipes.md in the Tweak repo. It keeps a local copy for when Tweak is unreachable.
If an environment ID leaks
The environment ID is the only thing protecting a clone's config. If one ends up somewhere it shouldn't, such as a ticket, a chat or an old machine:
- Rotate ID (on the clone): issues a new ID, and the old one stops working immediately. The clone's settings are kept. Put the new ID in the bot's
.envasTWEAK_ENV_ID; until then the bot runs on its cached config. - Delete (on the clone): permanently removes the clone, its values and its ID. You type the clone's name to confirm. The deleted values are shown afterwards in case you need to recreate it.
Errors and fixes
| Message | What to do |
|---|---|
| values don't match the schema | The listed path has a wrong value (e.g. '7am' where HH:MM is expected) or an unknown setting name. Fix it and save again. |
| clone changed since you read it | Someone else edited it. Reload, check their change, retry. |
| schema changed since you read it | Same, for the schema. Refresh the project. |
| already exists | Pick another ID, or edit the existing one. |
| not found | Check the ID; IDs are lowercase. |
| Invalid or revoked API key | Your key was revoked or mistyped. Ask for a new one. |
| …isn't valid JSON | A JSON box has a syntax error, such as a missing quote or a trailing comma. |
API and LLM access
- Everything here is also available over HTTP under
/api/v1. Seedocs/guide.mdin the Tweak repo for a step-by-step walkthrough withcurl. - LLM tools (Claude Code, Cursor) can use Tweak through MCP at
/mcpwith the same key:claude mcp add --transport http --scope user tweak https://<tweak-host>/mcp \ --header "Authorization: Bearer ${TWEAK_KEY}"Bulk edits and schema changes preview by default there too. LLMs can rotate an environment ID but can't delete clones; deletion is only available here and in the API.