Skip to content

ThinkWatch Core 0.71.0

Choose a tag to compare

@github-actions github-actions released this 12 Oct 05:44
· 7 commits to main since this release
911f937

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 --restart switches a server back.
  • The request store's schema is unchanged; nothing is cleared.
  • Configuration: a custom redaction rule takes two new optional keys, match (contains or regex) and case_sensitive. A custom redaction rule without match is 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 needs match: regex, and case_sensitive: true to keep telling cases apart. Existing configurations load unchanged; a configuration that uses match or case_sensitive on 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:
    • CustomRuleSave and SecurityTestRequest: match applies to redaction rules too (contains or regex; contains when not given); new case_sensitive (off when not given).
    • Matcher: contains and regex gain case_sensitive. A custom redaction rule is shown as contains or regex; a tool-call rule reports true unless its pattern starts with (?i).
    • RuleSnapshot gains case_sensitive; a custom redaction rule's snapshot now carries matching.

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.0

An installation made with the script switches to 0.71.0 with:

sudo twcore upgrade --version 0.71.0 --restart

Configuration, 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.sha256

On macOS:

shasum -a 256 -c twcore-aarch64-apple-darwin.sha256

On 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