Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
a890511
chore: pin the core dependency to a tag instead of tracking main (#25)
fylorn Sep 23, 2026
a90e9f3
feat!: forward what can be forwarded, convert only what must be (#26)
fylorn Sep 23, 2026
ca7647c
refactor: take the circuit-breaker registry and metric labels back fr…
fylorn Sep 23, 2026
a6678cf
refactor: redact PII with core's guard engine, and restore tool argum…
fylorn Sep 23, 2026
437a671
feat: inspect the tool calls an upstream returns (#29)
fylorn Sep 23, 2026
038796b
build: keep only line tables in dev and test builds (#30)
fylorn Sep 24, 2026
05f52e8
feat: one circuit-breaker state machine, and hidden characters in req…
fylorn Sep 24, 2026
8a76395
fix: make the integration suite pass again (#32)
fylorn Sep 24, 2026
6482690
test: fix the two flaky integration tests (#33)
fylorn Sep 24, 2026
9f46116
fix: bill a Chat stream whose caller did not ask for usage (#34)
fylorn Sep 24, 2026
3c35ab8
fix: a request the upstream refuses no longer fails over or trips bre…
fylorn Sep 24, 2026
234fcce
Merge main (v1.1.0) into dev
fylorn Sep 24, 2026
82f8061
docs: cut releases from a release branch, and keep the tag's headings…
fylorn Sep 24, 2026
bd57b5d
ci: run checks on pull requests into dev, including the integration s…
fylorn Sep 24, 2026
c1b2085
fix: bill cached input at cache prices, estimate missing usage, strip…
fylorn Sep 24, 2026
ad5b75b
refactor: take back what only this side used from core (#41)
fylorn Sep 24, 2026
3a617f3
feat(gateway): take official hosts, output fallback and error shapes …
fylorn Sep 24, 2026
ed1c8b5
feat(gateway): accept Gemini clients, and the Responses API over a We…
fylorn Sep 24, 2026
28d4387
test: count the probes that miss before the streaming cache hit (#44)
fylorn Sep 24, 2026
d3f36fc
refactor(server): move the catalog's SQL into repositories (#45)
fylorn Sep 24, 2026
83e9bcc
refactor(server): move the dashboard, limits and log-forwarding handl…
fylorn Sep 24, 2026
9b5b326
refactor(server): move the MCP handlers' SQL into repositories (#50)
fylorn Sep 24, 2026
86beb82
refactor(gateway): run the request guards on core's tw-guard engines …
fylorn Sep 24, 2026
1c80b83
refactor(server): move the identity handlers' SQL into repositories (…
fylorn Sep 24, 2026
c25b44f
fix(mcp): revoking a default connection no longer fails with a 500 (#51)
fylorn Sep 24, 2026
dbce845
test: leave the cancelled stream after it has started, not on a timer…
fylorn Sep 24, 2026
c3d2d57
refactor(server): move the access handlers' SQL into repositories (#48)
fylorn Sep 24, 2026
d92c2d1
fix(settings): read security.totp_required as a boolean, and seed aut…
fylorn Sep 24, 2026
7e95183
feat(gateway): record a client that leaves before its response exists…
fylorn Sep 24, 2026
367df3c
feat(auth): enforce the TOTP requirement (#55)
fylorn Sep 24, 2026
443de8d
feat(gateway): keep the last response on a Responses WebSocket (#56)
fylorn Sep 24, 2026
6890c72
Merge main (v2.0.0) into dev
fylorn Sep 24, 2026
d4f6d5a
docs(release): CI runs the integration suite on the release PR (#58)
fylorn Sep 24, 2026
ef3de87
docs(contributing): point vulnerability reports at the organization's…
fylorn Sep 25, 2026
c4f8b1c
docs(readme): current description of ThinkWatch Lite and ThinkWatch C…
fylorn Sep 25, 2026
f259962
feat(providers): authenticate Bedrock with an API key (#61)
fylorn Sep 28, 2026
78f9ab5
test: hold the early-cancel client once its key is known, not on a ti…
fylorn Sep 28, 2026
1b752f2
feat(providers): list Bedrock's models, and let the route editor take…
fylorn Sep 28, 2026
1f08421
fix(bedrock): refuse keys that will not decrypt, and keep instance-ro…
fylorn Sep 28, 2026
e480bfd
refactor(bedrock): use core's shared tw-bedrock, and core v0.55.0 (#65)
fylorn Sep 29, 2026
e1e7999
docs(readme): rewrite both READMEs to be short and accurate (#66)
fylorn Sep 30, 2026
af9eb81
Merge main (v2.1.0) into dev
fylorn Sep 30, 2026
ed6dd39
docs(readme): 37 MCP templates, what the setup wizard does, body reda…
fylorn Sep 30, 2026
9afcc08
fix(rbac): make the seeded team_manager work at team scope (#69)
fylorn Sep 30, 2026
fa3a5b9
fix(rbac): require gateway use, and count only grants that give it (#70)
fylorn Sep 30, 2026
a1eed6f
Merge main (v2.2.0) into dev
fylorn Sep 30, 2026
da88b09
Merge main (sponsor line) into dev
fylorn Oct 2, 2026
1a399e7
Allow clippy::double_must_use on the async_trait BlobStore (#73)
fylorn Oct 2, 2026
8d8d96f
Unify the request guards with thinkwatch-core's rule model (#72)
fylorn Oct 3, 2026
2baa123
Fix what the pre-release review of the guard unification found (#75)
fylorn Oct 3, 2026
33c22dc
Merge main (v3.0.0) into dev
fylorn Oct 3, 2026
dcc8616
Merge main (README: ThinkWatch Lite for individual developers) into dev
fylorn Oct 4, 2026
6b73344
Merge main (v3.1.0) into dev
fylorn Oct 5, 2026
03c393c
Usage limits: token limits refuse, key limits count on the key, Retry…
fylorn Oct 5, 2026
4075855
fix(redis): connect over TLS for rediss:// URLs (#82)
fylorn Oct 5, 2026
33234ee
Concurrent startup, body retention on restart, and Helm network polic…
fylorn Oct 5, 2026
b7a34e1
Merge main (v3.2.0) into dev
fylorn Oct 5, 2026
2ce6471
chore(deps): update rust crate rand to v0.10.1 [security] (#77)
renovate[bot] Oct 6, 2026
0682864
chore(deps): update rust crate xxhash-rust to v0.8.16 [security] (#78)
renovate[bot] Oct 6, 2026
1fbe7bd
chore(deps): take the Renovate dashboard upgrades (#85)
fylorn Oct 6, 2026
2e23fd9
chore(deps): pnpm 12, and ClickHouse row validation on (#86)
fylorn Oct 6, 2026
977cab0
ci: save Rust caches only on pushes, and let releases read CI's image…
fylorn Oct 8, 2026
a19ed65
Merge main (v3.2.1) into dev
fylorn Oct 8, 2026
ea2a81f
Merge main (v3.3.0) into dev
fylorn Oct 9, 2026
e8ae1f1
Stream endings and in-stream errors logged like answers; core v0.68.0…
fylorn Oct 10, 2026
0e070a9
Merge main (v3.3.1) into dev
fylorn Oct 10, 2026
633ad90
Merge main (v3.4.0) into dev
fylorn Oct 10, 2026
235e38b
fix(limits): enforce route caps and close the places limits leaked (#94)
fylorn Oct 10, 2026
31cbfc8
feat: GET /v1/usage answers for the key or its owner; API key limits …
fylorn Oct 10, 2026
8151cee
Merge main (v3.5.0) into dev
fylorn Oct 10, 2026
721b5ee
Merge main (README zh) into dev
fylorn Oct 10, 2026
4f87839
feat(console): Add to ThinkWatch Lite; gateway address for every user…
fylorn Oct 11, 2026
042e605
Merge main (v3.6.0) into dev
fylorn Oct 11, 2026
ebdb9dc
chore: add a contributor license agreement signed by PR comment (#102)
fylorn Oct 11, 2026
133d610
ci: name the CLA job cla (#103)
fylorn Oct 12, 2026
eef9ff4
Merge main (3.7.0) back into dev
fylorn Oct 12, 2026
30c5f5b
Merge main (3.7.1) back into dev
fylorn Oct 12, 2026
12f4f82
Merge main (3.8.0) back into dev
fylorn Oct 13, 2026
7ef4ff0
Merge main (3.8.1) back into dev
fylorn Oct 13, 2026
72888a4
Bump rustls to 0.23.46 (RUSTSEC-2026-0346 to -0349) (#108)
fylorn Oct 15, 2026
98b6790
Merge main (3.8.2) back into dev
fylorn Oct 16, 2026
d86570c
Tool inspection: decide on settled input, record whole calls (core v0…
fylorn Oct 19, 2026
a9c5150
chore(deps): update node.js to v24.22.0 (#111)
renovate[bot] Oct 19, 2026
57369f6
chore(deps): update pnpm to v12.14.0 (#99)
renovate[bot] Oct 19, 2026
9549fd6
chore(deps): update ghcr.io/zitadel/zitadel docker tag to v4.19.5 (#110)
renovate[bot] Oct 19, 2026
57c05d9
Merge main (3.8.3) back into dev
fylorn Oct 20, 2026
ff8ccf4
feat(web): carry a key's request limits in the Add to ThinkWatch Lite…
fylorn Oct 20, 2026
17b7e9c
Fix the rotation comment: key limits follow the lineage (#116)
fylorn Oct 20, 2026
caa6751
fix(auth): answer database failures in auth middleware with a 503, no…
fylorn Oct 20, 2026
fd13601
Run Argon2 on the blocking pool in request handlers (#118)
fylorn Oct 20, 2026
65aedcd
Merge main (3.9.0) back into dev
fylorn Oct 20, 2026
f9fe732
Route a conversation's encrypted history back to the provider that ho…
fylorn Oct 22, 2026
75b1327
Merge main (3.9.1) back into dev
fylorn Oct 23, 2026
28e537d
Retry upstreams that fail transiently before any content (#122)
fylorn Oct 24, 2026
3d42d54
Models: a reasoning setting; resend an effort the upstream refuses (#…
fylorn Oct 25, 2026
843da63
Reasoning off: lowest accepted level for a refused none; works across…
fylorn Oct 25, 2026
df35254
Merge main (3.10.0) back into dev
fylorn Oct 25, 2026
d90345e
Expanded redaction rules (core v0.85.0), saved upstream keys, rule te…
fylorn Oct 29, 2026
c54f7fd
Redaction exception values; scan cache for unchanged history (core v0…
fylorn Oct 30, 2026
3f6a82c
Stop a stream that generates too slowly (#131)
fylorn Oct 30, 2026
faa1ad5
Set the minimum generation speed when creating a provider (#132)
fylorn Oct 30, 2026
b569e40
Judge a stream's speed on what it generates visibly (#133)
fylorn Oct 30, 2026
dee3c39
fix: automatically manage gateway runtime and memory (#134)
fylorn Nov 3, 2026
49ac9ae
chore(release): tag 3.11.0
fylorn Nov 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 164 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,168 @@ target.

## [Unreleased]

## [3.11.0] — 2026-11-03

Execution and buffering now adapt automatically to available CPUs and memory,
including container limits. CPU work expands with traffic and extra threads
retire after ten idle seconds; no runtime configuration change is required.

Secrets and privacy recognises far more: the built-in redaction catalog
grows from 30 rules to 131 in 20 groups, and the API keys saved
for the providers are recognised by value, whatever their format. The
console shows the rules' names and descriptions as thinkwatch-core
writes them, in both languages. A value recognised by mistake can be made
an exception without switching its rule off, and long conversations are
no longer scanned from the start every turn. A provider can set the
slowest generation its streams may keep up, and a stream that falls
below it is cut so the client can try again. The thinkwatch-core crates
move from v0.83.1 to v0.89.0.

No new runtime setting. Two new provider columns
(`providers.min_speed_tokens_per_sec`, `providers.min_speed_window_secs`,
added on startup). New metrics cover minimum generation speed, buffering
pressure and audit backlog memory.
The redaction policy gains one optional field (`exceptions`), with
endpoints of its own under `/api/admin/security/redact/exceptions`.

### Read before upgrading

- **Buffering is admitted against a process-wide memory budget.** Request,
response, cache and retained streaming data use limits derived from the
container or machine's memory. Temporary pressure returns a retryable 429
with `Retry-After: 1`; an already-started stream receives an error event.
Large upstream responses and stream timelines are bounded, and new audit
entries are dropped with metrics if the separate audit byte budget fills.
See [automatic process resources](docs/operations/resources.md) for the
limits and monitoring. These allocation budgets are not a hard RSS cap.
- **UUIDs and blockchain credentials gain built-in rules.** Core v0.88.0
added 11 rules, enabled by default, including UUIDs, wallet keys and
accounts, exchange credentials and recovery material. Monitor records
findings; installations with protection On replace matching message
text. UUIDs, hashes and public accounts do not independently trigger
credential-exfiltration blocking. Review any workflows that send order
or trace identifiers as message text.
- **New credential rules ship on.** Under the On state of Secrets and
privacy (`security.redact` mode `enforce`), values they find start
being replaced with placeholders: among them credentials in request
headers, command lines, URLs and bot webhooks, and secrets assigned in
`.env`-style configuration (`DB_PASSWORD=…`). Under Monitor, the
factory state, they are only recorded. A rule that gets in the way can
be switched off on its own in the console.
- **Some keys are reported under a rule of their own.** `sk-or-v1-`
(OpenRouter), `sk-lf-` (Langfuse), `sk-svcacct-`, `sk-admin-` and
`sk-api-` keys were reported as `openai-api-key`; they now have their
own rule ids, and so do audit events and the
`gateway_redaction_found_total` metric's `kind` label, which follows
the new groups (`ai-services`, `dev-platforms`, `cloud`, …) instead of
`api-keys` and `personal`.

### Added

- **Expanded redaction rules** (thinkwatch-core v0.85.0 and later): about 50 more
prefixed credentials (AI services, code hosting, cloud, payments and
collaboration), credentials by position (request headers, command
lines, URL parameters, bot webhooks, configuration assignments),
cryptocurrency recovery phrases and private keys, and personal
information: identity documents, foreign ID numbers, IBAN, phone
numbers, addresses and coordinates, vehicles, device identifiers and
values by field name. Of the new personal-information rules only the
two that can be checked digit by digit ship on (passport
machine-readable zone, IBAN), alongside the resident ID and bank card
numbers as before.
- **Saved upstream keys are recognised by value.** The API keys in each
provider's credential headers (the `Bearer` scheme left out) and a
Bedrock provider's secret access key are matched wherever they appear
in a request, as written or JSON-escaped; values shorter than 12
characters are not. The list follows the providers: it is rebuilt when
they load, when one is created, changed or deleted, and on the other
instances through the router reload. The same keys count as
credentials for the dangerous-commands rule "credential sent to an
unknown host", each allowed to its own provider's host, and the
console's trial recognises them too.
- **A saved key's audit event names its provider, never the value.** The
`gateway.redaction_*` event of the rule `thinkwatch-saved-secret`
carries `sources` (`[{"kind": "upstream-key", "name": <provider>}]`)
and no masked values; the security log shows it as the provider's API
key.

- **Exception values** (`security.redact.exceptions`, the Exceptions
section under the Secrets and privacy rules). A value listed there is
neither replaced nor recorded, whichever rule finds it; the rules keep
working for every other value. A value is added by pasting it: the
server hashes it on arrival and keeps only its SHA-256, its masked form,
the rule that recognised it and when it was added, never the value. At
most 500; at the cap, adding is unavailable. Saved provider keys cannot
be exceptions. Listing them takes `settings:read`; adding and removing
take `settings:write` and `pii_redactor:write`, and each is written to
the audit log (`security.redact_exception_added`,
`security.redact_exception_removed`) with the rule and the masked value
only. The trial marks an excepted value as left as is.
- **Unchanged conversation history is not scanned again.** Each gateway
instance keeps, in memory, where the redaction rules matched in the
parts of recent requests it has scanned (positions and rule ids, never
values); the next turn of the same conversation takes those parts from
it and scans only what is new. What is found is the same as without it.
The cache is rebuilt whenever the rules, the providers' keys or the
exceptions change.
- **Stop a stream that generates too slowly** (the provider's
"Stop when generation is slower than … tokens/s for … s" setting,
`min_speed_tokens_per_sec` 1–1000 and `min_speed_window_secs` 5–120,
default 20, on the provider endpoints; blank or `null` is off, the
default). From the first token on, a stream is judged once a second:
when the visible text it streamed over the last window (answer,
reasoning streamed as generated, and tool arguments; four ASCII
characters or one other character to a token) comes to fewer tokens
per second than the minimum, the stream ends with a 503 error in the
client's format (`overloaded_error` for Anthropic, `response.failed`
for Responses, `UNAVAILABLE` for Gemini), the upstream connection is
closed, and the route is charged a failure. Left out of the window:
pauses of a quarter of the window or more (at most 5 s), and
summarised reasoning (Responses reasoning summaries, Anthropic thinking
blocks, Gemini thought parts, Bedrock reasoning content), which arrives
in bursts while the model reasons; a window more than half left out is
not judged. Nor is a second while summarised reasoning is open, one
whose last content is more than a second old, or anything after the
upstream has ended the answer. Counts the upstream reports are not
used: Gemini's includes its hidden thoughts. The session's affinity to
that route is dropped, the answer so far is billed as for any stream
cut short, and the log row carries the error `UpstreamTooSlow` and the
speed at the cut (`tokens_per_sec`). Whole answers and Responses
WebSocket turns are not measured.

### Changed

- **CPU execution grows with load.** The permanent I/O scheduler uses at
most eight workers by default. Parsing, redaction, inspection and
conversion share CPU admission with password hashing; sufficiently
provisioned 80-CPU machines can run 80 CPU jobs. Extra threads retire
after ten idle seconds. Argon2 also observes a memory-based concurrency
limit, retained until a running hash finishes even if its caller leaves.
- **Buffers and audit retries retain admission until their final owner
releases them.** Chunked AI/MCP responses, cache reads, transformed
output and detached stream tails use the same buffering budget. MCP
discovery, provider/registry catalogs, OAuth endpoints, S3 reads and
audit-forwarder diagnostics also use bounded reads. Local pressure does
not count as an upstream failure. Audit queue and ClickHouse retry
batches share a separate byte limit and expose buffered-memory and
dropped-entry pressure through metrics.
- **Release binaries use mimalloc.** Linux also applies a process-local
transparent-huge-page policy before runtime threads start. Host sysctls
and deployment resource settings need no changes.
- **A redaction policy written through `PATCH /api/admin/settings` keeps
the stored exceptions**, whatever it carries: they are changed only
through their own endpoints, and the console writes the rest of the
policy back from the rule view, which does not carry them.
- **Rule names and descriptions come from thinkwatch-core.** The security
page shows each built-in rule's name, reason and description and each
group's name as the rule view gives them, in English or Chinese; the
console no longer keeps its own name tables. Security-log rows and
trial hits name the rule by looking its id up in the rule view. The
rule search also matches prefixes, field names and descriptions.
- **The conversion of the old PII pattern list** switches on or off only
the four personal-information rules that list could express; the new
personal-information rules keep their factory setting.

## [3.10.0] — 2026-10-25

Upstreams that fail for the moment before any of the answer reached the
Expand Down Expand Up @@ -2694,7 +2856,8 @@ unreleased builds should: stop the gateway, run `db/schema.sql`
against PostgreSQL, restart against this tag. The schema is
idempotent end-to-end, so the apply is safe to repeat.

[Unreleased]: https://github.com/ThinkWatchProject/ThinkWatch/compare/v3.10.0...HEAD
[Unreleased]: https://github.com/ThinkWatchProject/ThinkWatch/compare/v3.11.0...HEAD
[3.11.0]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v3.11.0
[3.10.0]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v3.10.0
[3.9.1]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v3.9.1
[3.9.0]: https://github.com/ThinkWatchProject/ThinkWatch/releases/tag/v3.9.0
Expand Down
Loading
Loading