一条部署链,所有入口共用
网页上传、Public API 与 PubPage CLI V1 共用同一审核与发布流程。仓库已提供项目自有的 PubPage AI Skill 源文件 skills/pubpage/SKILL.md,用于指导 Agent 通过安全登录与官方 CLI 自动发布;外部 Skill registry/distribution 发布单独跟踪。
完成第一次 Public API 调用
API origin 使用控制面地址 https://www.pubpage.top。先把 Token 放进受保护的环境变量,再用最小 scope 调用接口。
export PUBPAGE_ORIGIN='https://www.pubpage.top'
export PUBPAGE_TOKEN='pp_live_...'1. 列出 Sites
/api/v1/sitessites:readcurl --fail-with-body -H "Authorization: Bearer $PUBPAGE_TOKEN" "$PUBPAGE_ORIGIN/api/v1/sites"2. 创建一个空白 Site
/api/v1/sitessites:createcurl --fail-with-body -H "Authorization: Bearer $PUBPAGE_TOKEN" -H 'Content-Type: application/json' --data '{"name":"My site","slug":"my-site","templateKey":"blank"}' "$PUBPAGE_ORIGIN/api/v1/sites"3. 上传 deployment candidate
/api/v1/sites/{siteId}/deploymentsdeployments:writeSITE_ID='00000000-0000-4000-8000-000000000000'
IDEMPOTENCY_KEY='deploy-20260829-001'
curl --fail-with-body -H "Authorization: Bearer $PUBPAGE_TOKEN" -H "Idempotency-Key: $IDEMPOTENCY_KEY" -F 'file=@./index.html;type=text/html' -F 'path=index.html' "$PUBPAGE_ORIGIN/api/v1/sites/$SITE_ID/deployments"4. 查询审核与发布状态
/api/v1/deployments/{deploymentId}deployments:readDEPLOYMENT_ID='00000000-0000-4000-8000-000000000000'
curl --fail-with-body -H "Authorization: Bearer $PUBPAGE_TOKEN" "$PUBPAGE_ORIGIN/api/v1/deployments/$DEPLOYMENT_ID"API Token 只放在可信服务端
Public API 使用 Authorization: Bearer <token>。Token 与网页登录 Session 是两套凭据,不能互换。
通过 Developer Console 获取 Token
登录 PubPage 后前往 Developer Console 创建、查看状态和撤销 API Token。Token 管理使用网页登录 Session 与 same-origin/CSRF 保护,不属于 Public API。
创建成功时完整明文只显示一次;之后列表只返回 prefix、scopes、过期/撤销时间和最后使用时间等非敏感 metadata。丢失完整 Token 时应撤销并重新创建。
安全保存
优先使用 secret manager、CI secret 或本机受保护环境变量。按 integration 分开创建 Token,并只授予需要的 scope。
禁止放进 HTML、浏览器 JavaScript、公开 Git、前端 bundle、普通日志、截图或上传到 Site 的文件,也不要把 Token 共享给不受信任的第三方。
Scopes
sites:readGET /api/v1/sitessites:createPOST /api/v1/sitessites:readGET /api/v1/templatesdeployments:writePOST /api/v1/sites/{siteId}/deploymentsdeployments:readGET /api/v1/deployments/{deploymentId}同一个 scope 可能服务多个 operation。创建非 blank template 的 Site 还需要 deployments:write,因为模板 bootstrap 会创建普通 deployment candidate。
列出和创建 Sites
/api/v1/sitessites:read列表只返回当前 Token 所属用户拥有的 Sites。响应 envelope 包含 data 与 requestId;字段定义见下方 OpenAPI-derived Reference。
/api/v1/sitessites:createname 必填;slug 与 templateKey 的约束直接来自 OpenAPI。不要在客户端复制 slug 正则或 template enum 作为长期权威值。
模板只是普通静态文件的起点
/api/v1/templatessites:read运行时通过这个 endpoint 发现 template key、名称、摘要和 tone。不要把模板目录复制进 SDK。
选择非 blank 模板后,服务器生成普通静态文件,再调用现有上传服务;状态通过 Site 响应里的 template.status 与 template.deploymentId 跟踪。模板不能绕过 Scan 或 Review。
上传 file,必要时指定 path
/api/v1/sites/{siteId}/deploymentsdeployments:write请求是 multipart/form-data。OpenAPI 当前要求一个 file 字段,并允许一个可选 path 字段。单文件部署可用 path 指定安全的站内相对路径;多文件站点应打包为受支持 archive 后作为 file 上传。
上传仍受 transport safety cap、账户 storage/file-count 配额、archive/path/MIME 安全规则、上传限流和 review quota 约束。传输安全上限不是套餐“单文件空间”。
只依赖公开 deployment state
不要解析内部 Prisma status。Public API 把内部 Upload / Release / Deployment 投影成下面这些稳定状态;枚举直接读取 OpenAPI。
queued已接收,等待现有审核工作流处理。
reviewing扫描、审核、批准或发布正在进行。
manual_review等待人工审核决定。
rejected内容未通过审核,不会成为当前公开版本。
published批准的 Release 已成为 Site 当前公开版本。
failed上传接收或发布没有完成。
进一步判断时读取 review.status、publish.status、finalUrl 和有界的 reason/error。只有 published 才表示批准的 Release 已成为当前公开版本。
按 error.code 分支,保留 requestId
{
"error": {
"code": "invalid_request",
"message": "The request is invalid."
},
"requestId": "..."
}每个稳定 V1 JSON 响应都有 x-request-id header,JSON envelope 也包含 requestId。日志和工单应保存 requestId,但不要记录 Authorization header。
当前 runtime 会返回的常见 machine codes 包括 invalid_api_token、token_expired、token_revoked、insufficient_scope、invalid_request、invalid_idempotency_key、not_found、site_limit_reached、storage_quota_exceeded、file_quota_exceeded、upload_too_large、upload_rejected、rate_limited 和 service_unavailable。
Public API runtime 会执行账户级请求限流;deployment 上传还有独立上传限流,并继续受 plan、storage、file-count 与 review quotas 约束。遇到 429 时停止突发请求并做有界退避,不要把某个客户端写死的请求频率当成 contract。
访问保护不等于审核豁免
Password Site 是 control-plane + wildcard UGC access-layer 能力,不是 Public API 认证模式。目前没有稳定的 /api/v1 密码管理 endpoint。
符合套餐条件的 Site 可在控制面启用密码保护。密码使用服务端 Argon2id 哈希;访问凭据绑定 Site 和 session version,生产 Cookie 为 Secure、HttpOnly、Host-only,受保护资源返回 private/no-store。HTML、CSS、JavaScript、图片、JSON、PDF 等资源都在同一访问边界后。
Operator validation pending:生产环境相关 DNS/TLS、CDN/cache、CAPTCHA、真实 PUBLIC/PASSWORD 站点与公网 E2E 仍需按独立验收清单验证。本页不把这些生产验收项描述为已完成。
CLI V1 与 PubPage AI Skill
PubPage CLI V1
CLI 是受审核发布 API 的普通外部客户端,不访问数据库、内部 Worker、UGC auth 或 published storage。现在支持 login、logout、whoami、sites、create、deploy 和 status。
pubpage login 使用浏览器中的现有 PubPage Session 与 127.0.0.1 loopback callback 完成授权;Token 明文不会要求用户复制到 AI 对话或终端输出。凭据保存在本机受限文件中,但不是 OS keychain。
项目自有 AI Skill
仓库内的 canonical instruction source 是 skills/pubpage/SKILL.md。Skill 优先调用官方 CLI;只有 CLI 不可用时才按 /openapi.json 回退 REST。仓库 artifact 已可用于 Agent onboarding,但这不等于已发布到外部 Skill registry。
Agent 不应读取或泄露 secret,不应把 Token 写进 prompt、网站文件或日志,也不会删除用户文件、绕过 Review,或在 rejected 后自动循环修改重投。
pubpage whoami
# 如果尚未登录:
pubpage login
# 当前目录必须是带根 index.html 的静态输出:
pubpage deploy .- Agent 先读取 PubPage Skill,并用
pubpage whoami检查身份;未登录时启动 browser login,不要求用户粘贴 API Token。 - 确定用户要求发布的目录。目录发布必须有 regular root
index.html;CLI 不会修改或删除源码。 - 未传
--site时,只有恰好一个 Site 才自动选择。没有 Site 时必须先询问用户是否创建;多个 Site 时先pubpage sites,由用户选择。 pubpage deploy .自动打包、上传 candidate 并进行有界轮询;仍保留 idempotency 与完整 Review pipeline。manual_review、rejected、failed都停止当前自动流程;特别是 rejected 后不能自动改写并重投。- 仅在 state 为
published时返回成功,并输出 API 提供的finalUrl;polling 达到边界时报告现状,不进入无限循环。
Reference 直接绑定 /openapi.json
下面的 endpoint、scope、parameter、request field、response 与 component schema 都由服务端同一份 publicOpenApiDocument 渲染。它不是第二份 contract。
/api/v1/sitessites:readList sites
Responses
200SitesobjectdefaultStable machine-readable API errorPublicApiError/api/v1/sitessites:createCreate a site
A non-blank template also requires deployments:write because template bootstrap enters the normal reviewed deployment pipeline.
Request · application/json
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
name | string | yes | minLength 1 · maxLength 120 |
slug | string | no | Lowercase hostname label. Reserved platform labels are rejected. · minLength 3 · maxLength 63 · pattern ^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])?$ |
templateKey | string | no | enum blank | personal | showcase | resume | landing | ai-tool | links | event |
Responses
201Site createdobjectdefaultStable machine-readable API errorPublicApiError/api/v1/templatessites:readList stable starter templates
Responses
200TemplatesobjectdefaultStable machine-readable API errorPublicApiError/api/v1/sites/{siteId}/deploymentsdeployments:writeUpload a deployment candidate
The upload is staged privately and processed by the existing scanner, AI/risk review, release and publish pipeline. This endpoint cannot bypass review.
Parameters
siteIdpath · string · uuid · requiredIdempotency-Keyheader · string · requiredRetries with the same user, site and key return the same deployment without creating another candidate release.
minLength 1 · maxLength 128 · pattern ^[A-Za-z0-9._:-]+$Request · multipart/form-data
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
file | string · binary | yes | — |
path | string | no | Optional safe relative path for a single-file deployment. · maxLength 2048 |
Responses
200Idempotent replayDeploymentEnvelopeheaders: Idempotency-Replayed201Deployment candidate createdDeploymentEnvelopedefaultStable machine-readable API errorPublicApiError/api/v1/deployments/{deploymentId}deployments:readGet deployment status
Parameters
deploymentIdpath · string · uuid · requiredResponses
200DeploymentDeploymentEnvelopedefaultStable machine-readable API errorPublicApiErrorComponent schemas
Site14 fields
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
id | string · uuid | yes | — |
name | string | yes | — |
slug | string | yes | pattern ^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])?$ |
hostname | string | yes | — |
url | string · uri | yes | — |
status | string | yes | — |
accessMode | string | no | enum public | password |
storageBytes | string | no | pattern ^[0-9]+$ |
template | object | no | — |
template.key | string | yes | enum blank | personal | showcase | resume | landing | ai-tool | links | event |
template.status | string | yes | enum not_requested | queued | failed |
template.deploymentId | string | null · uuid | yes | — |
createdAt | string · date-time | no | — |
updatedAt | string · date-time | no | — |
Template4 fields
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
key | string | yes | enum blank | personal | showcase | resume | landing | ai-tool | links | event |
name | string | yes | — |
summary | string | yes | — |
tone | string | yes | — |
DeploymentEnvelope2 fields
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
data | Deployment | yes | — |
requestId | string | yes | — |
Deployment12 fields
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
id | string · uuid | yes | Stable public deployment ID. It is the underlying upload/candidate identity, available before publication. |
siteId | string · uuid | yes | — |
state | string | yes | enum queued | reviewing | manual_review | rejected | published | failed |
review | object | yes | — |
review.status | string | yes | enum pending | in_progress | approved | manual_review | rejected | not_started |
review.reason | PublicReason | no | — |
publish | object | yes | — |
publish.status | string | yes | enum pending | published | not_published | failed |
publish.deploymentRecordId | string | null · uuid | no | — |
finalUrl | string | null · uri | yes | — |
createdAt | string · date-time | yes | — |
error | PublicReason | no | — |
PublicReason2 fields
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
code | string | yes | — |
message | string | yes | — |
PublicApiError2 fields
窄屏可左右滚动查看完整表格。
| Field | Type | Required | Contract detail |
|---|---|---|---|
error | PublicReason | yes | — |
requestId | string | yes | — |