Home / Demos / Vault app embed

A vault app, live inside this page

Below is an application running out of an encrypted vault, embedded in this page with nothing but a published read-only key. This walkthrough is the complete process that produced it — every command, the key that is deliberately public, and the one that never will be.

The one-sentence mechanism: the vault's ciphertext is fetched from the SG/Send server over CORS, decrypted in your browser with the read key printed on this page, and the vault's index.html is booted inside a sandboxed iframe with a window.sg bridge — the same shape SG/Vault's own vault-in-vault embedding uses. No copy of the app or its content exists on sgit.ai.

The live embed

Click to open it. The status line inside the app will read content: sg.vfs.readText("content.json") — live from the vault, which is the bridge proving itself: the app asked the embed host for a vault file and got it.

How this vault was made, end to end

1 — Write the app. A vault app is one self-contained index.html — CSS and JS inlined, content in content.json, generative SVG instead of image files — plus an app.json that auto-launches it. It follows the authoring contract: no <link href>, <script src> or <img src> against vault paths, content read via sg.vfs.readText with an inlined fallback, and sg-app-ready posted when rendered.

demo-vault-gallery/
├── index.html      # the app — one file, contract-compliant
├── content.json    # the editable content the app reads over the bridge
├── app.json        # { "entry": "index.html", "present": true, "auto_open": true }
└── README.md

2 — Make the folder a vault and push it.

$ sgit init --existing .
  Vault key: <passphrase>:4bshby5n   ← saved to a password manager. Not printed here, not ever.
$ sgit commit -m "Field Notes v1: app, content, README" && sgit push
  Push complete. Pushed 1 commit(s), 4 object(s) uploaded.

3 — Derive the read key. It is computed one-way from the vault key: it can decrypt everything in the vault and can never be turned back into write access.

$ sgit dev derive-keys '<vault-key>'
  read_key: 2848993a68c02a33…   ← this one is safe to publish. See below.

4 — Publish the read key. Deliberately, in full, in this page's source:

vault id : 4bshby5n
endpoint : https://dev.send.sgraph.ai
read key : 2848993a68c02a33ea5582902c391901191e53680d35b36c0e76185d4107ad81

Anyone can take those three lines and read this vault — from the CLI (sgit clone --read-key 2848993a68c0… 4bshby5n), from their own page, from a script. That is the point: read access is a capability you can hand out, without an account, without the host mediating it, and without it ever becoming write access. A write attempt with this key is refused by construction — try it.

5 — Embed it. The host on this page is assets/vault-embed.js, ~170 lines: derive the ref id with HMAC-SHA256, fetch ciphertext over CORS, decrypt with Web Crypto, boot the app in an iframe with sandbox="allow-scripts" (opaque origin — the app gets no cookies, no storage, no reach into this page), and answer its sg.vfs calls over postMessage.

What the app can and cannot do in here

CapabilityIn this embedWhy
sg.vfs.readText / sg.vfs.readworksserved by the host from decrypted vault objects
sg.loadCss / sg.loadJsworkssame read path, injected into the frame
any mutation (sg.fs.*, sg.vault.*, …)impossiblethe page holds only a read key — there is no write capability to misuse, not even by a bug in the host
reach this page, cookies, storage, top navigationblockedsandbox="allow-scripts" and an opaque origin; the only channel is postMessage

Embedding the full SG/Vault UI — the gap closed

The embed above is our minimal host: ~170 lines, enough bridge for a read-only app. The obvious next step was embedding the real SG/Vault interface — App-Mode chrome, or the vault browser with its FILES / SGIT / SETTINGS rail. When we first ran that experiment it worked in every respect but one: the credential. The loader documented a read-only format but the client rejected it, and the CLI's 64hex:vault_id shorthand was parsed as a passphrase, PBKDF2'd, and derived the wrong file ids (we measured abfd8ea0f1a2 where the vault's real ref id is 11ea50e81f4d — hence "Vault not found"). We filed both as asks.

They landed. The deployed loader now detects a read-key credential as format 6<64-hex read_key>:<vault_id>, matching what sgit clone already accepted — and, critically, checks it before the passphrase formats, which is exactly the ordering bug we hit. It also strips the sgit CLI's canonical key prefixes before detection, so a key pasted straight from new CLI output is understood — including sgit_rk1_, the read-only one, which is the form we publish. (Its write-credential sibling is the prefix our own build refuses to let onto this site at all: the release validator treats that string as a leak and fails the build, which is why you will not find it written out here.) Re-running the same experiment against the live build:

QuestionAnswerEvidence
Can the vault UI be iframed at all?Yesno X-Frame-Options, no frame-ancestors
Does App Mode work inside a cross-origin iframe?Yes — fullythe complete HUD: toolbar, URL bar, read/write badges
Can it open with only the read key?Yes — nowall three forms parse in the deployed loader (hex:id, sgit_rk1_hex:id, and the legacy id hex); App Mode booted this Field Notes app from the published read key alone, six SVG studies rendered, the app's own status line reading content.json over the bridge
Does the vault browser open read-only?Yesvault-shell with the FILES / SGIT / SETTINGS rail, the real decrypted tree (4 files, 10.6 KB), and an explicit R1 W0 + Read-only badge in the chrome
Is the SGit view reachable?Yes — but still no deep-linkdriving the nav reaches HISTORY / REFS / TREE / BRANCHES / STATUS / REPAIR with both real commits listed; no URL selects a view, so a host page cannot frame the SGit view in isolation. The one ask still open.

So the credential gap is closed, and the consequence is the point of this whole page: a published read key is now enough to embed the official interface — no account, no token, no write capability anywhere in the chain. And the mechanism got better than the one we first tested: the UI now ships an embed protocol, so the host page hands the key over a validated postMessage handshake instead of a URL fragment. The key never appears in any URL, is never written to the frame's storage (it lives in the frame's memory for the session), the frame proves it is the right recipient before the key is sent, and the host gets structured vault-ready / vault-error events back instead of guessing from load timings.

Below is exactly that — both surfaces, opened with the same published key printed further up this page, loading on click because they pull the full UI from another origin. The area is deliberately wider than this text column, because the vault browser is a full working surface, and there is a full-screen button for vaults with a big UX.

Loads dev.vault.sgraph.ai in an iframe and completes the embed handshake. Nothing on sgit.ai ever sees more than the published read key it already prints.

What is still not possible — precisely. The two buttons above select a surface (App Mode or the vault browser). They cannot select a view inside the browser: the SGIT inspector and SETTINGS panels are one click away on the left rail, but the embed message carries only {key, mode, deepLink} where deepLink is a file path — there is no field that names a view, and view switching stays an in-page event. So "open straight onto the commit history" is still one small protocol field away, and the briefing now states exactly that ask: an optional view field on vault-open, applied after mount. Everything else on this page works today.

Honest scope

Two hosts now run on this page, and keeping both is deliberate. The minimal host is ~170 lines and shows the protocol with nothing hidden: derive, fetch, decrypt, answer sg.vfs over postMessage. The official host is the real product — full window.sg surface, permissions, HUD chrome, and now an embed handshake that keeps the key out of URLs and storage. Read the small one to understand the mechanism; use the big one to see what a product built on it feels like. Everything above is live and reproducible from the commands on this page.

ShapeEvidence statusCopy or reference
Gallery (app + content)LIVE — the embed above reads the real vault on every loadReference — a push to the vault changes this page's embed with no rebuild

Related