Pasty 使用说明

团队自建的轻量分享板。文本或文件丢上去,拿到一个 6 位共享码,凭码即取,到期自毁。

站点 pasty.youlidao.ai 2026-10-16 线上实测
MN3W2R
共享码 6 位,字符集 31 个:23456789ABCDEFGHJKMNPQRSTUVWXYZ。去掉了易混淆的 0/O/1/I/L,口头报码不会听错;输入时大小写随意。

1它是什么

特性事实
HTML 页面给 AI 用:凭令牌托管一个 HTML 页面,得到一个点开即看的 /p/ 网址(见 §9)
登录不需要。创建和查看都不鉴权,持码即可看
内容类型纯文本 / 单个文件 / 文本 + 文件
文本上限50,000 字符(JSON 接口另受 100KB 请求体限制,见 §4.2)
文件上限100 MB,单个文件,任意类型
过期5 分钟 / 30 分钟 / 1 小时 / 1 天 / 7 天;默认 1 天
删除 / 编辑 / 列表都没有。创建后不可撤回,只能等过期
存储元数据和文本在 Redis(带 TTL),文件在 Cloudflare R2

适合:跨设备传一段文字或一个文件、面对面口头报一个码、把 Pod / 服务器上的产物交到远端用户手里、给客户发一个短期有效的下载链接。

不适合:需要撤回的内容、需要长期保存的内容、密钥 / 凭证 / 隐私数据(见 §7)。

2网页使用

2.1 创建粘贴

打开 pasty.youlidao.ai,默认在「创建粘贴」标签。

  1. 文本:在文本框输入或粘贴。右下角实时显示 已用 / 50,000,超限标红且按钮禁用。
  2. 文件(可选):拖到虚线区域、点击虚线区域选择,或焦点不在文本框时按 ⌘V(Windows Ctrl+V)粘贴剪贴板里的图片。只能带一个文件,一次拖多个会提示「仅支持上传单个文件」并只取第一个。图片显示缩略图,其他类型显示图标、文件名、大小、MIME,点 × 移除。
  3. 过期时间:下拉框,默认「1 天」。
  4. 点「创建粘贴」。带文件时按钮变成「上传中 N%」并有进度条。文本超 50,000 字或文件超 100MB 在本地直接拦下。
  5. 结果页:大号共享码、「此内容将在 X 后过期」、「复制共享码」、「创建新粘贴」。

2.2 查看粘贴

2.3 查看页长什么样

右上角是相对过期时间(「4 分钟后过期」「3 小时后过期」「6 天后过期」)。下面按内容类型呈现:

内容呈现
文本等宽 <pre> 原样展示(保留换行和缩进),下方「复制内容」按钮
图片png / jpeg / gif / webp / svg 页面内直接显示,点击在新标签打开原图;下方是文件名和大小
PDF页面内嵌浏览器自带阅读器,旁边有「新标签打开」和「下载文件」。iOS Safari 内嵌框只显示第一页,点「新标签打开」看全文
其他类型zip、Word、Excel、视频等:文件卡片(图标、文件名、大小、MIME)+「下载文件」按钮
文本 + 文件先文本,再一条「📎 附件」分隔线,再文件

请求超过 10 秒未返回会弹「请求超时,请重试」。

2.4 分享给别人的三种形式

形式例子适用
只报码MN3W2R口头 / 手写 / 电话,对方自己到首页输
查看页链接https://pasty.youlidao.ai/MN3W2R微信 / 邮件,点开就是完整页面
文件直链https://pasty.youlidao.ai/api/pastes/MN3W2R/file图片和 PDF 点开即渲染;其他类型点开即下载。适合塞进 <img> 或 wget

3命令行 / 脚本使用

3.1 发一段文本

curl -s -X POST https://pasty.youlidao.ai/api/pastes \
  -H 'Content-Type: application/json' \
  -d '{"content":"要分享的文本","expiresIn":"1h"}'
# → {"code":"7FZEN4","expiresAt":"2026-09-09T16:56:23.246Z"}

内容来自文件时用 jq 拼 JSON,避免手工转义:

jq -n --rawfile c notes.md '{content:$c, expiresIn:"1d"}' \
  | curl -s -X POST https://pasty.youlidao.ai/api/pastes \
      -H 'Content-Type: application/json' --data @-

3.2 发一个文件(可附说明)

curl -s -m 120 -X POST https://pasty.youlidao.ai/api/pastes \
  -F "file=@/path/报告.pdf" \
  -F "content=一句话说明这是什么" \
  -F "expiresIn=1h"
# → {"code":"KTCK33","expiresAt":"…","hasFile":true}

3.3 取回

# 元数据 + 文本
curl -s https://pasty.youlidao.ai/api/pastes/KTCK33
# 只要文本
curl -s https://pasty.youlidao.ai/api/pastes/KTCK33 | jq -r .content
# 下载文件(-J 用服务端给的文件名,-O 落盘)
curl -s -O -J https://pasty.youlidao.ai/api/pastes/KTCK33/file

没有 /raw 端点。GET /{CODE} 返回的是前端 SPA 的 HTML。脚本取文本一律走 /api/pastes/{CODE} 再取 .content。

3.4 两台机器之间传文件的推荐流程

# 发送端
shasum -a 256 FILE
curl -s -X POST https://pasty.youlidao.ai/api/pastes -F "file=@FILE" -F "expiresIn=30m"
# 接收端
curl -s -O -J https://pasty.youlidao.ai/api/pastes/CODE/file
shasum -a 256 FILE   # 与发送端比对

传输本身无损(实测 zip / png 往返字节一致),校验是为了防传错码或下到一半断线。

如果所处环境只能发 JSON 文本(老脚本、受限沙箱),可退回 base64 通道:base64 -i FILE | tr -d '\n' 作为 content 发,接收端 base64 -d。base64 后体积 ×1.33 且受 100KB 请求体限制,只适合小文件。

4API 参考

4.1 端点总览

方法路径作用限流(按 IP)
GET/api/health健康检查无
POST/api/pastesapplication/json = 纯文本;multipart/form-data = 文件 ± 文本JSON 10 次/分钟;multipart 5 次/分钟,两个计数器独立
GET/api/pastes/:code取元数据 + 文本无
GET/api/pastes/:code/file流式下载 / 预览文件无
POST/api/pages托管一个 HTML 页面(需令牌):JSON html 或 multipart file10 次/分钟
GET/p/:code在沙箱 CSP 下渲染托管页面无
GET/p/:code/pdf把托管页面导出为 A4 PDF(首次渲染后缓存)10 次/分钟

每次创建响应都带限流头:ratelimit-limit、ratelimit-remaining、ratelimit-reset(秒)、ratelimit-policy(如 10;w=60)。超限返回 429,body 的 code 字段为 RATE_LIMITED(文本路径)或 FILE_RATE_LIMITED(文件路径)。

4.2 POST /api/pastes · JSON 路径

{ "content": "必填,去首尾空白后非空,≤ 50,000 字符",
  "expiresIn": "5m | 30m | 1h | 1d | 7d,可省略,默认 1d" }
// 201
{ "code": "7FZEN4", "expiresAt": "2026-09-09T16:56:23.246Z" }

请求体上限 100KB。这是 Express 的 JSON 解析限制,在字符数校验之前生效:40,000 个汉字约 120KB,会得到 413 request entity too large。中文长文本要么控制在约 33,000 字以内,要么改走 multipart 路径的 content 字段(无此限制,完整 50,000 字可用)。

校验失败的格式(details 仅 JSON 路径有):

{ "error": "Content must not be empty",
  "code": "CONTENT_EMPTY",
  "details": [ { "path": "content", "message": "CONTENT_EMPTY" } ] }

4.3 POST /api/pastes · multipart 路径

字段必填规则
file与 content 至少一个单个文件 ≤ 100MB(104,857,600 字节),任意类型
content与 file 至少一个去首尾空白后非空,≤ 50,000 字符
expiresIn否同上,默认 1d

响应 201:{ "code", "expiresAt", "hasFile": true },无文件时没有 hasFile 字段。处理顺序:先传 R2,成功后才写 Redis;R2 失败不写 Redis;Redis 失败会回滚删除 R2 对象。

文件名规则:

4.4 MIME 判定:决定「预览」还是「下载」

服务端不直接采信客户端声明的 Content-Type,按以下顺序确定:

  1. 文件魔数(图片、PDF、zip、Office、音视频等二进制格式)
  2. 客户端声明,仅当非空且不是 application/octet-stream(覆盖 txt / csv / json / svg 这类无魔数的文本格式)
  3. 文件扩展名
  4. application/octet-stream

效果:curl -F file=@x.zip 存为 application/zip;无扩展名、无声明的 PDF 照样识别为 PDF 并可内联预览;把 PDF 改名成 .txt 上传仍按 PDF 处理。

4.5 GET /api/pastes/:code

code 必须恰好 6 位且全部落在字符集内,否则 400;服务端统一转大写。

{
  "content": "说明文字(纯文件时为空串)",
  "type": "text | file | mixed",
  "fileMetadata": {
    "fileName": "测试图片.png", "fileSize": 74, "mimeType": "image/png",
    "storageKey": "pastes/U9RBRW/测试图片.png"
  },
  "createdAt": "2026-09-09T17:00:41.045Z",
  "expiresAt": "2026-09-09T17:05:41.045Z"
}

type 和 fileMetadata 只在有文件时出现;早期纯文本记录可能连 type 都没有,按 text 处理。

4.6 GET /api/pastes/:code/file

流式返回文件本体,不经内存缓存。响应头:

头值
Content-Type判定后的 MIME
Content-Dispositionimage/* → inline;PDF → inline; filename=…;其他 → attachment; filename=…(RFC 5987 编码,中文名安全)
Content-Length文件大小
Content-Security-Policy内联响应(图片 / PDF)在站点基线上追加 sandbox,脚本一律不执行;附件响应保持基线

4.7 错误码总表

每个错误响应都是 { "error": <英文说明>, "code": <标识符> }。程序判断请用 code:它是稳定契约;error 是给人读的英文散文,措辞可能变。网页端不显示 error,它按 code 查本地文案,用访问者自己的语言呈现。

状态code场景
400CONTENT_EMPTYcontent 为空或全为空白
400INVALID_EXPIRYexpiresIn 不在 5m/30m/1h/1d/7d 内
400INVALID_CODE_FORMAT共享码不是 6 位合法字符
400NO_CONTENT_PROVIDEDmultipart 里 content 和 file 都没有
400FILENAME_TOO_LONG / FILENAME_INVALID文件名超 255 字符 / 含路径遍历
400VALIDATION_ERRORmultipart 损坏,或其他未细分的校验失败
400PP_SLUG_INVALID链接后缀格式、长度不符,或是保留路径
401PAGE_AUTH_REQUIRED创建页面时没带令牌,或令牌不对
401PP_AUTH_REQUIRED创建永久页面未带令牌,或令牌错误
404PASTE_NOT_FOUND码不存在或已过期
404NO_FILE_IN_PASTE该码是纯文本,没有文件
404FILE_NOT_FOUNDR2 对象已被清理
413CONTENT_TOO_LARGE文本超 50,000 字,或 JSON 请求体超 100KB
413FILE_TOO_LARGE文件超 100MB
413PAGE_TOO_LARGEHTML 超过 5MB
409PP_SLUG_TAKEN该后缀已被其他永久页面或正在使用的共享码占用
429RATE_LIMITED / FILE_RATE_LIMITED文本创建限流 / 文件上传限流,两者独立计数
501FILE_UPLOAD_DISABLED服务端未配置 R2 却收到文件
501PAGE_HOSTING_DISABLED服务端没配置 PAGE_API_TOKENS,页面托管处于关闭状态
501PP_HOSTING_DISABLED服务端未设置 PP_API_TOKENS,永久页面功能未开启
503SERVICE_UNAVAILABLE / SERVICE_BUSYRedis 故障 / 共享码碰撞重试 5 次仍失败
503STORAGE_UNAVAILABLE / FILE_UPLOAD_FAILEDR2 不可用 / 上传失败
503PDF_EXPORT_UNAVAILABLEPDF 渲染失败、排队已满,或服务端未配置 Chromium
500INTERNAL未捕获的服务端异常

5与 Claude Code / cc-hub 的配合

youlidao.ai 的 cc-hub 会话和建造中心 Pod 上已预装 pasty-share skill(v3)。用户对 AI 说「把 XX 发我」「这个文件给客户」时,AI 会:

  1. 用 multipart 把原始文件本体传上来。skill 里的铁律:禁止把 PDF / Word 转成 markdown 再发
  2. 默认 expiresIn=1h,多个文件先打 zip
  3. 回复共享码 + 查看链接;图片和 PDF 再附一条 /file 直链
  4. 每次明示过期时间和「持码任何人可见」

skill 的安全红线:不传 .env、私钥、token、凭证、数据库导出;内容可能敏感先问用户;用户坚持要传就用 5m / 30m。

6数据生命周期

阶段发生什么
创建Redis 写 paste:{CODE},TTL = 所选过期秒数;有文件则先写 R2 pastes/{CODE}/{文件名}
有效期内无限次读取,不计数
到期Redis key 自动消失,查询即 404
到期后清理服务端每小时扫一遍,删掉已过期记录对应的 R2 对象;R2 侧另有生命周期规则兜底(文档记为 8 天)
日志结构化日志只记 IP、大小、MIME、净化后的文件名,不记文本内容

共享码由 CSPRNG 经拒绝采样均匀生成,31 的 6 次方 = 887,503,681 种组合,碰撞时重试最多 5 次。

7安全边界,使用前必读

8常见问题

浏览器打开提示 ERR_CONNECTION_CLOSED,或 curl 返回 000
国内直连偶发,刷新或重试一次即通。健康检查和读取接口本身稳定。
上传按钮一直灰着
文本为空且没选文件,或文本超过 50,000 字(计数器变红)。
PDF 在手机上只看到第一页
iOS Safari 内嵌框的限制,点「新标签打开」。
中文文件名下载下来是乱码
2026-09-10 之前上传的记录会这样(服务端曾按 latin1 解码文件名)。此后上传的已修复;老记录到期自然消失。
zip 用 curl 传上去以前显示 application/octet-stream
curl 不认识 .zip 就声明 octet-stream,旧版直接采信。现在按文件内容判定,不需要再手动 ;type=。
能不能改内容、删掉、延长时间
不能,没有这些接口。重新创建一个。
返回 429 怎么办
看响应头 ratelimit-reset(秒)等一下。文本 10 次/分钟,文件 5 次/分钟,按 IP 计。多个文件打包成一个 zip。
中文长文本 JSON 发不出去,返回 413 request entity too large
JSON 请求体上限 100KB。改用 multipart 的 content 字段,或分段。
界面能换语言吗
能。页脚有语言选择(简体中文 / English / 日本語 / 한국어),选择记在本地;也可以用 ?lang=en 之类直接指定。

9HTML 页面托管:给 AI 用的临时网页

9.1 它解决什么

AI 生成了一份报告、看板或原型,需要给人一个点开就能看的网址,而不是一个要下载的文件。把一个 HTML 文档 POST 上来,拿到 https://pasty.youlidao.ai/p/{CODE},到期自毁。

特性事实
谁能创建持令牌者:请求头 Authorization: Bearer <token>。服务端没配置令牌时接口返回 501,功能处于关闭状态
谁能看和其他内容一样,持码 / 持链接即可看,不用登录
大小单个 HTML 文档 ≤ 5 MB
过期5m / 30m / 1h / 1d / 7d,默认 1 天
共享码和文本、文件共用一个码空间。/{CODE} 显示一张页面卡片,/p/{CODE} 直接渲染页面
限流10 次/分钟/IP

9.2 创建页面

curl -s -X POST https://pasty.youlidao.ai/api/pages \
  -H "Authorization: Bearer $PASTY_PAGE_TOKEN" \
  -F "file=@report.html" \
  -F "expiresIn=7d"
# → {"code":"MN3W2R","expiresAt":"…","url":"https://pasty.youlidao.ai/p/MN3W2R"}

HTML 已经在程序变量里时,用 JSON 路径,字段名是 html:

jq -n --rawfile h report.html '{html:$h, expiresIn:"1d"}' \
  | curl -s -X POST https://pasty.youlidao.ai/api/pages \
      -H "Authorization: Bearer $PASTY_PAGE_TOKEN" \
      -H 'Content-Type: application/json' --data @-

9.3 打开页面时的运行环境

/p/{CODE} 以 text/html; charset=utf-8 返回,并带一条专用 CSP:

可以不可以
运行内联脚本,以及从这些 CDN 加载脚本:cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com, cdn.tailwindcss.com调接口:fetch / XHR / WebSocket 全部禁止(connect-src 'none')
内联样式、Google Fonts、data: 字体提交表单(form-action 'none')
任意 https / data: / blob: 的图片和音视频嵌入 iframe、object
弹窗,新标签打开外部链接读写 localStorage / cookie:页面运行在独立沙箱里(不同源),访问会抛异常

沙箱的作用:页面脚本不能冒充 pasty 本站,也拿不到本站的任何存储。禁止外发数据,是为了让页面没法当钓鱼表单用;但这不是绝对的(图片地址、页面跳转仍能带出数据),真正的门槛是创建时的令牌。

码不存在、已过期,或者对应的是文本 / 文件而不是页面时,会 302 跳到 /{CODE},由普通查看页说明情况。通过匿名文件接口上传的 .html 永远只作为附件下载,不会在 /p/ 下渲染。

9.4 给 AI 的写页面清单

  1. 单文件:CSS 和 JS 内联;图片用 data: URI 或 https 外链;库只从 cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com, cdn.tailwindcss.com 加载,并锁定版本号。
  2. 不依赖网络请求:数据直接写进页面,不要再去 fetch JSON。
  3. 不用 localStorage;非用不可就包 try/catch,页面没有它也要能正常显示。
  4. 写上 <meta charset="utf-8"> 和 viewport,手机上要能看。国内访问 Google Fonts 不稳定,字体要写好系统字体回退。
  5. 过期默认用 1d,给人看的报告可以用 7d。回复用户时给出 url 和过期时间,并说明「拿到链接的人都能看」。
  6. 不放密钥、个人隐私或内部凭证,规则同 §7。

给机器读的完整说明见 /llms.txt。

9.5 导出 PDF

每个托管页面右下角都有一个「导出 PDF」按钮(读者浏览器语言不是中文时显示 Export PDF),对应 /p/{CODE}/pdf,也可以直接把这个地址发给别人。

10运维备忘(给维护者)

项值
仓库~/Codes/pasty,pnpm monorepo:client / server / shared,分支 main
部署Railway 项目 pasty / 服务 pasty-app + Redis 插件;push 到 main 自动构建。2026-10-16 起用仓库根目录的 Dockerfile 构建(Debian bookworm + Chromium + Noto CJK 字体,供 PDF 导出用),不再走 nixpacks
构建命令见 Dockerfile:pnpm i --frozen-lockfile --prod=false && pnpm run build;本地可用 docker build -t pasty . 复现线上镜像
健康检查GET /api/health → {status, redis, r2, uptime};uptime 归零 = 刚重启
环境变量PORT、REDIS_URL、NODE_ENV=production(必须,决定是否托管前端)、RATE_LIMIT_MAX(默认 10)、RATE_LIMIT_FILE_MAX(默认 5)、R2_ACCOUNT_ID / R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY / R2_BUCKET_NAME、PAGE_API_TOKENS(逗号分隔的页面令牌,留空 = 页面托管关闭)、RATE_LIMIT_PAGE_MAX(默认 10)、PUBLIC_ORIGIN(拼返回的页面 URL,默认 https://pasty.youlidao.ai)、CHROMIUM_PATH(PDF 导出用,Dockerfile 里已设为 /usr/bin/chromium;留空 = 导出关闭、页面不显示按钮)、RATE_LIMIT_PDF_MAX(默认 10)
测试根目录 pnpm test(vitest,Redis / R2 全 mock,不需要本地服务)
本页由 client/landing/guide/ 生成:structure.mjs 是骨架与共享代码片段,content/<lang>.mjs 只放散文。端点表和错误码表由代码生成,数字用占位符从常量注入
查部署状态Railway CLI 本机常 TLS 失败;用 ~/.railway/config.json 的 token 直打 GraphQL deployments / buildLogs。登录过期会返回 Not Authorized,重新 railway login

变更记录