@botiverse/porthole

中文文档
0.14.15发布于 2026/11/08Apache-2.0TypeScriptES Module零依赖构建来源已验证近 30 天下载 2.3万

A host-side sandbox for agent-made HTML: isolation, egress hardening, a themed shell, gated host functions, and the agent manual, from one config.

原始使用文档
文档目录8 节
  1. Two ways to show content
  2. Try it in two minutes
  3. The smallest opaque display
  4. Pages made of many files
  5. A page that is an app: the capability runtime
  6. What the agent gets
  7. Read next
  8. Subpaths

Porthole

@botiverse/porthole: show HTML an agent wrote (a widget, a whole page, an MCP Apps view) inside your app, safely. One config gives the frame its isolation, its CSP, a themed shell, the host functions it may call (each behind a gate), and the manual your agent reads.

npm install @botiverse/porthole

Node 24 or later, or Cloudflare Workers. The browser part is plain ES modules with no dependencies (bundle it, or serve dist/ with an import map: docs/ADOPTING.md § 4).

Two ways to show content

  • Opaque, in the page. No server needed: content runs in a sandboxed frame with an opaque origin. For widgets in a conversation.
  • Served, on an origin of its own. Each page, or each conversation's widgets, gets an origin like a-<id>.example-usercontent.com, so it can keep storage, open at a clean address in a new tab, and be framed by your app only. The host kit (createContentHost) is the server side; your app writes only its policy: which pages exist and who may see them.

Try it in two minutes

The package ships a whole app, the same code on Node and on Workers:

PORTHOLE_SECRET=$(npx porthole secret) node node_modules/@botiverse/porthole/examples/node/server.mjs
# open http://localhost:8790: a stored page, served on http://a-hello.content.localhost:8790
  • examples/app.mjs: the app (policy, routes, page), runtime-agnostic.
  • examples/node/server.mjs (nodeServer), examples/workers/ (contentHostFor, with its D1 migration and wrangler.jsonc).
  • examples/conformance/: Porthole's conformance suite run against it, on three engines.
  • examples/mcp/server.mjs: the agent's tools (show, guide, check) as an MCP server.

The smallest opaque display

import { createHost, presets } from "@botiverse/porthole";

const host = createHost(presets.inlineWidget());
const display = host.mount(document.getElementById("widget"), {
  displayId: "w1",
  title: "A chart",
  scheme: "light",
});
display.setContent("<p>Hello from an agent</p>");

In React: <PortholeProvider> around <PortholeFrame config displayId title scheme content /> from @botiverse/porthole/react.

Pages made of many files

A served page may come with files of its own: a build's output, a report with its images, a dashboard that reads its data (0.6). The store answers a manifest and the bytes by path, and the page's relative addresses are its own version's:

source: async (id, version) => ({
  files: { "index.html": { size: 397, sha256: "…" }, "assets/app.js": { size: 142870, sha256: "…" } },
  read: (path) => myStore.bytes(id, version, path),        // Uint8Array or a stream, whole
}),
entry: {
  // "/v/3/<file>" is version 3, any other path the latest's: answer with the version's own address.
  at: async (scope, path) => {
    const at = versionedAddress(path), version = at?.version ?? (await latestOf(scope));
    return at && { instance: pageOf(scope, version), base: versionBase(version), file: at.file, pinned: true };
  },
},

Each file is served from the manifest only, typed by Porthole's own table, nosniff, with a CSP that opens that version's path and nothing else of its origin; checkFileSet checks a version's paths and limits when it is published, lintFiles and checkPage({ files }) are the agent's side. docs/ADOPTING.md § Pages made of many files.

A page that is an app: the capability runtime

With runtime: "1" in its config (0.7), a page reaches its host through one global, for what it declared when it was published and what its viewer's level allows:

const db = await porthole.use("db");                 // a frozen namespace, or null: draw without it
const votes = db && (await db.query("votes")).docs;  // a refusal rejects with error.code and a sentence

The host keeps the services: its handlers (db, artifact, assets, comments; user is the viewer it gives), declareCapabilities at publish, the viewer's level in its access (Grant.level), and an asset store /server serves at /_blob/<id>. defineRuntimeConformance checks the handlers. The types: types/runtime/v1/*.d.ts. docs/ADOPTING.md § The capability runtime.

What the agent gets

import { admitContent, checkTool, guideTool, showTool } from "@botiverse/porthole";

const tools = [showTool(config), guideTool(config), checkTool(config)]; // name, description, JSON Schema
const admitted = admitContent(config, html); // refuses only what certainly fails, saying what to change

checkPage (@botiverse/porthole/check) runs a page in the real sandbox at phone and desktop width, light and dark, before anyone sees it, and tells the agent what broke in a few lines. examples/mcp/server.mjs wires the tools as an MCP server.

  • docs/ADOPTING.md: getting started, step by step: the server, the page, its CSP, Workers, local development, the agent's tools, conformance, troubleshooting.
  • Choosing egress: what content may load (closed, library CDNs, passive media from any https site), what each level opens and the risk it re-opens.
  • docs/THREAT_MODEL.md: what Porthole protects and from whom; for each use, what it guarantees, what is left to you, and what isn't covered.
  • docs/DESIGN.md: how it works. docs/DECISIONS.md: why, by P-number (the code cites them).

Subpaths

ImportWhat
@botiverse/portholecreateHost, presets, defineDisplays, portholeClient, lintContent, the tools (browser and server)
@botiverse/porthole/reactPortholeProvider, PortholeFrame, PortholeView
@botiverse/porthole/servercreateContentHost (routes), contentHostFor, prefixedName, d1TicketStore (any runtime)
@botiverse/porthole/nodenodeServer, nodeHost, nodeListener, nodeProbe (Node's http server)
@botiverse/porthole/mcpthe MCP Apps adapter
@botiverse/porthole/checkcheckPage, playwrightDriver, cdpDriver: the agent's pre-show check
@botiverse/porthole/conformancedefineHostConformance: the suite, against your app (Playwright)

npx porthole secret makes a secret; npx porthole d1-schema prints the D1 migration.

Apache-2.0. /server also serves copies of Chart.js 4.5.1 (MIT) and D3 7.9.0 (ISC) to live widgets (P079), each with its license beside it (/__porthole/lib/<name>@<version>/), from src/libraries/files.generated.ts; their notices, and those of the packages their files bundle, are in THIRD_PARTY_NOTICES.md.

Found a vulnerability? Report it privately, as SECURITY.md says, never in a public issue.

版本与发布记录

@botiverse/porthole 共发布 49 个版本,首次发布于 2026/10/11;过去 12 个月发布了 49 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.14.15latest2026/11/08今天
0.14.142026/11/071 天前
0.14.132026/11/071 天前
0.14.122026/11/062 天前
0.14.112026/11/062 天前
0.14.102026/11/062 天前

关于 @botiverse/porthole 的常见问题

@botiverse/porthole 是什么?

A host-side sandbox for agent-made HTML: isolation, egress hardening, a themed shell, gated host functions, and the agent manual, from one config.

如何安装 @botiverse/porthole?

在项目目录运行 npm install @botiverse/porthole,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 porthole,可以用 npx porthole 直接运行。

@botiverse/porthole 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@botiverse/porthole 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@botiverse/porthole 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@botiverse/porthole 可以商用吗?

@botiverse/porthole 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@botiverse/porthole 的下载量有多少?

根据 npm 官方下载统计,@botiverse/porthole 在 2026/10/09 – 2026/11/07 这 30 天内被下载 22,648 次,日均约 755 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@botiverse/porthole 还在维护吗?

最新版本 0.14.15 发布于 2026/11/08(今天);至今共发布 49 个版本,其中过去 12 个月发布了 49 个;首次发布于 2026/10/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。