diff --git a/src/content/docs-core/en/development.md b/src/content/docs-core/en/development.md index c2abf5b..420ede8 100644 --- a/src/content/docs-core/en/development.md +++ b/src/content/docs-core/en/development.md @@ -32,6 +32,10 @@ gh pr create --base main --head your-branch Commit messages follow Conventional Commits (`fix(scope): subject`) and are written in English. The body explains *why*, not only *what*. +## Contributor License Agreement + +A first pull request asks its author to sign the [Contributor License Agreement](https://github.com/ThinkWatchProject/ThinkWatch-Core/blob/main/CLA.md) once, by replying on the pull request with the sentence the bot gives. The signature covers every later pull request to the repository. The author keeps the copyright, and the agreement lets the project license the contribution under any terms, commercial ones included. + ## Mandatory rules ThinkWatch Lite and ThinkWatch Enterprise both depend on these crates, so working for a single use case is not sufficient. A pull request that violates any of these rules will be returned for revision, regardless of the quality of the diff: diff --git a/src/content/docs-core/en/overview.md b/src/content/docs-core/en/overview.md index b8ae450..7b32375 100644 --- a/src/content/docs-core/en/overview.md +++ b/src/content/docs-core/en/overview.md @@ -15,7 +15,7 @@ Clients such as Claude Code and Codex send their requests to the gateway, and Co - **Rule-based routing.** Rules match on the model, the gateway key, the input size, the presence of tools or images and other properties of a request, and send it to an upstream or a group, rewrite its parameters or refuse it. When the client and the upstream use different API formats, the request is converted between Anthropic Messages, OpenAI Chat Completions, OpenAI Responses and Gemini. - **Failover before the first byte.** Until the first byte reaches the client, a failing upstream is replaced by the next one without the client noticing. After that point, the failure is reported. A circuit breaker keeps requests away from an upstream that keeps failing. - **Cost accounting.** Token usage and cache hits are priced from a public price table, which the control plane refreshes daily, or from a price sheet in the configuration. Each request records its cost and where the price came from. Estimated amounts are marked as such, and usage that cannot be priced is labelled *unknown* rather than given an invented figure. -- **Outbound redaction.** Credentials and personal information anywhere in a request are replaced with placeholders before the request leaves, and restored when the model echoes them back. +- **Outbound redaction.** Credentials and personal information in a request are replaced with placeholders before the request leaves, and restored when the model echoes them back. Field names are not searched, and custom rules also skip the ids, types, roles and names a protocol needs as they are. - **Tool-call inspection.** Tool calls returned by an upstream are checked against a rule set, and a dangerous call can be cut off mid-stream. - **Content filter.** The user messages and tool results a client sends are checked by keyword, regular expression or code point, and each rule refuses the request, deletes what it matched or only records it; the built-in rules also catch hidden characters that can carry instructions. Each of the three guards is set to `off`, `observe` or `enforce`, and all start in `observe`. - **An encrypted control plane.** The desktop app and `twcore` commands reach core over a local socket (a loopback port on Windows) and, when it is enabled, a remote control port. Every control connection starts with a Noise handshake keyed by `listen.control.key`; there are no certificates. diff --git a/src/content/docs-core/zh-CN/development.md b/src/content/docs-core/zh-CN/development.md index bfa71fe..7a49100 100644 --- a/src/content/docs-core/zh-CN/development.md +++ b/src/content/docs-core/zh-CN/development.md @@ -32,6 +32,10 @@ gh pr create --base main --head your-branch 提交信息遵循 Conventional Commits(`fix(scope): subject`),以英文书写。正文应说明*原因*,而不仅是*改动内容*。 +## 贡献者许可协议 + +首次提交 PR 时,需在 PR 中按机器人给出的句子回复,签署一次[贡献者许可协议](https://github.com/ThinkWatchProject/ThinkWatch-Core/blob/main/CLA.zh-CN.md)。签署后,此后向该仓库提交的 PR 均已涵盖。贡献的版权仍归作者所有,协议授权项目以任何条款(包括商业条款)许可该贡献。 + ## 强制规则 ThinkWatch Lite 与 ThinkWatch 企业版都依赖这些 crate,因此仅满足单一场景并不足够。违反以下任何一条规则的 PR 都将被要求修改,无论 diff 质量如何: diff --git a/src/content/docs-core/zh-CN/overview.md b/src/content/docs-core/zh-CN/overview.md index cf18ec0..09e42db 100644 --- a/src/content/docs-core/zh-CN/overview.md +++ b/src/content/docs-core/zh-CN/overview.md @@ -15,7 +15,7 @@ Claude Code、Codex 等客户端把请求发往网关后,Core 提供以下功 - **按规则路由。** 规则按模型、网关密钥、输入规模、是否携带工具或图片等请求属性匹配,将请求发往某个上游或策略组、改写其参数,或拒绝请求。客户端与上游的接口格式不同时,请求在 Anthropic Messages、OpenAI Chat Completions、OpenAI Responses 与 Gemini 之间转换。 - **首字节前的故障转移。** 首字节到达客户端之前,出错的上游由下一个上游替换,客户端无从察觉;此后发生的故障如实报告。熔断器使持续出错的上游暂不接收请求。 - **费用核算。** token 用量与缓存命中按公开价目表计价(控制面每天刷新一次),或按配置中的价目表计价;每个请求都记录费用及价格来源。估算的金额另行标注,无法计价的用量标记为「未知」,不会填入虚构的数值。 -- **出站脱敏。** 请求发出之前,其中任何位置的凭据和个人信息替换为占位符;模型回显时再恢复原值。 +- **出站脱敏。** 请求发出之前,其中的凭据和个人信息替换为占位符;模型回显时再恢复原值。字段名不在查找之列,自定义规则还会跳过协议要求原样保留的 id、类型、角色和名称。 - **工具调用审查。** 上游返回的工具调用按规则集审查,高危调用可在流式传输中途截断。 - **内容过滤。** 客户端发送的用户消息和工具结果按关键词、正则表达式或码位检查,每条规则可拒绝请求、删除命中的内容或仅记录;内置规则也能查出可夹带指令的隐藏字符。三项防护各可设为 `off`、`observe` 或 `enforce`,出厂均为 `observe`。 - **加密的控制面。** 桌面应用与 `twcore` 命令通过本地 socket(Windows 上为回环端口)连接 core,开启后也可经远程控制端口连接。每条控制连接都先以 `listen.control.key` 完成 Noise 握手,不使用证书。 diff --git a/src/content/docs-lite/en/contributing.md b/src/content/docs-lite/en/contributing.md index 546d872..c2c136a 100644 --- a/src/content/docs-lite/en/contributing.md +++ b/src/content/docs-lite/en/contributing.md @@ -8,6 +8,10 @@ gh pr create --base dev --head your-branch `main` is the release branch; `dev` receives routine work. GitHub sets the base of a new pull request to the default branch, `main`, which is not the intended target. If a pull request was opened against `main`, select *Edit* next to its title and change the base; the commits and the discussion are preserved. +## Contributor License Agreement + +A first pull request asks its author to sign the [Contributor License Agreement](https://github.com/ThinkWatchProject/ThinkWatch-Lite/blob/main/CLA.md) once, by replying on the pull request with the sentence the bot gives. The signature covers every later pull request to the repository. The author keeps the copyright, and the agreement lets the project license the contribution under any terms, commercial ones included. + ## Settled scope The following decisions are settled and are not open to change through a pull request: diff --git a/src/content/docs-lite/en/features.md b/src/content/docs-lite/en/features.md index cc4a7bb..d28c714 100644 --- a/src/content/docs-lite/en/features.md +++ b/src/content/docs-lite/en/features.md @@ -75,13 +75,13 @@ Every request records the rule it matched, the group it went through and each at The Security page holds three protections: outbound redaction, tool-call inspection and the content filter. They apply to every upstream and every key alike, and each runs in one of three modes: Off, Observe (detect and record, change nothing) and a third mode named for what it does: Replace for outbound redaction, Cut off for tool-call inspection and Enforce for the content filter. All three start in Observe, so out of the box no request is changed or refused. -- **Outbound redaction** searches the whole request before it leaves, including the system prompt, earlier turns and tool calls, for credentials and personal information: API keys and tokens for Anthropic, OpenAI, GitHub, Slack, AWS, Google, GitLab, Stripe, npm, DigitalOcean and SendGrid, private keys, JWTs and passwords in connection strings, as well as Chinese resident ID numbers and bank card numbers, which count only when their structure and check digit are valid. In Replace mode they are replaced with placeholders such as `<>` and restored where the response repeats them. Rules for email addresses, Chinese mainland mobile numbers, internal IP addresses and internal domains are included and start off, and a custom rule can name its own placeholder: `PROJECT` gives `<>`. +- **Outbound redaction** searches the whole request before it leaves, including the system prompt, earlier turns and tool calls, for credentials and personal information: API keys and tokens for Anthropic, OpenAI, GitHub, Slack, AWS, Google, GitLab, Stripe, npm, DigitalOcean and SendGrid, private keys, JWTs and passwords in connection strings, as well as Chinese resident ID numbers and bank card numbers, which count only when their structure and check digit are valid. Field names are not searched, and custom rules leave alone the identifiers a protocol needs as they are, such as item and call ids, types, roles, tool names and model names, so a rule cannot break an id a client sends back. In Replace mode they are replaced with placeholders such as `<>` and restored where the response repeats them. Rules for email addresses, Chinese mainland mobile numbers, internal IP addresses and internal domains are included and start off, and a custom rule can name its own placeholder: `PROJECT` gives `<>`. - **Tool-call inspection** 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. In Cut off mode such a call cuts the response off, so the client never receives a complete call to run. 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 the user messages and tool results in each request, context compaction included. A rule matches a keyword, a regular expression or code points such as `U+E0000–U+E007F`, and in Enforce mode it refuses the request, deletes the matched text and sends the rest, or only records. Built-in rules 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. -The page lists every rule, the built-in ones grouped by kind. Built-in rules can be switched on or off one at a time; a built-in tool-call rule can be set to cut off or only record, and a built-in content rule to refuse, delete or only record. Custom rules are regular expressions, or for the content filter also keywords or code points. Any rule can be tried on a sample text first; for redaction and the content filter, the test also shows the text as it would be sent. 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. +The page lists every rule, the built-in ones grouped by kind. Built-in rules can be switched on or off one at a time; a built-in tool-call rule can be set to cut off or only record, and a built-in content rule to refuse, delete or only record. A custom redaction 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 matches a keyword, a regular expression or code points, and a custom tool-call rule is a regular expression. Any rule can be tried on a sample text first; for redaction and the content filter, the test also shows the text as it would be sent. 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 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 matched segments were deleted. 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. +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 matched segments were deleted. 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; outbound redaction protects what leaves the machine, not the file. diff --git a/src/content/docs-lite/en/keep-api-keys-from-relays.md b/src/content/docs-lite/en/keep-api-keys-from-relays.md index 24c4acb..e9e8334 100644 --- a/src/content/docs-lite/en/keep-api-keys-from-relays.md +++ b/src/content/docs-lite/en/keep-api-keys-from-relays.md @@ -4,14 +4,14 @@ A relay receives every request in full, including keys that end up in the conver ## Before you start -- ThinkWatch Lite, [installed](/lite/#install), with clients connected to the gateway. This guide follows version 2026.10.6. +- ThinkWatch Lite, [installed](/lite/#install), with clients connected to the gateway. This guide follows version 2026.10.17. - Both protections start in **Observe**: they record what they find and change nothing. **Off** checks nothing. ## Steps 1. Open the Security page. The **Log** tab lists every finding with its request; since both modes use the same rules, a few days in **Observe** show what **Replace** would change. 2. Open **Redaction**. Built-in rules turn on or off one at a time, and **Test…** shows **What is sent** for a pasted text. -3. For a key format the built-in rules do not cover, choose **New rule**, enter a **Name**, a pattern under **Match (regular expression)** and a **Placeholder name** such as `INTERNAL` (giving `<>`), then choose **Create**. +3. For a key format the built-in rules do not cover, choose **New rule** and enter a **Name**. Under **Match by**, **Contains** looks for the text exactly as written, so `.` and `*` have no special meaning, and **Regex** takes a regular expression; **Match case**, off by default, tells capital letters from small ones. Enter the text or the pattern and a **Placeholder name** such as `INTERNAL` (giving `<>`), then choose **Create**. 4. Set the mode at the top of the tab to **Replace**. 5. Open **Tool calls**. Each built-in rule is marked **Cut off** or **Record only**, which **View rule** can change. Set the mode to **Cut off**. 6. The log's **Action** column then shows **Replaced** or **Cut off**, and a cut-off call also raises a system notification. From ThinkWatch Lite 2026.10.12, an entry opens in place to show where the value was found and the placeholder it was replaced with, or the tool call that was cut off and the notice the client received. @@ -23,7 +23,8 @@ A relay receives every request in full, including keys that end up in the conver | **Observe** (default) | Findings are recorded; the request is sent unchanged. | Matching calls are recorded and returned as usual. | | **Replace** / **Cut off** | Findings are replaced with placeholders before sending and restored in the response. | Calls matching a **Cut off** rule never fully reach the client; **Record only** rules still only record. | -- **What redaction covers.** The whole request, system prompt, earlier turns and tool calls included, except base64 data such as images. Built-in rules find API keys and tokens for Anthropic, OpenAI, GitHub, Slack, AWS (access key IDs), Google, GitLab, Stripe, npm, DigitalOcean and SendGrid, private keys, JWTs, passwords in connection strings, and Chinese resident ID and bank card numbers whose structure and check digit are valid. The rules for email addresses, Chinese mainland mobile numbers, internal IP addresses and internal domains start off. +- **What redaction covers.** The whole request, system prompt, earlier turns and tool calls included, except field names and base64 data such as images. Custom rules also leave alone the identifiers a protocol needs as they are: item and call ids, types, roles, tool names and model names; built-in rules still search them. Built-in rules find API keys and tokens for Anthropic, OpenAI, GitHub, Slack, AWS (access key IDs), Google, GitLab, Stripe, npm, DigitalOcean and SendGrid, private keys, JWTs, passwords in connection strings, and Chinese resident ID and bank card numbers whose structure and check digit are valid. The rules for email addresses, Chinese mainland mobile numbers, internal IP addresses and internal domains start off. +- **Rules from before 2026.10.17.** A custom rule saved by an earlier version was a case-sensitive regular expression; it now looks for its text as written and ignores case. A rule that relies on a regular expression still loads but stops matching, with nothing reported, so open each one and choose **Regex**, and **Match case** where capital letters matter. - **How values come back.** A value keeps one placeholder throughout a request, on every upstream. Placeholders the model writes back, in text or tool-call arguments, are restored before the client receives them: a command using the key runs locally with the real value, while the relay sees only the placeholder. - **What is cut off.** Calls that download or decode code and run it, send out environment variables or credential files, read private keys or cloud credentials, send a credential to an unknown host, or install startup items or scheduled jobs. Built-in rules for deleting home or root, world-writable permissions and uploading a local file only record. - **What the relay still sees.** The key configured for its upstream in the app, and the rest of the request as written: code, file contents, the conversation. The key a client uses for the gateway is not forwarded, and neither is the client's identity unless **Forward client identity** is on for that upstream. diff --git a/src/content/docs-lite/zh-CN/contributing.md b/src/content/docs-lite/zh-CN/contributing.md index 909ca32..06f659d 100644 --- a/src/content/docs-lite/zh-CN/contributing.md +++ b/src/content/docs-lite/zh-CN/contributing.md @@ -8,6 +8,10 @@ gh pr create --base dev --head your-branch `main` 为发布分支,`dev` 用于合入日常工作。GitHub 新建 PR 时以默认分支 `main` 作为 base,**该默认值并非正确的目标分支**。如已向 `main` 提交 PR,点击 PR 标题旁的 *Edit* 修改 base 即可,提交与讨论记录均会保留。 +## 贡献者许可协议 + +首次提交 PR 时,需在 PR 中按机器人给出的句子回复,签署一次[贡献者许可协议](https://github.com/ThinkWatchProject/ThinkWatch-Lite/blob/main/CLA.zh-CN.md)。签署后,此后向该仓库提交的 PR 均已涵盖。贡献的版权仍归作者所有,协议授权项目以任何条款(包括商业条款)许可该贡献。 + ## 已确定的范围 以下决定已经确定,不接受相关 PR: diff --git a/src/content/docs-lite/zh-CN/features.md b/src/content/docs-lite/zh-CN/features.md index 24bfcdc..fdcc5aa 100644 --- a/src/content/docs-lite/zh-CN/features.md +++ b/src/content/docs-lite/zh-CN/features.md @@ -75,13 +75,13 @@ API 密钥和请求头的值可以写成 `${变量名}`,读取系统环境变 安全页有三项防护:出站脱敏、工具调用审查和内容过滤,对所有上游和所有密钥统一生效。每项都有三档:「关闭」「观察」,以及按作用命名的第三档,出站脱敏为「替换」,工具调用审查为「切断」,内容过滤为「处置」;其中「观察」只检测和记录,不做任何改动。三项出厂均为「观察」,因此默认不会改动或拒绝任何请求。 -- **出站脱敏**:请求发出之前查找整个请求(含系统提示、之前的对话和工具调用)中的凭据和个人信息,包括 Anthropic、OpenAI、GitHub、Slack、AWS、Google、GitLab、Stripe、npm、DigitalOcean、SendGrid 的 API 密钥与令牌,以及私钥、JWT 和连接串中的口令,还有身份证号与银行卡号(号码结构与校验位都正确才算)。「替换」档下把它们替换为 `<>` 这样的占位符,响应中回显时再还原。邮箱地址、中国大陆手机号、内网 IP 地址和内网域名四条规则出厂为停用,可以按需启用;自定义规则可以指定占位符名称,如 `PROJECT` 替换为 `<>`。 +- **出站脱敏**:请求发出之前查找整个请求(含系统提示、之前的对话和工具调用)中的凭据和个人信息,包括 Anthropic、OpenAI、GitHub、Slack、AWS、Google、GitLab、Stripe、npm、DigitalOcean、SendGrid 的 API 密钥与令牌,以及私钥、JWT 和连接串中的口令,还有身份证号与银行卡号(号码结构与校验位都正确才算)。字段名不在查找之列;自定义规则还会跳过协议要求原样保留的标识,如条目与调用的 id、类型、角色、工具名和模型名,因此规则不会改坏客户端回传的 id。「替换」档下把它们替换为 `<>` 这样的占位符,响应中回显时再还原。邮箱地址、中国大陆手机号、内网 IP 地址和内网域名四条规则出厂为停用,可以按需启用;自定义规则可以指定占位符名称,如 `PROJECT` 替换为 `<>`。 - **工具调用审查**:检查模型返回的工具调用中是否含有下载或解码后执行代码、外发环境变量或凭据文件、把凭据发往本机和其服务商以外的主机、读取私钥或云服务凭据、读写 ThinkWatch 自己的数据目录、写入启动项或定时任务等命令。只出现在要写入的文字里的路径(例如提到这个目录的文档)不算。「切断」档下命中即切断响应,客户端收不到一个完整、可执行的调用。删除主目录或根目录、设置全员可写权限、把本地文件上传到外部主机三条规则出厂只记录。 - **内容过滤**:检查每个请求中的用户消息和工具结果,压缩上下文的请求也在其内。规则按关键词、正则表达式或码位(如 `U+E0000–U+E007F`)匹配,「处置」档下按规则拒绝请求、删除命中的文字后发出,或仅记录。内置规则删除 Unicode 标签字符和双向控制符(人看不见、模型读得到,可用于隐藏指令),拒绝明确要求「忽略先前指令」的请求。零宽字符、私用区字符两条规则(表情符号、波斯文和图标字体也会用到这些字符),以及越狱、身份操纵、套取提示词等规则及其中文版本出厂为停用,可以按需启用。 -安全页列出全部规则,内置规则按类别分组。内置规则可以逐条启用或停用;工具调用审查的内置规则可以设为切断或仅记录,内容过滤的内置规则可以设为拒绝、删除或仅记录。自定义规则为正则表达式,内容过滤也可以使用关键词或码位。任何规则都可以先用一段文本测试,出站脱敏和内容过滤的测试还会显示发出时的文本。各项防护检出的内容都记入第一个标签页的日志,并注明所属的请求;隐藏字符拼出文字时,日志中一并显示这段文字。 +安全页列出全部规则,内置规则按类别分组。内置规则可以逐条启用或停用;工具调用审查的内置规则可以设为切断或仅记录,内容过滤的内置规则可以设为拒绝、删除或仅记录。出站脱敏的自定义规则按原样查找文字(「包含」,默认)或按正则表达式匹配(「正则」),默认不区分大小写,打开「区分大小写」后区分;内容过滤的自定义规则使用关键词、正则表达式或码位,工具调用审查的自定义规则为正则表达式。任何规则都可以先用一段文本测试,出站脱敏和内容过滤的测试还会显示发出时的文本。各项防护检出的内容都记入第一个标签页的日志,并注明所属的请求;隐藏字符拼出文字时,日志中一并显示这段文字。 -日志中的记录可以在原处展开。展开后列出规则命中的每一处,最多 50 处,其余注明处数:所在的部分(系统提示、第几条消息及其角色、工具调用或工具结果及工具名称、回答正文)、JSON 路径,以及前后各最多 80 个字符的上下文,其中的凭据以掩码显示。展开的内容给出命中时的规则:自定义规则的写法,或内置规则的名称及作出判定的 core 版本,因此规则修改或删除之后,记录仍然可读。展开的内容还写明具体的处置:凭据替换成的占位符,被切断的工具调用及客户端收到的提示,请求被拒绝时客户端收到的回复,或删除了几处命中的内容。所属的会话,以及与客户端请求的模型不同时实际发往上游的模型,也一并列出。请求时间线中的防护记录以同样的方式展开。「导出」把所选区间内的全部记录保存为 JSON(完整细节)或 CSV(每条一行)。日志的保留期限与请求记录相同,防护设置的改动不记入日志。 +日志中的记录可以在原处展开。展开后列出规则命中的每一处,最多 50 处,其余注明处数:所在的部分(系统提示、第几条消息及其角色、工具调用或工具结果及工具名称、回答正文)、JSON 路径,以及前后各最多 80 个字符的上下文,其中的凭据以掩码显示。展开的内容给出命中时的规则:自定义规则的写法与匹配方式,或内置规则的名称及作出判定的 core 版本,因此规则修改或删除之后,记录仍然可读。展开的内容还写明具体的处置:凭据替换成的占位符,被切断的工具调用及客户端收到的提示,请求被拒绝时客户端收到的回复,或删除了几处命中的内容。所属的会话,以及与客户端请求的模型不同时实际发往上游的模型,也一并列出。请求时间线中的防护记录以同样的方式展开。「导出」把所选区间内的全部记录保存为 JSON(完整细节)或 CSV(每条一行)。日志的保留期限与请求记录相同,防护设置的改动不记入日志。 各项防护作用于经过网关的请求和回答,不作用于磁盘上的文件。配置文件以明文保存密钥,只有所有者可读:挡得住其他用户,挡不住以同一用户身份运行的程序;出站脱敏保护的是带出本机的内容,不是这份文件。 diff --git a/src/content/docs-lite/zh-CN/keep-api-keys-from-relays.md b/src/content/docs-lite/zh-CN/keep-api-keys-from-relays.md index 7590b9b..6092072 100644 --- a/src/content/docs-lite/zh-CN/keep-api-keys-from-relays.md +++ b/src/content/docs-lite/zh-CN/keep-api-keys-from-relays.md @@ -4,14 +4,14 @@ ## 准备 -- 已[安装](/zh-CN/lite/#install) ThinkWatch Lite,并已接管客户端,请求经过网关。本文按 2026.10.6 版编写。 +- 已[安装](/zh-CN/lite/#install) ThinkWatch Lite,并已接管客户端,请求经过网关。本文按 2026.10.17 版编写。 - 两项防护出厂均为「观察」:只记录检出的内容,不做任何改动。「关闭」档不检查也不记录。 ## 步骤 1. 打开安全页。「日志」标签列出各项防护检出的内容及其所属的请求;两档使用同一套规则,因此先在「观察」档下运行几天,即可看出切换到「替换」后会改动哪些内容。 2. 打开「出站脱敏」标签。内置规则可以逐条启用或停用,「测试…」对粘贴的文本显示「发出的内容」。 -3. 内置规则未覆盖的密钥格式,选择「新建规则」,填写「名称」、「匹配(正则表达式)」和「占位符名称」(例如 `INTERNAL`,替换为 `<>`),然后选择「创建」。 +3. 内置规则未覆盖的密钥格式,选择「新建规则」,填写「名称」。「匹配方式」中,「包含」按原样查找文字,`.`、`*` 等字符没有特殊含义;「正则」按正则表达式匹配。「区分大小写」默认关闭。填写要查找的文字或正则表达式,以及「占位符名称」(例如 `INTERNAL`,替换为 `<>`),然后选择「创建」。 4. 把标签顶部的档位切换到「替换」。 5. 打开「工具调用审查」标签。每条内置规则的处置为「切断」或「仅记录」,可以在「查看规则」中修改。把档位切换到「切断」。 6. 此后日志的「处置」一栏显示「已替换」或「已切断」;调用被切断时还会发送系统通知。自 ThinkWatch Lite 2026.10.12 起,记录可以在原处展开,显示命中的位置与替换成的占位符,或被切断的工具调用及客户端收到的提示。 @@ -23,7 +23,8 @@ | 「观察」(出厂) | 检出的内容记入日志,请求原样发出。 | 命中的调用记入日志,照常返回。 | | 「替换」/「切断」 | 检出的内容替换为占位符后发出,响应中的占位符还原为原值。 | 命中「切断」规则的调用不会完整到达客户端;「仅记录」规则仍只记录。 | -- **出站脱敏的范围。**查找整个请求,包括系统提示、之前的对话和工具调用,但不查找图片等 base64 数据。内置规则覆盖 Anthropic、OpenAI、GitHub、Slack、AWS(访问密钥 ID)、Google、GitLab、Stripe、npm、DigitalOcean、SendGrid 的 API 密钥与令牌,私钥、JWT、连接串中的口令,以及号码结构与校验位都正确的身份证号和银行卡号。邮箱地址、中国大陆手机号、内网地址、内部域名四条规则出厂为停用。 +- **出站脱敏的范围。**查找整个请求,包括系统提示、之前的对话和工具调用,但不查找字段名和图片等 base64 数据。自定义规则还会跳过协议要求原样保留的标识:条目与调用的 id、类型、角色、工具名和模型名;内置规则照常查找这些值。内置规则覆盖 Anthropic、OpenAI、GitHub、Slack、AWS(访问密钥 ID)、Google、GitLab、Stripe、npm、DigitalOcean、SendGrid 的 API 密钥与令牌,私钥、JWT、连接串中的口令,以及号码结构与校验位都正确的身份证号和银行卡号。邮箱地址、中国大陆手机号、内网地址、内部域名四条规则出厂为停用。 +- **2026.10.17 之前保存的规则。**此前版本保存的自定义规则按区分大小写的正则表达式匹配,现在改为按原样查找文字、不区分大小写。依赖正则表达式的规则仍可加载,但不再命中,也没有任何提示;应逐条打开,选择「正则」,需要区分大小写的再打开「区分大小写」。 - **原值如何还原。**同一个值在整个请求中只对应一个占位符,无论发往哪个上游都不变。模型在文字或工具调用参数中写回的占位符,在交给客户端之前还原:用到这把密钥的命令在本机以原值执行,中转站始终只看到占位符。 - **哪些调用会被切断。**下载或解码后执行代码、外发环境变量或凭据文件、读取私钥或云凭据、把凭据发往陌生主机、写入启动项或安装定时任务的调用。删除主目录或根目录、开放全部写权限、上传本地文件三类内置规则出厂只记录。 - **中转站仍能看到的内容。**应用中为该上游配置的密钥,以及请求的其余内容,例如代码、文件内容和对话,均按原样发给中转站。客户端连接网关所用的密钥不会转发;客户端的身份信息也不转发,除非在该上游上打开「转发客户端身份」。 diff --git a/src/content/docs/en/configuration.md b/src/content/docs/en/configuration.md index 206d237..c8d2bed 100644 --- a/src/content/docs/en/configuration.md +++ b/src/content/docs/en/configuration.md @@ -506,16 +506,18 @@ Outbound redaction, tool-call inspection and the content filter each keep their - `mode` is `off`, `observe` or `enforce`. `observe`, the factory mode, records matches in the audit log and changes nothing; the console calls `enforce` Replace for outbound redaction, Cut off for tool-call inspection and Enforce for the content filter. - `enable` and `disable` switch built-in rules on and off by id, `actions` changes what a built-in rule does under `enforce`, and `custom` holds rules of the deployment's own. +- A custom redaction rule's `match` is `contains`, the default, which looks for `pattern` as written, so `.`, `*` and `\d` have no special meaning, or `regex`, which takes `pattern` as a regular expression. It ignores case unless `case_sensitive` is `true`. The console calls these Match by and Case-sensitive. +- Redaction does not search field names, and custom redaction rules leave alone the values of `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, and every other value is searched by every rule. The same holds when stored bodies are masked with `audit.body_redact_pii`. ```json { "mode": "enforce", "enable": ["email"], - "custom": [{ "name": "Employee ID", "pattern": "EMP-\\d{6}", "label": "EMPLOYEE" }] + "custom": [{ "name": "Employee ID", "pattern": "EMP-\\d{6}", "match": "regex", "label": "EMPLOYEE" }] } ``` -The Content Security page of the console edits these objects. Through `PATCH /api/admin/settings` a key is written whole and checked first: a misspelt field, an unknown built-in rule, a pattern that does not compile or a malformed code point is refused with `400`. Writing `security.redact` takes the `pii_redactor:write` permission, and the other two keys take `content_filter:write`. `GET /api/admin/security` lists each guard's mode and every rule, and `POST /api/admin/security/{guard}/test` tries a sample against them. A deployment upgraded from a version that kept `security.content_filter_patterns`, `security.pii_redactor_patterns`, `security.hidden_text` and `security.tool_inspection` has them converted into these keys at its first start. +The Content Security page of the console edits these objects. Through `PATCH /api/admin/settings` a key is written whole and checked first: a misspelt field, an unknown built-in rule, a pattern that does not compile or a malformed code point is refused with `400`. Writing `security.redact` takes the `pii_redactor:write` permission, and the other two keys take `content_filter:write`. `GET /api/admin/security` lists each guard's mode and every rule, and `POST /api/admin/security/{guard}/test` tries a sample against them. Custom redaction rules saved by ThinkWatch 3.0 to 3.6 carry neither `match` nor `case_sensitive`, so from 3.7.0 they look for their text as written and ignore case; one written as a regular expression needs `"match": "regex"`, and `"case_sensitive": true` where case matters. Change them only once every replica runs 3.7.0, since 3.6 cannot read a redaction policy that uses these keys. A deployment upgraded from a version that kept `security.content_filter_patterns`, `security.pii_redactor_patterns`, `security.hidden_text` and `security.tool_inspection` has them converted into these keys at its first start; the old redaction patterns become case-sensitive `regex` rules. #### Limits that cannot be checked diff --git a/src/content/docs/en/deployment-guide.md b/src/content/docs/en/deployment-guide.md index 416238f..1d05a39 100644 --- a/src/content/docs/en/deployment-guide.md +++ b/src/content/docs/en/deployment-guide.md @@ -231,7 +231,7 @@ docker compose -f deploy/docker-compose.yml --env-file .env.production up -d To pin a release instead of `latest`, set `IMAGE_TAG` to its version, or to the SHA of a commit on `main`: ```bash -IMAGE_TAG=3.5.0 docker compose -f deploy/docker-compose.yml --env-file .env.production up -d +IMAGE_TAG=3.7.0 docker compose -f deploy/docker-compose.yml --env-file .env.production up -d ``` This starts: @@ -752,8 +752,8 @@ The server is stateless, so rolling updates work out of the box: ```bash # Update the image tag helm upgrade think-watch deploy/helm/think-watch \ - --set image.server.tag=3.5.0 \ - --set image.web.tag=3.5.0 \ + --set image.server.tag=3.7.0 \ + --set image.web.tag=3.7.0 \ --reuse-values ``` diff --git a/src/content/docs/zh-CN/configuration.md b/src/content/docs/zh-CN/configuration.md index 3cd4720..6303e78 100644 --- a/src/content/docs/zh-CN/configuration.md +++ b/src/content/docs/zh-CN/configuration.md @@ -506,16 +506,18 @@ openssl rand -base64 32 - `mode` 为 `off`、`observe` 或 `enforce`。出厂为 `observe`:命中的内容记入审计日志,不做任何改动;`enforce` 在控制台中按作用命名,出站脱敏为「替换」,工具调用审查为「切断」,内容过滤为「处置」。 - `enable` 和 `disable` 按 id 启用或停用内置规则,`actions` 改变内置规则在 `enforce` 下的处置,`custom` 保存部署自己的规则。 +- 出站脱敏的自定义规则以 `match` 指定匹配方式:`contains`(默认)按原样查找 `pattern`,`.`、`*`、`\d` 等没有特殊含义;`regex` 把 `pattern` 作为正则表达式。`case_sensitive` 为 `true` 时区分大小写,否则不区分。控制台中两项分别为「匹配方式」和「区分大小写」。 +- 出站脱敏不查找字段名;自定义脱敏规则还会跳过 `id`、`call_id`、`item_id`、`tool_use_id`、`tool_call_id`、`toolUseId`、`previous_response_id`、`response_id`、`type`、`role`、`name`、`namespace`、`model`、`status`、`object` 和 `phase` 的值。内置规则照常查找这些值,其余的值由所有规则照常查找。按 `audit.body_redact_pii` 对存储的请求体脱敏时同样如此。 ```json { "mode": "enforce", "enable": ["email"], - "custom": [{ "name": "员工编号", "pattern": "EMP-\\d{6}", "label": "EMPLOYEE" }] + "custom": [{ "name": "员工编号", "pattern": "EMP-\\d{6}", "match": "regex", "label": "EMPLOYEE" }] } ``` -控制台的「内容安全」页编辑这些对象。经 `PATCH /api/admin/settings` 写入时,一个键整体写入,写入前先校验:字段名拼错、内置规则 id 不存在、正则无法编译或码位写法有误时返回 `400`。写入 `security.redact` 需要 `pii_redactor:write` 权限,另外两个键需要 `content_filter:write`。`GET /api/admin/security` 列出每项防护的档位与全部规则,`POST /api/admin/security/{guard}/test` 用一段样本测试这些规则。从仍使用 `security.content_filter_patterns`、`security.pii_redactor_patterns`、`security.hidden_text` 和 `security.tool_inspection` 的版本升级时,这些设置在首次启动时转换为上述三个键。 +控制台的「内容安全」页编辑这些对象。经 `PATCH /api/admin/settings` 写入时,一个键整体写入,写入前先校验:字段名拼错、内置规则 id 不存在、正则无法编译或码位写法有误时返回 `400`。写入 `security.redact` 需要 `pii_redactor:write` 权限,另外两个键需要 `content_filter:write`。`GET /api/admin/security` 列出每项防护的档位与全部规则,`POST /api/admin/security/{guard}/test` 用一段样本测试这些规则。ThinkWatch 3.0 至 3.6 保存的自定义脱敏规则不带 `match` 和 `case_sensitive`,自 3.7.0 起按原样查找文字、不区分大小写;按正则表达式编写的规则需要加上 `"match": "regex"`,需要区分大小写的再加上 `"case_sensitive": true`。3.6 读不了使用这两个键的脱敏策略,因此应在所有副本都升级到 3.7.0 之后再修改。从仍使用 `security.content_filter_patterns`、`security.pii_redactor_patterns`、`security.hidden_text` 和 `security.tool_inspection` 的版本升级时,这些设置在首次启动时转换为上述三个键,原有的脱敏写法转换为区分大小写的 `regex` 规则。 #### 无法检查限额时 diff --git a/src/content/docs/zh-CN/deployment-guide.md b/src/content/docs/zh-CN/deployment-guide.md index 66a7188..88639a4 100644 --- a/src/content/docs/zh-CN/deployment-guide.md +++ b/src/content/docs/zh-CN/deployment-guide.md @@ -231,7 +231,7 @@ docker compose -f deploy/docker-compose.yml --env-file .env.production up -d 如需固定某个版本而非 `latest`,将 `IMAGE_TAG` 设为该版本号,或 `main` 分支上某次提交的 SHA: ```bash -IMAGE_TAG=3.5.0 docker compose -f deploy/docker-compose.yml --env-file .env.production up -d +IMAGE_TAG=3.7.0 docker compose -f deploy/docker-compose.yml --env-file .env.production up -d ``` 这将启动: @@ -752,8 +752,8 @@ ThinkWatch 在 Web 控制台中内置了**配置指南**页面,位于 `/guide` ```bash # Update the image tag helm upgrade think-watch deploy/helm/think-watch \ - --set image.server.tag=3.5.0 \ - --set image.web.tag=3.5.0 \ + --set image.server.tag=3.7.0 \ + --set image.web.tag=3.7.0 \ --reuse-values ``` diff --git a/src/data/core-docs/config.md b/src/data/core-docs/config.md index 4320ddf..ff22925 100644 --- a/src/data/core-docs/config.md +++ b/src/data/core-docs/config.md @@ -759,7 +759,9 @@ Before a request leaves, the whole request (system prompt, earlier answers and tool calls included) is searched for credentials and personal information. Under `enforce` what is found is replaced with placeholders, and put back where the answer repeats them. Images, files and other base64 -payloads are not searched. +payloads are not searched, nor are field names; custom rules also leave alone +the identifiers a protocol needs as they are, such as item and call ids, +types, roles, tool names and model names. @@ -769,7 +771,7 @@ payloads are not searched. | `mode` | `off` \| `observe` \| `enforce` | `observe` | `off` does nothing; `observe` detects and records only, and changes nothing; `enforce` detects and acts. | | `enable` | list of strings | `[]` | Built-in rules to switch on that are off out of the box, by id. | | `disable` | list of strings | `[]` | Built-in rules to switch off, by id. | -| `custom` | list of [`security.redact.custom[]`](#cfg-security-redact-custom) | `[]` | Rules of your own: whatever a pattern matches is replaced like a credential. | +| `custom` | list of [`security.redact.custom[]`](#cfg-security-redact-custom) | `[]` | Rules of your own: whatever a rule matches is replaced like a credential. | @@ -778,8 +780,10 @@ payloads are not searched. | Field | Type | Default | Description | |---|---|---|---| | `name` | string | **required** | Name shown in logs and in the app; it identifies the rule and has to be unique within this guard. | -| `pattern` | string | **required** | Regular expression. | -| `label` | string | `SECRET` | Placeholder name: what the pattern matches is replaced with `<>`, numbered per name. Capital letters, digits and underscores, starting with a letter, at most 24 characters. | +| `pattern` | string | **required** | The text to look for; a regular expression with `match: regex`. | +| `match` | `contains` \| `regex` | `contains` | `contains`: `pattern` is looked for as written; characters such as `.` and `*` have no special meaning. `regex`: `pattern` is a regular expression. | +| `case_sensitive` | bool | `false` | Tell capital letters from small ones. Off by default: `ABC` and `abc` both match. | +| `label` | string | `SECRET` | Placeholder name: what the rule matches is replaced with `<>`, numbered per name. Capital letters, digits and underscores, starting with a letter, at most 24 characters. | | `disabled` | bool | `false` | Switches the rule off and keeps it in the file. | diff --git a/src/data/core-docs/config.zh-CN.md b/src/data/core-docs/config.zh-CN.md index 4934f04..4021405 100644 --- a/src/data/core-docs/config.zh-CN.md +++ b/src/data/core-docs/config.zh-CN.md @@ -561,7 +561,7 @@ pricing: #### `security.redact` -请求发出前,在整个请求中(包括系统提示、之前的回答和工具调用)查找凭据和个人信息。`enforce` 下将其替换为占位符,回答中重复出现时再换回原值。图片、文件等 base64 内容不查。 +请求发出前,在整个请求中(包括系统提示、之前的回答和工具调用)查找凭据和个人信息。`enforce` 下将其替换为占位符,回答中重复出现时再换回原值。图片、文件等 base64 内容不查,字段名也不查;自定义规则还不查协议要求原样保留的标识,如条目和调用的 id、类型、角色、工具名、模型名。 @@ -571,7 +571,7 @@ pricing: | `mode` | `off` \| `observe` \| `enforce` | `observe` | `off` 不检测;`observe` 检测并记录,不改变任何行为;`enforce` 检测并处置。 | | `enable` | 字符串列表 | `[]` | 打开出厂时关着的内置规则,按 id。 | | `disable` | 字符串列表 | `[]` | 关掉内置规则,按 id。 | -| `custom` | 对象列表,见 [`security.redact.custom[]`](#cfg-security-redact-custom) | `[]` | 自定义规则:正则匹配到的内容和凭据一样替换。 | +| `custom` | 对象列表,见 [`security.redact.custom[]`](#cfg-security-redact-custom) | `[]` | 自定义规则:匹配到的内容和凭据一样替换。 | @@ -580,8 +580,10 @@ pricing: | 字段 | 类型 | 默认值 | 说明 | |---|---|---|---| | `name` | 字符串 | **必填** | 日志和应用里显示的名字,也是规则的标识;同一项防护里不能重名。 | -| `pattern` | 字符串 | **必填** | 正则表达式。 | -| `label` | 字符串 | `SECRET` | 占位符名称:正则匹配到的内容替换为 `<>`,每个名称各自编号。只能使用大写字母、数字和下划线,以字母开头,最多 24 个字符。 | +| `pattern` | 字符串 | **必填** | 要查找的文字;`match: regex` 时为正则表达式。 | +| `match` | `contains` \| `regex` | `contains` | `contains`:按原样查找 `pattern`,`.`、`*` 等字符没有特殊含义。`regex`:`pattern` 是正则表达式。 | +| `case_sensitive` | 布尔 | `false` | 区分大小写。默认不区分:`ABC` 和 `abc` 都会匹配。 | +| `label` | 字符串 | `SECRET` | 占位符名称:匹配到的内容替换为 `<>`,每个名称各自编号。只能使用大写字母、数字和下划线,以字母开头,最多 24 个字符。 | | `disabled` | 布尔 | `false` | 停用这条规则,规则本身留在文件里。 | diff --git a/src/data/core-docs/manifest.json b/src/data/core-docs/manifest.json index 18e4e4a..52b9db5 100644 --- a/src/data/core-docs/manifest.json +++ b/src/data/core-docs/manifest.json @@ -1,9 +1,9 @@ { "repository": "ThinkWatchProject/ThinkWatch-Core", - "ref": "v0.70.0", + "ref": "v0.71.0", "files": { - "docs/config.md": "6f281936051e2586c4076c62e22759a0addaad7b493b53dc427d74966a84a109", - "docs/config.zh-CN.md": "bc2a649755cc7f705151658213232a78dcdcf176b770f8f66cf1fdf9a7e27a6f", + "docs/config.md": "78a0133f0351e44c164261cc9ed7b2239360243c5b6bcbd807996dd7ae50c436", + "docs/config.zh-CN.md": "a9f2ad1f11adf2e3582692933d915f3e8ce6f955bebf9d675453b376e5910ee8", "docs/server.md": "5e1e9b901bef2b46d417aea057a1db24c78457d3c1938b8540ecb4766515b68f", "docs/server.zh-CN.md": "1f508e7c39b8ded28653773ca4d8701e6bdc2247bcd359c3a8fd00fd1401a6ff" }