gbrain Publish Skill 实战指南:零 LLM 调用的脑页安全分享与 AES-256-GCM 客户端加密发布
【免费下载链接】gbrainGarry's Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain
gbrain 的publish技能(位于 plugin/skills/publish/SKILL.md)用于把大脑(brain)中的 Markdown 页面一键转换成精美、自包含的 HTML 文档对外分享,可选客户端 AES-256-GCM 密码加密,全程零服务器依赖、零 LLM 调用。读完本文,你将掌握gbrain publish的完整命令用法、内置的隐私脱敏规则、四种分享工作流,以及底层加密与 HTML 生成的源码级实现原理,可直接把交易备忘录、人物简报、尽调分析等脑内内容安全地送出脑库。
技能定位:Thin Harness, Fat Skills
publish是一个code + skill 配对的组合:
- 确定性代码
gbrain publish负责真正的工作——脱敏、加密、生成 HTML,逻辑全部固化在 src/commands/publish.ts 中,不产生任何 LLM 调用,因此输出可复现、无随机性、零 Token 成本; - 技能本身(
SKILL.md)只负责"何时用、怎么用"的策略判断,通过 frontmatter 中的triggers自动响应"share this page"、"publish page"、"create shareable link"等自然语言指令,并声明依赖get_page与search两个工具、mutating: false(发布动作不修改脑库)。
这种"薄编排、厚代码"的设计理念意味着:Agent 只需识别意图,复杂的脱敏与加密逻辑交给经过测试验证的确定性代码,避免每次分享时由 LLM 临场发挥带来隐私泄露风险。
何时触发发布
技能明确的触发场景包括:
- 用户请求分享某个脑页、生成可分享链接,或直接说 "give me a page";
- 需要把交易备忘录(deal memo)、人物简报(person briefing)、研究报告发给外部人员;
- 需要发布尽调分析(data room analysis)或行程计划(trip plan);
- 任何"脑内容需要离开脑库,但不能暴露整个系统"的场合。
核心判断标准只有一个:内容要离开脑库。凡是输出给外部、且不希望携带内部元数据的场景,都应该走publish通道而不是手工复制 Markdown。
铁律:默认永远加密
SKILL.md将"默认加密"列为契约与反模式双重强调项:
Brain content is private. Default to password-protected unless the user explicitly says "open", "no password", or "public".
- 用户未指定密码时,自动生成一个随机密码;
- 密码与分享链接必须走不同渠道传达(如链接发邮件、密码发短信),避免同信道泄露;
- 只有用户明确说出 "open"、"no password"、"public" 才允许输出明文 HTML。
快速参考:核心命令
以下命令均来自SKILL.md的 Quick Reference,且与 src/commands/publish.ts 中runPublish的参数解析逻辑一一对应:
# 基础发布(输出本地 HTML 文件,不加密) gbrain publish brain/companies/acme.md # 密码保护(自动生成密码) gbrain publish brain/companies/acme.md --password # 密码保护(指定密码) gbrain publish brain/companies/acme.md --password "secret123" # 自定义标题 gbrain publish brain/companies/acme.md --password --title "Acme -- Deal Analysis" # 自定义输出路径 gbrain publish brain/companies/acme.md --out /tmp/acme-share.html参数行为说明(源自runPublish源码解析逻辑 src/commands/publish.ts):
| 参数 | 含义 | 默认值 |
|---|---|---|
<page-path> | 输入脑页 Markdown 路径(首个非--开头的参数) | 必填 |
--password | 启用加密;后接非--参数时作为指定密码 | 未指定时自动生成 16 位随机密码 |
--title "..." | 覆盖 HTML 标题 | 缺省时取 Markdown 第一个 H1(extractTitle) |
--out <path> | 输出文件路径 | <输入文件名>.html(basename(inputPath, '.md') + '.html') |
注意:--out目录不存在时会通过mkdirSync(dirname(outPath), { recursive: true })自动递归创建;缺少输入参数时命令会打印完整 Usage 并以退出码 1 结束。
发布时剥离什么:隐私脱敏白名单
SKILL.md给出的脱敏清单,在源码中由makeShareable()函数逐条实现(src/commands/publish.ts):
| 剥离项 | 示例 | 原因 | 对应实现正则 |
|---|---|---|---|
| YAML frontmatter | title:、type:、tags: | 内部元数据 | ^---[\s\S]*?---\n* |
[Source: ...]引用 | 所有格式 | 来源溯源属内部信息 | \s*\[Source:[^\]]*\] |
| 确认编号 | ABC123DEF→ "on file" | PII/预订数据 | \*\*Confirmation:\*\*\s*[A-Z0-9]{6,}等三种格式 |
| 脑内交叉链接 | Jane→Jane | 内部路径 | \[([^\]]+)\]\(\.[^)]*\/[^)]+\)(保留显示文本) |
| Timeline 章节 | ---与## Timeline之下的全部内容 | 原始证据日志 | \n---\n\n## Timeline[\s\S]*$ |
| "See also" 行 | 内部引用导航 | 脑库导航信息 | ^-?\s*See also:.*$(多行模式) |
保留项:外部 URL(https://...,形如https://example.com的链接不受影响)以及其他所有正文内容。处理结束后还会把连续 3 行以上的空行折叠为最多 2 行(\n{3,}→\n\n),并trim()掉首尾空白。
以上每一条脱敏规则都有对应的测试用例背书,见 test/publish.test.ts:例如strips YAML frontmatter验证title: Secret与type: person被移除、Jane is CTO [Source: Crustdata enrichment, 2026-04-01] of Acme.被净化成Jane is CTO of Acme.、Works with Jane Doe at Acme.变成Works with Jane Doe at Acme.,同时preserves external URLs确认https://example.com/blog被完整保留。测试还覆盖了.raw/相对链接(脑内原始数据目录)的剥离与空输入、纯 frontmatter 输入等边界情况。
四种分享工作流
方案 A:本地文件(最简)
gbrain publish brain/people/jane-doe.md --password --out ~/Desktop/jane-briefing.html生成单文件 HTML 后通过邮件、Slack、Airdrop 分享,密码另行单独发送。这是最直接、无任何外部依赖的路径。
方案 B:上传云存储(Supabase Storage)
# 先本地发布 gbrain publish brain/companies/acme.md --password "secret" --out /tmp/acme.html # 上传到 Supabase Storage gbrain files upload /tmp/acme.html --page shares/acme # 获取签名 URL(1 小时过期) gbrain files signed-url shares/acme/acme.htmlgbrain files upload与gbrain files signed-url由 src/commands/files.ts 实现:upload子命令(src/commands/files.ts 的分发入口,实现于 uploadFile)会先校验存储后端存在(requireStorageBackend('upload')),再按内容哈希去重(已上传且哈希一致时提示File already uploaded (hash match)),最后调用storage.upload写入对象;signed-url子命令(signedUrl)则为存储中的对象生成带 1 小时有效期的访问链接。分享时把签名 URL 与密码分渠道送达,URL 过期后可随时重新生成。
方案 C:静态托管(Render、Netlify、S3)
把生成的 HTML 文件直接上传到任意静态托管服务即可。由于文件完全自包含、无服务器逻辑,密码保护页面通过浏览器内置的Web Crypto API在纯客户端完成解密,托管方永远接触不到明文。
方案 D:GitHub Pages / Gist
gbrain publish brain/trips/japan-2026.md --out trip.html # 上传到 GitHub Gist 或 Pages 仓库适合一次性分享(如行程单),不需要独立服务器或云存储账号。
密码保护机制:从加密到解密的完整链路
SKILL.md声明的加密参数,与 src/commands/publish.ts 的encryptContent()完全一致:
| 项目 | 值 | 说明 |
|---|---|---|
| 算法 | AES-256-GCM | 带认证标签(auth tag)的认证加密 |
| 密钥派生 | PBKDF2,100,000 次迭代,SHA-256 | 抗暴力破解 |
| 盐(Salt) | 每次加密随机 16 字节 | 相同密码产生不同密文 |
| IV | 每次加密随机 12 字节 | 与 salt 一样随机化 |
| 解密 | 客户端 Web Crypto API(SubtleCrypto) | 无需服务端鉴权 |
| 记住本机 | localStorage | 按location.pathname为键存储 |
加密端(Node.js,发布时执行)
const salt = randomBytes(16); const iv = randomBytes(12); const key = pbkdf2Sync(password, salt, 100_000, 32, 'sha256'); const cipher = createCipheriv('aes-256-gcm', key, iv); // ... ciphertext + 16 字节 authTag,三者 base64 编码后写入 HTML加密后的 HTML 中只含window.__SALT、window.__IV、window.__CT三段 base64 密文数据,明文不出现于文件任何位置。
解密端(浏览器,打开文件时执行)
内嵌的DECRYPT_JS(src/commands/publish.ts)执行完全对称的逆过程:crypto.subtle.importKey('raw', password, 'PBKDF2', ...)→deriveKey({ name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' }, ...)→crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, combined)。密钥派生参数(100K 迭代、SHA-256)与加密端严格一致,这是两端能够互解的前提。错误密码会触发错误提示与卡片抖动动画;勾选 "Remember on this device" 后,密码按页面路径名存入localStorage,下次打开自动解锁。
自带安全防护
- 内联 marked.js:发布时把 marked 的 UMD 构建直接读入并写入 HTML(
MARKED_JS,见 src/commands/publish.ts),保证产物零 CDN 依赖、完全离线可用(测试inlines marked.js (no CDN dependency)专门断言产物不含cdn.jsdelivr.net); - XSS 消毒:
sanitizeHtml()(src/commands/publish.ts)在marked.parse渲染后移除script/iframe/object/embed/form标签,并清除所有on*事件属性与javascript:协议属性,防止脑页中内嵌的恶意 HTML 在分享文件中执行; - 标题转义:
escapeHtml()(src/commands/publish.ts)对标题做 HTML 实体转义(测试escapes HTML in title验证<script>被转为<script>); - 深色模式:内置
prefers-color-scheme: dark响应式 CSS 变量主题(测试同样覆盖); - 密码字符集:
generatePassword()(src/commands/publish.ts)默认生成 16 位密码,字符集刻意剔除易混淆字符0 O l 1 I(测试excludes ambiguous characters断言),且每次调用基于randomBytes保证唯一性(测试generates unique passwords验证 20 个密码无一重复)。
更新已发布页面
重新执行发布命令并指向同一输出路径即可:
gbrain publish brain/companies/acme.md --password "same-password" --out shares/acme.html同一文件、同一 URL(若已托管),内容自动更新。需要提醒的是:必须沿用原密码,否则接收方手中的旧密码将无法解开新文件。
吊销访问权限
- 本地文件:直接删除文件;
- 签名 URL:1 小时后自动过期,无需手动吊销;
- 静态托管:从托管平台移除该文件。
由于整份 HTML 自带解密逻辑、不依赖任何服务端鉴权,"删除文件/撤销 URL"是唯一的吊销手段——这也意味着文件一旦流出,本质上无法远程作废,因此更应在发布前依赖脱敏层把内部数据彻底剥离。
反模式清单
SKILL.md明确列出了四类应当避免的用法:
- 不加密就发布——脑内容默认私有,除非用户明确说 "open"、"no password"、"public";
- 同一渠道分享密码与 URL——必须分渠道传达,防止被同一窃听方同时截获;
- 向用户塞原始 Markdown——
gbrain publish能产出精美 HTML,不要用复制粘贴 Markdown 替代; - 手动传播含内部元数据的内容——凡带 frontmatter、Source 引用或 Timeline 章节的内容都不得手工外发,一律交给 publish 命令脱敏。
输出格式约定
发布成功后,CLI 会输出结构化结果摘要,SKILL.md给出的格式如下:
PUBLISHED: [page title] ======================== File: [output path] Encrypted: [yes (AES-256-GCM) / no] Password: [auto-generated password / user-provided / none] Size: [file size] Share the file via: [email / Slack / Airdrop / cloud upload] Share the password via: [a different channel]实际运行时控制台还会额外输出Published: <outPath>与加密状态说明((password protected, AES-256-GCM encrypted)或(no password, content in cleartext),见 src/commands/publish.ts);使用--password且未显式给定时,自动生成的密码会打印在控制台中供 Agent 转达给用户。
源码导览
如需深入实现细节,建议按以下顺序阅读:
- plugin/skills/publish/SKILL.md —— 技能策略层:触发条件、契约、工作流与反模式;
- src/commands/publish.ts —— 实现层:
makeShareable(脱敏)、encryptContent/generatePassword(加密)、generateHtml(HTML 生成与 XSS 消毒)、runPublish(CLI 入口); - test/publish.test.ts —— 测试层:脱敏规则、标题提取、加密随机性、密码字符集、HTML 生成的完整断言矩阵;
- src/commands/files.ts —— 分享配套:
files upload与files signed-url的云存储链路。
整套设计的关键收益在于:隐私脱敏与加密不是靠模型临场发挥,而是由确定性代码 + 测试矩阵保证的工程能力,Agent 只需判断"何时发布、是否加密、走哪种渠道",即可安全地把脑内价值输出到墙外。
【免费下载链接】gbrainGarry's Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考