Datasette Apps:カスタム HTML アプリケーションを Datasette 内でホスト可能に
Simon Willison が Datasette に、サンドボックス化された環境で JavaScript アプリを実行し、データベースと安全に連携できる「Datasette Apps」機能を導入した。
キーポイント
サンドボックス型アプリ実行環境の提供
Datasette 上で HTML と JavaScript で構築された独自アプリケーションを、iframe を介して厳格な制約付きでホスト・実行できる機能を追加した。
データ操作とセキュリティの両立
JavaScript を用いて読み取り専用 SQL クエリを実行可能であり、設定次第では書き込みクエリも可能だが、外部通信やローカルストレージへのアクセスは CSP と iframe 属性で厳重にブロックされる。
開発者体験の拡張と用途
当初は AI アーティファクト(Claude Artifacts)のような UI 構築を目的としていたが、現在では Datasette エコシステム内の独自ツールやダッシュボード構築のための汎用プラットフォームへと進化している。
影響分析・編集コメントを表示
影響分析
この発表は、従来の静的なデータベースビューアーとしての Datasette の役割を、インタラクティブなアプリケーションプラットフォームへと拡張する重要な転換点です。開発者がセキュリティリスクを懸念せずとも独自ツールを構築できるため、データドリブンな組織内ツールの迅速なプロトタイピングと展開が容易になります。
編集コメント
データ分析ツールの開発において、セキュリティを犠牲にせず柔軟な UI を構築できる手法として注目すべき機能です。特に Datasette ユーザー層にとっては、即座にカスタムツールを展開できる強力な武器となります。
今日、Datasette プロジェクトのブログに公開された この発表記事 にて、Datasette 向けの新しいプラグイン datasette-apps をリリースしました。その記事では「何(what)」について説明されていますが、ここでは「なぜ(why)」について少し詳しく解説していきます。
TL;DR(要約)
Datasette Apps は、あなたの Datasette アプリケーション上でホストされる厳密に制約された *サンドボックス* 内で動作する、自己完結型の HTML+JavaScript アプリケーションです。これらは JavaScript を使用して Datasette のデータに対して読み取り専用 SQL クエリを実行でき、いくつかの保存済みクエリ で設定すれば、書き込みクエリも実行可能です。
以下に 非常にシンプルな例 と、より複雑なカスタムタイムラインの例 を示します。後者の外観は以下の通りです:

Apps は JavaScript の実行や HTML、CSS のレンダリングが許可されています。ただしアクセス権限には制限があり、動作する *サンドボックス* により、Cookie や localStorage へのアクセスは防止されています。また、この研究 に基づいて注入された CSP ヘッダー(Content Security Policy)が機能し、外部ホストへの HTTP リクエストをブロックします。これにより、悪意のあるアプリやバグを含むアプリによる機密データの漏洩を防ぐことができます。
Datasette Apps は、Datasette Agent 向けの Claude Artifacts メカニズムを構築しようとした私の試みから始まりましたが、すぐにこのサンドボックス化されたパターンが、インターフェース表面にカスタムアプリを追加するだけでなく、はるかに多くの興味深い用途があることに気づき、Datasette エコシステム内の独自のトップレベル概念へと昇格させました。
また、これは私が長年取り組んできた vibe-coded HTML ツールの実験 を、私の主要プロジェクトのコア機能に変える楽しい方法でもあります!
agent.datasette.io のデモインスタンスに GitHub でサインインすることで、Datasette Apps を試すことができます。
なぜこれを構築したのか?
初回リリース以来、Datasette は JSON API を通じてカスタム HTML アプリを作成するための柔軟なバックエンドを提供してきました。
私が Eventbrite に勤務していた頃に取り組んだ初期の Datasette プロジェクトの一つは、ドキュメント用の内部検索エンジンでした。これは、異なるシステムからドキュメントを cron ジョブで SQLite にインポートし、Datasette API を直接照会するカスタム HTML+JavaScript 検索インターフェースを持つ Datasette インスタンスを通じて提供されるものでした。
私はクライアントサイドの JavaScript で SQL クエリを構築していましたが、これは当初はエンジニアリングのジョークとして意図されたものでしたが、結果的にアプリの反復開発において非常に生産的な*方法となりました!
このプロジェクトと、私の HTML ツールコレクションの構築 building my HTML tools collection や Claude Artifacts に関する実験 experiments with Claude Artifacts の経験が相まって、自己完結型の HTML フロントエンドに Datasette スタイルのバックエンドを追加することが、驚くほど強力な組み合わせであることを確信させられました。
Claude Artifacts が永続的なリレーショナルデータベースへのアクセスを持っていたら、どれほど有用になるか想像してみてください。それが私が Datasette Apps で構築しているものです!
Datasette Apps における素晴らしいアイデア
このプロジェクトを構築する過程で見つけた、長続きすると考えるいくつかのアイデアとパターンをご紹介します。
+
これが、Datasette Apps が実現可能となるための魔法のような組み合わせです。私は、認証された Datasette インスタンスがあらゆる種類のプライベートデータを含みうる極めて機密性の高いドメイン上で、信頼できない HTML と JavaScript を実行する必要があります。sandbox=属性を使えば、その信頼できないコードを親アプリケーションと相互作用できない方法で実行できます。つまり、DOM を読み取ったり、クッキーにアクセスしたり、localStorage から秘密情報を盗んだりすることはできません。ただし、fetch() や関連する関数を使って他のドメインからコンテンツを読み込んだり(あるいはデータを漏洩させたり)することは可能です。しかし……実は、HTML ページを <meta http-equiv="Content-Security-Policy"> ヘッダーで開始することで、追加のポリシーを設定し、他のドメインへのアクセスをロックダウンできることがわかりました。悪意のある JavaScript がこのヘッダーを更新または削除できないかと心配しましたが、実際には それは機能しない ことが判明しました——一度設定されると、そのフレーム内のコンテンツに対する CSP ポリシーは不変となります。
Locked down APIs with postMessage() and MessageChannel()
これらの iframe を何の興味深いこともできないほど厳しくロックダウンした結果、課題はそれらを再び開放し、読み取り専用 SQL クエリ(指定されたデータベースに対して実行するもの)から始まる許可リストに基づいた操作を実行できるようにすることでした。
私は最初、postMessage() を使用してこの最初のバージョンを作成しました。これにより、子 iframe から親ウィンドウへメッセージを送信できるようになります。親が SQL クエリを実行するよう要求するためのシンプルなプロトコルを作成し、実行前にそのクエリが許可されたデータベースに対するものであるかを確認できるようにしました。
LLM ツールの一つ(GPT-5.5 だったと思います)は、iframe が何らかの形で信頼できないドメインから追加コードを読み込む場合、postMessage() 単体では悪用される可能性があることを示唆しました。Datasette Apps ではこの問題は当てはまらないと考えていますが、防御を多層化するという考えに基づき、GPT-5.5 に手伝ってもらい、MessageChannel() ベースの転送プロトコルへ移行しました。
MessageChannel() の利点は、ページが別の場所へナビゲートするとチャネルが自動的に閉じられることです。これにより、信頼できない外部ページから送信されたコマンドを実行する可能性を完全に排除できます。
クエリとエラーの可視ログ
タイムラインデモ にアクセスし、"usercontent" という文字列を検索すると、user-images.githubusercontent.com ドメインから画像を埋め込んだ検索結果がいくつか表示されます。このドメインは CSP(Content Security Policy)の許可リストに含まれていないため、エラーが発生します。
これらのエラーは捕捉され、親フレームに送信され、そこで有用なエラーログに表示されます。これは、従来は目に見えなかった問題を表面化させることで、アプリの構築作業をより生産的にするためのものです。
私は、この仕組みを、何が壊れるかに基づいて CSP 許可リストを構築するためのワンクリックで許可するメカニズムに変換できることを示す 実験 を作成しました。ただし、このアイデアはまだ datasette-apps に統合していません。
SQL クエリも可視化されてログに記録されます。タイムラインページの下部 にスクロールすると、その動作を確認できます。
書き込み操作のための保存済みクエリ
アプリが条件付きでデータベースに書き込めるようにしたいと考えていますが、これは SQL の読み取りよりも *さらに* 危険な提案です!
私の解決策は、Datasette の 保存済みクエリ 機能を利用するものです。これは「 canned queries(事前定義されたクエリ)」から名称変更され、最近の Datasette 1.0a31 で大規模なアップグレードが施されました 詳細はこちら - この開発は直接 Datasette Apps にインスパイアされたものです。
ユーザーは、挿入または更新を実行する保存済み書き込みクエリを作成し、特定のアプリで使用するためにそのクエリを許可リストに登録できます。アプリ内のコードからの使用例は以下のようになります:
const result = await datasette.storedQuery("todos", "add_todo", {
due_date: "2026-06-20",
priority: "high",
completed: false
});
私はまだこの機能が開く可能性を探り始めたばかりですが、私の目標は、Datasette Apps として安全に構築された完全な読み書きアプリケーションをサポートすることです。
アプリを構築するためのプロンプトをコピー&ペーストする
Datasette Apps プラグインには LLM(大規模言語モデル)への依存は一切ありませんが、これらの自己完結型アプリは、現代の LLM によって記述されるのに最適な形をしています。
アプリ作成フォームの最後には、コピー可能なプロンプトが含まれています。このプロンプトには、新しいアプリを構築するためにモデルが必要とするすべての情報が含まれており、選択されたデータベースのスキーマもその中に含まれます。

つまり、「コピー」をクリックして、ChatGPT や Claude、Gemini などに貼り付け、必要なものを伝えるだけで、モデルがアプリを構築するために必要なコードを出力してくれる可能性が高いのです。
Datasette Agent がインストールされている場合、AI アシスタントも、Claude Artifacts スタイルで新しいアプリの作成や既存アプリの編集を行うためのツールを獲得します。

AI の支援を非常に多く受けて構築されました
Datasette Apps は、4 月に 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 氏によって以下の攻撃手法が指摘されました:
- 権限の低いユーザー(アプリ作成権限を持つ)が、SQLite の利用可能な全テーブルを検索し、CSP を介して自分が許可リストに登録したホストへすべてのデータを抽出・転送するアプリを作成します。
- 次に、機密データへのアクセス権を持つ管理者ユーザーを騙して、そのアプリに訪問させます。
- …すると、そのアプリは当該ユーザーの権限でクエリを実行し、機密データを盗むことが可能になります!
これは明らかに許容できません。私はこれを是正するため、ドメインの許可リスト登録機能を「apps-set-csp」という新しい権限に制限しました。この権限は信頼されたスタッフのみを対象としています。サイト管理者はまた、Datasette の設定において、許可される CSP 送信元(allowed_csp_origins)のリストを指定することもできます。これにより、一般ユーザーがその中から選択できるようになります。例えば、cdnjs.cloudflare.com を許可リストに追加すれば、ユーザーは cdnjs CDN から追加の JavaScript ライブラリを読み込むアプリを構築できるようになります。
私は Datasette Apps を非常に厳密にレビューしました。特にセキュリティに関連する部分については、複数の AI 支援によるプロトタイプとテストに基づいて設計された、重要なサンドボックスおよび CSP 設定が採用されています。
これまでのところ良好な状況です
この初期リリースには大変満足しています。
Datasette は、読み取り専用データの提供という起源を超えて、収集されたデータを実用的に活用するためのより豊かなツールエコシステムへと成長しつつあります。
Datasette のルーツはデータジャーナリズムにあります。私は常に、ジャーナリストが世界の巨大なデータダンプを入手した後に「次」に何が起こるのかという問いに関心を持ってきました。Datasette はそれを探索し公開することをサポートします。Datasette Agent は AI の支援を受けてデータを照会する機能を追加しました。そして今、Datasette Apps は、隠されたストーリーを解き放つためにカスタムインターフェースや可視化を構築することへとその範囲を広げます。
Tags: iframes, javascript, projects, sandboxing, ai, datasette, generative-ai, llms, ai-assisted-programming, content-security-policy
原文を表示
Today we launched a new plugin for Datasette, datasette-apps, with this launch announcement post on the Datasette project blog. That post has the *what*, but I'm going to expand on that a little bit here to provide the *why*.
The TL;DR
Datasette Apps are self-contained HTML+JavaScript applications that run in a tightly constrained * sandbox hosted on your Datasette application. They can use JavaScript to run read-only SQL queries against data in Datasette, and can run write queries too if you configure them with some stored queries.
Here's a very simple example and a more complex custom timeline example - the latter looks like this:

Apps are allowed to run JavaScript and render HTML and CSS. They are limited in terms of access - the `` they run in prevents them from accessing cookies or localStorage and they also have an injected CSP header (thanks to this research) which prevents them from making HTTP requests to outside hosts, preventing a malicious or buggy app from exfiltrating private data.
Datasette Apps started out as my attempt at building a Claude Artifacts mechanism for Datasette Agent, but I quickly realised that the sandboxed pattern is interesting for way more than just adding custom apps to the interface surface and promoted it to its own top-level concept within the Datasette ecosystem.
They're also a fun way to turn my multi-year experiment in vibe-coded HTML tools into a core feature of my main project!
You can try out Datasette Apps by signing in with GitHub to the agent.datasette.io demo instance.
Why build this?
Since the very first release, Datasette has offered a flexible backend for creating custom HTML apps via its JSON API.
One of my earliest Datasette projects was an internal search engine for documentation when I worked at Eventbrite - it worked by importing documents from different systems into SQLite on a cron and then serving them through a Datasette instance with a custom HTML+JavaScript search interface that directly queried the Datasette API.
I had client-side JavaScript constructing SQL queries, which originally was intended as an engineering joke but turned out to be a really productive* way of iterating on the app!
That project, combined with my experience building my HTML tools collection and my experiments with Claude Artifacts, has convinced me that adding a Datasette-style backend to a self-contained HTML frontend is an astonishingly powerful combination.
Imagine how much more useful Claude Artifacts could be if they had access to a persistent relational database. That's what I'm building with Datasette Apps!
Neat ideas in Datasette Apps
Here are a few of the ideas and patterns I've figured out building this which I think have staying power.
+
This is the magic combination that makes Datasette Apps feasible in the first place. I need to run untrusted HTML and JavaScript on a highly sensitive domain - an authenticated Datasette instance can contain all sorts of private data. The sandbox= attribute lets me run that untrusted code in a way that cannot interact with the parent application - it can't read the DOM, or access cookies, or steal secrets from localStorage. It can however use fetch() and friends to load content (or exfiltrate data) from other domains. But... it turns out if you *start* an HTML page with a `` header you can set additional policies that lock down access to other domains. I was worried that malicious JavaScript would be able to update or remove that header but it turns out that doesn't work - once set, the CSP policy is immutable for the content of that frame.
Locked down APIs with postMessage() and MessageChannel()
Having locked down those iframes to the point that they couldn't do anything interesting at all, the challenge was to open them back again such that they could run an allow-list of operations, starting with read-only SQL queries against specified databases.
I built the first version of this with postMessage(), which allows a child iframe to send messages to the parent window. I created a simple protocol for requesting that the parent run a SQL query - the parent could then verify it was against an allow-listed database before executing it.
One of the LLM tools, I think it was GPT-5.5, suggested that postMessage() on its own can be exploited if the iframe somehow loads additional code from an untrusted domain. I don't think that applies to Datasette Apps, but I also believe in defense in depth, so I had GPT-5.5 help me port to a MessageChannel() based transport instead.
MessageChannel() has the advantage that if a page navigates to somewhere else the channel closes automatically, removing any chance of executing commands sent from an untrusted external page.
Visible logs, for queries and errors
If you navigate to the timeline demo and search for the string usercontent you'll pull in some search results that embed images from the user-images.githubusercontent.com domain. This domain is not in the CSP allow-list, so it trips an error.
Those errors are captured and transmitted back to the parent frame, where they can be displayed in a useful error log. This is meant to make hacking on apps more productive by surfacing otherwise-invisible problems.
I built an experiment demonstrating that you can even turn this into a one-click-to-allow mechanism for building the CSP allow-list based on what breaks, but I haven't integrated that idea into datasette-apps just yet.
SQL queries are also visibly logged - scroll to the bottom of the timeline page to see that in action.
Stored queries for write operations
I want apps to be able to conditionally write to the database, but this is an *even more* dangerous proposition than SQL reads!
My solution involves Datasette's stored queries feature, rebranded from "canned queries" and given a major upgrade in the recent Datasette 1.0a31 - work that was directly inspired by Datasette Apps.
Users can create a stored write query that performs an insert or update, then allow-list that specific query for an app to use. Usage from code inside an app looks like this:
const result = await datasette.storedQuery("todos", "add_todo", {
title: "Buy milk",
due_date: "2026-06-20",
priority: "high",
completed: false
});I'm only just beginning to explore the possibilities this unlocks myself, but my goal is to support full read-write applications built safely as Datasette Apps.
Copy and paste a prompt to build an app
The Datasette Apps plugin has no dependency on LLMs at all, but these self-contained apps are the perfect shape to be written by a modern LLM.
The create app form includes a copyable prompt at the end. This prompt has everything a model needs to know to build a new app, including the schema of any selected databases.

This means you can click "copy", paste it into ChatGPT or Claude or Gemini, tell it what you need, and there's a good chance the model will spit out the code necessary to build the app.
If you have Datasette Agent installed your AI assistant will also gain tools to both create new apps and edit existing ones, Claude Artifacts style.

Built with so much AI assistance
Datasette Apps started life back in April as datasette-agent-artifacts, a plugin I have since renamed to datasette-agent-edit keeping only its editing tools. I built that as one of the first plugins for Datasette Agent, to help get the plugin hooks into the right shape. That first prototype was mainly built using Claude Opus 4.6 in Claude Code.
When I switched track to Datasette Apps I started with a plan constructed using Codex Desktop and GPT-5.5 xhigh, based on extensive dialog and feeding in both datasette-agent-artifacts and other prototypes I had built.
Most of the work that followed stuck with Codex, but in the few short days that we had access to Claude Fable 5 I had it run a security evaluation of the product (an ability that would get it banned by the US government shortly afterwards) and it found a very real problem.
I was allowing users to allow-list CSP hosts for their apps, but Fable pointed out the following attack:
- A less privileged user with create-app permission creates an app that queries SQLite for all available tables and selects and exfiltrates all of the data to a host they had allow-listed via CSP.
- They then trick an administrator user with access to private data into visiting their app.
- ... and the app can now run queries as that user and steal their private data!
That's clearly unacceptable. I fixed it by restricting the ability to allow-list any domain to a new apps-set-csp permission, which is intended just for trusted staff. Site administrators can also configure Datasette with a list of allowed_csp_origins, which regular users can then select. This means you can do things like allow cdnjs.cloudflare.com and your users will be able to build apps that load extra JavaScript libraries from the cdnjs CDN.
I've reviewed Datasette Apps extremely closely, especially the security-adjacent parts of it. The critical sandbox and CSP configuration are based on multiple AI-assisted prototypes and tests.
It's looking good so far
I'm really pleased with this initial release.
Datasette is growing beyond its origins as an application for serving read-only data into a much richer ecosystem of tools for doing useful things with that data once it has been collected.
Datasette's roots are in data journalism. I've always been interested in the question of what comes *next* after a journalist gets their hands on a giant dump of data about the world. Datasette supports exploring and publishing it. Datasette Agent adds interrogating it with AI assistance. Now Datasette Apps expands that to building custom interfaces and visualizations to help unlock the stories that are hidden within.
Tags: iframes, javascript, projects, sandboxing, ai, datasette, generative-ai, llms, ai-assisted-programming, content-security-policy
関連記事
datasette-acl 0.6a0 のリリース
Alex Garcia 氏らが中心となり、データセットのテーブル単位での権限管理から、一般リソース共有システムへと拡張された「datasette-acl」バージョン 0.6a0 が公開されました。これにより、複数ユーザー環境でリソースへのアクセス制御が細かく行えるようになります。
高度な結合技術:LATERAL 結合、セミ結合、アンチ結合
KDnuggets は、サブクエリが FROM クラースの先行列を参照できる LATERAL 結合や、一致する行のみを返すセミ結合、一致しない行を返すアンチ結合といった SQL の高度な結合技術について解説した。
GLM-5.2 はおそらく最も強力なテキスト専用オープンウェイト大規模言語モデルである
中国の AI ラボ Z.ai が、7530億パラメータ(アクティブ400億)を持つテキスト専用モデル「GLM-5.2」を MIT ライセンスで公開した。これは同社が提供するオープンウェイト大規模言語モデルの中で最も強力なものである。
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み