内容
# hart — 代理优先的工件托管
> 将自包含的 HTML 或 JSX 发布到实时、可共享的 URL — 从**任何**终端代理,在**您的**基础设施中,通过一个 CLI 调用。Claude Artifacts,解耦且通用。
`hart`(HTML ARTifacts)是一个**代理优先、仅限 CLI、开源、自托管**的工件托管平台,构建为一个静态的 **MFL / [machin](https://github.com/javimosch/machin)** 二进制文件 — 它既是客户端(`hart publish …`),又是托管守护进程(`hart serve`)。它是 grepapi 的 *show*,bland-cli 的 *call*,和 crm-cli 的 *remember*。
> **实时演示实例:** **[hart.intrane.fr](https://hart.intrane.fr)** (免费/开放,速率限制)。
> **预构建二进制文件:** [Releases](https://github.com/javimosch/machin-hart/releases)。
> **🤖 适用于代理:** 阅读 **[`llms.txt`](llms.txt)** 以获取 30 秒的概述,或运行 **`hart guide`** (也在任何实例的 `/llms.txt` 和 `/guide.md` 处提供)以获取完整的、版本精确的手册。要使代理在此处**默认**发布,请自行安装插件:**`hart skill > ~/.claude/skills/hart/SKILL.md`** (也在 `/skill.md` 处提供)。
---
## 为什么
终端代理(Claude Code、Cursor、aider、Codex CLI、Gemini CLI、自定义 SDK 代理、cron 作业)生成丰富的 HTML 可交付成果 — 仪表板、报告、通话表、模型 — 但**没有地方可放**。`open` 在共享时无效;gist 会破坏样式;手动建立托管不是代理可操作的。Claude 在 *claude.ai* 内解决了这个问题;其他人则陷入困境。`hart` 是缺失的基元 — 适用于每个代理,在您自己的机器上:
```
$ hart publish call-budget.html --owner acme --artifact call-budget
{"ok":true,"id":"acme/call-budget","url":"https://hart.intrane.fr/a/acme/call-budget","version":1,"visibility":"unlisted"}
```
任何代理调用它,解析 JSON,并将 URL 交给用户。无需 UI,无需构建步骤,无需锁定。
## 使用现有实例(最快)
在一步中安装 CLI — 它将 `hart` 放在您的 PATH 上,已经指向该实例:
```sh
curl -fsSL https://hart.intrane.fr/install.sh | sh
```
然后发布:
```sh
export HART_URL=https://hart.intrane.fr
hart publish page.html --owner you --artifact my-page
# → {"url":"https://hart.intrane.fr/a/you/my-page", ...}
```
没有 `hart` 二进制文件?每次写入都只是一个 HTTP POST — 使用 `curl`:
```sh
curl -X POST 'https://hart.intrane.fr/v1/publish?owner=you&artifact=my-page' \
-H 'content-type: text/html' --data-binary @page.html
```
## 自托管
```sh
# 1. 获取二进制文件 — 从 Release 中,或构建它(需要 `machin` 在 PATH 上)
./runtime/fetch.sh # 播种 JSX 运行时(react/react-dom/babel)
./build.sh # → ./hart (供应商框架/machweb.src)
./test.sh # 可选:38-check 端到端回归套件(启动一个临时守护进程)
# 2. 运行守护进程(SQLite 存储,您的机器)。免费/开放默认。
HART_DB=~/.hart.db HART_RUNTIME_DIR=./runtime HART_LANDING=./landing.html ./hart serve 8799 &
# 3. 发布 — 所有者 + 工件 = 一个稳定、易读的 URL
export HART_URL=http://localhost:8799
hart publish report.html --owner alice --artifact q3
# → {"id":"alice/q3","url":"http://localhost:8799/a/alice/q3","version":1}
hart publish report.html --owner alice --artifact q3 # 重新发布 → v2,最新版本移动
```
使用 `HART_TOKEN=<secret>` 保护发布。使用反向代理(例如 Traefik + Cloudflare + Let's Encrypt via [hotify-cli](https://github.com/javimosch/hotify-cli))公开,并设置 `HART_PUBLIC=https://your.domain` 以使返回的 URL 规范化。
将更新部署到 systemd 主机?[`scripts/deploy.sh`](scripts/deploy.sh) 阶段构建的二进制文件,验证传输,备份 + 交换 + 重启,健康检查,并**轮换旧备份**(保留最新的 N 个二进制文件/数据库快照)。`./scripts/deploy.sh --build --db-backup`(通过 `HART_DEPLOY_HOST` / `HART_KEEP_BINARY_BACKUPS` / … 配置;`--dry-run` 预览)。
## 发布合约
`hart` 托管**自包含**页面 — 一个文件,所有内容内联。守护进程将您的主体包装在 `<!doctype>…<head>` 骨架中(标题,视口,CSP),并在严格的沙盒**内容安全策略**下提供服务:
- ✅ 内联 `<style>` / `<script>`,`data:` URI 图片/字体,自包含页面
- ❌ 外部 `src`/`href`(CDN 脚本,远程样式表,网页字体,远程图片)
- ❌ `fetch` / `XHR` / WebSocket 到任何主机(无 `connect-src` → `default-src 'none'`)
与 Claude 自己的工件相同的信封 — 它渲染页面,同时将其作为攻击或渗出向量进行中和。**发布时校验器**拒绝外部引用 + 网络调用(HTTP 422),除非您传递 `--force`;`hart publish --dry-run` 校验而不存储。完整规范:[`CONTRACT.md`](CONTRACT.md)。
## 版本控制
重新发布相同的 `--owner/--artifact` 会追加一个版本;`latest` 跟踪最新版本,旧版本永久固定。
- `/a/<owner>/<artifact>` 或 `…/latest` → 最新版本
- `/a/<owner>/<artifact>/v<n>` → 固定版本
- `hart rollback <id> <v>` 重新指向最新版本(非破坏性)
## 模板 + 数据(无需重新上传)
发布一个**模板**,然后推送**数据** — 它会重新渲染。两种机制:`{{key}}` 占位符在标记中,和 `window.HART_DATA` (您的脚本/JSX 读取的 JS 全局变量)。
```sh
hart publish chart.html --owner you --artifact sales
hart data you/sales '{"points":[3,1,4,1,5]}' # 重新渲染,相同 URL — 适用于代理驱动的仪表板
```
## JSX
`hart publish app.jsx --format jsx` — 编写 React/JSX;守护进程提供一个**同源** React+Babel 运行时 (`/_hart/runtime/*`) 并在**浏览器中**编译。无需构建步骤,无需 CDN。`React`/`ReactDOM` 是全局变量;渲染到 `#root`。
## 可见性和发现
- **未列出** *(默认)* — 公共读取,不在任何地方列出
- **公共** — 公共读取 + 在 **`/explore`** (全局)和 **`/o/<owner>`** (每个所有者)中列出/可搜索;`hart explore [query]` 是 JSON 源
- **私有** — **门控读取**:浏览器获取密码**解锁页面**(→ 签名 cookie);代理发送 **`X-Hart-Read-Key`** 头部(`HART_READ_KEY`)
在发布时使用 `--visibility` / `--read-key` 设置,或使用 `hart visibility <id> <mode>` (无新版本)更改。
## 所有权(写入保护,即使在免费实例上)
对新 `--owner` 的第一次写入会声明它。传递 `--owner-key <secret>` (或 `HART_OWNER_KEY`)来声明命名空间;然后所有对此所有者的写入都需要密钥(否则 `403`)。匿名(无所有者)工件会获得一个随机 ID。免费创建新内容;您的命名空间始终属于您。
## 命令表面(代理优先)
每个命令在**stdout**上打印 **JSON**,在 **stderr**上构造错误,并使用**语义退出代码** (`0` ok · `80–89` 输入 · `90–99` 资源 · `100–109` 集成 · `110–119` 内部)。非交互且幂等。`hart guide` 打印完整的手册。
| 命令 | 执行 |
|---|---|
| `publish <file> [--owner --artifact --title --format html\|jsx --visibility --read-key --unguessable --dry-run --force]` | 上传 → `{id,url,version}` |
| `data <id> '<json>'` | 更新实时数据 — 模板重新渲染 |
| `visibility <id> <unlisted\|public\|private> [--read-key --clear-read-key]` | 更改可见性 |
| `versions <id>` / `rollback <id> <v>` | 历史记录 / 即时回滚 |
| `list [--owner <who>]` / `get <id>` / `rm <id>` | 管理工件 |
| `stats <id>` | 实时交付分析 — 视图,最后一次查看,新鲜度,顶部引用者(服务端,CSP 安全) |
| `fresh <id> <30s\|15m\|2h\|1d\|off>` / `stale [--owner <who>] [--older-than <dur>]` | 新鲜度 SLA + 陈旧信号(JSON;您的代理提醒) |
| `explore [query]` | 公共发现源(JSON) |
| `admin owners` / `admin list [--owner <who>]` | 操作员跨所有者可见性(需要 `HART_ADMIN_TOKEN`) |
| `serve [port]` | 运行托管守护进程 |
| `mcp` | 运行为 stdio MCP 服务器(适用于 MCP 能力的代理的原生工具) |
| `login <token>` / `guide` / `skill` | 存储凭证 / 打印手册 / 打印插入式代理 SKILL.md |
### MCP(适用于 MCP 能力的代理的原生工具)
除了 CLI + 插入式技能外,hart 还可以作为 **stdio MCP 服务器** 运行,以便 MCP 原生代理(Claude Desktop、Cursor、…)获得 hart 作为一流工具 — 一个二进制文件,无需额外运行时。它包装了 hart 自己的 HTTP API,并继承了 env 中的身份验证。配置您的 MCP 客户端:
```json
{"mcpServers":{"hart":{"command":"hart","args":["mcp"],"env":{"HART_URL":"https://hart.intrane.fr"}}}}
```
工具:`hart_publish`, `hart_data`, `hart_list`, `hart_get`, `hart_stats`, `hart_stale`, `hart_explore`. CLI 仍然是主要接口;MCP 是一个额外的接口。
## 管理员 — 操作员可见性(您托管的实例)
运行其他人也使用的实例,但想要审计**您的**代理/操作员在**您的**机器上生成的每个工件?在守护进程上设置 `HART_ADMIN_TOKEN` — 一个**独立**于 `HART_TOKEN` 的超级令牌(发布令牌从不授予管理员权限)。然后,从任何操作员机器:
```sh
export HART_ADMIN_TOKEN=<secret> # 或:hart admin login <secret>
hart admin owners # → [{owner, artifacts, bytes, has_owner_key, updated}, …]
hart admin list [--owner <who>] # → [{id, owner, url, visibility, has_read_key, version, updated}, …]
hart admin mv <old-id> <new-owner/new-name> # 移动/重命名就地 — 保留版本历史 + 读取密钥
```
`admin mv` 重命名一个工件及其**所有版本**到新的所有者/名称 — 历史记录,可见性,实时数据和读取密钥都保留(无需重新上传);旧 URL 404。例如 `hart admin mv am/fleet-monitor intrane/am-fleet-monitor`.
所有者和读取密钥存储为**哈希**,因此管理员界面显示 `has_owner_key` / `has_read_key` 布尔值 — 而不是秘密(要进入私有工件,请使用 `hart visibility <id> private --read-key <new>` 重置其密钥)。当 `HART_ADMIN_TOKEN` 未设置时,管理员 API **关闭** (`403`),在这种多租户实例上,跨所有者的 `hart list` (无 `--owner`)是管理员专属的 — 作用域 `list --owner X` 不受影响。 这是您的机器和您的 SQLite 文件;这只是通过 CLI 公开访问权限。
**环境变量** — 客户端:`HART_URL`, `HART_TOKEN` (发布令牌,如果实例需要),`HART_OWNER_KEY` (命名空间写入密钥),`HART_READ_KEY` (读取私有工件),`HART_ADMIN_TOKEN` (操作员管理员 API — 跨所有者列表;与 `HART_TOKEN` 分开)。服务器:`HART_DB`, `HART_RUNTIME_DIR`, `HART_PUBLIC`, `HART_LANDING`, `HART_MAX_SUBMITS_PER_MIN` (10),`HART_MAX_OWNER_MB` (30),`HART_EXPLORE=0`, `HART_COOKIE_SECRET`.
## 架构
```
┌──────────────┐ 发布/数据/可见性 (HTTP) ┌──────────────────────────────┐
│ 任意代理 │ ─────────────────────────────────► │ hart serve (一个 MFL 二进制文件)│
│ hart <cmd> │ ◄───────── {id,url,version} ───── │ ─ machweb HTTP 服务器 │
└──────────────┘ │ ─ SQLite 存储(工件, │
│ 版本,所有者) │
浏览器 ── GET ─► /a/<owner>/<artifact> ─────────►│ ─ doctype-wrap + 严格 CSP │
(默认拒绝 CSP;私有 = 解锁页面) │ ─ 同源 JSX 运行时 │
└──────────────────────────────┘
```
- **一个二进制文件,两个角色。** `hart <cmd>` 是客户端;`hart serve` 是守护进程(相同的 `machin-backend` 惯例:`machweb` 路由器,`sqlite` 存储,签名 cookie)。
- **存储是一个单独的 SQLite 文件** (`HART_DB`) — 零依赖,一个文件备份。
- **沙盒。** 每个工件都在一个默认拒绝 CSP 下提供服务;一个恶意页面无法访问网络或外部主机。签名 cookie 保护私有工件。
## 构建和验证
```sh
machin encode framework/machweb.src src/hart.src > build/hart.mfl
machin build build/hart.mfl -o hart
```
`machin encode` 运行类型检查器(大多数错误在没有 C 编译器的情况下出现)。通过发布一个夹具并 `curl` 工件 URL 验证:它必须渲染,并且对任何主机的内联 `fetch()` 调用必须被 CSP 阻止。
## 定位
**代理优先的工件托管。** 不是 Netlify(人类优先的 PaaS),不是 Pastebin(无渲染,无安全性) — **代理时代的发布基元**。任何终端代理,任何基础设施,从 HTML 到 URL 的 CLI 调用。参见 [VISION.md](VISION.md) 和正在进行的 [ROADMAP.md](ROADMAP.md).
## 状态和许可证
**实时和已发货:** 发布(HTML/JSX)· 版本控制 + 回滚 · CSP 沙盒 + 校验器 · 模板 + 数据 · 可见性(未列出/公共/私有,门控读取)· 发现(`/explore`, `/o/<owner>`)· 所有者声明密钥 · 速率限制 · `hart guide` / `/llms.txt`。开源,自托管 — 不是托管服务。许可证:待定于第一个标记版本(MIT/Apache-2.0)。