mcpField
N
NullWire-MCP

NullWire-MCP

本地服务
github.com/VoxNode-ux开发工具01 天前更新

关于 NullWire-MCP

NullWire-MCP 是面向开发者的 Go HTTP 调试代理和 MCP 服务,用于排查 API 流量。流量仅保存在内存中,秘密值会替换为稳定的加盐标签,Agent 可据此关联值而看不到原始秘密。

主要功能

  1. 01本地 HTTP 代理与反向网关
  2. 02仅存于内存的近期流量环形缓冲区
  3. 03加盐标签保留秘密值身份
  4. 04筛选并检查脱敏流量
  5. 05导出可重放的脱敏 curl 命令

适用场景

  1. 01比较不同请求的认证标头
  2. 02确认令牌是否复用或轮换
  3. 03生成命令以重放失败请求

MCP 相关

服务通过 stdio 与 MCP 客户端通信,并提供检查近期脱敏 HTTP 流量及导出脱敏 curl 命令的工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

⚡ NullWire-MCP

你的 AI Agent 可以调试实时 HTTP 流量。
它看不到任何秘密。

每个令牌、Cookie 和 IP 都会替换为稳定的加盐标签,因此 Agent 仍能分辨
“同一令牌被重复使用”和“令牌已轮换”:这是大多数身份验证错误背后的问题。

CI Release Go Dependencies: 0 Disk writes: 0 bytes MCP License: MIT

快速开始 · 工作原理 · 安装 · 工具 · 安全性 · [局限性]#-honest-scope-&-known-limitations) · 常见问题


👀 看看效果有何不同

 You send        Authorization: Bearer sk-live-51Hk3...
 Your AI sees    Authorization: Bearer [REDACTED_TOKEN#a1b2c3]

 Request #1      Bearer [REDACTED_TOKEN#a1b2c3]
 Request #2      Bearer [REDACTED_TOKEN#a1b2c3]   ◄── same token, reused ✔
 Request #3      Bearer [REDACTED_TOKEN#7f09de]   ◄── different token, it rotated ✔

盲目地用 [REDACTED] 替换内容能保护你,却会让 AI 无法调试身份验证错误。NullWire 保留每个秘密的身份,并丢弃它的值。

📦 0 项依赖💾 0 字节写入磁盘🔍 1 个待审查的源文件🖥️ 9 个发布目标
纯 Go 标准库仅驻留内存的 50 流环形缓冲区main.goLinux · macOS · Windows · FreeBSD

⚡ 60 秒快速开始

# 1. install (Go 1.23+), or grab a prebuilt binary from Releases
go install github.com/VoxNode-ux/nullwire-mcp@latest

# 2. register it with your AI client (Claude Code shown; Cursor/Windsurf/Desktop configs below)
claude mcp add nullwire -- nullwire-mcp -port 8080 -target https://api.example.com

# 3. point your app at the proxy instead of the real API
curl http://127.0.0.1:8080/v1/orders -H "Authorization: Bearer $MY_TOKEN"

然后直接向你的 Agent 提问:

“为什么 /v1/orders 返回 401?比较最近 5 个请求的身份验证标头。” “请求 3 和 4 使用了相同的令牌,还是令牌轮换了?” “给我一条 curl 命令,用于重放失败的调用。”

你的应用始终会收到真实、未经修改的响应。只有 Agent 看到的内容会经过脱敏。


🔥 为什么要做这个项目

让 AI Agent 读取你的网络流量时,典型的流量捕获代理会把所有内容交给它:实时 Bearer 令牌、会话 Cookie、内部 IP。所有内容都会进入云端大模型的上下文窗口,而且往往还会写入留在磁盘上的捕获文件。

盲目地用 [REDACTED] 替换内容可以阻止泄露,却也会让调试变得不可能。NullWire-MCP 会在内存中脱敏所有秘密,并将其替换为每个进程独有的确定性加盐标签:Agent 能保留每个秘密的身份,却永远看不到它的值。

标签的计算方式为 SHA-256(nonce ‖ secret)[:6]。nonce 是启动时从 crypto/rand 生成的 32 个随机字节,仅保存在内存中,进程退出后便会消失。请参阅安全性深入解析,了解这为何重要。


🧭 工作原理

flowchart LR
    APP["Your app / curl / SDK"] -->|"plain HTTP to 127.0.0.1"| PROXY["NullWire proxy<br/>loopback only"]
    PROXY -->|"original request, untouched"| UP["Upstream API<br/>http or https"]
    UP -->|"original response"| PROXY
    PROXY -->|"original response, untouched"| APP
    PROXY -.->|"async copy"| DLP["DLP scrubber<br/>regex, then SHA-256 tag"]
    DLP --> RING[("RAM ring<br/>50 flows")]
    RING -->|"MCP over stdio<br/>scrubbed data only"| AGENT["AI agent<br/>Claude · Cursor · Windsurf"]

两条路径,一条规则:**真实字节发送给你的应用,只有经过脱敏的副本才会发送给 AI。**任何内容都不会写入磁盘。

图表无法显示(手机端或 Mermaid 已禁用)?打开 ASCII 版本
╔══ [1] LOCAL APP / CLI ══════════════════════════════════════╗
║ curl · SDK · browser · your service                         ║▒
║ speaks plain HTTP to 127.0.0.1:8080                         ║▒
╚═════════════════════════════════════════════════════════════╝▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                               │
                               ▼  plain HTTP, loopback only
╔══ [2] NULLWIRE PROXY  127.0.0.1:8080 ═══════════════════════╗
║ bound with net.Listen(tcp4) + IsLoopback() check            ║▒
║ forward mode : absolute URL  (curl -x)                      ║▒
║ reverse mode : -target flag / X-Target-URL header           ║▒
║ guards : DNS-rebind 403 · self-loop 508 · CONNECT 501       ║▒
╚═════════════════════════════════════════════════════════════╝▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                               │
                               ▼  io.TeeReader split
               ┌───────────────┴────────────────┐
               ▼                                ▼
╔══ [3a] ROUNDTRIP ══════════╗   ╔══ [3b] DLP SCRUBBER ═══════╗
║ http.Transport (isolated)  ║▒  ║ async copy, capped at 64KB ║▒
║ Accept-Encoding stripped   ║▒  ║ regex scrub, THEN truncate ║▒
║ Host = upstream host       ║▒  ║ SHA-256(nonce|secret)[:6]  ║▒
║ bytes forwarded untouched  ║▒  ║ 32-byte crypto/rand nonce  ║▒
╚════════════════════════════╝▒  ╚════════════════════════════╝▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
               │                                │
               ▼                                ▼
┌── UPSTREAM API ────────────┐   ╔══ [4] RAM RING ════════════╗
│ http:// or https://        │▒  ║ 50 slots · sync.RWMutex    ║▒
│ TLS handled by Go, no MITM │▒  ║ req 8KB / resp 16KB max    ║▒
└────────────────────────────┘▒  ║ newest-first reads         ║▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒  ║ disk writes: none          ║▒
               │                 ╚════════════════════════════╝▒
◄──┘ original response            ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
    returns to the app                          │
                                                ▼
╔══ [5] JSON-RPC 2.0 MCP (2026-07-28 + legacy) ═══════════════╗
║ initialize · discover · ping · tools/list · call            ║▒
║ inspect_traffic · export_sanitized_curl · clear_ram_buffer  ║▒
║ notifications → zero bytes written                          ║▒
╚═════════════════════════════════════════════════════════════╝▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
               │                                │
               ▼                                ▼
╔══ STDOUT ▓▓▓ ══════════════╗   ┌── STDERR ░░░ ──────────────┐
║ one compact JSON line      ║▒  │ ANSI status bar,           │▒
║ per frame, ends in \n      ║▒  │ banners, errors            │▒
║ NOTHING else, ever         ║▒  │ (human telemetry)          │▒
╚════════════════════════════╝▒  └────────────────────────────┘▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
               │                                │
               ▼                                ▼
╔══ AI AGENT (MCP) ══════════╗   ┌── HUMAN ───────────────────┐
║ Claude Code · Desktop      ║▒  │ you, in a terminal         │▒
║ Cursor · Windsurf          ║▒  └────────────────────────────┘▒
╚════════════════════════════╝▒   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

**你的应用始终会收到真实、未经修改的响应。**只有 AI Agent 看到的内容会经过脱敏。

stderr 上的实时遥测栏(通过 \r\033[K 原地重绘):

⚡ [NullWire-MCP] Proxy 127.0.0.1:8080 | RAM Ring: 14/50 | Scrubbed: 9 Tokens, 4 Cookies, 6 IPs | Disk: 0B
## 📊 NullWire-MCP 与传统 MITM MCP 包装器的对比
⚡ NullWire-MCP典型的基于 MITM 的 MCP 包装器
第三方依赖0(仅使用 Go 标准库;go.mod 中没有 require)数十个传递依赖的 Python / Node / JVM 软件包
磁盘占用0 B,仅驻留内存的 50 槽环形缓冲区;代码中没有文件 I/O磁盘上的流量文件 / SQLite 数据库
密钥脱敏加盐 SHA-256 关联标签,可证明重复使用,且不会暴露密钥无,或使用无法关联的盲目 [REDACTED]
启动原生静态二进制文件:无需解释器、虚拟机或软件包解析解释器 / 虚拟机冷启动
MCP 协议2026-07-28 无状态 + 2025-11-25 / 2024-11-05 旧版通常仅支持旧版
HTTPS 上游反向网关:无需根 CA,也无需更改钥匙串安装根 CA 并拦截 TLS
需审计的供应链一个 main.go整个依赖树

右侧列描述的是一类通用工具:通过 MITM 代理捕获流量并将其交给大模型。这并非针对任何特定产品的说法。我们尚未发布启动基准测试;启动这一行描述的是架构。


🚀 30 秒安装

选项 A:go install(Go 1.23+)

go install github.com/VoxNode-ux/nullwire-mcp@latest

选项 B:预编译静态二进制文件,通过 GitHub Releases 中的 SHA256SUMS.txt 校验:

平台文件
Linux x86-64 / ARM64nullwire-linux-amd64 / nullwire-linux-arm64
Linux ARMv7(Raspberry Pi 等)/ RISC-V 64nullwire-linux-armv7 / nullwire-linux-riscv64
macOS Intel / Apple Siliconnullwire-darwin-amd64 / nullwire-darwin-arm64
Windows x86-64 / ARM64nullwire-windows-amd64.exe / nullwire-windows-arm64.exe
FreeBSD x86-64nullwire-freebsd-amd64
# Linux example (swap the file name for your platform)
BASE=https://github.com/VoxNode-ux/nullwire-mcp/releases/latest/download
curl -LO "$BASE/nullwire-linux-amd64"
curl -LO "$BASE/SHA256SUMS.txt"
grep ' nullwire-linux-amd64$' SHA256SUMS.txt | sha256sum -c -     # macOS: shasum -a 256 -c -
chmod +x nullwire-linux-amd64
sudo mv nullwire-linux-amd64 /usr/local/bin/nullwire-mcp
# Windows (PowerShell)
Get-FileHash .\nullwire-windows-amd64.exe -Algorithm SHA256   # compare with SHA256SUMS.txt

选项 C:从源代码构建

git clone https://github.com/VoxNode-ux/nullwire-mcp && cd nullwire-mcp
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o nullwire-mcp .
标志
标志默认值含义
-port8080环回端口。主机固定为 127.0.0.1;无法绑定到其他地址。0 会选择一个空闲端口。
-target(无)反向网关模式的默认上游基础 URL,例如 https://api.example.com
不使用 AI 客户端,直接试用

只要标准输入保持打开,MCP 端就会持续运行。因此,快速演示时,可以定时向它发送请求:

( sleep 2
  echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"inspect_traffic","arguments":{"limit":5}}}'
  sleep 1 ) | nullwire-mcp -port 8080 &

sleep 1
curl -s -x http://127.0.0.1:8080 http://example.com/ \
  -H 'Authorization: Bearer my_secret_token_123' \
  -H 'Cookie: sid=my_secret_cookie' \
  -H 'X-Forwarded-For: 198.51.100.1' > /dev/null
wait

你会在标准错误输出中看到实时状态栏,在标准输出中看到流量,其中的标头会显示为 Bearer [REDACTED_TOKEN#…]、[REDACTED_COOKIE#…] 和 [REDACTED_IP#…]。

接入 AI 客户端

客户端会将 nullwire-mcp 作为子进程启动,并通过标准输入输出进行 JSON-RPC 通信。然后,你的应用将流量发送到 127.0.0.1:8080。(配置文件的位置会随客户端版本变化;如果下方路径已过时,请查阅客户端文档。)

Claude Code CLI

claude mcp add nullwire -- nullwire-mcp -port 8080 -target https://api.example.com

Claude Desktop(claude_desktop_config.json)

{
  "mcpServers": {
    "nullwire": {
      "command": "nullwire-mcp",
      "args": ["-port", "8080", "-target", "https://api.example.com"]
    }
  }
}

Cursor(~/.cursor/mcp.json,或项目中的 .cursor/mcp.json)

{
  "mcpServers": {
    "nullwire": {
      "command": "nullwire-mcp",
      "args": ["-port", "8080", "-target", "https://api.example.com"]
    }
  }
}

Windsurf(~/.codeium/windsurf/mcp_config.json)

{
  "mcpServers": {
    "nullwire": {
      "command": "nullwire-mcp",
      "args": ["-port", "8080", "-target", "https://api.example.com"]
    }
  }
}

如果只使用正向代理模式或每个请求的 X-Target-URL 标头,请省略 -target。


🔀 两种捕获模式

模式 1:正向代理(纯 HTTP)

通过代理发送完整 URL。请求会原样转发,经过清理的副本则会进入环形缓冲区。

curl -x http://127.0.0.1:8080 http://httpbin.org/headers

# or for any app that honours the standard variable
HTTP_PROXY=http://127.0.0.1:8080 ./my-service

HTTPS_PROXY 会使客户端发送 CONNECT 请求;按设计,此类请求会收到 501 响应。请参阅明确的适用范围。对于 HTTPS,请使用模式 2。

模式 2:反向网关(HTTPS 上游,无 MITM 证书)

通过纯 HTTP 与 NullWire 通信;NullWire 使用 Go 的常规证书验证,通过 TLS 与真实服务端通信。无需在信任存储中安装任何内容。

# Fixed upstream for the whole session
nullwire-mcp -target https://api.github.com
curl http://127.0.0.1:8080/users/octocat

# Or choose the upstream per request (the header is removed before forwarding)
curl -H 'X-Target-URL: https://httpbin.org' http://127.0.0.1:8080/anything?api_key=hunter2hunter2

只需更改基础 URL,即可将 SDK 指向该网关:

export OPENAI_BASE_URL=http://127.0.0.1:8080/v1   # with: nullwire-mcp -target https://api.openai.com

上游 Host 标头会设为目标主机,因此虚拟主机和 TLS SNI 均可正常工作。


🧰 MCP 工具参考

这三个工具都返回 {"resultType":"complete","content":[{"type":"text","text":"…"}],"isError":false}。工具级问题(例如未知的流 ID)会将 isError 设为 true,而不会导致 RPC 失败。

inspect_traffic

最近的脱敏流量,按时间倒序排列。

{
  "name": "inspect_traffic",
  "inputSchema": {
    "type": "object",
    "properties": {
      "limit":      { "type": "integer", "default": 10, "minimum": 1, "maximum": 50 },
      "method":     { "type": "string",  "description": "HTTP method filter, case-insensitive" },
      "url_filter": { "type": "string",  "description": "Substring of the scrubbed URL, case-insensitive" }
    },
    "additionalProperties": false
  }
}

结果文本示例:

{
  "ring_size": 3,
  "ring_capacity": 50,
  "returned": 1,
  "scrubbed_totals": { "tokens": 5, "cookies": 1, "ips": 1 },
  "flows": [
    {
      "id": 3,
      "timestamp": "2026-10-25T14:03:11Z",
      "method": "POST",
      "url": "http://[REDACTED_IP#98f1a2]:9000/v1/orders?api_key=[REDACTED_TOKEN#3c9a1e]",
      "request_headers": {
        "Authorization": "Bearer [REDACTED_TOKEN#a1b2c3]",
        "Content-Type": "application/json",
        "Cookie": "[REDACTED_COOKIE#d4e5f6]"
      },
      "request_body": "{\"item\":\"widget\",\"client_secret\":\"[REDACTED_TOKEN#5b7e20]\"}",
      "status_code": 401,
      "response_headers": { "Content-Type": "application/json" },
      "response_body": "{\"error\":\"token expired\"}",
      "latency_ms": 42,
      "redaction_count": 5
    }
  ]
}
export_sanitized_curl

为单个流量重建可运行的 POSIX curl 命令。每个脱敏标签都会变为环境变量占位符,并附带注释图例,将每个占位符映射回其替换的标签,方便你了解每个位置对应哪个真实值。

{
  "name": "export_sanitized_curl",
  "inputSchema": {
    "type": "object",
    "properties": {
      "flow_id": { "type": "integer", "minimum": 1, "description": "An id returned by inspect_traffic" }
    },
    "required": ["flow_id"],
    "additionalProperties": false
  }
}

上方流量 3 的结果文本示例:

# Replay of flow 3 (2026-10-25T14:03:11Z)
# Set these environment variables first (distinct tags share one placeholder):
# export NULLWIRE_IP=...   # was [REDACTED_IP#98f1a2]
# export NULLWIRE_AUTH_TOKEN=...   # was [REDACTED_TOKEN#3c9a1e]
# export NULLWIRE_AUTH_TOKEN=...   # was [REDACTED_TOKEN#5b7e20]
# export NULLWIRE_AUTH_TOKEN=...   # was [REDACTED_TOKEN#a1b2c3]
# export NULLWIRE_COOKIE=...   # was [REDACTED_COOKIE#d4e5f6]
curl -X 'POST' 'http://'"$NULLWIRE_IP"':9000/v1/orders?api_key='"$NULLWIRE_AUTH_TOKEN" \
  -H 'Authorization: Bearer '"$NULLWIRE_AUTH_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Cookie: '"$NULLWIRE_COOKIE" \
  --data-raw '{"item":"widget","client_secret":"'"$NULLWIRE_AUTH_TOKEN"'"}'

逐跳标头、Host、Content-Length 和 Accept-Encoding 会被省略,以便由 curl 自行设置。对于已截断或经过编码的请求正文,会显示 # WARNING: 行,而不会静默重放不完整的内容。

clear_ram_buffer

将全部 50 个槽位清零并重置脱敏计数器。流 ID 会持续递增,因此旧 ID 永远不会指向新流量。不接受任何参数({"type":"object","properties":{},"additionalProperties":false})。


🛡️ 安全与协议工程深入解析

以下各项均在 main.go 中实现,并由 main_test.go 测试。

措施作用原因
严格绑定回环地址监听器地址硬编码为 127.0.0.1,使用 net.Listen("tcp4", …) 打开,然后通过 TCPAddr.IP.IsLoopback() 再次检查。如果检查失败,进程将退出。没有 -host 标志。局域网中的任何设备都无法访问会接触你令牌的代理。
DNS 重绑定防护在反向模式下,任何 Host 标头不是 127.0.0.1、localhost 或 ::1 的请求都会收到 403 Forbidden。恶意网页即使将其域名重绑定到 127.0.0.1,也无法将你的网关用作经过身份验证的中继。
自环保护如果目标解析到代理自身的 host:port,就会返回 508 Loop Detected。防止配置错误的 -target 导致请求风暴。
先清理,再截断每个请求正文最多捕获 64 KB,完整进行正则清理,之后才截断为 8 KB(请求)/16 KB(响应)。跨越 8 KB 边界的密钥在截断前就已被替换为标签。若先截断,就会保留半个令牌。
每个进程独有的临时随机数从 crypto/rand 获取 32 字节,仅保存在 RAM 中。标签 = SHA-256(nonce ‖ secret) 的前 6 个十六进制字符。如果没有盐,低熵密钥(短 PIN、容易猜中的密码)的哈希可能会根据泄露的记录被暴力破解或通过彩虹表破解。使用秘密随机数后,任何人都无法预先计算,而且重启后标签也无法关联。
严格遵守 stdout 规范stdout 只传输以 \n 结尾的单行 JSON-RPC 帧,并在互斥锁保护下写入。横幅、错误和状态栏写入 stderr。stdout 中任何多余字节都会破坏 MCP 流。
静默通知不带 id 的消息(或方法为 notifications/* 且 id: null 的消息),例如 notifications/initialized,会写入 0 字节。JSON-RPC 禁止回复通知;如果回复,严格的客户端会断开连接。
原始流量不受影响应用通过流式、可刷新的副本接收真实响应。清理操作在后台 goroutine 中对单独的副本进行。代理绝不能影响你正在调试的对象。
隔离传输使用专用的 http.Transport,不使用代理、不启用压缩,也不跟随重定向。避免意外的代理链;重定向会原样传递给应用。
剥除逐跳标头双向移除 Connection、Proxy-*、TE、Upgrade 等标头。符合 RFC 9110 的代理行为规范。
崩溃隔离捕获和 RPC 分发会从 panic 中恢复,而不会终止进程。异常载荷不会导致你的会话中断。

会清理的内容

模式标签
Authorization、Proxy-Authorization、X-API-Key、API-Key、X-Auth-Token 标头值;任何位置的 Bearer / Basic / Token 凭据TOKEN
Cookie、Set-Cookie 标头值(整个标头作为一个整体进行哈希)COOKIE
JWT(eyJ….….…)TOKEN
API 密钥:sk-…、ghp_…、github_pat_…、AIza…、xox[baprs]-…、AKIA…TOKEN
JSON 字段:access_token、refresh_token、id_token、api_key、apikey、secret、client_secret、password、passwd、session_id、auth_tokenTOKEN
查询/表单参数:api_key、apikey、token、access_token、refresh_token、id_token、secret、client_secret、password、passwd、keyTOKEN
URL 中的 user:password@hostTOKEN
IPv4 地址(127.0.0.1 和 0.0.0.0 保留不变)IP

⚖️ 诚实说明范围与已知限制

我们宁愿由我们来告诉你,也不希望你在生产环境中才发现。

  • HTTP CONNECT 会返回 501 Not Implemented,这是有意为之。 通过 CONNECT 检查 HTTPS 意味着要对 TLS 实施中间人攻击,这就需要生成根 CA,并将其加入操作系统或浏览器的信任存储。这会涉及密钥材料并污染密钥链,与零足迹工具的目标背道而驰。反向网关无需这些操作就能提供同样的可见性:将应用指向 http://127.0.0.1:8080,让 NullWire 处理到上游的 TLS。
  • 不支持 WebSocket / Upgrade 透传。 Upgrade 和 Connection 会被视为逐跳标头并移除,因此升级后的协议无法通过代理运行。支持普通请求/响应和流式响应(例如 SSE)。
  • Android / Termux: Android 上的静态 Go 二进制文件没有 DNS 解析器配置,因此上游主机名解析会失败,并出现 502 … lookup … connection refused。请在 termux-chroot 中运行(pkg install proot),它会提供解析器。Linux 桌面、macOS、Windows 和 FreeBSD 预计不受影响。
  • 脱敏基于模式,并非万能。 它可以识别上表中的格式。如果密钥采用我们无法识别的格式、出现在我们不了解的字段中,或跨越正文边界被拆分,就可能漏过。请把脱敏器视为可靠的安全带,而非保证,并使用你自己的流量进行测试。目前尚未对 IPv6 地址进行脱敏。看起来像 IPv4 地址、实际并非地址的字符串(某些版本号)可能会被误报并脱敏。
  • 标签为 24 位。 六个十六进制字符足以区分 50 槽缓冲区中的少量密钥,但理论上仍可能发生冲突。标签用于关联,而非识别。
  • 正文长度有限。 每个正文只会捕获前 64 KB,存储的正文会截断至 8 KB / 16 KB。带有 Content-Encoding(例如 gzip)的响应会以简短占位内容存储,而不会解码。NullWire 会从转发的请求中移除 Accept-Encoding,因此大多数服务会返回未压缩内容,但它无法强制服务这么做。
  • “仅驻留内存”意味着程序永远不会写入文件。 状态栏中的 Disk: 0B 是设计约束(源代码不执行文件 I/O),而非测量所得的计数。操作系统仍可能将内存页换出到磁盘或写入崩溃转储;NullWire 无法控制这些行为。
  • 环形缓冲区是临时的。 进程退出时,最近的 50 个流量记录会消失。这正是设计目的。
  • 你的应用会看到原始流量。 NullWire 保护的是 AI Agent 看到的内容,而不是你的本地应用看到的内容。它并不是应用本身的安全边界。
  • 协议支持范围以文档所述为准。 支持 2026-07-28 / 2025-11-25 / 2024-11-05 规范中的初始化、发现、ping、tools/list 和 tools/call。资源、提示词和采样尚未实现,调用时会返回 -32601。
  • 发布的二进制文件未签名。 请使用 SHA256SUMS.txt 验证。在 macOS 上,你可能需要运行 xattr -d com.apple.quarantine ./nullwire-darwin-arm64。

🧪 测试平台

平台状态运行内容
Linux x86-64(Ubuntu 24.04)✅ 已验证go vet、使用 -race 的单元测试、冒烟测试、端到端脱敏
macOS 15(Apple Silicon)✅ 已验证冒烟测试、静默通知、端到端脱敏
macOS 15(Intel)✅ 已验证冒烟测试、静默通知、端到端脱敏
Windows x86-64✅ 已验证冒烟测试、静默通知、端到端脱敏
FreeBSD amd64(VM)✅ 已验证冒烟测试、静默通知、端到端脱敏
Linux ARMv7(QEMU)🟡 模拟运行冒烟测试、静默通知、端到端脱敏
Linux RISC-V 64(QEMU)🟡 模拟运行冒烟测试、静默通知、端到端脱敏
Linux ARM64(Android / Termux)✅ 已在设备上验证实时上游代理、脱敏标签、export_sanitized_curl、反向网关、防护措施(需要 termux-chroot)
Windows ARM64✅ 已验证冒烟测试、静默通知、端到端脱敏

🟡 模拟运行表示在 QEMU 下运行,而非在真实硬件上运行。每次运行的完整日志可通过 docs/test-results.md 和 Actions 选项卡查看。

✅ 验证与测试

go vet ./...
go test -v ./...
go test -v -race ./...      # the race detector needs cgo (gcc/clang) on most platforms

测试套件仅使用标准库(net/http/httptest),涵盖:

  • 确定性标记:相同的密钥共用一个标签,不同的密钥使用不同标签,不会泄漏任何内容,127.0.0.1 会保留。
  • 环形缓冲区绕回淘汰,以及使用单调递增 ID 的 Clear()。
  • 完整的反向网关往返流程:移除 Accept-Encoding、将令牌清理后存入环形缓冲区,并导出有效的脱敏 curl。
  • MCP 分发:server/discover、initialize、ping、tools/list、tools/call、错误代码,以及通知的标准输出字节数为零。

CI(.github/workflows/release.yml)运行 go vet 和 go test -race,为 9 个目标交叉编译静态 CGO_ENABLED=0 二进制文件(Linux amd64/arm64/armv7/riscv64、macOS amd64/arm64、Windows amd64/arm64、FreeBSD amd64),并在 v* 标签下发布这些文件及 SHA256SUMS.txt。


❓ 常见问题

AI 会看到我的真实令牌吗?

不会通过 NullWire 的工具看到。Agent 只会收到类似 [REDACTED_TOKEN#a1b2c3] 的标签。脱敏基于模式,因此,若秘密的格式不被清理器识别,就可能通过。参见坦诚说明。

它会改变我的应用收到的内容吗?

你的应用会通过流式副本收到真实响应,而清理操作会在后台 goroutine 中对另一个副本单独运行。有一点需要注意:NullWire 会在上游请求中移除 Accept-Encoding,因此响应通常以未压缩的形式到达。参见坦诚说明。

如何调试 HTTPS API?

使用模式 2:反向网关(HTTPS 上游,无 MITM 证书):你的应用通过普通 HTTP 与 127.0.0.1 通信,NullWire 则通过经过验证的 TLS 与真实服务器通信。无需根 CA,也无需修改信任库。按设计,HTTPS_PROXY(CONNECT)请求会收到 501 响应。

Agent 能否在不知道秘密的情况下重放请求?

可以。export_sanitized_curl 会生成可运行的 curl 命令,将每个标签替换为环境变量,例如 $NULLWIRE_AUTH_TOKEN。你设置真实值;Agent 无法获取这些值。

我的流量存储在哪里?

存储在一个有 50 个槽位的 RAM 环形缓冲区中。进程退出后,数据便会消失;你也可以按需使用 clear_ram_buffer 清除数据。该程序不包含任何文件 I/O。

我可以在网络上公开它吗?

不可以。监听器被硬编码为 127.0.0.1,并通过 IsLoopback() 进行检查;没有可用于绑定到其他地址的标志。


🤝 参与贡献

欢迎提交问题和 PR。请阅读 CONTRIBUTING.md,了解基本规则、测试命令和 PR 检查清单。安全问题请通过 SECURITY.md 报告,不要提交到公开问题区。


📄 许可证

MIT © 2026 VoxNode-ux

如果 NullWire 帮你避免将有效令牌粘贴到聊天窗口中,欢迎点个 ⭐,让更多工程师发现它。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。