产物公开分享(公开 HTTPS 链接)
把 .md / .html / .xlsx 产物发布为无需登录的短链,Slack/邮件只发链接不发文件;支持 TTL / 密码 / 下载次数 / HTML 在线预览。
如果你的工作流生成了一份报表 (.md / .html / .xlsx / .pdf) 要发给别人看,最直接的办法是让 Slack/邮件模块上传文件附件。但大文件会慢,消息平台通常有单文件大小和附件数量限制。
产物公开分享 是另一条路:把单个产物发布成一个无需登录的 HTTPS 短链,消息里只发链接,点开就能看 —— 文件本身留在服务器,既省流量,又支持浏览器直接预览 (.md 可以在线渲染成 HTML)。一次要分享多个文件时,还可以把整个产物目录发布成一个链接,见下方「目录级公开链接」。
什么时候用
- 报表 / 仪表盘要发 Slack / 飞书 / 邮件,不想让所有人各存一份 3MB 的 xlsx
- 需要让外部协作者 (没有平台账号) 看一份 app store 数据快照
- 临时分享一份 .md 结论给客户,希望对方用手机浏览器直接看 HTML
- 要给链接一个过期时间 / 下载次数上限 / 密码保护
怎么用 —— 从 UI 生成
- 打开一条已经执行完的 execution,切到"产出物"Tab,看到每个产物旁边有一个 🔗 分享 按钮
- 点击 🔗 分享,弹出分享对话框
- 按需配置:
- 访问范围:公开 (默认) / 密码访问 / 团队内 / 指定成员,详见下方"访问范围与访问申请"
- 有效期:永不过期 (默认) / 1 小时 / 24 小时 / 7 天 / 30 天,会记住你上次的选择
- 下载次数上限:0 = 不限制,>0 = 用完后链接自动失效
- 访问密码 (密码访问模式):点开链接时浏览器弹出密码框输入
- 在线 HTML 渲染 (.md 专属):勾选后访问链接直接看到排版好的 HTML,不是下载 .md 源文件
- 点「生成链接」→ 得到形如 https://braidrun.com/p/a/hN2kQ9... 的 URL,点「复制」粘贴到任意地方
怎么用 —— 在工作流里自动发布(推荐)
工作流执行完自动把产物变成公开链接,再自动把链接推到 Slack —— 全程无需手动点击分享。
工作流引擎会在每个 code step 自动注入本次执行 ID、短期访问令牌与平台访问地址。内置 artifact publish 模块会读取这些运行期参数,所以你只需要告诉它"分享哪个步骤产生的产物"(from_step)。访问令牌有效期较短,运行结束后无需手动维护。
典型三步编排
- 生成产物的步骤照常写,不需要任何特殊标记 —— 例如:
- step: build_report agent: reporter input: | 生成今天的业务报表,写入 /tmp/.../daily.md。 # 这一步完成后,引擎自动注册一个产物(execution 的 artifacts 列表里会出现) - 接上内置 artifact publish 模块,传入上一步的步骤名 `from_step: build_report`。模块会自动查 execution 的产物列表,找到那个步骤产生的文件,生成公开链接:
- step: share_report sub_workflow: name: dingyue-module-artifact-publish inputs: from_step: "build_report" # ← 唯一必需字段:引用上一步的步骤名 ttl_minutes: "0" # 永不过期;如需限时可改成分钟数 render_markdown_as_html: "true" # .md 直接在线 HTML 预览 # base_url / api_token 不用填 —— 引擎自动注入运行时访问令牌 outputs: share_url: public_url # 下游可以用 {{var:share_url}} 引用 depends_on: [build_report] - 再接一个内置 Slack 投递模块,把 public_url 传进去 → Slack 只收到一条"文字消息+链接",不上传文件:
- step: notify sub_workflow: name: dingyue-module-slack-deliver inputs: slack_webhook_url: "{{var:slack_webhook_url}}" message_text: "📊 今日报表已生成" public_url: "{{var:share_url}}" # ← 复用 share_report 的输出 depends_on: [share_report]
多产物怎么办
如果 build_report 步骤产生了多个文件(比如同时写了 daily.md 和 metrics.json),用 artifact_name 过滤:
- step: share_report
sub_workflow:
name: dingyue-module-artifact-publish
inputs:
from_step: "build_report"
artifact_name: "daily.md" # ← 精确挑选
ttl_minutes: "0"
outputs:
share_url: public_url不加 artifact_name 时模块取第一个匹配的产物,并在日志里提示还有几个未使用的。
目录级公开链接:一个 URL 分享整个产物目录
单文件链接一次只能分享一个产物。如果一次执行产出了一组文件 (报表 + 数据 + 图片,甚至一个完整的多页静态网站),可以把整个产物目录发布成一个链接:
- UI 入口:执行详情的「产物」Tab 点「分享全部产物」,生成覆盖这次执行全部产物的目录链接;配置项 (访问范围 / 有效期 / 下载配额 / 密码 / 在线渲染) 与单文件一致,另可自定义显示名称
- API 还支持只分享某个步骤的产物。分享整个执行:
POST /api/executions/{id}/directory-public-link;只分享单个步骤:POST /api/executions/{id}/steps/{stepName}/directory-public-link - 链接形如 https://braidrun.com/p/d/hN2kQ9.../,访问者无需账号 (公开模式下)
访问者看到什么
- 默认是一个文件列表页:文件名、大小、类型,以及链接的有效期和下载配额
- 目录根部有 index.html 时,直接按静态站点打开 —— 工作流生成的多页 HTML 报告可以整站分享;URL 加 ?listing=1 强制回到文件列表视图
- 勾选了在线渲染时,目录里的 .md 文件以排版好的 HTML 展示
- 目录内文件始终由平台按正确的 Content-Type 转发 (不走对象存储 302 直链),保证 HTML / CSS / JS 能被浏览器正常渲染
- 下载配额按整个目录累计,用完后链接自动失效
访问范围与访问申请
单文件链接和目录链接共用同一套访问范围:
| 模式 | 谁能打开 |
|---|---|
PUBLIC 公开 | 任何拿到链接的人 |
PASSWORD 密码访问 | 拿到链接且知道密码的人;浏览器弹窗输入,脚本客户端用 Authorization: Basic 或 X-Artifact-Password 头 |
TEAM 团队内 | 需登录 Braidrun 账号,且属于指定团队 |
USERS 指定成员 | 需登录,且在成员名单里;名单只能从所选团队的成员里挑 |
团队内 / 指定成员模式下,链接变成"需要登录的深链":未登录访问会被引导去登录;登录了但不在名单里,会看到"无权访问"页。
访问申请流程 (团队内 / 指定成员模式可选开启):
- 创建链接时勾选「允许登录用户在无权访问时提交申请」
- 无权访问者打开链接,在"无权访问"页填一份申请表单,可附说明;重复提交会更新原申请
- 分享者或链接管理员在「账户 → 分享管理」里批准 / 拒绝;批准前需要确认对方已属于所选团队
- 批准后对方即被加入可访问名单,用原链接直接打开
管理目录链接
- 「账户 → 分享管理」把单文件和目录链接放在一起管:改访问范围、改有效期、撤销,以及查看访问计数与访问日志 (时间、账号身份、IP、User-Agent)
- 对应的管理 API,列出 / 修改 / 撤销分别是:
GET /api/directory-public-links·PATCH /api/directory-public-links/{token}·DELETE /api/directory-public-links/{token} - 创建者默认是链接管理员,可以再指定额外管理员,一起处理访问申请和撤销
支持的分享方式
| 部署 | 是否支持 | 说明 |
|---|---|---|
| 单机 + 本地盘 | ✅ | 平台直接返回文件内容 |
| 多节点 + 本地盘 | ⚠️ 不推荐 | 请求可能命中没有文件的节点 → 410 Gone。建议启用对象存储。 |
| 任意节点数 + 对象存储 | ✅ 推荐 | 302 到临时签名下载链接 (10 分钟 TTL,每次访问新签),平台不转发文件流量 |
上表针对单文件链接;目录链接始终由平台转发文件流,任何部署形态都可用。
安全与合规
- Token 不可枚举 —— 24 字节 SecureRandom → base62 ≈ 143 bits 熵,暴力扫描不可行
- 防爬虫索引 —— 响应头 X-Robots-Tag: noindex, nofollow + Referrer-Policy: no-referrer,Google/Bing 不会索引链接
- 速率限制 —— 网关层: 同一 IP 每秒 10 次,突发 20 次;服务层: 按 token 的 maxDownloads 硬性限制
- 密码保护 (可选) —— Argon2id 哈希;浏览器访问时弹窗输入,脚本客户端用 Authorization: Basic 或 X-Artifact-Password 头。密码不放 URL 查询参数,避免落入访问日志
- 审计日志 —— 每次创建 / 访问 / 撤销都写一条 audit_log,包含客户端 IP + User-Agent + 时间戳
- GDPR 兼容 —— 账户删除时自动级联清理所有链接;数据导出时 artifact_public_links.jsonl 打入 ZIP
- 即时撤销 —— 分享管理页面点"撤销"即刻生效,被撤销的链接访问直接 410 Gone (不是 404,避免和"不存在"混淆)
管理已分享的链接
进入"账户 → 分享管理"(或直接访问 GET /api/public-links) 可以看到你创建过的所有链接、访问计数、是否已撤销。点撤销按钮链接立刻失效。
常见问题
Q: 链接点开为什么显示 410 Gone?
- 链接已过期(有效期到了)
- 链接已被创建者或管理员撤销
- 下载次数已达上限(配额消耗完会自动失效)
- 密码输错了(没输过密码时会先看到浏览器的密码弹窗,而不是 410)
Q: 我能让链接永不过期吗?
可以,但 UI 会弹一个二次确认 —— 永久链接意味着除非你手动撤销,任何拿到链接的人都可以永久访问,请谨慎。管理员可以通过 BRAIDRUN_PUBLIC_LINK_REJECT_PERMANENT=true 全局禁用永久链接。
Q: 链接会被搜索引擎索引吗?
不会。响应头强制 noindex,nofollow,并且 nginx 层 also 追加一道。但如果你把链接贴到公开网页上,搜索引擎可能通过外部爬虫发现 URL —— 所以永远不要把链接公开到 GitHub README / 博客文章里。
Q: 多大的文件合适?
对象存储模式下:理论上无限;实际受限于用户的浏览器超时 (一般几十 MB 没问题)。本地盘模式下:受限于平台文件读取速度,不建议超过 100MB。
Q: 如果我删除了产物对应的 execution,旧链接还能用吗?
不能。删除 execution 的时候系统会级联撤销所有指向该 execution 的链接,后续访问直接 410 Gone。
Q: 这个功能是默认开启的吗?
需要管理员通过环境变量 BRAIDRUN_PUBLIC_BASE_URL 显式启用;未启用时 UI 看不到分享按钮。见运维文档 PRODUCTION_GUIDE §2.4。