今天我們為 Datasette 推出了一個新外掛 datasette-apps,並在 Datasette 專案部落格上發布了相關公告。該公告說明了這個外掛是什麼,而我將在此進一步闡述其背後的原因。

Datasette Apps 是獨立的 HTML+JavaScript 應用程式,它們在您的 Datasette 應用程式中,於一個嚴格受限的 <iframe> 沙盒內運行。這些應用程式可以使用 JavaScript 對 Datasette 中的資料執行唯讀 SQL 查詢,若您配置了儲存查詢,它們也能執行寫入查詢。

這裡有一個非常簡單的範例和一個更複雜的客製化時間軸範例。應用程式被允許運行 JavaScript 並渲染 HTML 和 CSS。

它們的存取權限受到限制,其運行的 <iframe> 沙盒會阻止它們存取 cookie 或 localStorage。此外,它們還注入了 CSP 標頭,防止對外部主機發出 HTTP 請求,以避免惡意或有缺陷的應用程式洩露私人資料。

Datasette Apps 最初是我嘗試為 Datasette Agent 建立 Claude Artifacts 機制。但我很快意識到,這種沙盒模式不僅僅適用於在聊天介面中添加客製化應用程式,因此我將其提升為 Datasette 生態系統中的頂層概念。

這也是一個有趣的方式,將我多年來在「氛圍編碼」HTML 工具方面的實驗,轉化為我主要專案的核心功能。您可以透過 GitHub 登入 agent.datasette.io 示範實例來試用 Datasette Apps。

自 Datasette 首次發布以來,它就透過其 JSON API 為建立客製化 HTML 應用程式提供了靈活的後端。我最早的 Datasette 專案之一,是在 Eventbrite 工作時為文件建立的內部搜尋引擎。

它透過 cron 將不同系統的文件匯入 SQLite,然後透過 Datasette 實例提供服務,並帶有一個直接查詢 Datasette API 的客製化 HTML+JavaScript 搜尋介面。

我當時使用客戶端 JavaScript 建構 SQL 查詢,這最初只是一個工程師的玩笑,但後來證明是迭代應用程式的極其高效方式。

該專案,結合我建立 HTML 工具集的經驗以及對 Claude Artifacts 的實驗,讓我確信將 Datasette 風格的後端與獨立的 HTML 前端結合,是一種驚人的強大組合。想像一下,如果 Claude Artifacts 能夠存取持久性關聯式資料庫,它們將會多麼有用。這就是我透過 Datasette Apps 正在實現的目標!

<iframe> 的 sandbox="allow-scripts" srcdoc="..." 結合 meta http-equiv="Content-Security-Policy" 是 Datasette Apps 得以實現的關鍵組合。我需要在高度敏感的網域上運行不受信任的 HTML 和 JavaScript,因為經過驗證的 Datasette 實例可能包含各種私人資料。

sandbox= 屬性讓我不受信任的程式碼以無法與父應用程式互動的方式運行,它無法讀取 DOM、存取 cookie 或從 localStorage 竊取秘密。

然而,它可以使用 fetch() 等方法從其他網域載入內容或洩露資料。但事實證明,如果您在 HTML 頁面開頭使用 <meta http-equiv="Content-Security-Policy"> 標頭,您可以設定額外的策略來鎖定對其他網域的存取。

我曾擔心惡意 JavaScript 能夠更新或移除該標頭,但結果證明這行不通,一旦設定,CSP 策略對於該框架的內容是不可變的。

在將這些 <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 的儲存查詢功能,該功能從「預設查詢」重新命名,並在最近的 Datasette 1.0a31 中進行了重大升級,這項工作直接受到 Datasette Apps 的啟發。

使用者可以建立一個執行插入或更新的儲存寫入查詢,然後將該特定查詢列入應用程式的允許清單。

應用程式內部程式碼的使用方式如範例所示。我才剛開始探索這所帶來的可能性,但我的目標是支援安全地建構為 Datasette Apps 的完整讀寫應用程式。

Datasette Apps 外掛本身完全不依賴大型語言模型(LLM),但這些獨立的應用程式非常適合由現代 LLM 編寫。

建立應用程式表單的末尾包含一個可複製的提示詞。這個提示詞包含了模型建立新應用程式所需的所有資訊,包括任何選定資料庫的綱要。

這表示您可以點擊「複製」,將其貼到 ChatGPT、Claude 或 Gemini 中,告訴它您的需求,模型很有可能會生成建立應用程式所需的程式碼。

如果您安裝了 Datasette Agent,您的 AI 助理也將獲得工具,以 Claude Artifacts 的風格建立新應用程式和編輯現有應用程式。

Datasette Apps 最初在四月以 datasette-agent-artifacts 的形式誕生,後來我將其重新命名為 datasette-agent-edit,僅保留其編輯工具。

我將其作為 Datasette Agent 的首批外掛之一來建構,以協助完善外掛掛鉤。第一個原型主要是在 Claude Code 中使用 Claude Opus 4.6 建構的。

當我轉向 Datasette Apps 時,我根據廣泛的對話,並結合 datasette-agent-artifacts 和我建立的其他原型,使用 Codex Desktop 和 GPT-5.5 xhigh 制定了一個計畫。

隨後的大部分工作都與 Codex 相關,但在我們能夠使用 Claude Fable 5 的短短幾天內,我讓它對產品進行了安全評估(這項能力隨後很快就被美國政府禁止),它發現了一個非常真實的問題。

我原本允許使用者為其應用程式將 CSP 主機列入允許清單,但 Fable 指出了以下攻擊方式:一個權限較低的用戶,擁有建立應用程式的權限,建立了一個應用程式,該應用程式查詢 SQLite 中所有可用的表格,並將所有資料選擇並洩露到他們透過 CSP 允許的主機。

然後,他們誘騙擁有私人資料存取權限的管理員用戶訪問他們的應用程式,結果應用程式現在可以以該用戶的身份執行查詢並竊取他們的私人資料!

這顯然是不可接受的。我透過將允許任何網域的能力限制為新的 apps-set-csp 權限來解決這個問題,該權限僅供受信任的員工使用。網站管理員還可以為 Datasette 配置 allowed_csp_origins 清單,普通用戶隨後可以從中選擇。

這意味著您可以允許 cdnjs.cloudflare.com 等,您的用戶將能夠建立從 cdnjs CDN 載入額外 JavaScript 函式庫的應用程式。

我對 Datasette Apps 進行了極其嚴格的審查,特別是與安全相關的部分。關鍵的沙盒和 CSP 配置是基於多個 AI 輔助的原型和測試。

到目前為止,它看起來很不錯。

我對這次的初步發布感到非常滿意。Datasette 正在超越其作為唯讀資料服務應用程式的最初定位,發展成為一個更豐富的工具生態系統,用於在資料收集後執行有用的操作。

Datasette 的根源在於資料新聞學。我一直對記者獲得大量世界資料後,接下來會發生什麼感到興趣。Datasette 支援探索和發布這些資料。Datasette Agent 則增加了 AI 輔助的查詢功能。現在,Datasette Apps 將其擴展到建立客製化介面和視覺化,以幫助解鎖隱藏在資料中的故事。