返回 2026-06-20
🛠 工具 / 开源

Datasette Apps:在 Datasette 内托管自定义 HTML 应用Datasette Apps: Host custom HTML applications inside Datasette

simonwillison.net·2026-06-18

Datasette 推出了全新的插件 `datasette-apps`,允许开发者在 Datasette 环境中直接托管独立的 HTML+JavaScript 应用程序。这些应用在高度受限的安全沙箱内运行,确保了数据处理的隐私与安全。该方案提供了一种轻量级的方式来扩展 Datasette 的功能并构建定制化的交互界面。作者认为,这极大地降低了基于已有数据快速构建内部工具或前端面板的门槛。

Simon Willison

2026年6月18日

今天我们为 Datasette 发布了一个新插件 datasette-apps,并在 Datasette 项目博客上发布了这篇发布公告。那篇帖子介绍了“是什么”,但在这里我将稍微展开谈谈“为什么”。

太长不看(TL;DR)

Datasette Apps 是自包含的 HTML+JavaScript 应用程序,它们运行在托管于你 Datasette 应用中受到严格限制的 <iframe> 沙箱里。它们可以使用 JavaScript 对 Datasette 中的数据执行只读 SQL 查询,如果你为它们配置了一些存储查询,也可以执行写入查询。

这里有一个非常简单的例子,还有一个更复杂的自定义时间线示例——后者看起来是这样的:

应用被允许运行 JavaScript 并渲染 HTML 和 CSS。它们的访问权限受到限制——它们运行所在的 <iframe sandbox="allow-scripts allow-forms"> 阻止它们访问 cookies 或 localStorage,并且它们还被注入了 CSP 标头(多亏了这项研究),这阻止了它们向外部主机发起 HTTP 请求,从而防止恶意或有缺陷的应用泄露隐私数据。

Datasette Apps 最初是我尝试为 Datasette Agent 构建一个 Claude Artifacts 机制,但我很快意识到,这种沙箱化模式的用武之地远不止于向界面添加自定义应用,因此我将其提升为 Datasette 生态系统中的一个独立的顶级概念。

这也是一种很有趣的方式,可以将我多年来在凭感觉编写(vibe-coded)HTML 工具方面的实验,转变为我主要项目的核心功能!

你可以通过 GitHub 登录 agent.datasette.io 演示实例来试用 Datasette Apps。

为什么要开发这个?

从第一个版本发布以来,Datasette 就通过其 JSON API 为创建自定义 HTML 应用提供了灵活的后端。

我最早的 Datasette 项目之一是在 Eventbrite 工作时开发的一个文档内部搜索引擎——它的工作原理是通过定时任务(cron)将来自不同系统的文档导入到 SQLite 中,然后通过一个带有自定义 HTML+JavaScript 搜索界面的 Datasette 实例来提供服务,该界面直接查询 Datasette API。

我当时用客户端 JavaScript 构建 SQL 查询,这最初只是个工程上的玩笑,但结果证明这是一种非常高效的迭代应用的方式!

那个项目,结合我构建 HTML 工具集合的经验以及我对 Claude Artifacts 的实验,让我坚信:将 Datasette 风格的后端与自包含的 HTML 前端结合起来,是一个极其强大的组合。

想象一下,如果 Claude Artifacts 能够访问持久化的关系型数据库,它会有多有用。这就是我正在用 Datasette Apps 打造的!

Datasette Apps 中的巧妙构思

以下是我在构建这个项目时摸索出的一些想法和模式,我认为它们具有很强的生命力。

<iframe sandbox="allow-scripts" srcdoc="..."> + <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:;">

这正是让 Datasette Apps 变得切实可行的神奇组合。我需要在一个高度敏感的域名上运行不受信任的 HTML 和 JavaScript——一个经过身份验证的 Datasette 实例可能包含各种私密数据。sandbox= 属性让我能够以无法与父应用程序交互的方式运行那些不受信任的代码——它无法读取 DOM,无法访问 cookie,也无法从 localStorage 中窃取机密。然而,它可以使用 fetch() 及其相关方法从其他域名加载内容(或泄露数据)。但是……事实证明,如果你在 HTML 页面开头加上一个 <meta http-equiv="Content-Security-Policy"> 标头,你就可以设置额外的策略来锁定对其他域名的访问。我原本担心恶意的 JavaScript 能够更新或删除该标头,但事实证明这是行不通的——一旦设置,CSP 策略对于该框架的内容来说就是不可变的。

使用 postMessage() 和 MessageChannel() 锁定 API

在将这些 iframe 锁定到它们根本无法做任何有趣事情的程度后,接下来的挑战是如何重新开放它们,使它们能够运行一个包含允许操作的白名单,首先是对指定数据库执行只读的 SQL 查询。

我用 postMessage() 构建了它的第一个版本,它允许子 iframe 向父窗口发送消息。我创建了一个简单的协议,用于请求父窗口运行 SQL 查询——父窗口随后可以在执行之前验证它是否针对的是白名单内的数据库。

其中一个 LLM 工具,我记得好像是 GPT-5.5,提示说如果 iframe 以某种方式从不受信任的域名加载了额外的代码,那么单独使用 postMessage() 可能会被利用。我认为这不适用于 Datasette Apps,但我也深信纵深防御的原则,所以我让 GPT-5.5 帮我改用了基于 MessageChannel() 的传输机制。

MessageChannel() 的优势在于,如果页面导航到了其他地方,通道会自动关闭,从而消除了执行来自不受信任的外部页面发送的命令的任何可能性。

可见的查询与错误日志

如果你导航到时间线演示页面并搜索字符串 usercontent,你会看到一些嵌入了来自 user-images.githubusercontent.com 域名图片的搜索结果。这个域名不在 CSP 白名单中,因此触发了错误。

这些错误会被捕获并传回给父框架,在那里它们可以显示在一个实用的错误日志中。这旨在通过暴露出原本不可见的问题,让开发应用程序变得更加高效。

我构建了一个实验性项目,证明了你甚至可以将其变成一种一键允许的机制,根据出错的记录来构建 CSP 白名单,但我还没有将这个想法集成到 datasette-apps 中。

SQL 查询也会被清晰地记录下来——滚动到时间线页面的底部即可看到其实际效果。

用于写操作的存储查询

我希望应用程序能够有条件地写入数据库,但这比 SQL 读取更加危险!

我的解决方案涉及 Datasette 的存储查询功能,该功能由“canned queries”更名而来,并在最近的 Datasette 1.0a31 版本中进行了重大升级——这项工作是直接受到 Datasette Apps 的启发而完成的。

用户可以创建一个执行插入或更新操作的存储写查询,然后将该特定查询加入白名单,供应用程序使用。在应用程序内部的代码中,其用法如下所示:

const result = await datasette.storedQuery("todos", "add_todo", {
  title: "Buy milk",
  due_date: "2026-06-20",
  priority: "high",
  completed: false
});

我也才刚刚开始亲自探索这所解锁的各种可能性,但我的目标是支持将完整的读写应用安全地构建为 Datasette Apps。

复制并粘贴提示词来构建应用

Datasette Apps 插件完全不依赖 LLM,但这些独立的应用非常适合由现代 LLM 来编写。

创建应用表单的末尾包含一个可复制的提示词。这个提示词包含了模型构建新应用所需了解的所有信息,包括所选数据库的 schema。

这意味着你可以点击“复制”,将其粘贴到 ChatGPT、Claude 或 Gemini 中,告诉它你的需求,模型就很有可能会直接生成构建该应用所需的代码。

如果你安装了 Datasette Agent,你的 AI 助手也将获得创建新应用和编辑现有应用的工具,类似于 Claude Artifacts 的风格。

在大量 AI 辅助下构建

Datasette Apps 最初在四月以 datasette-agent-artifacts 的形式诞生,这是一个我后来改名为 datasette-agent-edit 并仅保留其编辑工具的插件。我将其作为 Datasette Agent 的首批插件之一进行构建,以帮助将插件钩子调整到正确的形态。第一个原型主要是使用 Claude Code 中的 Claude Opus 4.6 构建的。

当我将重心转向 Datasette Apps 时,我首先使用 Codex Desktop 和 GPT-5.5 xhigh 制定了计划,该计划基于广泛的对话,并输入了 datasette-agent-artifacts 以及我构建的其他原型。

后续的大部分工作仍然使用 Codex 完成,但在我们能够访问 Claude Fable 5 的短短几天里,我让它对该产品进行了安全评估(这项能力不久后就被美国政府禁止了),并且它发现了一个非常现实的问题。

我原本允许用户为其应用将主机加入 CSP 白名单,但 Fable 指出了以下攻击方式:

  • 拥有 create-app 权限的低权限用户创建一个应用,该应用查询 SQLite 中的所有可用表,并将所有数据选中并外泄到他们通过 CSP 加入白名单的主机上。
  • 然后,他们诱骗有权访问私有数据的管理员用户访问他们的应用。
  • ……然后该应用就可以作为该用户运行查询并窃取其私有数据了!
  • 这显然是无法接受的。我通过将任何域名加入白名单的权限限制为新的 apps-set-csp 权限(仅供受信任的员工使用)来修复了这个问题。站点管理员还可以使用 allowed_csp_origins 列表配置 Datasette,普通用户随后可以从中进行选择。这意味着你可以执行诸如允许 cdnjs.cloudflare.com 之类的操作,你的用户将能够构建从 cdnjs CDN 加载额外 JavaScript 库的应用。

    我极其仔细地审查了 Datasette Apps,特别是其中与安全相关的部分。关键的沙盒和 CSP 配置是基于多个 AI 辅助的原型和测试完成的。

    目前看来效果不错

    我对这个初始版本感到非常满意。

    Datasette 正在超越其最初作为提供只读数据应用的角色,成长为一个更加丰富的工具生态系统,以便在数据收集完毕后对其进行有效利用。

    Datasette 起源于数据新闻。当记者拿到一份关于世界的海量数据转储后,接下来该做些什么?我一直对这个问题很感兴趣。Datasette 支持对这些数据进行探索和发布。Datasette Agent 则增加了借助 AI 辅助对其进行交互查询的功能。现在,Datasette Apps 将其进一步扩展,支持构建自定义界面和可视化效果,从而帮助挖掘隐藏在其中的故事。

    需要完整排版与评论请前往来源站点阅读。