Repository navigation
ThinkWatch Core 0.71.0
This release keeps outbound redaction away from the structure of a request, so a rule can no longer break the ids a client sends back, and lets a custom redaction rule look for its text as written or by a regular expression, with or without telling capital letters from small ones.
Upgrade notes
- The control-plane protocol version (
CONTROL_API_VERSION) goes from 46 to 47. ThinkWatch Lite connects only to a core with the same protocol version: ThinkWatch Lite 2026.10.16 includes 0.70.1 (protocol 46) and does not connect to 0.71.0. A server used with it stays on 0.70.1 until the app is updated to a release that includes 0.71.0;sudo twcore upgrade --version 0.70.1 --restartswitches a server back. - The request store's schema is unchanged; nothing is cleared.
- Configuration: a custom redaction rule takes two new optional keys,
match(containsorregex) andcase_sensitive. A custom redaction rule withoutmatchis now looked for as written and ignores case; before, every custom redaction rule was a case-sensitive regular expression. Such a rule still loads and is not reported anywhere; it simply stops matching, and what it used to replace is sent as it is. Check every custom redaction rule before upgrading: one written as a regular expression needsmatch: regex, andcase_sensitive: trueto keep telling cases apart. Existing configurations load unchanged; a configuration that usesmatchorcase_sensitiveon a redaction rule does not load in 0.70.x. - New message codes:
config.rule_match_bad,security.redact_match_bad. - Changed in the protocol:
CustomRuleSaveandSecurityTestRequest:matchapplies to redaction rules too (containsorregex;containswhen not given); newcase_sensitive(off when not given).Matcher:containsandregexgaincase_sensitive. A custom redaction rule is shown ascontainsorregex; a tool-call rule reportstrueunless its pattern starts with(?i).RuleSnapshotgainscase_sensitive; a custom redaction rule's snapshot now carriesmatching.
Redaction leaves the request's structure alone. Rules were run over the whole request body, field names and identifiers included, so a custom rule matched inside the ids a client sends with its history: Codex sends ids such as fco_ followed by a hexadecimal UUID, and a rule whose text could be spelled in hexadecimal turned one into fco_…<<TW_SECRET_1>>…. The upstream rejected the request because the id contained characters an id cannot have, and since the history is sent again on every turn, every later turn of that conversation was rejected too. No rule looks inside field names any more. Custom rules also leave alone the values a protocol needs exactly as they are: id, call_id, item_id, tool_use_id, tool_call_id, toolUseId, previous_response_id, response_id, type, role, name, namespace, model, status, object and phase. Built-in rules still search those values, since a key or a number cannot be mistaken for a hexadecimal id, and every other value is searched by every rule as before, including fields such as user_id or aws_access_key_id inside tool arguments.
Private keys stay within one text. The private-key rule looked for the end of a key block anywhere after its first line. A header without an end in one message, such as a file read halfway or code that spells the marker, joined up with an end line in a later message, and everything in between, earlier turns included, was replaced by a single placeholder. The header and the end are now looked for within the same text.
Match by. A custom redaction rule now says how it matches, as content rules already do: contains looks for the text exactly as written, so ., * and the like have no special meaning, and regex takes a regular expression. A rule matches regardless of case unless it sets case_sensitive: true. Code points remain a content-filter matcher only and are refused for redaction. The security log records how a custom redaction rule matched and whether it told cases apart when it found something.
Downloads
| Platform | Binary | Archive for server installation |
|---|---|---|
| Linux, x86_64 | twcore-x86_64-unknown-linux-gnu |
twcore-x86_64-unknown-linux-gnu.tar.gz |
| Linux, aarch64 | twcore-aarch64-unknown-linux-gnu |
twcore-aarch64-unknown-linux-gnu.tar.gz |
| macOS, Apple silicon | twcore-aarch64-apple-darwin |
— |
| Windows, x64 | twcore-x86_64-pc-windows-msvc.exe |
— |
| Windows, ARM64 | twcore-aarch64-pc-windows-msvc.exe |
— |
Each file is published with a .sha256 file beside it. A Linux archive contains twcore, the systemd unit twcore.service and LICENSE. ThinkWatch Lite includes its own copy of twcore; the files here are for running core separately, such as on a server.
Server installation
On Linux (x86_64 or aarch64), the install script sets up twcore as a systemd service. This installs 0.71.0:
curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version 0.71.0An installation made with the script switches to 0.71.0 with:
sudo twcore upgrade --version 0.71.0 --restartConfiguration, the remote control port and connecting ThinkWatch Lite are described in docs/server.md.
Verifying a download
A .sha256 file holds the SHA-256 of the file followed by its name. With both files in the current directory, on Linux:
sha256sum -c twcore-x86_64-unknown-linux-gnu.tar.gz.sha256On macOS:
shasum -a 256 -c twcore-aarch64-apple-darwin.sha256On Windows, in PowerShell, the following prints True when the binary matches:
(Get-FileHash .\twcore-x86_64-pc-windows-msvc.exe).Hash -eq (Get-Content .\twcore-x86_64-pc-windows-msvc.exe.sha256).Split()[0]The install script and twcore upgrade check the SHA-256 themselves.
What's Changed
- chore: add a contributor license agreement signed by PR comment by @fylorn in #328
- ci: name the CLA job cla by @fylorn in #329
- fix(redact): leave JSON keys and identifiers alone; keep PEM matches inside one string by @fylorn in #330
- feat(redact): match text as written or by regex, case switch; v0.71.0 by @fylorn in #331
Full Changelog: v0.70.1...v0.71.0