Skip to content
ThinkWatch
Start typing to search the docs.
ThinkWatch Lite · Features

Features#

A page-by-page reference to what ThinkWatch Lite shows and does. The Lite page gives the short version.

The main window has ten pages: Overview, Traffic, Clients, Keys, Upstreams, Routing, Security, MCP, Plugins and Settings.

Usage and cost#

The Overview page covers the last 24 hours, 7 days, 30 days, a custom range or a single day. Six metrics sit at the top: Tokens, Cost, Requests, Cache hits, First token and Traffic, each with its figure and the change against the previous period of the same length; the change is left out when the previous period has no records. The selected metric is drawn below as one large chart with axes: tokens, cost, requests and traffic as shaded areas, with failed requests and downloads as a thin second line; cache hits as the hit rate; first token as the median and the 95th percentile, with stretches without requests bridged by a faint dashed line. A period still in progress is dashed, and periods before request records begin are marked Not recorded instead of being drawn as zero. Hovering a period shows its values and the models that used the most. With Cache hits selected, the net savings the cache brought and the split of input between cache reads, uncached input and cache writes are listed as well; Traffic is what requests sent to their upstreams and received from them (see Traffic and sessions).

Below the chart, Distribution shows the selected metric by model, upstream or key, and Quotas and balances lists the subscription windows and balances the upstreams report; it is hidden when no upstream reports any. Past year is a heatmap with one cell per day for tokens, cost or requests, with the total, daily average, busiest day and streak; clicking a day switches the overview to that day, and the heatmap stays in place. Breakdown at the bottom lists the requests, tokens, cost, cache hits, first token and generation speed by model, upstream or key (generation speed by model and by upstream only), and a row opens the matching requests in Traffic; Distribution and Breakdown follow the same choice of model, upstream or key. Failed requests, tool calls that were cut off, upstreams that cannot be used and models without a price go to the notices (see Menu bar, system tray and notifications) rather than the overview. The notice that request recording could not start and the banners for a lost connection or rejected configuration can be ignored, and Show again in Settings › General brings every ignored warning back. In Live, Tokens, Cost and Requests follow the last ten minutes, while the other metrics, Distribution and Breakdown cover the last 24 hours. Until a range has been chosen, the overview covers the last 24 hours.

The cost figure states how much of it is estimated, for instance for a response that was cut off before it finished. Requests whose model has no price, and requests whose upstream reported no usage, are counted separately and never added in as zero. Prices come from price sheets: the default one follows LiteLLM’s public prices and is refreshed once a day, and a custom one applies a multiplier and its own prices for particular models, as needed for a relay whose prices differ from the official ones. A subscription account such as a ChatGPT sign-in is priced from the price sheet like any other upstream, and an upstream such as a local model can be set to free. Each request’s cost is fixed when the request finishes, and the request records the price sheet and the date of the prices it was costed with.

Until the first request has gone to an upstream, the Overview page shows a get-started checklist instead: add an upstream, connect a client, send a first request. Other pages show a hint for the next step where it applies, such as connecting a client once an upstream exists. A hint can be hidden, and Settings › General shows the hidden ones again.

Traffic and sessions#

The Traffic page lists requests as they arrive: status, key, model, upstream, time to first token and total time (with the generation speed on hover), tokens (with the upload and download on hover; a running request shows its current generation speed here instead) and cost, with marks for a converted API format, redacted keys, text deleted or replaced by the content filter and a blocked or suspicious tool call. Long upstream, model and key names are cut short, and hovering shows them in full. The list can be searched (path, key, upstream, model and error message), filtered by key, upstream and model, or narrowed to failed or unpriced requests. It holds the latest 2,000 requests, and a search or filter also runs over the stored history, further back on request. With content search on, it also covers what each request newly sent (the last user turn, tool results included) and its answer (tool calls included), for as long as payloads are kept; a match shows its excerpt under the request. The Sessions view groups the requests of one conversation into turns, with the input tokens and the cost of each turn. Each turn also shows how full the model’s context window was: the tokens sent to the model over the window, with cache reads as their own segment. Opening a turn shows an estimate of how much the system prompt, the tool definitions, the earlier conversation and the last user message took. A session’s Conversation tab replays it turn by turn: the messages each turn added and its answer, with text, folded thinking, tool calls beside their results, and images by type and size. Changes to the system prompt and restarts after compaction are marked, and a turn whose bodies are past retention, were too large to keep whole or cannot be read says so. Each turn opens its request.

A request opens into its timeline, its routing (the rule it matched, the group it went through and each attempt with its status and duration), its content, and its usage and cost. The timeline gives the proxy the request went out through (Egress, or Direct) and how much it sent to its upstreams and received from them (Upload and Download), measured as it crossed the network: after the gateway’s own compression, before decoding, and summed over failover attempts. A request from DeepSeek Harness also shows the size of the session log it carried, the whole conversation the client attaches to every request; the gateway removes it before a request goes to an upstream other than DeepSeek. A finished request can be sent again, unchanged, to another upstream after an estimate of its cost, and the two responses are shown side by side. When the gateway’s own code fails during a request, the request’s error says what failed and where.

The Content tab has two views, Parsed and Raw, and both are live while the request runs. Parsed shows the conversation, with the answer appearing as it streams, thinking and tool-call arguments included. Answers, thinking and system prompts render their Markdown, so headings, bold, lists, tables and code blocks appear formatted, here and in a session’s Conversation tab; HTML in an answer is shown as text, links are not clickable and images are not loaded. Raw shows the request and response lines, headers and bodies, with each streamed event appended as it arrives; Follow latest keeps the newest in view. In Raw, Client side and Upstream side show what the client and the upstream each exchanged with the gateway, which differ when the API format is converted or a plugin rewrites the request. Keys are replaced in both views, as in stored requests. The places a security rule matched are marked in both views, in the colors of the security log, and a folded part that holds one carries a dot.

The attempts also show an upstream passed over at its concurrency limit (At its concurrency limit), while rate limited (Rate limited) or at a usage limit (Usage limit reached, or Usage limit reached for this period for a day, week or month), an attempt answered with 429 and the pause it caused (429 · Paused 30 s), the time spent queued for any of these, an upstream given up because it sent no content within the no-response timeout (No response · 300 s at the default), with the input tokens that upstream may have billed for it, and the attempt under way when a request was aborted (Aborted). A retry after an upstream failed temporarily is an attempt of its own, marked Retry 1 with the wait before it (Waited 2.1 s), and an attempt whose reasoning effort a rule changed, or that was resent with another level after the upstream refused one, says which level was asked for, which was sent and why, such as Reasoning effort max was rejected by the upstream; resent with high. A request that is waiting shows Queued, or Retrying while it waits to be sent again, with the upstreams it waits for in place of the upstream. A request refused by its key’s usage limit, or not taken because every upstream stayed at its concurrency limit, stayed rate limited, was at a usage limit, or none became available in time, shows Usage limit, At capacity, Rate limited, Upstream limit or Wait timed out in place of an upstream, and a connection in the Responses API’s WebSocket mode is listed one request per turn, each with its own usage and cost.

A request in progress can be stopped with Abort request in its menu. Abort session, in a session’s menu or at the top of its panel, stops every request in progress in that session after a confirmation. The upstream call is dropped at once, the client receives an error, and the request is marked Aborted, in grey; its upstream is not paused for it. Turns on a Responses API WebSocket connection cannot be aborted.

Client setup#

The Clients page points Claude Code, Claude Desktop, Codex, opencode, Pi, oh-my-pi, Grok Build, Qwen Code, Hermes Agent, Zed, Aider and DeepSeek Harness at the gateway. Before anything is written, it lists the fields that change and what else the change affects (the ChatGPT desktop app, for instance, reads the same configuration file as Codex), shows the full diff and backs up the original file. Only the settings that point the client at the gateway change, and each client receives a key of its own. Claude Desktop is connected through its official third-party inference mode, and the page lists each of the files that change for it; a Claude Desktop managed by an organization is left as it is. Claude Desktop accepts only Claude model names; when no upstream offers a Claude model, the takeover asks which model it should use and adds a routing rule for its key, which cancelling the takeover removes. A connected client can be restored at any time, on its own or together with all the others; a restored Codex keeps a plain OpenAI entry in place of the gateway’s, so sessions started while it was connected can still be opened. opencode (v1 and v2), Pi, oh-my-pi, Grok Build and Qwen Code also get the list of models their key can use on the gateway, with each model’s context window and, where the client uses them, its output limit and whether it reasons and takes images; when that list or a model’s specs change, the page offers to update it, through the same diff. DeepSeek Harness is covered in its web app, its desktop app and headless runs, which all read the same configuration; models used through a DeepSeek account signed in to the desktop app still go to DeepSeek directly. Cursor, Continue and Antigravity CLI come with step-by-step instructions and a key created for them. For every client the page shows whether it is in use, waiting for its first request or not in effect, and its requests over the last 24 hours.

On Windows, Claude Code and Codex installed inside WSL appear in a group of their own for each distribution, next to the clients on the computer itself. They are pointed at the gateway on Windows, restored and diagnosed the same way, each with a key separate from the Windows copy, and their files are edited through \\wsl.localhost. They are given 127.0.0.1, the same address as the clients on Windows, which WSL reaches in two setups:

  • WSL 1, which shares the network with Windows.
  • WSL 2 with mirrored networking: networkingMode=mirrored under [wsl2] (or the older [experimental]) in %USERPROFILE%\.wslconfig. It needs Windows 11 22H2 or later and WSL 2.0.5 or later.

WSL 2 uses NAT networking by default, and the gateway on Windows cannot be reached from inside WSL that way; the gateway does not listen on the WSL virtual adapter for it. The WSL group then explains this instead of offering to connect, and offers to switch to mirrored networking: networkingMode in .wslconfig is added or changed, and nothing else in the file is touched, through the same diff, confirmation and full backup as a client. The switch takes effect once WSL restarts, which the page also offers (wsl --shutdown, which stops every running distribution). A full uninstall leaves .wslconfig as it is, and its backup is kept. On Windows 10 and Windows 11 21H2, which have no mirrored networking, and with a WSL older than 2.0.5, the group says so. When connected to a remote core, clients in WSL are pointed at the server like those on Windows, whatever the networking.

Keys#

Clients reach the gateway with a key, on the local machine as well. The Keys page lists the default key, used by clients that were not given one of their own, and a key for each connected client, labelled with the client it belongs to so that its requests can be told apart in Traffic. Each key has a route, the models it may use (all, none, or chosen models and patterns such as gpt-5*), an optional limit on concurrent requests, optional usage limits, and its requests and cost over the last 24 hours. A key can be disabled, which rejects every request made with it, or rotated; rotating writes the new key into the configuration of the client that uses it.

Usage limits keep one client from using up a budget: each caps the key at a number of requests, tokens or a cost in USD per minute, hour, day, week or month, or every few minutes or hours, such as every 5 hours. When a minute or hour limit is full, a request waits for room within Wait at most in Settings › Failover and is refused if none frees up; once a day, week or month limit is used up, the key’s requests are refused until it resets, with the key marked Limit reached. Token limits can count cache reads, models without a price count as zero toward a cost limit and are listed in the key dialog, and a daily, weekly or monthly limit raises a notification at 80% and when it is reached.

Upstreams#

Upstreams are the services requests are forwarded to: API keys for Anthropic, OpenAI, Google Gemini, DeepSeek or any compatible endpoint, Amazon Bedrock (with an API key, access keys or an AWS profile), a ChatGPT account or a Z.ai / BigModel account signed in from the app, relays such as OpenRouter, Sub2API and New API / One API, a ThinkWatch Enterprise gateway, and local models such as Ollama. When a client and an upstream use different API formats, requests are converted between Anthropic Messages, OpenAI Chat Completions, OpenAI Responses and Gemini, and the fields that cannot be carried over are listed on the request. A model’s reasoning survives the conversion: the reasoning a Chat Completions upstream such as DeepSeek, Kimi or GLM returns reaches the client as it is and goes back to that upstream on the next turn, so a conversation with tool calls can continue, and the thought signatures on a Gemini model’s tool calls are kept as well. When a Chat Completions model writes a tool call into its answer text, the gateway recognises it as an ordinary tool call, provided the request defined that tool, and Dangerous commands checks it like any other. A request body a client sends compressed with gzip, deflate or zstd is read as usual, and an answer an upstream sends compressed reaches the client decoded. Clients such as Codex mark no prompt-cache breakpoints; when their requests are converted for an upstream in Anthropic Messages format, or for a Claude model on Bedrock that AWS lists for prompt caching (Claude 3.5 Sonnet v2, Claude 3.7 Sonnet, and Claude 4.5 and later), the gateway marks them, so each turn reads what the previous one cached. An upstream that refuses the marks receives the request again without them. Upstreams can be reached through an outbound proxy and priced with a price sheet of their own; aliases, proxies and price sheets have tabs on the same page. The Traffic · 24 h column of the Proxies tab gives the upload and download through each proxy over the last 24 hours, with 7 and 30 days on hover. An optional Concurrency limit suits relays and accounts that allow only so many requests at once, and optional Usage limits keep an upstream within the quota behind it: requests, tokens or cost per minute, hour, day, week or month, or every few minutes or hours, such as 10,000,000 tokens every 5 minutes. A rate limit does not interrupt the client: an upstream that answers 429 is paused for the time it asks for, and its requests go to the next upstream or wait for it, as they do for an upstream that is full or at a minute or hour limit; an upstream that has used up a day, week or month limit is skipped until it resets. None of these counts as a failure. A streamed request that waits receives the response headers and keep-alive pings meanwhile, so the client sees only a slower first token, and a 429 reaches the client only after Wait at most in Settings › Failover. The Upstreams page tags an upstream that cannot take requests for now with a countdown, such as Rate limited · back in 42 s or Usage limit reached · back in 1 min 10 s, and Daily usage limit reached (weekly, monthly) for a used-up period. The details are in Fail over and balance load. In Edit upstream › Models, the context window and price cells and the menu at the end of each model row (also on right-click, and in the model popover of the upstream list) offer Specs…, Set price… and Add alias…; an unknown context window reads Unknown · Set and an unpriced model Unpriced · Set price. Specs… sets by hand a model’s context window, maximum output, and whether it reasons and takes images, for a model the price table lacks or gets wrong; the values set there are used in place of the price table’s. Set price… opens the price sheet the upstream uses with an override for that model; an upstream on the default price sheet gets a new sheet of its own, which the next Set price… edits. A connection test times the DNS lookup and the TCP, TLS and proxy handshakes without incurring any cost; an inference test measures the time to first token and estimates its cost before it runs.

New upstream starts with the service. The services are listed in three groups, each with its logo and a one-line description, and can be searched: Model vendors (Anthropic, OpenAI, Google Gemini, DeepSeek, Z.ai / BigModel), Platforms and relays (Amazon Bedrock, OpenRouter, ThinkWatch, Sub2API, New API / One API) and Local and custom (Ollama, Custom). Picking one goes straight to Connection, which asks only for what that service needs. Authentication offers the methods the service has: an API key or a ChatGPT account for OpenAI, an API key or an account sign-in for Z.ai / BigModel, an API key, access keys or an AWS profile for Bedrock, and an API key or OAuth for custom endpoints, while ThinkWatch, Sub2API and New API / One API take an API key; a service with a single method shows no choice. What the service already decides, such as its address, its protocol or that Ollama takes no key, is filled in or left out. Request headers, client identity forwarding, the behaviour when the proxy is unavailable, the concurrency limit and usage limits are under Advanced settings, which opens by itself when one of them is set. Next checks the connection at no cost: when it passes, Models opens with the latency, the number of models found and any balance on top; when it fails, the reason is shown with Retry and Continue anyway, for an upstream that refuses the check but serves requests. A ChatGPT or Z.ai / BigModel account is signed in inside the dialog, once its notes are acknowledged: in the default browser, or in another browser through Copy link, and a ChatGPT account also with a device code. Once signed in, the upstream is saved and the dialog goes to Models, with the account on top. When the app is connected to a remote core, a ChatGPT account signs in with a device code only, and the verification address and the code can each be copied.

Edit upstream recognizes the service from the upstream’s address, or for Sub2API, New API / One API and ThinkWatch from where its balance is read, shows its logo and name, and asks what creating it would ask. A value already set is always shown, even where the service would not ask for it. A Z.ai or BigModel upstream written by signing in shows Account sign-in as its method, with Sign in again; one added with a key can switch to Account sign-in, and signing in replaces its key while the model scope, billing and other settings stay as they are.

The Quota / billing column shows the quotas and balances that services report. A ChatGPT account shows its usage limits and reset times. So does an upstream on a GLM Coding Plan, that is, one whose address is on api.z.ai or open.bigmodel.cn, whether it was signed in from the app or added with a key: its 5-hour and weekly limits and, on a plan billed in credits, the credits left (“1,976 / 2,000 credits left”). Other upstreams show their balance: the key’s quota, or the tightest of its time windows, as a bar with what is left; the money left in the account (“Balance $18.40”); or, for a key without a limit, what it has spent (“$12.40 spent this month”). Hovering lists every window with its use and reset time, the exact amounts and when the balance was read, followed by each of the upstream’s own usage limits with its use. Balances are read from OpenRouter, DeepSeek, Moonshot, Sub2API, New API / One API and ThinkWatch Enterprise; the source is recognized from the address, or set by the service picked when the upstream was created. On a ThinkWatch Enterprise gateway, a key with limits of its own shows its own limits and use; a key without them shows the account’s limits and its total over all of its keys, marked as the account’s. The connection check shows the balance with its result, and an upstream’s menu has Refresh balance. A balance that cannot be read shows why on hover; reading it never affects requests.

An upstream’s models are the ones it lists plus any added by hand. Upstreams often leave out models they serve: an account backend hides newer models, a relay lists only some. Add models… in an upstream’s model list adds such models by their exact IDs, several at a time. An ID cannot contain * or ? or have spaces around it, is at most 256 characters, and is added once; the dialog points out an ID that breaks these rules, or one the upstream already lists, before saving. A model added by hand is marked Manual, and routing and failover, aliases, routing rules that name a model, a key’s allowed models, the model list clients see, the dry run and the speed test all treat it like a listed model. The summary at the top of the list counts the models the upstream lists apart from those added by hand. Hovering over a manual model shows Remove, which takes effect at once, with Undo in the message that follows. The Models section of the New upstream and Edit upstream dialogs ends with the same input, for an upstream with or without a list, so models can also be added while it is created or edited. They are saved with the rest of the upstream, and with Selected models a model added there is enabled at once. For an upstream that lists no models, the models added by hand are its whole list.

The Aliases tab gives a model the name clients use for it. An alias lists the names the same model has on different upstreams, such as claude-sonnet-5 on Anthropic and us.anthropic.claude-sonnet-5-v1:0 on Bedrock; every upstream that offers one of them serves the alias under its own name, and they back each other up. Clients see aliases in their model lists, and answers carry the name the client asked for, while the request log shows the model each upstream was sent. When the same Claude model has different names on the official API, Bedrock, Vertex or OpenRouter, the tab and the new-alias dialog suggest grouping them. An alias can also be created from an upstream’s model list. The dialog shows which upstream receives which name and warns when the name would take over another upstream’s model of the same name. A key whose model scope allows an upstream model can also use the aliases that list it.

Each upstream is also compared over the last 7 days with the others serving the same model, and a deviation is marked next to its name in the list: Model name differs when answers name a model other than the one sent, Input reported high or Input reported low when the input tokens it reports, as a multiple of the gateway’s own estimate, are well off the other upstreams’, and Low cache reads when follow-up turns read a smaller share of their input from the prompt cache. Hovering over a mark shows the evidence and the sample sizes, and a deviation is marked only when both sides have enough samples.

API keys and header values can be written as ${NAME} to read a system environment variable. On macOS these come from the login shell, so variables exported in ~/.zshrc and similar files apply, and the same holds on Linux (~/.bashrc, ~/.profile and so on); on Windows they are the environment variables configured in system settings. After a variable changes, reopening the app picks it up. Proxy variables such as HTTPS_PROXY, and PATH, are not read.

A relay, a vendor or a ThinkWatch Enterprise console can hand out an import link, thinkwatch://import?… or its web form https://thinkwat.ch/import#…, that opens the New upstream dialog with the service, address, key, headers, concurrency limit, usage limits, models, model specs and a price sheet already filled in. The dialog names the host that will receive requests and keys, and nothing is written or sent before Next is chosen or a sign-in is started. A link only ever adds one upstream and cannot set proxies, aliases, routes or environment variable references; a link that needs a newer version of the app says so. The parameters for each service and a link builder are in Import links. Share… in an upstream row’s menu writes an import link for an existing upstream, with or without its key, and Export N selected upstreams… on the toolbar exports the selected upstreams as a config pack (see Sync and backup).

Routing and failover#

Each key follows a route, and keys without one follow the default route. A route is a list of rules evaluated in order. A rule matches on the model, the key, the client’s API format, input tokens, max_tokens, the number of tools, images, extended thinking, streaming, prompt caching, the kind of auxiliary request or the time of day; it then forwards the request to an upstream, a group or specified models, or refuses it, and can rewrite the model, max_tokens or the reasoning. Reasoning can be Off or one of the levels low, medium, high, xhigh and max: a level changes only requests that already have reasoning on, so requests without it, such as title generation, are left as they are and reasoning is never turned on. Off is written the way the upstream’s format writes it, also when the request is converted to another format: OpenAI Chat Completions and Responses upstreams receive none, Gemini 2.5 Flash a thinking budget of 0, and Anthropic and Bedrock upstreams no thinking configuration, while Gemini models that cannot stop thinking (2.5 Pro, Gemini 3) are sent no thinking configuration. When an upstream refuses a reasoning level, the request is sent once more to the same upstream with a level it accepts: a refused none is replaced by the lowest level the upstream lists, and the effort is left out only when it lists none. The choice is remembered for that upstream and model for 7 days, so later requests send a working level straight away. A rule saved before 2026.10.29 that turned extended thinking on or off no longer loads, and the app opens in safe mode: Repair removes the setting, after which the rule’s Reasoning can be set again, with Off matching the old off. Specified models name an upstream and one of its models, with backups tried in order, and send the model as written; to have one client use one model under another model’s name, a rule on that client’s key does it without changing what the name means for other clients. A model condition written for an upstream model also matches the aliases that list it. The time of day is judged by the local clock of the machine the gateway runs on: pick the weekdays and a start and end time, several windows per rule if needed, and an end earlier than the start is an overnight window. Setting max_tokens caps the length of answers: the upstream stops at that point by itself, without an error. A group puts several upstreams behind one name and decides the order in which they are tried: as listed, manually selected, in turn, lowest latency first or lowest cost first. In a round-robin group each member has a weight from 1 to 100, its long-run share of requests, and Distribute by can multiply the weights by each upstream’s speed (time to first token), its reliability (recent success rate) or both; conversations in progress stay on their upstream. When an attempt fails, the request moves on to the next upstream, and by default a session stays on one upstream so that its prompt cache keeps hitting. When every upstream has failed temporarily before the answer began, by being overloaded, answering with a server error, being unreachable or dropping the connection, the gateway waits 2, 4 and 8 seconds, then 16 seconds each time, and sends the request again, also when there is only one upstream; a longer wait asked for by the upstream is respected. Retrying and queueing together stay within Wait at most in Settings › Failover. An answer that has already started is never sent again, and errors in the request itself, a used-up quota, rejected credentials and rate limits are not retried. An upstream that answers 429 is paused instead of failing, and its requests move on or wait for it. An upstream that sends no content for the No-response timeout in Settings › Failover, 300 seconds by default, is given up on; keep-alive pings do not count as content. Before anything has reached the client, the request moves to the next upstream, and when none is left the client receives a timeout error; once the answer has started, it ends with an error. A map at the top of the page traces every key through its route and groups to the upstreams, and requests in flight move along their path from the key to the upstream, each in its own colour.

Auxiliary requests that clients send on their own (health checks, warm-ups, titles, topic detection and input suggestions) can be answered locally at no cost, or forwarded. Forwarded ones go through the routing rules like any other request, and a rule can send a given kind to a lower-cost upstream.

Every request records the rule it matched, the group it went through and each attempt with its status and duration. A dry run evaluates the rules for a given request and shows where it would go and why, without sending anything and without incurring any cost.

Security#

The Security page has a Log tab and a tab for each of three protections: Secrets & privacy, Dangerous commands and Content filter. They apply to every upstream and every key alike. Each protection is On, Monitor or Off, shown as a dot on its tab. On handles each match by the action of the rule that found it. Monitor records every match in the log and raises a notification, and changes nothing. Off checks nothing. All three start in Monitor, so out of the box no request or answer is changed or refused.

  • Secrets & privacy searches the text written in a request before it leaves, including the system prompt, earlier turns, reasoning, tool-call arguments, tool results and tool descriptions, for credentials and personal information, with 120 built-in rules. The keys saved in ThinkWatch (upstream keys, gateway keys, proxy passwords, sync credentials) are recognised by value, whatever their format; on top of those come API keys and tokens of about 60 services (such as Anthropic, OpenAI, DeepSeek-style sk- keys, xAI, Groq, OpenRouter, Hugging Face, Zhipu, Baidu Qianfan, GitHub, GitLab, AWS, Alibaba Cloud, Tencent Cloud, Slack and Stripe), private keys, JWTs, passwords in connection strings, and wallet seed phrases and private keys. Credentials recognised by where they appear have only the value replaced: Authorization and API-key headers, curl -u style command-line passwords, token parameters in URLs, bot webhooks (Feishu/Lark, DingTalk, WeCom, Slack, Discord, Teams) and assignments such as DB_PASSWORD=... in config files. Of personal information, resident ID numbers, bank card numbers, IBANs and passport machine-readable zones count only when their structure and check digits are valid, and start on; foreign ID numbers, mobile and landline numbers, email addresses, street addresses, precise coordinates, licence plates, vehicle identification numbers, device identifiers, public and internal addresses, and name, address, birth date, account and health fields recognised by their field names start off and can be switched on. Values a protocol needs as they are, such as item and call ids, types, roles, tool names, model names and signatures, are never searched, nor are field names, so a rule cannot break an id a client sends back. A rule either replaces what it finds with a placeholder such as <<TW_SECRET_1>>, or records it and sends it unchanged. A placeholder the answer repeats is restored exactly as the value was written: a private key keeps its line breaks, and a value inside a tool call’s arguments or Codex’s freeform tool input, such as apply_patch, comes back so the client can use it. A hit on a saved key is logged with where the key is saved (such as the upstream’s name), never its value. A custom rule can name its own placeholder: PROJECT gives <<TW_PROJECT_1>>.
  • Dangerous commands checks the tool calls a model returns for commands that download or decode code and run it, send out environment variables or credential files, send a credential to a host that is neither local nor the credential’s own provider, read private keys or cloud credentials, read or change ThinkWatch’s own data directory, or install startup items and scheduled jobs. A path that only appears in text being written, such as a document that mentions the directory, does not count. A rule either cuts the answer off, so the client never receives a complete call to run, or only records the call. Deleting the home or root directory, making files world-writable and uploading a local file to an outside host are only recorded by default.
  • Content filter checks requests and, rule by rule, the model’s answers. In a request it checks the user messages and tool results, context compaction included. In an answer it checks the text the model writes, including Codex’s freeform tool input; tool calls are left to Dangerous commands, and thinking is not checked. A rule matches a keyword, a regular expression or code points such as U+E0000–U+E007F, and refuses the request, deletes the match without leaving a marker, replaces it with the rule’s replacement text (*** by default), or only records it. A replacement is not undone in the answer. Answers keep streaming: only the few characters at the end that could still become a match are held back. While a rule on answers can delete or replace, token probabilities (logprobs) are left out of streamed answers. Built-in rules check requests: they delete Unicode tag characters and bidirectional controls, which can hide instructions from people but not from a model, and refuse explicit “ignore previous instructions” phrasing. Rules for zero-width and private-use characters, which emoji, Persian text and icon fonts also use, and for jailbreaks, persona manipulation, prompt extraction and their Chinese counterparts start off and can be switched on.

Every rule has its own switch and its own action, both set directly in the list: Secrets & privacy rules Replace with placeholder or Record only; Dangerous commands rules Cut off or Record only; Content filter rules Refuse, Delete, Replace or Record only. While a protection is in Monitor, each rule shows its action with Monitoring next to it. Built-in rules are grouped by what they protect; groups start collapsed, and each group head shows its number of rules, how many are on and how many matches the last 7 days had. Rule names and descriptions follow the app’s language. A search box filters rules by name and pattern and opens the groups that match.

A value Secrets & privacy matches by mistake can be listed as an exception: choose List as exception in a hit’s details in the security log, or paste the value under Exceptions below the rule list. An excepted value is neither replaced nor recorded by any rule, while the rules keep working for every other value. The configuration keeps only a hash of the value and its masked form; keys saved in ThinkWatch cannot be excepted. Up to 500 exceptions are kept, and each can be removed.

A custom Secrets & privacy rule looks for its text as written (Contains, the default) or takes a regular expression (Regex), and ignores case unless Match case is on. A custom content rule says what it Checks: Request, Answer or Request and answer. It matches a keyword, a regular expression or code points, and ignores case unless Match case is on. A rule on answers cannot refuse and cannot match code points; the dialog shows those options as unavailable, with the reason. A custom Dangerous commands rule is a regular expression. Any rule can be tried on a sample text first, and for Secrets & privacy and the content filter the test also shows the text after processing; a content sample can be tried as a request or as an answer.

Record only, and every rule while its protection is in Monitor, writes the match to the log and raises a system notification that names the rule and the upstream, never the matched text. The same rule notifies at most once every 5 minutes, and matches that arrive together are merged into one notification. Everything the protections find is kept in the log on the first tab, together with the request it came from; when hidden characters spell out text, the log shows that text.

An entry in the log opens in place. It lists every place the rule matched, up to 50 with the number of the rest: the part of the request or answer it was in (the system prompt, a message with its number and role, a tool call or tool result with the tool’s name, or the answer text), its JSON path, and up to 80 characters of context on either side, with credentials masked. It gives the rule as it was when it matched, a custom rule’s pattern and how it matches, or a built-in rule’s name with the core version that evaluated it, so the entry stays readable after the rule is changed or deleted. It states exactly what was done: the placeholder a value was replaced with, the tool call that was cut off and the notice the client received instead, the reply the client received for a refused request, or how many places were deleted or replaced, and with what text. The session and, when it differs from the model requested, the model sent to the upstream are given as well. The entries in a request’s timeline open the same way. Export saves every entry of the selected period as JSON (full detail) or CSV (one row per entry). The log is kept as long as the request records, and changes to the protections’ settings are not written to it.

The protections act on requests and answers passing through the gateway, not on files on disk. The configuration file holds keys in plain text and only its owner can read it, which keeps out other users but not programs running as the same user; Secrets & privacy protects what leaves the machine, not the file.

MCP#

The MCP page covers what clients load from their own configuration files, which does not pass through the gateway.

  • Servers: the MCP servers configured in Claude Code, Claude Desktop, Cursor, Codex, opencode, Antigravity CLI, Zed, Pi, oh-my-pi, Grok Build, Qwen Code, Hermes Agent and DeepSeek Harness, side by side. A server can be copied from one client to another or removed from a client; the change is shown before anything is written, and the original file is backed up. Copying and removing work for Claude Code, Claude Desktop, Cursor and Codex; the other clients are listed but not written to. A remote server on another host is marked as third party, since using it sends the surrounding context to that host, and a server configured differently in different clients is marked as well and can be compared side by side.
  • Skills and hooks: the installed skills and configured hooks, with the client each belongs to; skills in the shared ~/.agents/skills folder are listed as such.
  • Findings: client configuration, skills, hooks, slash commands, subagents and project instruction files are scanned for hidden characters, prompt injection, dangerous commands and overly broad permissions, and each finding is graded high, medium or low. The scan only reports; it never changes a file.

The app watches these files while it runs, and a new finding raises a system notification. After a file is edited, only the findings that are new are reported; those already in it are not reported again, even when they moved to another line.

Plugins#

Plugins are short JavaScript files that change requests before they go to an upstream and answers before they reach the client, such as asking for answers in a chosen language or converting file paths in tool calls between WSL and Windows. They run after routing, in a sandbox inside core with no network, files or memory between requests, see placeholders instead of the keys in a request, and pass through the same protections afterwards. Two plugins ship with the app, both off until turned on. A global tw gives plugins helpers for common edits, such as adding a line to the system prompt or replacing words in streamed answers. Each plugin is one file that also holds its scope, its behavior on errors and its settings. It is edited in one editor with a Settings tab, whose changes are written into the file, and a Code tab; adding a plugin opens the same editor. Routine changes are saved directly; for a plugin that may change tool calls, installing it, turning it on, changing its code and approving a changed file are confirmed in a system dialog. A plugin whose file changes outside the app stops running until the new version is approved. The page lists each plugin with its status, permissions, scope and statistics, and offers a trial run on a recent request and its log; requests changed by plugins are marked in Traffic. The API, permissions, limits and security model are described in Plugins.

Settings#

Settings has eight sections. Connection lists the local core and the saved remote cores, described in Connecting to a remote core. General sets the language, the appearance, what the menu bar item shows on macOS (or that it is hidden), launch at login, whether notices arrive as system notifications, in the app only or not at all, and shows hidden guidance hints and ignored warnings again. Listening sets who can reach the gateway (this machine only, the local network of a chosen interface, or every interface), its port and the allowed address ranges. Failover sets how long a failing upstream is paused before requests go to the next one: after how many consecutive failures, for how long, and separate pauses for an insufficient balance, a used-up quota and rate limits. It also sets the no-response timeout, after which an upstream that has sent no content is given up on (300 seconds by default, from 30 to 3,600), and Wait at most, how long in total a request may spend queueing for a full, rate-limited or usage-limited upstream (or a key’s per-minute or per-hour limit) and retrying upstreams that failed temporarily: 120 seconds by default, after which the request ends with an error, and 0 for no queueing or retrying. Log retention sets how long request payloads and request records are kept, and a size cap for payloads; the security log expires with the request records. Sync and backup exports and imports the configuration, turns on sync and lists the versions of the configuration that can be restored (see Sync and backup). About shows the version, checks for updates and produces a diagnostics bundle with keys and addresses masked. Uninstall restores every connected client and removes the autostart entry, and is meant to be run before the app is deleted.

Sync and backup#

A new computer, a reinstall or a second device does not mean entering the configuration again: it can be exported to one file and imported, or synced between devices automatically through a folder, WebDAV or S3-compatible storage of the user’s choosing, encrypted with a passphrase by default. Every change to the configuration leaves a version that can be restored, and a deleted upstream can be brought back on its own.

It needs ThinkWatch Lite 2026.10.25 or later. The export and import options, how to set up each sync location, conflicts and restoring are described in Sync and backup.

On macOS the menu bar shows today’s tokens above the current generation speed of all running requests together (42 t/s), updated every second while requests run: 0 t/s while they wait for their first token, and a dash when nothing runs. Today’s cost is in the opened menu and the tooltip. While requests are running, a band of light sweeps across the mark; in the numbers-only style it sweeps across the numbers. It pauses while the menu is open and stands still when Reduce Motion is on. A subscription quota that is nearly used up or spent does not colour the item; only its row in the opened menu turns orange or red, and the mark carries a red badge when the gateway is not running. Menu bar in Settings › General can reduce the item to the icon or to the numbers, or set it to Hidden, which takes it off the menu bar: the gateway keeps running in the background, after a launch at login as well, and opening ThinkWatch Lite again from Finder or Spotlight shows the main window.

Clicking it opens a native menu. Open ThinkWatch Lite always comes first, followed by unread notices and a Today block: today’s tokens in large type, with requests, failures and cost on the line below and a small chart of tokens per hour beside them. The block’s top line gives the gateway’s state, the server’s name when connected to a remote core, and the current generation speed. Below it, each quota window an upstream reports has a row with how much is used and when it resets (subscription accounts and GLM Coding Plan upstreams; a plan billed in credits shows the credits left under the bar), followed by the requests in progress, each with its current speed and running time. The actions come last: choosing the upstream of a manually selected group, copying the gateway address, which is shown beside the item, or the default key, installing a new version when one is available, settings, switching connections and checking for updates, all without opening the main window.

When the gateway is not running, Open ThinkWatch Lite is followed by its state, the reason, and items to restart it (or, for a remote core that cannot be reached, to retry the connection) and to show the details.

On Windows the icon sits in the notification area. Hovering over it shows the gateway’s state and today’s tokens and cost; a left click opens the main window, and a right click opens the same menu, with the Today block and the quota bars written out as text.

On Linux the icon sits in the system tray. Clicking it opens the same menu, with the Today block and the quota bars written out as text as on Windows; its first item, Open ThinkWatch Lite, brings up the main window.

System notifications, native on macOS and Windows and sent through the desktop’s notification service on Linux, report when the gateway stops forwarding or keeps restarting, the connection to a remote core drops, a subscription quota runs out, a sign-in expires or an upstream rejects its credential, a proxy cannot be reached, the configuration file fails validation, a tool call is cut off, a security rule records a match (see Security), a key nears or reaches a daily, weekly or monthly usage limit, or suspicious content appears in a client’s configuration. A new version found by the automatic check is listed among the notices, in grey, and announced the same way (see Updates). Some are only listed in the app: an unreachable upstream, which a fallback usually covers; failed requests, gathered into one notice that lists the failed requests in Traffic when opened; and models without a price, one notice per model and upstream that opens Upstreams to set a price and clears once no request in the last 7 days is left unpriced. Notices as a whole can be set to system notifications, in-app only, or off. Marking a notice as read stops the bell from counting it; the notice stays in the list until the problem behind it clears or the list is cleared.

Last updated 2026-10-30 Edit on GitHub