# https://github.com/Amal-David/pagecast 项目说明书

生成时间：2026-06-23 02:57:17 UTC

## 目录

- [Pagecast 概览与系统架构](#page-1)
- [核心功能与发布流程](#page-2)
- [AI 代理集成与浏览器扩展](#page-3)
- [安全模型、Cloudflare 部署与运维](#page-4)

<a id='page-1'></a>

## Pagecast 概览与系统架构

### 相关页面

相关主题：[核心功能与发布流程](#page-2), [安全模型、Cloudflare 部署与运维](#page-4)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/Amal-David/pagecast/blob/main/README.md)
- [package.json](https://github.com/Amal-David/pagecast/blob/main/package.json)
- [src/cli.js](https://github.com/Amal-David/pagecast/blob/main/src/cli.js)
- [src/server.js](https://github.com/Amal-David/pagecast/blob/main/src/server.js)
- [src/markdown.js](https://github.com/Amal-David/pagecast/blob/main/src/markdown.js)
- [llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)
- [extension/manifest.json](https://github.com/Amal-David/pagecast/blob/main/extension/manifest.json)
- [extension/background.js](https://github.com/Amal-David/pagecast/blob/main/extension/background.js)
- [extension/popup.js](https://github.com/Amal-David/pagecast/blob/main/extension/popup.js)
- [extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md)
- [extension/store/SUBMIT.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/SUBMIT.md)
- [feedback/worker.js](https://github.com/Amal-David/pagecast/blob/main/feedback/worker.js)
- [plugin/README.md](https://github.com/Amal-David/pagecast/blob/main/plugin/README.md)
- [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)
</details>

# Pagecast 概览与系统架构

## 项目定位与适用场景

Pagecast 是一个**本地优先**的发布工具，主要面向 AI 编码代理（Claude Code、Codex、Cursor 等）生成的 HTML 报告、Markdown 文档以及小型静态 Web 项目。它的核心价值是把本地保存的 `.html`/`.htm`/`.md`/`.markdown` 文件一键转化为运行在用户**自有** Cloudflare Pages 账户上的可分享公共链接。资料来源：[README.md:1-12]()

**适合的场景**：Playwright/Lighthouse 报告、数据仪表盘、计划书、设计文档、发布说明，以及构建后的 `dist`/`build`/`out` 静态站点。**不适合的场景**：私有草稿、需要后端运行时才能工作的服务（必须先导出为静态资产）。资料来源：[README.md:13-19]()

## 系统架构总览

整个系统由三层组成：本地应用层、Cloudflare 部署层、以及配套的浏览器扩展与代理技能。

```mermaid
graph TB
    subgraph 本地环境["本地环境（Node.js >= 20）"]
        CLI["pagecast CLI (src/cli.js)"]
        Server["pagecast Server (src/server.js)"]
        MD["Markdown 渲染器 (src/markdown.js)"]
        Store[(".pagecast/ 数据目录")]
    end

    subgraph 浏览器侧["浏览器侧"]
        Ext["Chrome 扩展 (extension/manifest.json)"]
        Popup["Popup/右键菜单"]
    end

    subgraph Cloudflare["用户自有 Cloudflare 账户"]
        Pages["Cloudflare Pages<br/>/p/&lt;token&gt;/ 公共 URL"]
        Worker["Feedback Worker (feedback/worker.js)"]
        KV["PAGECAST_FEEDBACK KV"]
    end

    subgraph 代理生态["AI 代理生态"]
        Skill["publish-report 技能"]
        Hook["PostToolUse 钩子"]
    end

    CLI --> Server
    Server --> MD
    Server --> Store
    Server -->|"部署资产"| Pages
    Server -->|"配置 wrangler.toml"| Worker
    Worker --> KV
    Ext -->|"POST /api/publish-local"| Server
    Popup --> Ext
    Skill --> CLI
    Hook --> Skill
```

关键设计原则：**用户本地运行的服务器直接与用户自己的 Cloudflare 账户通信**——中间没有 Pagecast 托管的中转层，因此无需在 Pagecast 注册账户，也不用担心服务被取消（"no Pagecast-hosted middleman"）。资料来源：[extension/store/listing.md:21-27]()

## 核心组件与职责

| 组件 | 路径 | 职责 |
|---|---|---|
| CLI 入口 | `src/cli.js` | 解析命令行参数，提供 `npx pagecast` 启动本地服务 |
| HTTP 服务 | `src/server.js` | 暴露管理 UI（127.0.0.1:4173）、本地预览（4174）、`/api/publish-local` 等接口，调用 Wrangler 部署 |
| Markdown 渲染器 | `src/markdown.js` | 零依赖的 Markdown 渲染子集，严格转义 HTML/属性，URL 走安全白名单 |
| Chrome 扩展 | `extension/manifest.json`、`extension/background.js`、`extension/popup.js` | 在 `file://` 页面提供一键发布（工具栏按钮或右键菜单） |
| 反馈 Worker | `feedback/worker.js` | 收集已发布页面的浏览量、表情反应、国家/设备/来源粗粒度统计 |
| 代理技能 | `plugin/skills/publish-report/SKILL.md` | 指导 AI 代理在生成可分享产物时主动询问"是否用 Pagecast 发布" |

`package.json` 的 `bin` 字段把 `pagecast` 命令映射到 `src/cli.js`，`exports` 指向 `src/server.js`，本地与库式调用共用同一份入口；`files` 数组声明发布到 npm 时包含 `src/`、`public/`、`plugin/`、`feedback/` 以及技能文件。资料来源：[package.json:5-32]()

## 发布流程与运行模式

### 1. 本地启动
执行 `npx pagecast` 后，CLI 启动本地服务并打开管理 UI；本地数据与配置存放在当前目录下的 `.pagecast/` 中。资料来源：[README.md:23-33]()

### 2. Cloudflare 一次性连接
首次使用在管理 UI 中点击 **Connect Cloudflare**。OAuth 完成后 Pagecast 会自动创建 Pages 项目；子域名不一定等于项目名——`pagesBaseUrlFromDeployOutput` 会从 Wrangler 输出中解析 `<deploy-hash>.<subdomain>.pages.dev` 形式。资料来源：[src/server.js:pagesBaseUrlFromDeployOutput]()

### 3. 发布一条新链接（默认模式）
```sh
npx pagecast publish "/abs/path/report.md" --json
# → {"ok":true,"url":"https://pagecast.pages.dev/p/<token>/", ...}
```
- Markdown 文件由 `src/markdown.js` 渲染为自包含、安全加固的 HTML 文档（公开页面的文本/代码先转义，URL 走 `sanitizeUrl` 白名单）。资料来源：[src/markdown.js:1-35]()
- 通过 Wrangler 将文件部署到用户的 Pages 项目，生成形如 `/p/<token>/` 的公共路径。资料来源：[llms.txt:21-32]()

### 4. 整站部署（直接模式）
```sh
npx pagecast pages deploy "/abs/path/dist" --project pagecasthq --branch main --json
```
该模式会**覆盖**目标 Pages 项目的内容，适用于把整个 `dist` 推上去；不传 `--branch` 时默认 `main`。资料来源：[llms.txt:28-32]()

### 5. 浏览器扩展入口
Chrome 扩展通过 `host_permissions: ["http://127.0.0.1/*", "http://localhost/*"]` 调用本地服务的 `/api/publish-local`；右键菜单项 `pagecast-publish` 仅在 `file://` 页面且 URL 命中 `/\.(html?|md|markdown)(?:[?#].*)?$/i` 时启用。资料来源：[extension/manifest.json:1-25]()、[extension/background.js:1-19]()

### 6. 反馈与统计
发布页面内嵌 `widget.js`（由反馈 Worker 提供），浏览/表情反应以聚合形式写入 KV（`stats:<slug>`），只存国家/来源主机/设备类等粗粒度信号，不存 IP/Cookie/个人数据。资料来源：[feedback/worker.js:1-15]()

## 过期链接与社区设计取舍

v0.1.6 引入**过期 URL** 能力：过期时间在边缘（与密码保护共用的 Pages Function）强制检查，到期即失效；用户关机期间链接仍按计划死亡。这是默认 30 天、可按安装/按发布覆盖的策略。资料来源：[plugin/skills/publish-report/SKILL.md:相关段落]()

社区里有用户提问"为什么是 Pages 而非 Workers with Static Assets"——这正是当前架构的明确取舍：项目依赖 Pages 自带的静态托管 + Functions（用于过期/密码校验），URL 解析逻辑也是按 Wrangler 的 Pages 部署输出格式实现的；Workers with Static Assets 当时并不具备等价的静态资源原语，作者未来可能据此再评估。资料来源：[src/server.js:URL 解析段]()

## 常见错误模式

- **`401`**：OAuth 未完成或已过期，需运行 `npx pagecast pages setup` 或在 UI 中重新连接 Cloudflare。资料来源：[llms.txt:39-43]()
- **`409`**：检测到多个 Cloudflare 账户，需传 `--account <account-id>` 或在 UI 中选择。资料来源：[llms.txt:39-43]()
- **OAuth 缺少 Workers/KV 权限**：部署反馈 Worker 时会触发 Cloudflare `code: 10000`/`authentication error`，`server.js` 会自动用扩展后的 `FEEDBACK_OAUTH_SCOPES` 重新登录并重试。资料来源：[src/server.js:FEEDBACK_OAUTH_SCOPES 重试段]()
- **扩展提示"Pagecast isn't running"**：本地服务未启动，按提示执行 `npx pagecast` 即可。资料来源：[extension/popup.js:状态检查段]()

## 另请参阅

- 发布工作流与代理技能：[plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)
- 浏览器扩展商店上架材料：[extension/store/SUBMIT.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/SUBMIT.md)
- 反馈 Worker 实现：[feedback/worker.js](https://github.com/Amal-David/pagecast/blob/main/feedback/worker.js)
- 面向 LLM/代理的入口说明：[llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)

---

<a id='page-2'></a>

## 核心功能与发布流程

### 相关页面

相关主题：[Pagecast 概览与系统架构](#page-1), [安全模型、Cloudflare 部署与运维](#page-4)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/Amal-David/pagecast/blob/main/README.md)
- [package.json](https://github.com/Amal-David/pagecast/blob/main/package.json)
- [src/server.js](https://github.com/Amal-David/pagecast/blob/main/src/server.js)
- [src/markdown.js](https://github.com/Amal-David/pagecast/blob/main/src/markdown.js)
- [feedback/worker.js](https://github.com/Amal-David/pagecast/blob/main/feedback/worker.js)
- [plugin/README.md](https://github.com/Amal-David/pagecast/blob/main/plugin/README.md)
- [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)
- [extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md)
- [llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)
- [web/src/App.tsx](https://github.com/Amal-David/pagecast/blob/main/web/src/App.tsx)
</details>

# 核心功能与发布流程

## 概述

Pagecast 是一个**本地优先**的发布工具，专注于把本地生成的 HTML 报告、Markdown 文档以及小型静态站点（`dist`/`build`/`out`）一键发布到**用户自己的 Cloudflare Pages 账户**，从而获得可分享的公开链接 资料来源：[README.md:1-15]()。

整个项目以零全局安装的 `npx pagecast` 命令启动，兼具本地管理界面、命令行接口和代理插件三种使用形态，适合人类开发者和 AI 编码代理（Claude Code、Codex、Cursor 等）共同使用 资料来源：[README.md:31-40]()。

## 核心功能模块

### 1. 本地预览与管理界面

启动后 Pagecast 暴露两个本地端口：

| 端点 | 用途 |
|---|---|
| `http://127.0.0.1:4173` | 管理后台（Admin UI） |
| `http://127.0.0.1:4174` | 已发布页面的本地预览（保留 `/p/<slug>/` 真实路径形态） |

所有数据与配置（已发布页面、Cloudflare 凭据、反馈密钥等）统一保存在当前工作目录的 `.pagecast/` 文件夹中，便于迁移和版本控制 资料来源：[README.md:33-40]()。

管理界面基于 React + TypeScript 实现，支持对已发布页面进行**拖拽排序、选择、删除、撤销、重新同步**等操作 资料来源：[web/src/App.tsx:46-78]()。

### 2. 零依赖 Markdown 渲染器

考虑到发布的页面是**公开的**，Pagecast 自带一个零依赖的安全加固 Markdown 渲染器 `src/markdown.js`，主要安全策略包括：

- 文本/代码片段在输出前必须经过 `escapeHtml` 转义三个结构字符 `& < >`
- 属性值额外通过 `escapeAttribute` 转义双引号，防止恶意 URL/alt 文本逃逸属性
- 链接与图片 URL 走 `sanitizeUrl` 严格白名单：`javascript:`、`data:`、`vbscript:`、`file:` 等危险 scheme 一律降级为 `#`

资料来源：[src/markdown.js:8-37]()

### 3. 发布到 Cloudflare Pages

发布核心逻辑封装在 `createCloudflarePagesPublisher` 中。它使用用户本地的 Wrangler 凭据，将 `pages-deploy` 目录推送到用户的 Pages 项目。每个发布页在发布前会自动注入以下元素：

- **反馈/分析 Widget**（当反馈 Worker 已配置时），用于收集浏览量与表情反应
- **"Published with Pagecast" 徽章**（可在白名单场景下关闭）
- **Open Graph / Twitter 社交元数据**，使分享链接展开时呈现富卡片

资料来源：[src/server.js:140-200]()

### 4. 反馈与统计分析

Pagecast 在用户账户下部署一个轻量 Cloudflare Worker（`feedback/worker.js`），用于承载：

- 浏览量（views）
- 表情反应（`👍` `❤️` `🎉` `🚀` `👀` 五个内置白名单）
- 国家（来自 `cf.country`）、引荐来源（仅 host，不含完整 URL）、设备分类（粗粒度）

所有统计以**单页聚合**形式存储在 KV（`stats:<slug>`），KV 不支持原子递增，故统计为尽力而为（best-effort）。隐私上**不存储 IP、Cookie、个人访客记录或 PII** 资料来源：[feedback/worker.js:1-20]()。

### 5. v0.1.6 过期链接（Expiring URLs）

最新版本中，每个发布链接可设置过期窗口（数小时 / 1 天 / 2 天 / 7 天 / 30 天 / Never），默认 **30 天**，支持安装级默认配置和发布级覆盖。过期检查在边缘由 Cloudflare Pages Function 强制执行，**即使用户机器离线链接也会准时失效** 资料来源：[community_context #v0.1.6]()。

## 发布流程

下面是 Pagecast 的一次完整发布流程（从文件落地到获得公开 URL）：

```mermaid
flowchart TD
    A[用户在本地生成<br>HTML/Markdown 文件] --> B{触发方式}
    B -->|CLI| C[npx pagecast publish<br>绝对路径 --json]
    B -->|编码代理| D[$publish-report 技能<br>询问并执行]
    B -->|Chrome 扩展| E[工具栏按钮 / 右键菜单]
    C --> F[本地 Markdown 渲染<br>src/markdown.js]
    D --> F
    E --> F
    F --> G[注入反馈 Widget + 徽章 + OG Meta]
    G --> H[Wrangler 推送至<br>用户 Cloudflare Pages 项目]
    H --> I[返回公开 URL<br>https://*.pages.dev/p/&lt;slug&gt;/]
    I --> J[反馈 Worker 接收<br>浏览/反应信标]
```

关键命令速查 资料来源：[llms.txt:11-23]()：

```sh
# 一次性 Cloudflare 授权
npx pagecast pages setup --project pagecast --json

# 发布 HTML/Markdown 报告
npx pagecast publish "/abs/path/report.html" --json
npx pagecast publish "/abs/path/report.md" --json

# 部署整个静态目录到 Pages 项目
npx pagecast pages deploy "/abs/path/dist" --project pagecasthq --branch main --json
```

## 与社区关注的关联

社区中一个被反复问到的设计问题是：**为什么选择 Cloudflare Pages 而非 Workers + Static Assets？** 这是 Pagecast 当前架构的核心取舍——它依托 Pages Functions 在边缘执行过期检查和密码保护 资料来源：[community_context #Pages-not-Workers]()，因此在 Pages Functions 继续作为边缘执行点的前提下，过期 URL（v0.1.6）等功能得以在用户的 Pages 项目中本地完成而无需引入 D1 或 Durable Objects。

## 常见失败模式

- **`401 未授权`**：运行 `npx pagecast pages setup` 重新连接 Cloudflare 资料来源：[llms.txt:39-41]()
- **`409 多账户冲突`**：通过 `--account <account-id>` 指定账户，或在管理界面中选择 资料来源：[llms.txt:42-43]()
- **首次部署无 workers.dev URL**：需在 Cloudflare 控制台启用 `workers.dev` 子域后再重试 资料来源：[src/server.js:208-220]()
- **反馈权限不足**：Wrangler 报 `code: 10000` 时，Pagecast 会自动升级 OAuth scope 后重试一次 资料来源：[src/server.js:222-236]()

## See Also

- [README.md](https://github.com/Amal-David/pagecast/blob/main/README.md) — 项目总览与快速上手
- [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md) — AI 代理发布技能规范
- [extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md) — Chrome 扩展商店文案
- [llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt) — 给 AI 代理的命令参考

---

<a id='page-3'></a>

## AI 代理集成与浏览器扩展

### 相关页面

相关主题：[Pagecast 概览与系统架构](#page-1), [核心功能与发布流程](#page-2)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [plugin/README.md](https://github.com/Amal-David/pagecast/blob/main/plugin/README.md)
- [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)
- [plugin/hooks/hooks.json](https://github.com/Amal-David/pagecast/blob/main/plugin/hooks/hooks.json)
- [plugin/hooks/detect-report.mjs](https://github.com/Amal-David/pagecast/blob/main/plugin/hooks/detect-report.mjs)
- [plugin/hooks/detect-artifacts-on-stop.mjs](https://github.com/Amal-David/pagecast/blob/main/plugin/hooks/detect-artifacts-on-stop.mjs)
- [.codex/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/.codex/skills/publish-report/SKILL.md)
- [extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md)
- [extension/store/SUBMIT.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/SUBMIT.md)
- [llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)
- [package.json](https://github.com/Amal-David/pagecast/blob/main/package.json)
- [README.md](https://github.com/Amal-David/pagecast/blob/main/README.md)
</details>

# AI 代理集成与浏览器扩展

## 概述与定位

Pagecast 围绕"本地优先 + 一键发布"的设计理念，将本地的 HTML / Markdown 报告、规划文档、静态迷你应用转成可分享的 Cloudflare Pages 公网链接。其面向 AI 代理与终端用户的入口由两条独立但互补的通道构成：AI 代理侧通过 `plugin/` 目录下的 **Skill + Hook 组合**接入 Claude Code、Codex、Cursor 等 Agent Skills 兼容工具；终端用户侧通过 Chrome 浏览器扩展在浏览 `file://` 页面时一键发布。资料来源：[README.md](https://github.com/Amal-David/pagecast/blob/main/README.md) 与 [plugin/README.md](https://github.com/Amal-David/pagecast/blob/main/plugin/README.md)。

项目关键词在 `package.json` 中被显式声明：`codex`、`claude-code`、`ai-agents`、`agent-skill` 等。资料来源：[package.json](https://github.com/Amal-David/pagecast/blob/main/package.json)。`llms.txt` 进一步明确：发布动作**必须**得到用户的明确确认，代理不可静默发布任何文件。资料来源：[llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)。

## AI 代理插件架构

### 核心组件：Skill 与 Hook

Pagecast 的代理集成采用"被动提示 + 主动技能"的双层设计。

- **Skill (`plugin/skills/publish-report/SKILL.md`)**：一份为代理阅读的 Markdown 提示词，描述"何时提议发布"、"如何调用 CLI"以及"如何维护一个目标进度页面"。其 `description` 字段明确指出——代理在生成 HTML / Markdown 报告、规划、仪表盘或构建出静态站点后，应主动提议使用 Pagecast 发布。资料来源：[plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)。
- **Hook (`plugin/hooks/hooks.json` + `detect-report.mjs` + `detect-artifacts-on-stop.mjs`)**：在 Claude Code 中通过 `PostToolUse` 钩子监测文件写入事件；当代理写出 `.html` / `.htm` / `.md` / `.markdown` 文件时，向代理发出一条提示，引导其调用 `publish-report` 技能。资料来源：[plugin/hooks/hooks.json](https://github.com/Amal-David/pagecast/blob/main/plugin/hooks/hooks.json) 与 [plugin/hooks/detect-report.mjs](https://github.com/Amal-David/pagecast/blob/main/plugin/hooks/detect-report.mjs)。

### 工作流（一次性快照 vs 持续目标页）

Skill 中区分了两类典型工作流：

| 场景 | 命令 | URL 行为 |
| --- | --- | --- |
| 单次发布 | `npx pagecast publish "/abs/path/file.md" --json` | 每次生成新 token |
| 持续目标进度 | `npx pagecast goal publish "/abs/path/pagecast-goal.md" --json` | 同 URL 原地更新 |
| 整站部署 | `npx pagecast pages deploy "/abs/path/dist" --project <name> --json` | 替换命名 Pages 项目 |

资料来源：[plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md) 与 [llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)。

### 多平台安装路径

`plugin/README.md` 列出了三种安装方式：Codex CLI/桌面版复制 `.codex/skills/publish-report/` 到 `~/.codex/skills/`；Claude Code 通过 `/plugin marketplace add Amal-David/pagecast` + `/plugin install pagecast@pagecast` 一键安装；其它 Agent Skills 兼容工具复制可移植版本即可。资料来源：[plugin/README.md](https://github.com/Amal-David/pagecast/blob/main/plugin/README.md)。

## 浏览器扩展（Chrome）

### 形态与权限

扩展在 Chrome Web Store 列表中以 "Pagecast — Local to Public" 命名，定位于开发者工具类。资料来源：[extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md)。其触发方式有两种：工具栏按钮点击，或在 `file://` 页面右键菜单选择 "Publish to Pagecast"。资料来源：[extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md) 与 [README.md](https://github.com/Amal-David/pagecast/blob/main/README.md)。

扩展声明的权限是"最小且本地化"的：`activeTab`/`tabs` 用于读取当前标签页 URL 以识别 `file://` 来源；`127.0.0.1`/`localhost` 用于把文件路径发给用户本机上的 Pagecast 服务；`contextMenus` 用于注册右键菜单；`notifications` 用于回显结果或报错。**不收集任何用户数据，远程代码不加载**——文件路径仅到达用户自己的本地回环服务，不会发送到任何远端。资料来源：[extension/store/SUBMIT.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/SUBMIT.md)。

### 联动本地 App

扩展不是独立发布器，它依赖本机运行的 `npx pagecast` 进程：扩展把 `file:///.../report.html` 路径 POST 到本机 `127.0.0.1:4173` 的管理服务，Pagecast App 完成 Cloudflare 鉴权与 Pages 部署，再回传一个 `https://pagecast.pages.dev/p/<token>/` 链接给扩展。资料来源：[extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md) 与 [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)。同一文件重复发布时链接原地更新，旧链接可通过 `npx pagecast` 的 revoke 控制失效。

## 数据流总览

```mermaid
flowchart LR
  A[AI 代理<br/>写入 .html / .md] -->|PostToolUse Hook| B(Skill: publish-report)
  B -->|用户确认 Yes| C[npx pagecast publish --json]
  A2[浏览器扩展<br/>file:// 页面] -->|POST 本机 4173| C
  C --> D[(本地 .pagecast/ 数据目录)]
  D --> E[Cloudflare Pages 部署]
  E --> F[公网 pagecast.pages.dev 链接]
  F -->|反馈/分析 widget| G[Cloudflare Worker + KV]
```

资料来源：[plugin/README.md](https://github.com/Amal-David/pagecast/blob/main/plugin/README.md)、[extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md) 与 [feedback/worker.js](https://github.com/Amal-David/pagecast/blob/main/feedback/worker.js)。

## 社区反馈与使用注意

社区中曾有用户提问"为什么选 Pages 而不是 Workers + Static Assets"——这是当前架构选型的明确信号：所有公开页面托管在 **Cloudflare Pages** 上，过期与密码保护由 Pages Function 在边缘强制执行（v0.1.6 的 Expiring URLs 即基于此），无需常驻机器即可让链接按计划失效。资料来源：[plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)（边缘执行说明）。

使用时的关键约束：

- **必须显式确认**：代理和扩展都不得在用户未同意的情况下发布任何文件。资料来源：[llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt) 与 [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)。
- **必须使用绝对路径**：`npx pagecast publish` 要求绝对路径，相对路径需先基于 cwd 解析。资料来源：[plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)。
- **错误码含义**：`401` 提示未完成 Cloudflare 登录；`409` 表示存在多个 Cloudflare 账号，需通过 `--account` 显式选择。资料来源：[llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)。
- **不要混用发布语义**：`publish <file>` 产出新的 `/p/<token>/` 分享链接；`publish site` / `pages deploy` 直接替换整个命名 Pages 项目的内容。资料来源：[llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt) 与 [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)。
- **Chrome 扩展需开启"Allow access to file URLs"**，否则无法读取本地文件页面。资料来源：[extension/store/listing.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/listing.md)。

## See Also

- 核心数据流与 Pages 部署：[README.md](https://github.com/Amal-David/pagecast/blob/main/README.md)
- Agent 命令总览与错误码：[llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)
- 本地 Markdown 渲染与安全策略：[src/markdown.js](https://github.com/Amal-David/pagecast/blob/main/src/markdown.js)
- 反馈与分析 Worker：[feedback/worker.js](https://github.com/Amal-David/pagecast/blob/main/feedback/worker.js)
- 管理服务与发布管线：[src/server.js](https://github.com/Amal-David/pagecast/blob/main/src/server.js)

---

<a id='page-4'></a>

## 安全模型、Cloudflare 部署与运维

### 相关页面

相关主题：[Pagecast 概览与系统架构](#page-1), [核心功能与发布流程](#page-2), [AI 代理集成与浏览器扩展](#page-3)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [SECURITY.md](https://github.com/Amal-David/pagecast/blob/main/SECURITY.md)
- [src/crypto.js](https://github.com/Amal-David/pagecast/blob/main/src/crypto.js)
- [src/markdown.js](https://github.com/Amal-David/pagecast/blob/main/src/markdown.js)
- [src/server.js](https://github.com/Amal-David/pagecast/blob/main/src/server.js)
- [src/cli.js](https://github.com/Amal-David/pagecast/blob/main/src/cli.js)
- [feedback/worker.js](https://github.com/Amal-David/pagecast/blob/main/feedback/worker.js)
- [llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)
- [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md)
- [extension/store/SUBMIT.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/SUBMIT.md)
- [README.md](https://github.com/Amal-David/pagecast/blob/main/README.md)
- [package.json](https://github.com/Amal-David/pagecast/blob/main/package.json)
</details>

# 安全模型、Cloudflare 部署与运维

## 1. 概述与架构选型：为什么选择 Cloudflare Pages 而非 Workers

Pagecast 是一款本地优先的发布工具，其设计目标是把本地 HTML/Markdown 文件转换为可分享的公共链接。围绕该目标，它将部署目标选定在 Cloudflare Pages 而不是 Workers + Static Assets。这一点由项目维护者在社区中被多次问及 ([README.md](https://github.com/Amal-David/pagecast/blob/main/README.md))。

选择 Pages 的关键原因与 Pages Functions 的能力直接相关：Pages Functions 可以和静态资源共存，并通过 `_routes.json` 把中间件限制在特定前缀下。这正是密码保护、过期链接等"边缘执行"特性的工程前提。资料来源：[src/crypto.js](https://github.com/Amal-David/pagecast/blob/main/src/crypto.js) 中 `renderAuthMiddleware` 与 `renderRoutesJson` 的职责划分即体现了这一点——前者生成 Pages Function 源，后者生成仅作用于 `/p/<slug>/` 前缀的路由白名单，使得未受保护的站点保持纯静态。

下表总结了 Pagecast 与 Cloudflare 资源模型的对应关系：

| 组件 | Cloudflare 资源 | 作用 |
|---|---|---|
| 已发布页面 | Pages Project 静态资源 | 承载 `/p/<token>/` 路径的 HTML/Markdown 内容 |
| 密码/过期中间件 | Pages Function (`functions/_middleware.js`) | 在边缘拦截访问，进行认证与过期判断 |
| 反馈统计 | Worker + KV (`PAGECAST_FEEDBACK`) | 聚合浏览量、表情反应与粗粒度访问者特征 |
| 部署触发 | 本地 CLI shell 调用 Wrangler | 复用官方部署 CLI，避免重复实现上传逻辑 |

## 2. 安全模型：边缘强制的密码保护与过期链接

Pagecast 的核心安全边界是"内容部署为明文，仅在边缘进行访问控制"。具体实现细节如下：

- **PBKDF2 哈希**：Node 端使用 `node:crypto` 的 `pbkdf2Sync` 计算口令哈希，迭代次数为 `PBKDF2_ITERATIONS = 100000`，摘要算法 `sha256`，盐长 16 字节，导出密钥长 32 字节。资料来源：[src/crypto.js](https://github.com/Amal-David/pagecast/blob/main/src/crypto.js)。该参数刻意低于公开下载场景常用的 600k，因为哈希永远不会被作为静态资源暴露给攻击者，拉伸属于纵深防御而非主防线。
- **跨运行时一致性**：Node 端使用 WebCrypto PBKDF2 重新计算并比较口令，确保与 Functions 中 WebCrypto 的结果一致。资料来源：[src/crypto.js](https://github.com/Amal-David/pagecast/blob/main/src/crypto.js) 中关于 cross-runtime parity 的注释，并被 `test/crypto.test.js` 验证。
- **常时比较**：使用 `timingSafeEqual` 避免哈希比较时的侧信道。
- **过期链接（v0.1.6）**：链接可设为小时/1d/2d/7d/30d 或 `Never`，默认 30 天，可在安装级别配置并在每次发布时覆盖。过期检查在边缘由同一个 Pages Function 执行，因此即便用户机器离线，链接也会按时失效。资料来源：[SECURITY.md](https://github.com/Amal-David/pagecast/blob/main/SECURITY.md) 与 [src/crypto.js](https://github.com/Amal-David/pagecast/blob/main/src/crypto.js) 中描述的同一中间件。
- **Markdown 渲染安全**：发布页是公开的，渲染器从不透传原始 HTML——所有文本/代码段在发出前都会被转义；链接/图片 URL 经过 `sanitizeUrl` 校验，仅放行允许的 scheme（如相对路径、片段、查询），其他 scheme（`javascript:`、`data:`、`vbscript:`、`file:` 等）会被中和为 `#`。资料来源：[src/markdown.js](https://github.com/Amal-David/pagecast/blob/main/src/markdown.js) 的 `escapeHtml`/`escapeAttribute`/`sanitizeUrl`。

## 3. Cloudflare 部署流程

部署由本地 CLI 触发，最终通过 `wrangler pages deploy` 完成。Pagecast 在 `src/server.js` 的 `createCloudflarePagesPublisher` 中封装了站点目录组装、`pages-site` 与 `pages-deploy` 的工作树管理，以及超时（默认 180 秒）的 `spawn` 调用。资料来源：[src/server.js](https://github.com/Amal-David/pagecast/blob/main/src/server.js)。

CLI 层面提供两条发布路径（[src/cli.js](https://github.com/Amal-David/pagecast/blob/main/src/cli.js)）：

- `publish <file>` —— 为单文件生成新的 `/p/<token>/` 分享链接。
- `pages deploy <dir> --project <name>` —— 将整个静态目录部署到指定 Pages 项目；省略 `--branch` 时默认 `main`。直接站点部署会替换目标项目的内容。
- `pages setup` —— 一次性连接 Cloudflare 并准备项目。
- `pages status` / `pages projects list` —— 查询项目状态与列表。

```mermaid
flowchart LR
  A[本地 HTML/Markdown] --> B[pagecast publish]
  B --> C{需要保护?}
  C -- 是 --> D[生成 _middleware.js]
  C -- 否 --> E[纯静态产物]
  D --> F[pages-site/p/&lt;slug&gt;/]
  E --> F
  F --> G[wrangler pages deploy]
  G --> H[Cloudflare Pages]
  H --> I{边缘拦截}
  I -- 密码正确 / 未过期 --> J[返回内容]
  I -- 失败 --> K[401 挑战]
```

## 4. 反馈 Worker 与隐私

反馈功能由一个独立部署的小型 Worker 提供支持（[feedback/worker.js](https://github.com/Amal-David/pagecast/blob/main/feedback/worker.js)）。其设计要点：

- **存储**：使用单个 JSON 聚合对象，按 slug 写入 KV（绑定 `PAGECAST_FEEDBACK`，键 `stats:<slug>`）。读写采用 `get → mutate → put` 模式。KV 不支持原子自增，因此在强并发下计数为尽力而为（best-effort），这对于浏览/反应统计是可接受的权衡，避免了引入 D1 的成本与复杂度。
- **反应白名单**：`REACTIONS = ["👍", "❤️", "🎉", "🚀", "👀"]`，任何超出白名单的内容会被忽略，防止攻击者写入任意字符串。
- **隐私边界**：仅存储粗粒度聚合信号——国家（来自 `request.cf.country`）、来源 HOST（非完整 URL）、设备类别（来自 UA）。无 IP、无 Cookie、无单访客记录、无 PII。

## 5. 扩展权限模型与运维

Chrome 扩展通过本地回环与运行中的 Pagecast 服务器通信（[extension/store/SUBMIT.md](https://github.com/Amal-David/pagecast/blob/main/extension/store/SUBMIT.md)）。其权限被刻意收窄：`activeTab`/`tabs` 用于检测可发布的 `file://` 页面；`contextMenus` 用于右键入口；`notifications` 用于展示结果；主机权限 `127.0.0.1`/`localhost` 用于发送文件路径。扩展声明不收集或使用任何用户数据，所有 JS 均随包发布，无远程代码。

运维方面，`llms.txt` 总结了关键错误码的处理范式（[llms.txt](https://github.com/Amal-David/pagecast/blob/main/llms.txt)）：

- `401`：未连接 Cloudflare，需执行 `npx pagecast pages setup`，或在应用中点击 Connect Cloudflare。
- `409`：存在多个 Cloudflare 账号，可通过 `--account <account-id>` 指定或在应用中选择。

`SECURITY.md` 列出的硬化路线图包括：私密/口令/过期链接、`feedback destroy` 拆解命令、API Token 鉴权（替代宽 OAuth）、Wrangler 与 npm 的固定/出处校验。资料来源：[SECURITY.md](https://github.com/Amal-David/pagecast/blob/main/SECURITY.md)。这与 v0.1.6 已落地的"过期链接"演进方向一致——每一项硬化都把更多控制权从信任用户操作转向由边缘强制。

## See Also

- [README.md](https://github.com/Amal-David/pagecast/blob/main/README.md) — 项目总览与快速开始
- [plugin/skills/publish-report/SKILL.md](https://github.com/Amal-David/pagecast/blob/main/plugin/skills/publish-report/SKILL.md) — 编码代理发布技能
- [SECURITY.md](https://github.com/Amal-David/pagecast/blob/main/SECURITY.md) — 安全策略与硬化路线图

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Doramagic 踩坑日志

项目：amal-david/pagecast

摘要：发现 8 个潜在踩坑项，其中 1 个为 high/blocking；最高优先级：安装坑 - 来源证据：Pages not Workers?。

## 1. 安装坑 · 来源证据：Pages not Workers?

- 严重度：high
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个安装相关的待验证问题：Pages not Workers?
- 对用户的影响：可能增加新用户试用和生产接入成本。
- 证据：community_evidence:github | https://github.com/Amal-David/pagecast/issues/1 | 来源类型 github_issue 暴露的待验证使用条件。

## 2. 配置坑 · 可能修改宿主 AI 配置

- 严重度：medium
- 证据强度：source_linked
- 发现：项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主，或安装命令涉及用户配置目录。
- 对用户的影响：安装可能改变本机 AI 工具行为，用户需要知道写入位置和回滚方法。
- 证据：capability.host_targets | https://news.ycombinator.com/item?id=48590505 | host_targets=claude_code, claude

## 3. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 证据：capability.assumptions | https://news.ycombinator.com/item?id=48590505 | README/documentation is current enough for a first validation pass.

## 4. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 证据：evidence.maintainer_signals | https://news.ycombinator.com/item?id=48590505 | last_activity_observed missing

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 证据：downstream_validation.risk_items | https://news.ycombinator.com/item?id=48590505 | no_demo; severity=medium

## 6. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 证据：risks.scoring_risks | https://news.ycombinator.com/item?id=48590505 | no_demo; severity=medium

## 7. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 证据：evidence.maintainer_signals | https://news.ycombinator.com/item?id=48590505 | issue_or_pr_quality=unknown

## 8. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 证据：evidence.maintainer_signals | https://news.ycombinator.com/item?id=48590505 | release_recency=unknown

<!-- canonical_name: amal-david/pagecast; human_manual_source: deepwiki_human_wiki -->
