diff --git a/Cargo.lock b/Cargo.lock index 05cb3b7..716669a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3143,7 +3143,7 @@ dependencies = [ [[package]] name = "tw-api" -version = "0.73.0" +version = "0.74.0" dependencies = [ "serde", "serde_json", @@ -3154,7 +3154,7 @@ dependencies = [ [[package]] name = "tw-bedrock" -version = "0.73.0" +version = "0.74.0" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -3175,7 +3175,7 @@ dependencies = [ [[package]] name = "tw-breaker" -version = "0.73.0" +version = "0.74.0" dependencies = [ "serde", "serde_json", @@ -3183,7 +3183,7 @@ dependencies = [ [[package]] name = "tw-config" -version = "0.73.0" +version = "0.74.0" dependencies = [ "blake3", "libc", @@ -3208,7 +3208,7 @@ dependencies = [ [[package]] name = "tw-control" -version = "0.73.0" +version = "0.74.0" dependencies = [ "axum", "base64", @@ -3251,7 +3251,7 @@ dependencies = [ [[package]] name = "tw-dialect" -version = "0.73.0" +version = "0.74.0" dependencies = [ "memchr", "serde", @@ -3260,7 +3260,7 @@ dependencies = [ [[package]] name = "tw-engine" -version = "0.73.0" +version = "0.74.0" dependencies = [ "chrono", "serde", @@ -3274,7 +3274,7 @@ dependencies = [ [[package]] name = "tw-gateway" -version = "0.73.0" +version = "0.74.0" dependencies = [ "arc-swap", "async-stream", @@ -3325,7 +3325,7 @@ dependencies = [ [[package]] name = "tw-guard" -version = "0.73.0" +version = "0.74.0" dependencies = [ "aho-corasick", "base64", @@ -3343,7 +3343,7 @@ dependencies = [ [[package]] name = "tw-link" -version = "0.73.0" +version = "0.74.0" dependencies = [ "serde", "serde_json", @@ -3357,7 +3357,7 @@ dependencies = [ [[package]] name = "tw-observe" -version = "0.73.0" +version = "0.74.0" dependencies = [ "tokio", "tracing", @@ -3366,7 +3366,7 @@ dependencies = [ [[package]] name = "tw-plugin" -version = "0.73.0" +version = "0.74.0" dependencies = [ "libc", "rand 0.10.2", @@ -3381,7 +3381,7 @@ dependencies = [ [[package]] name = "tw-pricing" -version = "0.73.0" +version = "0.74.0" dependencies = [ "arc-swap", "flate2", @@ -3394,14 +3394,14 @@ dependencies = [ [[package]] name = "tw-secret" -version = "0.73.0" +version = "0.74.0" dependencies = [ "thiserror", ] [[package]] name = "tw-store" -version = "0.73.0" +version = "0.74.0" dependencies = [ "blake3", "bytes", @@ -3421,7 +3421,7 @@ dependencies = [ [[package]] name = "tw-types" -version = "0.73.0" +version = "0.74.0" dependencies = [ "serde", "serde_json", @@ -3430,7 +3430,7 @@ dependencies = [ [[package]] name = "tw-watch" -version = "0.73.0" +version = "0.74.0" dependencies = [ "libc", "notify", @@ -3441,7 +3441,7 @@ dependencies = [ [[package]] name = "tw-yaml" -version = "0.73.0" +version = "0.74.0" dependencies = [ "saphyr-parser", "serde_yaml_ng", @@ -3451,7 +3451,7 @@ dependencies = [ [[package]] name = "twcore" -version = "0.73.0" +version = "0.74.0" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 6a343e3..b48580f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ categories = ["network-programming", "web-programming::http-server"] # 二进制走 CalVer,crate 走 SemVer —— 两者是两回事,卖给不同的人。 # 这里是 crate 的版本。 -version = "0.73.0" +version = "0.74.0" [workspace.dependencies] # ── 内部 crate ─────────────────────────────────────────── diff --git a/docs/config.md b/docs/config.md index df23ec8..7ef1c66 100644 --- a/docs/config.md +++ b/docs/config.md @@ -756,14 +756,16 @@ refuses, deletes, replaces or only records. #### `security.redact` -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 a rule finds is replaced with placeholders, -and put back where the answer repeats them; what a rule with `action: record` -finds is sent as it is and only recorded. Images, files and other base64 -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. +Before a request leaves, the text written in it is searched for credentials +and personal information: system prompts, the messages of every role +(earlier answers included), reasoning, tool-call arguments, tool results and +tool descriptions. Under `enforce` what a rule finds is replaced with +placeholders, and put back where the answer repeats them; what a rule with +`action: record` finds is sent as it is and only recorded. Values the +protocol needs as they are, such as ids, types, roles, tool names, model +names and signatures, are never searched, nor are field names, images, +files, settings, or parts of a request the gateway does not recognize as +written text. diff --git a/docs/config.zh-CN.md b/docs/config.zh-CN.md index 89df577..1940b24 100644 --- a/docs/config.zh-CN.md +++ b/docs/config.zh-CN.md @@ -561,7 +561,7 @@ pricing: #### `security.redact` -请求发出前,在整个请求中(包括系统提示、之前的回答和工具调用)查找凭据和个人信息。`enforce` 下将规则找到的内容替换为占位符,回答中重复出现时再换回原值;`action: record` 的规则找到的内容原样发送,只作记录。图片、文件等 base64 内容不查,字段名也不查;自定义规则还不查协议要求原样保留的标识,如条目和调用的 id、类型、角色、工具名、模型名。 +请求发出前,在请求中写出的正文里查找凭据和个人信息:系统提示、各角色的消息(包括之前的回答)、推理、工具调用的参数、工具结果和工具说明。`enforce` 下将规则找到的内容替换为占位符,回答中重复出现时再换回原值;`action: record` 的规则找到的内容原样发送,只作记录。协议要求原样保留的值(如 id、类型、角色、工具名、模型名、签名)一律不查,字段名、图片、文件、配置项以及网关不认作正文的部分也不查。 diff --git a/release-notes/0.74.0.md b/release-notes/0.74.0.md new file mode 100644 index 0000000..3a44079 --- /dev/null +++ b/release-notes/0.74.0.md @@ -0,0 +1,18 @@ +This release makes outbound redaction look only at the text written in a request, located by the request's format, so it can no longer change an id or another value the protocol needs and get the request refused. + +**Upgrade notes** + +- The control-plane protocol version stays at 48; ThinkWatch Lite releases that include 0.73.0 connect to 0.74.0 and the other way round. +- The request store's schema and the configuration are unchanged; nothing is cleared. +- Requests to paths the gateway does not recognize, and WebSocket connections other than Responses and Realtime, are no longer searched by redaction; they were searched as whole text before. +- Request fields outside the written text, such as `metadata`, are no longer searched. +- For crates that embed the gateway: `tw_guard::redact::flow::find`, `look`, `look_from`, `look_hits` and `replace` take the request's `Shape`, and `reported` takes a `Scope`. `hits` still searches the whole text; `hits_in` searches a `Scope`. New module `tw_dialect::written` with `Shape`, `spots` and `ranges`. + +**Redaction searches only written text.** Rules used to run over the whole JSON body, with field names and a fixed list of identifier fields left out for custom rules only. A built-in rule could still match inside a protocol value: with the mobile-number rule on, eleven digits inside a Codex reasoning item id (`rs_` followed by hexadecimal) were replaced with a placeholder, the upstream refused the id, and since the history is sent again on every turn, every later turn of that conversation was refused too. + +Redaction now searches only the text people, models and tools wrote: +- Anthropic, Chat Completions, Responses, Gemini and Converse requests: system prompts and instructions, the text of every message whatever its role, reasoning text and summaries, tool-call arguments and inputs, tool results, and tool descriptions. +- Embeddings (`input`), legacy completions (`prompt` and `suffix`), Gemini embeddings, and the client events of a Realtime connection. +- Ids, types, roles, names, model names, signatures, encrypted content, images and files, and settings are never searched, and neither are blocks or fields not on this list. + +Reasoning text stays on the list: placeholders in an answer are restored before the client sees it, and when the client sends the history back, the values in it are replaced again. A request converted to another format is searched as the format it is sent in. Stored bodies and security-log excerpts are still masked as whole text.