# 隐语 · 完整说明 ## 这是什么 一个浏览器端工具:把任意文本按字节编码成汉字。用途是让链接、微信号、联系方式在会被自动扫描的聊天场景里不被识别为链接。强度来自"字符表私有"与"明文含不可生成成分",不是来自算法保密。 ## 两种模式 ### 字节级(mode=dedup / hash / raw) 见下方"规则细节"。密钥是**字符表文本**,双方必须持有同一段文本并核对指纹。 ### 词级(mode=word) 密钥只有一个**种子字符串**,双方不必传字符表文本。表由「内置词表 + 种子」派生,置换过程三层,各层字符集互不相交,因此解密无歧义: 1. **词 → 词**:按内置词表(12043 词)切分明文,每个词换成**同长度、同词频档**的另一个词;功能词(的/了/是/在…)保持不变,语法骨架不动,只换实词。 2. **单字 → 单字**:未命中词表的单个汉字换成常用字表(1500 字)里的另一个字,密文仍是汉字。 3. **非中文 → 符号**:链接、数字、字母等逐字节换成符号表(256 个符号)里的符号,避免生僻汉字在 IM 里显示成豆腐块。 同一种子的两端结果逐位一致(前端、Python、Cloudflare Worker 三套实现由测试守着)。密文的中文部分读起来像另一句话,链接与账号变成符号串。 约定卡(可直接贴给对方): ``` 模式 = 词级(表由种子派生,双方只约定种子) 种子 = "<双方约定的种子>" 规则:明文按内置词表切分;词换成同长度同词频档的另一个词(功能词不变);单字换成单字表里的另一个字;非中文逐字节换成符号表里的符号。 还原:明文 = 拼接( 对 "密文" 切分后的每个 t : 若 t 在词表中 则 取映射前的词 否则 若 t 在单字表中 则 取原字 否则 取符号表对应的字节 ) ``` 词级模式下 `source` 不需要传,`fingerprint` 为**种子与自定义词表**的指纹(`fnv1a(seed + "|" + 词表原文)`),双方核对种子是否一致、词表是否相同。 ### 自定义词表(词级可选) 参数 `words` 传一份自定义词表(一行一词或空格分隔)。它会排在内置词表**前面**,分词时优先命中,于是"甲方乙方"这种词会被当成一个词整体替换,而不是拆成单字。 - 用途:把行业黑话、代号、内部术语藏进密文 - **双方必须用同一份词表**,否则密文对不上 —— 指纹把词表算进去了,一方有、一方没有时指纹立刻不同 - 词表越大,分词越准;几百到几千词比较实用 ### 口令的念读版 `spell_out("EC1:芦苇-湖泊-9066-0MKN::-")` → `衣 希 一 · 芦 苇 湖 泊 · 九 零 六 六 · 零 艾姆 凯 嗯`。字母与数字换成能念的音节(K→凯、M→艾姆、N→嗯…),中文按字念。音节字与口令词表无交集,念出来不会混淆。 `/api/session-check` 与 MCP `check_session` 的结果里带 `spell` 字段。 ### 会话串体检 MCP 工具 `check_session`(支持 `sessions` 数组批量)与 `POST /api/session-check`:给一个(或一批)会话串,回答"双方说的是同一件事吗"。 ```json {"session": "EC1:芦苇-湖泊-9066-0MKN::-"} ``` 返回 `{"valid", "seed", "phrase", "words_fingerprint", "mask", "fingerprint", "issues", "notes"}`。`issues` 是真正的问题(校验位不过、词表指纹对不上),`notes` 是提示(例如没有自定义词表)。Agent 在用一套约定之前可以先体检一遍。 ### 认领密文(这段是发给我的吗) `POST /api/decrypt` 接受 `sessions`:拿一段密文去试各个会话串。 ```json {"cipher": "收到的密文", "sessions": ["EC1:芦苇-湖泊-9066-0MKN::-", "EC1:海鸥-夜鹭-3453-0VKN::-"]} ``` 返回每个会话串的 `ok` / `plain` / `cjk`(汉字占比)/ `readability`(通顺度)/ `common`(常用词占比)/ `coverage`(词表覆盖率),以及: - `confidence`:`none`(一条都解不出)/ `single`(只有一条能解出)/ `multiple`(多条都能解出) - `candidates`:能解出的候选,按像不像人话排序,每条带 `similarity`(与首选的字符相似度)与 `differs` - `decoded_by`:**只有** `confidence` 为 `single` 时才给序号,多条能解出时是 `-1`(不硬猜赢家) - `best`:排序第一条的下标,仅作参考 **注意**:词级模式下错的会话串也会解出一段像中文的文本 —— 因为词按"同长度、同词频档"置换,错解出来的用词频率分布跟正确解几乎一样,所以汉字占比与通顺度都**不足以**判定对错。接口因此不替用户猜:能解出的候选全列出来并标出彼此差异,最终靠语义确认。真正的硬信号是**解不出来**:字节层解不出合法 UTF-8,就说明字符表不对。MCP 侧对应工具是 `identify_sender`。 ### 群发(多收件人) `POST /api/encrypt` 接受 `recipients`:同一段明文,按各人自己的会话串/字符表各加密一份。 ```json {"text": "今晚八点老地方见", "recipients": [{"session": "EC1:芦苇-湖泊-9066-0MKN::-"}, {"session": "EC1:海鸥-夜鹭-3453-0VKN::-"}]} ``` 返回 `{"results": [{"recipient": 0, "cipher": "...", "session": "...", "share_link": "..."}, ...], "count": n}`。某一位的会话串有问题时,该条结果里带 `error`,其余照常。带 `session` 的收件人自动按词级模式处理。 收件人还可以带自己的 `text`(只对这个人说的话),此时该条按收件人的措辞加密,其余收件人仍用公共措辞。 ### 临时口令(带有效期) 会话串有两种格式: ``` EC1:口令:词表指纹:掩码标记 永久 EC2:口令:词表指纹:掩码标记:签发时间:有效期 单位分钟 ``` EC2 过期后**解析直接被拒**(真的解不了,不是提示一下),容忍 5 分钟时钟偏差。`/api/encrypt` 传 `ttl_minutes` 就能生成;`check_session` 的结果里带 `ttl_minutes` 与 `expires_at`。 注意:这是"过期即失效",不是"用一次即失效"——后者需要双方同步一次性计数器。 ### 会话串(一次约定一整串) 词级模式要约定的东西打包成一串:`EC1:口令:词表指纹:掩码标记`,例 `EC1:芦苇-湖泊-9066-0MKN:CFDE52D2:M`。 - 传 `session` 即可,等价于 seed + words + mask 三样 - 口令校验位不过、词表指纹对不上,都会直接报错,不会给"看着像对"的结果 - 响应里会给出 `session` 与 `share_link`(扫码后直接打开工具并填好整套约定) - 词表指纹为空表示没有自定义词表;会话串只用口令形式的种子 ### 可读口令(带校验位) 词级模式的种子可以写成口令:`词-词-数字-校验位`,例如 `芦苇-湖泊-9066-0MKN`。 - 前三段就是**实际生效的种子**;末四位是校验位(FNV-1a 取 16 位 → 32 字母表,去掉了容易混淆的 I L O U) - 手抄错字立刻能发现:校验位对不上直接报错,不会给你"看着像对"的结果 - 接口里用 `seed_phrase` 传口令(HTTP 与 MCP 都一样),服务端会校验后转成种子 - 也接受任意种子串(`seed`),只是那样没有防抄错保护 - 口令与 `seed` 等价时结果完全一致:同一口令与同一基础种子产生同一张表 ## 规则细节 - 字符表:文本按出现顺序取前 N 个不重复汉字。mode=dedup 时,把 amb 个字放到 0xE0-0xEF、0xC2-0xDF 这些合法首字节槽位,同一密文字因此对应多个合法字节,解密存在多解。 - mode=hash:取字规则相同,但顺序由整段文本的 FNV-1a 哈希派生,文本改动一个字符则表与密文全变。 - mode=raw:原样取前 N 个字,保留重复。 - 编码:非标点字符取 UTF-8 字节逐字节查表;标点原样写出。N=256 时密文长度等于字节数。 - 掩码(mask=true):字节先与掩码流异或,掩码流 = FNV-1a(表字符串) 播种的 mulberry32 逐字节生成。 - N≠256 时改为打包:每 log2(N) bit 一段,按 8/gcd(8,log2(N)) 个符号一组,组内字节数 = log2(N)/gcd(8,log2(N)),前置 2 字节大端长度用于去掉末组补零。 ## 威胁模型 - 算法公开。密钥只有字符表文本。 - 私有表 → 256 个字有 256! 种排列,暴力不可行;但公开经典当表可被逐表试解。 - 掩码开启后,攻击者没有表就算不出掩码,无法验证任何候选明文,重复模式也被摧毁。 - 明文熵决定成本:加入随机串或暗语后,攻击者的假设空间按 2^n 增长。 - 这不是经过审计的标准加密。高风险通信请用 Signal、WireGuard 或 age。 ## 可用接口 ### MCP(JSON-RPC 2.0) stdio:`python mcp/server.py` HTTP:`POST /api/mcp` | 工具 | 说明 | 参数 | |---|---|---| | `build_table` | 由字符集文本生成 256 项映射表,并给出全文指纹供双方核对。 | `source`、`lang`、`mode`、`seed`、`n`、`amb` | | `encrypt` | 用字符表把明文编码成汉字密文。 | `text`*、`source`、`lang`、`mode`、`seed`、`seed_phrase`、`session`、`words`、`n`、`amb`、`mask`、`ttl_minutes`、`items`、`recipients`、`dry_run` | | `decrypt` | 用字符表把密文还原为明文,返回所有合法候选。 | `cipher`*、`source`、`lang`、`mode`、`seed`、`seed_phrase`、`session`、`sessions`、`words`、`n`、`amb`、`mask`、`items`、`dry_run` | | `generate_phrase` | 生成一份能念的口令:口令、实际种子、念读版、会话串与模式指纹一次给全,起一段词级约定时先用它。 | `ttl_minutes`、`words` | | `self_check` | 部署自检:把整条链路跑一遍(建表 → 加密 → 解密 → 会话串 → 词级往返 → 认领 → 识别 → 文档契约 → 吞吐),逐项返回结论与耗时。 | 无 | | `check_session` | 会话串体检:校验位过不过、词表指纹对不对、最终生效的种子是什么。 | `session`*、`sessions`、`words` | | `identify_sender` | 认领密文:拿一段密文去试各个会话串。能解出的候选会全部列出(带相似度),只有一条能解出时 confidence=single;多条都能解出时词级模式分不出对错,按语义确认。 | `cipher`*、`sessions`*、`words` | | `identify_table` | 识别字符集来源:给 8 位指纹或字符集原文,返回它是哪个内置经典,还是自定义表。 | `fingerprint`、`source` | 调用示例: ```json {"jsonrpc":"2.0","id":1,"method":"tools/call", "params":{"name":"build_table", "arguments":{"source":"<双方约定的字符集文本>","mode":"dedup","n":256,"amb":16}}} ``` ```json {"jsonrpc":"2.0","id":2,"method":"tools/call", "params":{"name":"encrypt", "arguments":{"text":"我的微信是 ABCD1234","source":"<同一份字符集>", "mode":"dedup","n":256,"amb":16,"mask":false}}} ``` ### MCP 资源与提示词 除工具外,服务器还提供 MCP 的 resources 与 prompts: | 方法 | 内容 | |---|---| | `resources/list` | 18 个资源:`entropy://tables/index`、`entropy://tables/`(10 部经典的全文)、`entropy://endpoints`、`entropy://agent-kit`、`entropy://index`、`entropy://self-check`、`entropy://events`、`entropy://recipes`、`entropy://skill` | | `resources/read` | 读资源;`entropy://tables/index` 返回「key / 名称 / 指纹」三列表;`entropy://endpoints` 与 `entropy://agent-kit` 返回 JSON;`entropy://skill` 返回 Markdown | | `prompts/list` | 8 个提示词:`troubleshoot`、`encrypt_text`、`share_link`、`group_chat`、`encrypted_call`、`start_agreement`、`claim_cipher`、`decrypt_text` | | `prompts/get` | 返回一段可执行提示,先 `build_table` 核对指纹再调工具 | ### 批量 `encrypt` 与 `decrypt`(HTTP 与 MCP 都一样)接受 `items` 数组,一次最多 50 条: ```json {"items": [ {"text": "第一条", "source": "<字符集文本>"}, {"text": "第二条", "mode": "word", "seed": "abc"} ]} ``` 返回 `{"results": [ {...}, {...} ], "count": 2}`。**单条出错不拖垮整批**:这一条的结果里带 `error` 与 `status`,其余照常返回。Agent 一次要处理一串消息时,用这个比循环调用省事。 ### dry_run(只校验,不出密文) `encrypt` / `decrypt` 都支持 `dry_run: true`: - `encrypt` + dry_run → 只回 `{"dry_run": true, "ok": true, "fingerprint", "table_size", "distinct", "entropy_bits"}`,**没有 cipher** - `decrypt` + dry_run → 只回 `{"dry_run": true, "ok": true, "decodable": true, "count"}`,**没有 plain** - 批量时顶层 `dry_run` 对每一条生效,也可以单条覆盖 用途:Agent 在真正动手前先批量预检 —— 表建得对不对、这段密文能不能解出来,而密文与明文都不落到日志里。 ### 词级密文的三层来源 词级模式加密时会同时记录每个输出字符来自哪一层:`word`(整词替换)、`char`(单字替换)、`symbol`(非中文走符号层)。`/api/encrypt` 的词级响应带 `layers: {"word": n, "char": n, "symbol": n}`;工具页把密文按这三层着色,图标说明每一段是怎么来的。 ### 口令二维码 工具页可以把口令渲染成二维码(面对面传递)。二维码内容是 `https://argot.im/app.html#phrase=<口令>`:对方扫码后工具会自动切到词级并填好口令;口令放在 URL fragment 里,不会随请求发给服务器。编码器是自研的 `public/assets/qr.js`:字节模式、纠错 M 级、版本 1–10、UTF-8 原文,不引第三方库,离线可用。正确性由 `tests/test_qr.py` 用 OpenCV 独立解码验证。 ### 提示词 `prompts/list` 给8个:`encrypt_text`、`decrypt_text`、`share_link`(藏链接)、`group_chat`(群聊里每人一份)、`encrypted_call`(加密通话)。后三个是场景提示词,内容里直接写清了该用哪种模式与怎么走。 ### 按场景选路 `/api/agent-kit` 的 `scenarios` 给出4个常见场景(发链接 / 群聊 / 加密通话 / 批量处理),每个都写了该用哪种模式、为什么、以及具体几步。加 `?scenario=group_chat` 可以只返回那一个场景。 ### 部署自检 `GET /api/self-check` 把整条链路跑一遍(建表 → 加密 → 解密 → 会话串 → 词级往返 → 认领 → 识别 → 文档契约 → 吞吐),逐项返回 `{name, ok, detail, ms}`,顶层 `ok` 表示是否全部通过。部署完或换环境后先跑这个。 ### HTTP - `GET /api/agent-kit` 上手包:能力、场景、工作流、坑 参数:`scenario(查询串)` 示例:`GET /api/agent-kit?scenario=group_chat` - `GET /api/endpoints` 本目录本身:每个端点的用途、参数与示例 参数:无 示例:`GET /api/endpoints` - `GET /api/self-check` 端到端自检:建表→加密→解密→会话串→词级往返→认领→识别→吞吐,逐项返回结论与耗时 参数:无 示例:`GET /api/self-check` - `GET /api/health` 存活检查 参数:无 示例:`GET /api/health` - `GET /api/stats` 埋点聚合计数(事件名→次数)与关键路径(产品动作链每步的计数与比例)。只对同源请求开放,运维可用 X-Stats-Token 头 参数:`format(查询串)` 示例:`GET /api/stats?format=csv` - `POST /api/encrypt` 加密:字节级传 source,词级传 session/seed;支持 items 批量与 recipients 群发 参数:`text`、`source`、`preset`、`lang`、`session`、`seed`、`seed_phrase`、`words`、`mode`、`n`、`amb`、`mask`、`ttl_minutes`、`items`、`recipients`、`dry_run` 示例:`{"text":"我的微信是 ABCD1234","session":"EC1:芦苇-湖泊-9066-0MKN::-"}` - `POST /api/decrypt` 解密;传 sessions 数组即可认领(逐个会话串试解) 参数:`cipher`、`source`、`preset`、`lang`、`session`、`sessions`、`seed`、`seed_phrase`、`words`、`mode`、`n`、`amb`、`mask`、`items`、`dry_run` 示例:`{"cipher":"<密文>","sessions":["EC1:芦苇-湖泊-9066-0MKN::-"]}` - `POST /api/phrase` 生成一份能念的口令:口令、实际种子、念读版、会话串与模式指纹一次给全 参数:`ttl_minutes`、`words` 示例:`{"ttl_minutes": 30}` - `POST /api/identify` 识别字符集来源:给指纹或原文,返回是哪个内置经典还是自定义表 参数:`fingerprint`、`source` 示例:`{"fingerprint":"CFDE52D2"}` - `POST /api/session-check` 会话串体检:校验位过不过、词表指纹对不对、生效种子、念读版、有效期 参数:`session`、`sessions`、`words` 示例:`{"session":"EC1:芦苇-湖泊-9066-0MKN::-"}` - `POST /api/event` 第一方埋点:只记事件名与日期 参数:`name`、`props` 示例:`{"name":"demo_encrypt"}` - `POST /api/mcp` MCP 服务器(JSON-RPC 2.0):tools/list、tools/call、resources/list、prompts/list 参数:`jsonrpc`、`id`、`method` 示例:`{"jsonrpc":"2.0","id":1,"method":"tools/list"}` ### 技能包(可下载、可直接装成 skill) 整份技能包挂在 `/skills/entropy-cipher/`: | 路径 | 内容 | |---|---| | `/skills/entropy-cipher/SKILL.md` | 技能包正文(带 YAML frontmatter,Agent 装 skill 直接用这一份) | | `/skills/entropy-cipher/scripts/ec.py` | 零依赖命令行:`encrypt` / `decrypt` / `build-table` / `session-check` / `self-check` | | `/skills/entropy-cipher/references/endpoints.md` | HTTP 端点参考(由实现生成) | | `/skills/entropy-cipher/references/mcp.md` | MCP 工具、资源、提示词参考(由实现生成) | | `/skills/entropy-cipher/references/recipes.md` | | 命令行两种用法:`--api https://站点` 走 HTTP 接口;不给就用本地内核(离线可用)。输出是 JSON。 ### 埋点接口返回什么 `GET /api/stats` 的 JSON 里有四个字段: - `counts` —— 「事件名|日期 → 次数」,只有名字与次数 - `names` —— 出现过的事件名 - `funnel` —— 产品动作链(落地页 → 工具 → 试玩 → 加密 → 解密 → 认领 → 核对)每一步的次数与比例。 埋点没有用户标识,所以那是**计数之比**,不是用户级转化率 - `catalog` —— 事件名到人话说明的对照表(50 条),看板与 Agent 都读它 ### 客户端清单 `/mcp.json`(MCP 客户端可直接引用)、`/.well-known/capabilities.json`(自述接口、隐私属性与事件目录)、`/api/openapi.json` ## 数据处理 明文与字符表不离开浏览器。服务端接口是可选的,服务端不记录请求体。埋点只记事件名与日期,没有账号、IP、UA,也没有 Cookie。