Home / Vaults / Vault App Mode

Vault App Mode — nine POCs and a demo

The simplest vault on this site, and the one to read first if you are going to build one. Nine proof-of-concepts climb from a single inline HTML file to a fifteen-test diagnostic harness over the vault filesystem, then a small four-page app puts the lot together. It answers one question end to end: how do you load CSS, JavaScript and data when your files live in an encrypted vault instead of on a web server?

The one problem everything here solves. The HTML parser fires requests for <link> and <script src> the moment it meets them — before the vault bridge has patched fetch. Those requests hit the iframe's blob origin, where no files exist, and 404. So vault pages load their resources at runtime, with sg.loadCss() and sg.loadJs(). Every POC in this vault is a consequence of that one race.
Open it yourself — the key is the whole credential.
Read key: sgit_rk1_05f2391f22e4135ea27bca6b697dca18c54ab91b046325bef74f62f5324b8bc8:xth1xt78
In the official UI: open it read-only in a new tab · From the CLI: sgit clone sgit_rk1_05f2391f22e4135ea27bca6b697dca18c54ab91b046325bef74f62f5324b8bc8:xth1xt78
Published deliberately. It grants read, and only read. This is a republication — the vault this content was authored in could not be opened by a read key at all, for a reason worth reading in the audit below.

See it live, here

Both surfaces open automatically below. You can also open the app in its own window ↗.

What this vault demonstrates

FeatureHow this vault uses it
Runtime resource loadingsg.loadCss() and sg.loadJs() fetch vault files through the bridge and inject them, because declarative tags lose the race against the parser. POC-02 and POC-03 isolate each one; POC-04 shows dependency order between modules
The filesystem, read and writtensg.vfs.readText() in POC-07, sg.vfs.write() in POC-08 — which persists a real JSON file into the vault and lists it back. Writes are local-first: they live in the working copy until an sgit push, and the POC says so rather than implying instant durability
A test harness for the platform itselfPOC-09 is the unusual one: fifteen graded operations over sg.vfs — read, write, list, nested folders, binary round-trips, deliberate error cases, path-variant comparison — each showing the exact call, the raw result, per-assertion pass/fail and timing. It exists because guessing at the API's behaviour cost a session; instrumenting it settled the question
Paths, the thing that actually bitessg.vfs takes vault-absolute paths, and a page's own folder is knowable only from sg.app.selfPathlocation.pathname is always / inside the iframe. The vault documents this as its most important rule, and POC-09's environment readout prints the ground truth
FOUC handled deliberatelypages hide the body until their CSS resolves, then reveal by removing a class. The distinction matters: clearing an inline style cannot override a stylesheet rule, which is exactly the bug this vault was carrying when it arrived (see the audit)
Its own failure modes, on a pagethe survey ships a Try the failure modes panel that attempts writes which should be rejected — path traversal, URL-encoded traversal, a protected .vault-settings.json, a null byte, a 1500-character path — so the guards are demonstrated rather than asserted
The reasoning ships with it_docs/ carries a field guide (the canonical page skeleton and path rules), a gotchas manual written by symptom, and a debrief explaining why the code looks the way it does. It is the best-documented vault on this site for its size

What is going on here, step by step

The embeds above are the real vault, which makes it easy to scroll past the parts that matter. Each row points at one of them. Every screenshot is of this vault, driven by a script holding nothing but the published read key.

what opens

A contents page, not a file listing

Opening the read key lands on the POC hub — app.json sets entry to _poc-hub/index.html. The steps are numbered like a magazine contents page, each card tagged with the API it exercises and a difficulty level, with the diagnostic lab and the demo as full-width cards beneath.

It is worth noticing what this is: a static HTML file in an encrypted store, rendering as a navigable site, with no server anywhere in the path.

The hub — nine numbered steps, tagged by the API each one exercises.
the baseline

POC-01: everything inline, so nothing can fail

The first step deliberately has no external files at all — CSS in a <style> block, JS in a <script> block. It is the control case: if this renders, the vault is serving your HTML correctly and anything that breaks later is about resource loading, not about the vault.

It is also the one page that does not use the shared stylesheet, because loading one would contradict the point it is making.

The control case — no external files, so nothing can 404.
the fix, isolated

POC-02: one CSS file, loaded at runtime

The smallest possible demonstration of the pattern. One sg.loadCss() call in a head script, and the FOUC guard around it: the body starts hidden and is revealed when the promise settles — including when it rejects, so a failed load leaves a readable page rather than a permanently invisible one.

The status pill on the page reports the load lifecycle as it happens, which makes the async step visible instead of theoretical.

One sg.loadCss(), with the reveal guarded on both outcomes.
order matters

POC-04: two modules, and the dependency between them

Once JavaScript arrives at runtime, load order becomes yours to manage. utils.js must execute before app.js can call into it, so the POC awaits them in sequence and logs each step with a [poc-04] prefix.

The parallel case is covered too: independent resources go through Promise.all, because serialising things that do not depend on each other is just a slower page.

Sequential where there is a dependency, parallel where there is not.
data

POC-05: fetch() works, because the bridge intercepts it

Once the bridge is installed, an ordinary fetch('data.json') resolves against the vault. No special API, no rewriting of your data layer — the call you would already have written works, which is the point of intercepting fetch rather than replacing it.

This is the step where a vault app stops looking exotic and starts looking like a normal front end that happens to have an encrypted origin.

An ordinary fetch(), resolved against encrypted storage.
writing

POC-08: a form that puts a real file in the vault

Submitting writes responses/poc-08-<timestamp>.json through sg.vfs.write(), then the page lists what is there and lets you edit or delete it. The delete control only appears if sg.vfs.delete actually exists — capability-checked rather than assumed.

The callout that matters is the honest one: writes are local-first. They live in the working copy until someone runs sgit push, which the vault demonstrated the hard way — a submission made in one browser session was gone from a fresh one until the push happened.

A real write, with the local-first caveat stated rather than glossed.
the interesting one

POC-09: testing the platform instead of trusting it

Fifteen graded tests over sg.vfs, each card showing the exact call with its resolved arguments, the raw result, a live progress log, per-assertion pass/fail and a runtime in milliseconds. Above them, an environment readout printing location.href, location.pathname, sg.app.selfPath and the keys actually present on sg.vfs and sg.app.

It was built mid-project when path bugs kept multiplying, on the reasoning that the API should be measured rather than guessed at. It is the most transferable idea in the vault: when a platform surprises you, instrument it and keep the instrument.

A test harness for the vault filesystem, kept as part of the vault.
all together

The cities demo: four pages, one data file

The demo hub offers a bar chart, a world map, a survey and a responses viewer — each tagged READ-ONLY or WRITES so you know what it will do before you click. The chart and the map read the same cities.json and render it two ways; editing that one file in the vault browser changes both.

This hub could not be reached at all before publication — see the audit. It is now linked from the POC hub, which is how you get here.

Four pages over one data file, each labelled with what it will do.
read and render

The same data, drawn two ways

The bar chart reads cities.json through the bridge and draws from it directly. The map projects the same records onto an equirectangular world and doubles as a navigation test — proving that same-vault <a href> links resolve correctly between pages.

Nothing here is fetched from the network. The data, the code and the styling are all objects in the encrypted store, decrypted in the reader's browser.

One cities.json, rendered by two pages that never touch the network.
write, then read back

The survey, and its own failure modes

The form writes a JSON file per submission and offers two submit buttons — the canonical sg.vfs.write() and the window.sgVault.writeFile alias — so you can confirm both produce identical commits.

Underneath is the panel worth the visit: Try the failure modes, six buttons that attempt writes which must be refused. Path traversal, URL-encoded traversal, the protected settings file, a null byte, a path past the length cap. A demo that shows you its guards working is a stronger claim than one that tells you they exist.

Two write paths, and six writes that are supposed to be refused.
list, read, aggregate

The viewer — including the response that is trying to attack it

sg.vfs.list() enumerates the responses folder, sg.vfs.readText() reads each file, and the page aggregates live: total responses, average excitement, top role, total bytes. A View raw control dumps the unprocessed listing so you can see what the API actually returned.

The first card is deliberate. One stored response carries <img src=x onerror=alert(1)> in its name field, and the viewer renders it as visible text. It is a standing escaping test, kept in the data so that a regression on either render path would be obvious on sight — which matters, because that is a bug this vault actually had.

Three responses, aggregates computed live — and a payload rendered, correctly, as text.
the source

Everything is readable, including the parts that explain it

The vault browser shows the whole tree under the same read key: nine POC folders, the shared stylesheet, the demo, and _docs/ — a field guide, a gotchas manual organised by symptom, and a project debrief.

That last folder is why this vault is worth copying as a pattern. The code demonstrates the technique; the docs record what went wrong on the way, which is the part that usually evaporates when a project ends.

The tree under one read key — including the write-up of its own mistakes.

The audit, honestly

This vault was reviewed before its key was published, and the review changed both the vault and what got published. Findings first.

The submitted vault could not be opened by a read key at all. Its read key decrypted every object correctly — verified by decrypting its HEAD ref by hand — but the reader could never find HEAD. sgit has two ways of assigning a vault's ref file id: Vault__Crypto derives it from the read key and vault id, while Vault__Branch_Manager falls back to 'ref-pid-muw-' + secrets.token_hex(6) — a random value. This vault got a random one. Browser readers derive the id, so they looked in the right place for a file stored under a name nothing could compute. The CLI worked throughout, because it resolves HEAD through the branch index instead. sgit's own tooling states the consequence plainly: "write_key, ref_file_id, and branch_index_file_id are not derivable from a read_key alone." A read key published for that vault would have been a credential that decrypts everything and opens nothing — so the content was republished into a new vault, xth1xt78, created through the deriving path.

The whole cities demo rendered blank. Four pages set body { display: none } in a stylesheet and cleared it with document.body.style.display = ''. That clears an inline property which was never set; the stylesheet rule kept applying and the pages stayed invisible. The host said so out loud — "App loaded but is showing nothing — hidden (display:none)" — for the hub, the map, the survey and the responses viewer. Only the bar chart, which has no such script, was unaffected. Fixed by hiding through a class and removing it, which is the pattern the POCs already used and the vault's own gotchas file documents.

The demo was unreachable. app.json opens the POC hub; the hub linked only the nine POCs; and the demo's own hub linked back to it. A one-way door, with a third of the vault behind it. The hub now carries a card for it.

Two survey pages pointed at files that were not there. Both live in demo-cities/survey/ but named style.css and hub.html as though they sat a level up, so each had a missing stylesheet and a dead back-link. Separately, both used relative paths for sg.vfslist('responses/') returned "No such path" — which is precisely the gotcha this vault's own documentation warns about. They now derive the folder from sg.app.selfPath, the way POC-09 does, and normalise entry.path to absolute before reading, which was doubling the prefix.

A stored-XSS gap on the error path. The responses viewer escaped every field it rendered on the success path, and interpolated the filename and error text raw on its two failure paths. The vault contained an unlabelled XSS payload in a leftover test response, so someone had clearly probed this and fixed only the half that showed. Both paths now escape, and the payload has been kept deliberately — rewritten as a labelled escaping test, so the property stays visible in the UI rather than living in a comment.

Removed, not fixed: an empty zero-byte check-undefined.html referenced by nothing, and POC_STRUCTURE.md, whose guidance had been superseded by _docs/ and whose structure diagram described a six-POC vault under a former name. README.md was rewritten for the same reason — it named the wrong entry point and omitted three POCs. Both files remain in the vault's history.

The credential finding. The vault's own documentation recorded the project's push token in plain text, three times, as part of the workflow instructions. Publishing a read key would have handed that out with the content. The token has been replaced with a placeholder; the instructions still read correctly. The vault is otherwise clean on credentials — no vault key inside its own files, no sgit_vk1_ or sgit_private_vault_ string, no delete_auth, no API keys or private-key blocks. All 57 files were fetched back after publication using only the read key above and re-scanned.

On data: the survey responses are the only personal-shaped content, and all three are invented for the demo. The rest is code, styling and documentation. The rule that goes with any published key applies here too: revocation is not retroactive — anybody who fetches these objects keeps them.

Derived facts

57 files · 251 KB · 8 commits · HEAD obj-cas-imm-4517bfe1735d · app entry _poc-hub/index.html · 15 HTML, 12 JSON, 12 CSS, 10 JS, 6 markdown — derived from the read key alone by admin/build/catalogue_derive.py, the same derivation that populates the catalogue, with no token and no clone.