Skip to main content
DocsRunPublic Artifact Sharing

Public Artifact Sharing (Public HTTPS Links)

Publish .md / .html / .xlsx products as short links that do not require login. Slack/email only sends links but not files; supports TTL / password / number of downloads / HTML online preview.

If your workflow generates a report (.md / .html / .xlsx / .pdf) and wants to send it to others, the most direct way is to let the Slack/mail module upload the file attachment. But large files will be slow, and messaging platforms usually have limits on the size of a single file and the number of attachments.

Public Artifact Sharing is another route: publish a single artifact as a login-free HTTPS short link, send only the link in a message, and it opens right up — the file itself stays on the server, which saves bandwidth and lets the browser preview it directly (.md can be rendered to HTML online). To share several files at once, you can also publish an entire artifact directory as one link; see "Directory-level public links" below.

When To Use It

  • Reports/dashboards need to be sent to Slack/Feishu/email, and I don’t want everyone to save a 3MB xlsx each.
  • An external collaborator (no platform account) needs to see an app store data snapshot
  • You share a quick .md summary with a client who should read it as HTML in a mobile browser
  • You want the link to have an expiry, a download cap, or password protection

How to Use It — From the UI

  1. Open a finished execution, switch to the Artifacts tab, and each artifact has a 🔗 Share button next to it
  2. Click 🔗 Share to open the share dialog
  3. Configure as needed:
    • Access scope: public (default) / password / team / specified members; see "Access scope and access requests" below
    • Lifetime: never expires (default) / 1 hour / 24 hours / 7 days / 30 days; it remembers your last choice
    • Download cap: 0 = unlimited, >0 = the link expires automatically once used up
    • Access password (password mode): the browser prompts for a password when the link is opened
    • Online HTML rendering (.md only): when ticked, the link shows formatted HTML instead of downloading the .md source
  4. Click "Generate link" → get a URL like https://braidrun.com/p/a/hN2kQ9..., then click "Copy" and paste it anywhere

After the workflow is executed, the product is automatically turned into a public link, and then the link is automatically pushed to Slack - no manual clicks to share are required in the entire process.

A Typical Three-Step Arrangement

  1. Write the artifact-producing step as usual, no special markers needed — for example:
    - step: build_report
      agent: reporter
      input: |
        生成今天的业务报表,写入 /tmp/.../daily.md。
      # 这一步完成后,引擎自动注册一个产物(execution 的 artifacts 列表里会出现)
  2. Attach the built-in artifact publish module and pass the previous step's name `from_step: build_report`. The module looks up the execution's artifact list, finds the file that step produced, and creates a public link:
    - 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]
  3. Then attach a built-in Slack delivery module and pass public_url in → Slack receives just a text message with the link, no file upload:
    - 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]

What About Multiple Artifacts

If the build_report step produced several files (say both daily.md and metrics.json), filter with 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

Without artifact_name the module takes the first matching artifact and logs how many others were left unused.

A single-file link can share only one artifact at a time. If one execution produces a set of files (a report + data + images, or even a complete multi-page static site), you can publish the entire artifact directory as one link:

  • UI entry: on the execution details "Artifacts" tab, click "Share all artifacts" to generate a directory link covering all of this execution's artifacts; the options (access scope / lifetime / download quota / password / online rendering) are the same as for a single file, plus you can set a custom display name
  • The API can also share the artifacts of a single step. To share the whole run: POST /api/executions/{id}/directory-public-link; to share a single step: POST /api/executions/{id}/steps/{stepName}/directory-public-link
  • The link looks like https://braidrun.com/p/d/hN2kQ9.../, and visitors need no account (in public mode)

What Visitors See

  • By default, a file-listing page: file name, size, and type, plus the link's lifetime and download quota
  • When there's an index.html at the directory root, it opens as a static site — a multi-page HTML report generated by a workflow can be shared as a whole site; add ?listing=1 to the URL to force back to the file-listing view
  • When online rendering is enabled, .md files in the directory are shown as formatted HTML
  • Files in the directory are always forwarded by the platform with the correct Content-Type (not via a 302 redirect to object storage), so the browser can render HTML / CSS / JS properly
  • The download quota accumulates across the whole directory, and the link expires automatically once it's used up

Access Scope and Access Requests

Single-file links and directory links share the same set of access scopes:

ModeWho Can Open It
PUBLIC PublicAnyone with the link
PASSWORD Password AccessAnyone with the link who knows the password; the browser prompts for it, and script clients use the Authorization: Basic or X-Artifact-Password header
TEAM Within The TeamRequires a signed-in Braidrun account that belongs to the specified team
USERS Designated MembersRequires sign-in and being on the member list; the list can only be chosen from members of the selected team

In team or specified-member mode, the link becomes a "sign-in-required deep link": accessing it while signed out redirects to login; signed in but not on the list shows a "no access" page.

Access request flow (optional in team or specified-member mode):

  1. When creating the link, check "Allow signed-in users to submit a request when they lack access"
  2. A visitor without access opens the link, fills in a request form on the "no access" page (a note can be attached); submitting again updates the original request
  3. The sharer or a link administrator approves / rejects under "Account → Share management"; before approving, confirm the person already belongs to the selected team
  4. Once approved, the person is added to the access list and can open the original link directly
  • "Account → Share management" manages single-file and directory links together: change the access scope, change the lifetime, revoke, and view access counts and access logs (time, account identity, IP, User-Agent)
  • The corresponding management API — list / update / revoke — is: GET /api/directory-public-links · PATCH /api/directory-public-links/{token} · DELETE /api/directory-public-links/{token}
  • The creator is the link administrator by default and can designate additional administrators to jointly handle access requests and revocations

Supported Sharing Methods

DeploymentSupportedDescription
Single node + local diskThe platform directly returns the file content
Multiple nodes + local disk⚠️ Not recommendedThe request may hit a node that does not have the file → 410 Gone. It is recommended to enable object storage.
Any number of nodes + object storage✅ Recommended302 to the temporary signature download link (10 minutes TTL, new signature for each visit), the platform does not forward file traffic

The table above is for single-file links; directory links always have the platform forward the file stream, so they work in any deployment form.

Security & Compliance

  • Tokens are not enumerable —— 24-byte SecureRandom → base62 ≈ 143 bits of entropy; brute-force scanning is infeasible
  • Crawler indexing protection —— Response headers X-Robots-Tag: noindex, nofollow + Referrer-Policy: no-referrer; Google/Bing will not index the links
  • Rate Limiting —— Gateway layer: 10 times per second for the same IP, 20 bursts; service layer: hard limit based on token’s maxDownloads
  • Password protection (optional) —— Argon2id hashing; the browser prompts on access, and script clients use the Authorization: Basic or X-Artifact-Password header. The password is not placed in the URL query string, to keep it out of access logs
  • Audit Log —— Every create / access / revoke writes an audit_log entry with client IP, User-Agent, and timestamp
  • GDPR compliant —— Account deletion cascades to all links automatically; data exports include artifact_public_links.jsonl in the ZIP
  • Instant revocation —— Clicking Revoke in share management takes effect immediately; revoked links return 410 Gone (not 404, to avoid confusion with "does not exist")

Go to Account → Share management (or call GET /api/public-links) to see every link you created, its access count, and whether it is revoked. Clicking Revoke disables the link immediately.

FAQ

  • The link has expired (its validity period ended)
  • The link was revoked by its creator or an administrator
  • The download count has hit its limit (the link expires automatically once the quota is used up)
  • The password was wrong (before you've entered one, you'll see the browser's password prompt rather than a 410)

Yes, but the UI asks for a second confirmation — a permanent link means anyone holding it can access it forever unless you revoke it manually, so be careful. Administrators can disable permanent links globally with BRAIDRUN_PUBLIC_LINK_REJECT_PERMANENT=true.

No. Response headers enforce noindex, nofollow, and the nginx layer adds another. But if you paste the link on a public page, crawlers may still discover the URL — so never publish these links in a GitHub README or blog post.

Q: what file sizes are appropriate?

In object storage mode: Theoretically unlimited; actually limited by the user's browser timeout (generally tens of MB is no problem). In local disk mode: limited by the platform file reading speed, it is not recommended to exceed 100MB.

No. Deleting an execution cascades a revoke to every link pointing at it; later visits get 410 Gone.

Q: IS This feature enabled by default?

An administrator must enable it explicitly via the BRAIDRUN_PUBLIC_BASE_URL environment variable; without it the Share button is hidden in the UI. See PRODUCTION_GUIDE §2.4 in the operations docs.