mcpField
F
FaintMark

FaintMark

本地服务
github.com安全13 周前更新

关于 FaintMark

FaintMark 是本地运行的 Python 命令行工具和 MCP 服务,可为每位接收者生成单独的水印图片副本,并检查共享图片对应的已登记副本。登记数据保存在本机;水印针对重压缩和缩放设计,同时明确说明适用限制。

主要功能

  1. 01为每位接收者生成水印副本
  2. 02对照本地登记库检查图片
  3. 03列出已登记的图片副本
  4. 04深度检查翻转和小角度旋转
  5. 05保留 JPEG、PNG 和 WebP 格式

适用场景

  1. 01分发带有接收者专属标记的照片
  2. 02检查转发图片是否含有已登记水印
  3. 03查看本地登记过的图片副本

MCP 相关

向 MCP 客户端提供 `mark_image`、`check_image` 和 `list_copies`。服务在本地运行,并通过本地登记库将水印 ID 与接收者标签关联。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"matchalatteguy-faintmark": {
"command": "uvx",
"args": [
"faintmark"
]
}
}
}
启动命令uvx faintmark

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypifaintmark0.1.0

工具

3

收录的工具列表 · 更新于 2 周前

参数类型必填说明
formatany否默认值:源格式
image_pathstring是要添加水印的图像的绝对路径
output_dirany否默认值:图像旁的 marked/ 文件夹
recipientsarray是1 到 100 个标签,每个复制一份

详细介绍

FaintMark

CI PyPI

为每位接收者添加不可见的图像水印。为每位接收者创建一份带标记的副本, 然后识别共享或泄露的图像对应哪份副本。设计上可承受聊天应用造成的 重新压缩和缩放;请参阅下方的应用测试和限制。 可在本地运行,无需账户,也不会上传文件。提供 Python CLI 和 MCP 服务。

$ faintmark mark photo.jpg alice bob
alice  991e318a21  /Users/me/Pictures/marked/photo-991e318a21.jpg
bob  746ac9fa88  /Users/me/Pictures/marked/photo-746ac9fa88.jpg
2 copies in /Users/me/Pictures/marked

$ faintmark check ~/Downloads/forwarded.jpg
matched: alice  991e318a21  marked 2026-10-06T23:11:54Z

原始场景、带标记副本的 WhatsApp 聊天截图,以及 CLI 检查结果:收到的副本匹配,原图没有标记,移植的 id 会被标记

只向每个人发送专属文件;文件名包含 id,而非姓名。 匹配结果会指出该副本是以哪个标签发放的,而非由谁分享。 此标记旨在让照片上的标记不易被肉眼察觉。JPEG、PNG 和 WebP 保持原格式;HEIC 会转换为 JPEG。

安装

uvx faintmark mark photo.jpg alice bob   # runs without installing; needs uv
uv tool install faintmark                # or: pipx install faintmark

macOS、Linux 或 Windows 上需要 Python 3.11 或更高版本(Intel Mac:3.11 至 3.13)。 uv 可安装 Python 并运行 FaintMark。首次运行 mark 或 check 时,会将 Adobe 的 TrustMark Q 模型(65 MB,已验证 SHA-256) 下载到 ~/.faintmark。处理 HEIC 输入时,请安装 faintmark[heic]: uv tool install 'faintmark[heic]';或者不进行持久安装时,运行 uvx --from 'faintmark[heic]' faintmark mark photo.heic alice。 uv tool uninstall faintmark 会移除命令,但保留 ~/.faintmark。

faintmark mark IMAGE RECIPIENT... [-o DIR] [--format png|jpeg|webp] [--json]
faintmark check IMAGE [--deep] [--files] [--json]
faintmark list [-n N] [--files] [--json]

除非通过 -o 指定其他位置,否则副本会存放在图像旁边的 marked/ 目录中。标签中含有空格时,请加引号:faintmark mark photo.jpg 'Alice Smith'。单次运行可为 1 至 100 位接收者添加标记。--json 会打印匹配的 MCP 工具返回的结果; mark 会包含源文件名和输出路径,check 和 list 则不会包含文件名和路径,除非设置了 --files。check --deep 还会尝试翻转和小幅旋转;如果未检测到标记,则会列出最多三个已注册 id,条件是它们与原始解码器输出的 96 个码字位中有 26 位以内的距离,并附上位距离。这些结果未经校准,可能只是巧合或伪造;绝不会被视为匹配。使用 --json 时,错误会以 {"error": "..."} 的形式输出。check 在结果为 matched 时退出码为 0;结果为 unregistered 或 not_detected 时退出码为 1;发生错误时退出码为 2。

mark 接受不超过 25 百万像素且不带透明度的非动画 JPEG、PNG、WebP 和 HEIC 图像(16 位 RGB 会以 8 位读取;不支持 16 位灰度和 CMYK),处理此大小的图像约需 1.5 GB 内存。check 会读取 Pillow 可打开的任何图像的第一帧,最多支持 100 百万像素。

Agent

claude mcp add --scope user faintmark -- uvx faintmark mcp
codex mcp add faintmark -- uvx faintmark mcp
gemini mcp add -s user faintmark uvx faintmark mcp

其他读取 mcpServers 配置的客户端(Claude Desktop、Cursor、Windsurf):将此条目添加到客户端的 MCP 配置中。客户端必须能在 PATH 中找到 uvx;如要使用其他注册表,请在服务的环境中设置 FAINTMARK_HOME。

{ "mcpServers": { "faintmark": { "type": "stdio", "command": "uvx", "args": ["faintmark", "mcp"] } } }

每个客户端选择一种接入方式;插件和扩展也会捆绑此技能。对于 HEIC 输入,请使用 uvx --from 'faintmark[heic]' faintmark mcp 启动服务;捆绑的启动器使用基础包。

  • 添加到 Cursor
  • 添加到 VS Code
  • Claude Code 插件:claude plugin marketplace add matchalatteguy/FaintMark,然后运行 claude plugin install faintmark@faintmark
  • Codex 插件:codex plugin marketplace add matchalatteguy/FaintMark,然后运行 codex plugin add faintmark@faintmark
  • Gemini 扩展:gemini extensions install https://github.com/matchalatteguy/FaintMark
  • 适用于任何带 shell 的 Agent 的技能:npx skills add matchalatteguy/FaintMark
工具参数返回值
mark_imageimage_path、recipients,可选的 output_dir、format输出文件夹,以及每个副本的 id、path、warnings
check_imageimage_path,可选的 files、deepstatus 为 matched(如果存在已存储的哈希值,还包含 recipient、id、marked_at 和 same_image)、unregistered(包含 id)或 not_detected;使用 deep 时,还会包含 transform,指出恢复结果所用的翻转或旋转方式;如果未检测到结果,则会包含 suspects(id、recipient、bit_errors、same_image)以及 registry_size
list_copies可选的 limit、files已注册的副本,按时间从新到旧排列

请向工具提供绝对路径。unregistered 表示解码出了一个 id,但它不在此机器的注册表中;not_detected 表示没有可读取的标记,这并不能说明图像的来源。same_image 会将感知哈希与已签发的副本进行比较:false 表示它们的差异超过了阈值,裁剪或将 id 复制到另一张图片中都可能导致这种情况;此比较也可能无法发现差异。

check_image 和 faintmark check --json 会返回以下结果之一:三种普通状态之一(如果匹配结果中的 same_image: false,表示图片与已签发的副本不同);使用 deep 时,返回经变换恢复的结果,或包含候选项的未匹配结果(如果没有已注册的 id 在截断范围内,suspects 为空)。CLI 会将错误打印为最后一行;MCP 工具会将其作为工具错误(isError)抛出:服务返回的消息相同,但参数验证消息不同。

{"status": "matched", "id": "991e318a21", "recipient": "alice", "marked_at": "2026-10-06T23:11:54Z", "same_image": true}
{"status": "matched", "id": "991e318a21", "recipient": "alice", "marked_at": "2026-10-06T23:11:54Z", "same_image": false}
{"status": "unregistered", "id": "3123f6f877"}
{"status": "not_detected"}
{"status": "matched", "id": "991e318a21", "recipient": "alice", "marked_at": "2026-10-06T23:11:54Z", "same_image": true, "transform": "rotated 4"}
{"status": "not_detected", "suspects": [{"id": "991e318a21", "recipient": "alice", "bit_errors": 21, "same_image": true}], "registry_size": 100}
{"error": "forwarded.jpg: no such file"}

mark_image 和 faintmark mark --json 返回第一行;list_copies 和 faintmark list --json 返回第二行,按时间从新到旧排列:

{"original": "photo.jpg", "output_dir": "/Users/me/Pictures/marked", "copies": [{"id": "991e318a21", "recipient": "alice", "path": "/Users/me/Pictures/marked/photo-991e318a21.jpg", "warnings": []}]}
{"copies": [{"id": "991e318a21", "recipient": "alice", "marked_at": "2026-10-06T23:11:54Z", "warnings": []}]}

在真实应用中测试

2026 年 10 月 7 日,FaintMark 0.1.0,三张源照片(iPhone 场景、CC0 肖像、CC0 日落)。每一行报告的是在默认照片质量下,通过该路径实际发送的样本。 截图行是接收手机的截图,检查前已裁剪至照片区域。这些是单次往返的观察结果, 并非某个应用的恢复率或保证。

路径接收结果结果
WhatsApp 照片,三张长边 1600 px,155 至 317 KB匹配 3/3
Discord 附件,场景原始尺寸,1.7 MB匹配 1/1
用场景制作的 Discord 贴纸217x285 PNG匹配 1/1
Telegram 照片,场景960x1280,102 KB匹配 1/1
WeChat 照片,肖像和日落长边 1644 至 1920 px,67 至 302 KB匹配 2/2
Instagram 私信,肖像1206x804,176 KB匹配 1/1
WhatsApp、Discord、Telegram、WeChat、Instagram 中的聊天截图照片长边 286 至 1022 px匹配 9/9
Tinder 个人资料卡,肖像(截图)大幅裁剪未检测到 0/1

每次匹配也都报告了 same_image: true。原图、所有接收文件、截图、裁剪图,以及预期 ID 和结果清单 (docs/evidence-2026-10-07.json)都包含在 证据发布版本中。 在没有此注册表的机器上,接收文件会返回 unregistered,并附上清单中的 ID;原图和 Tinder 裁剪图则会返回 not_detected。X、Signal、iMessage 和 Instagram 快拍未经测试。

工作原理

TrustMark Q(Adobe,MIT)将一个 40-bit ID 嵌入为覆盖整张图像的图案,旨在耐受 JPEG 重新压缩、WebP 转换和中度缩放。这是取证水印,不是隐写术:任何拥有 TrustMark 的人都能读取 ID;只有你的注册表会将其映射 到名称。FaintMark 为每位接收者生成一个随机 ID,以源格式写入副本(JPEG 使用源图像自身的量化表,PNG、WebP; HEIC 转为 JPEG),解码保存的副本以验证其 ID 是否可恢复,并将 ID、接收者、文件和副本的感知哈希记录到本地 SQLite 注册表中。匹配到已注册记录时,check 会比较感知哈希,并在二者距离超过阈值时报告 same_image: false。 如果源 JPEG 设置会导致水印丢失,则改用质量 95 保存,并在警告中说明。若图像已有水印,再次添加水印时会替换原水印,并予以说明。预检会将每个副本重新编码为 JPEG 质量 70,分别按原始尺寸(某一边超过 65,500 px 时跳过并给出警告,这是 JPEG 的限制)和缩放至 1280 px(图像较小时缩至一半尺寸),并在 ID 未能保留时发出警告;请将这些副本作为文件而非照片发送。

保留 ICC 配置文件,使 Display P3 照片保持原有色彩。不会复制 EXIF 和其他元数据。

已加入与 Adobe 实现的兼容性检查:tests/fixtures 包含 BCH 向量,以及两张由上游 TrustMark 0.9.2 添加水印的图像;FaintMark 可解码其中的 ID,并能复现水印图像像素,每个 RGB 通道的偏差不超过一个值。真实模型测试中的聊天应用风格处理序列(将收到的 JPEG 字节缩放至 1600、1280 和 1024 px,JPEG 质量 70 至 80、WebP 75,以及连续缩放再转 JPEG)在两张 12-megapixel 合成封面和 320 px 夹具图像上均能恢复 ID,只有夹具图像上的 WebP 75 例外。

我们测量到的攻击

拥有此仓库及其副本的人可以进行以下操作;测试使用了 3 张 2000 px 的照片,每张照片对应 2 个 ID,完整表格见随证据发布一同附上的攻击报告。

  • 无痕擦除标记。 使用随机载荷重新运行公开编码器,可在 42 到 47 dB PSNR 下移除标记(6/6)。随后,check 会返回 not_detected,与未标记照片完全相同。
  • 覆盖标记。 使用另一个 ID 重新标记每次都会覆盖原标记(后写入者胜出,41 到 44 dB)。在这些试验中,解码器读出替换后的 ID,从未读出之前的 ID。
  • 合并副本。 对两名或三名接收者的副本取平均,每次都能击败普通解码器。在包含 100 个 ID 的注册表中,距离最近的注册码字在 264 次试验中有 264 次属于串谋者(相距 12 到 30 位;最近的无辜者相距 28 到 41 位);使用 26 位阈值时,check --deep 在 264 次试验中有 253 次至少列出一名串谋者,从未列出无辜者。对于随机解码器输出,每个已注册 ID 落在阈值内的概率约为百万分之 4,因此注册表越大,巧合就越多。
  • 伪造图片校验。 将 ID 直接移植到另一张照片会导致 same_image 校验失败(相距 28 到 32 位)。经过调整的伪造会同时微调亮度,以翻转代价最低的哈希位,在 27 dB PSNR 下通过两项校验,产生明显但看起来很普通的色调变化。
  • 常见编辑。 模糊、噪声、将 JPEG 质量降至 40、缩小至 100 px 后再放大、翻转、亮度和对比度调整,以及打印再拍摄模拟,都未能破坏标记(6/6)。每侧裁剪 15% 或更多,以及旋转超过 4 度,都会使普通解码器无法识别标记;--deep 可恢复约 10 度以内的旋转。

局限

  • matched 表示解码出的 ID 已登记给该接收者。它不能证明是谁分享了图片:副本可以被转发。
  • matched 是注册表查询结果,不是签名。任何知道某个 ID 的人都可以将其嵌入另一张图片。感知哈希比较可能会标记出不同的图片,但无法验证副本身份,也无法检测同一图片被重新标记。TrustMark 也公开发布了水印移除工具;知情的接收者可以去除标记,重度编辑也可以去除标记。
  • 接收者合并副本即可击败普通 check;--deep 会根据码字距离列出候选项,但这些结果可能是巧合或伪造的,绝不能作为证据。
  • 对于宽高比大于 2:1 的图片,标记仅嵌入中心正方形区域;若编辑导致图片跨越该边界,可能无法解码。严重缩小和裁剪会破坏标记。
  • 结果依赖于此设备上的注册表。请妥善保密:任何能编辑注册表的人都能更改归属信息。

注册表

~/.faintmark/registry.sqlite3 将 ID 映射到接收者姓名。请妥善保密并进行备份;没有注册表时,可恢复的 ID 会报告为 unregistered。 FAINTMARK_HOME 可指定其他目录。

采用 MIT 许可证。TrustMark 及其模型归 Adobe 所有,同样采用 MIT 许可证。参见 CONTRIBUTING、 SECURITY 和 releases。

评论

支持 Markdown

第一次接触 MCP?

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

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