Document apps
Agent-built HTML apps that live on a Seed document, sync like any file, and read Hypermedia as the viewer. Working note, 2026-10-20.

Document apps

Working note for PR #1262 (feat/document-apps, draft, targets main). It replaces #1139's model (session-private apps on a loopback host), which Eric rejected on 2026-10-20: apps live at a normal hm:// URL inside an account space, and all apps are made that way. #1139 was closed as superseded the same day.

Problem

On #1139 an app the agent builds is a session-private attachment (application/vnd.seed.app+json), readable only by the session owner and served from an ephemeral loopback listener. It cannot be shared: there is no document, no CID, nothing another desktop could sync.

Decision

An app is a UnixFS folder with an index.html referenced from a document's metadata. Sharing the document shares the app: regular document sync plus IPFS file fetch bring the folder to the other desktop.

    Metadata field app on the document: ipfs://<cid> of the folder (a bare UnixFS file is accepted too and treated as index.html). Documented in hypermedia/metadata.md and hypermedia/apps/document-apps.md.

    Origin per document, seed-app://<digest of the hm id>/ on desktop, so localStorage survives app revisions but no two documents share state. The author controls both the document and its app, so that is the right trust boundary. Agreed with Eric 2026-10-20.

    Sandbox: the app is untrusted code once it can be shared. default-src 'self': it loads only its own folder; no network, no daemon, no secret, no parent access. Its one channel is the bridge.

    Bridge: /.seed/client.js, served inside the app origin, defines window.seed (promises over postMessage): context(), resource(hmUrl), directory(hmUrl, {all}). The embedding Seed window answers with the viewer's client, so an app sees exactly what the viewer can see. Reads only in this pass; writes will need a consent prompt like browser actions.

    Agent: no tool. The agent writes the HTML under its memory and then does an ordinary write hm://<account>/<path> with options.app naming that folder (or one HTML file). The folder is published as a UnixFS directory in the same publish as the change and the document's app attribute points at it; options.action: "update" + options.app publishes a revision. The result carries a ```seed-app fence (body: the hm:// URL) that the chat renders as the running app. Eric, 2026-10-20: "i dont think that tool is necessary if the app is created and stored using a doc/resource. and all apps should be made this way."

What landed (PR #1262, four commits on main, no #1139 code)


    Daemon backend/hmnet/filemanager.go: GET /ipfs/{cid}/{path...} walks UnixFS directories, types entries by extension, serves a folder as its index.html; the browser gate treats these reads as public like a bare blob. Test TestGetDirectoryEntries.

    Shared and desktop: @shm/shared/document-app (scheme, protocol, client script, message guard, app CSP, platform hook); directoryToIpfsBlobs in @seed-hypermedia/client (codec-correct dag-pb CIDs, the memory blockstore returns raw ones); desktop app-document-apps.ts (privileged scheme, handler backed by the daemon, document-app-open IPC), frame policy and CSP admit seed-app: frames; DocumentAppView in @shm/ui/document-app, embedded by the resource page when not editing; web /hm/api/file/<cid>/<path> passthrough.

    Agents: collectDocumentAppFiles in document-apps.ts (folder or single HTML, dotfiles skipped, 256 files / 16 MiB, index.html required); write hm:// document create/update take options.app, publish the folder beside the change and set the attribute; the write guide ("Apps" section, with the window.seed bridge API) and the Agent Guide tell the agent when and how. Chat: DocumentAppEmbed renders a ```seed-app fence.

    Docs: hypermedia/apps/document-apps.md, metadata row, links from apps and desktop pages.

Verified: Go hmnet and daemon gate tests; vitest client, shared, desktop (full suite 1379); bun agents 230 incl. a service test that publishes a folder as a document, updates it in place, and refuses without the Publish grant; typechecks for desktop, ui, shared, client, web, agents. Not yet eyeballed in the running app.

Why the first live try failed (2026-10-20 afternoon)

Eric asked Clever Hare (gpt-6-astra, grants incl. apps and publish) for a full-screen 2048 app in the dev agents at :3051. The agent never called the (then) apps tool: it wrote ~/memory/apps/2048.html, tested it with execute, and replied with [Play 2048](~/memory/apps/2048.html), a private path nobody can open; the :3051 server was also running the old checkout. Eric's verdict: the tool is unnecessary, apps are documents at hm:// addresses, full stop. So the branch was rebuilt on main without any of #1139's session-preview machinery (no apps tool, no loopback host, no session attachments): the agent publishes an app with the write verb it already uses for every document.

Open

    Eyeball on desktop: open a document with app set, confirm the frame loads over seed-app://, seed.context() answers, localStorage persists across an app update.

    Web embed: hyper.media needs a sandbox origin (or an opaque-origin sandbox="allow-scripts" iframe) before it can run apps; today it shows a note to open the desktop app.

    Bridge writes (comments, document edits) behind a consent prompt.

    Should the web pane (the browser tab UI) also accept seed-app:// URLs, so an app can open "full screen" in a tab? Today the app replaces the document body instead.

Log

    2026-10-20: branch created from the rebased #1139 head; note opened. Same day: #1138 merged, #1139 rebased onto main and retargeted; all four pieces implemented, tested, pushed as draft #1262. Afternoon: first live try showed the agent never calling apps; Eric dropped the tool model; branch rebuilt on main with write + options.app and a chat seed-app fence; ~/Code/Seed moved to it for testing.

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime

Connections0

Connections show where other documents or comments link to this document.

Related documents

Loading connections…