Home / SG/Vault / Static hosting

Static hosting — GitHub Pages / S3

The same vault-app HTML runs against the live API or a 100% static file host, transparently. The app only talks to window.sg; static vs live is a property of the transport, not the app. Shipped, opt-in, default-off.

Why it works

Everything needed to open and browse a vault is a plain GET to a deterministic path: file IDs (commits, trees, refs, indexes) are content hashes or HMACs of the key, computed client-side — no discovery or listing call exists. So a static host that serves those exact paths just works: the browser GETs ciphertext from GitHub Pages or S3 and decrypts locally. No backend, no server, still zero-knowledge — the bytes on the CDN are ciphertext.

<!-- on the hosting page, before the host boots -->
<script>
  window.SG_STATIC   = true;                    // batch reads → GETs; writes → EREADONLY
  window.SG_ENDPOINT = 'https://my-org.github.io/my-vault';
</script>

What works statically — and what doesn't

CallStatic?
Open, browse, read files, history — sg.vfs.read/readText/list✅ plain GETs
Large reads✅ falls back from presigned to direct GET
Batch reads✅ fan out to parallel GETs, identical result shape
Writes, deletes, sg.append.*, vault creation❌ rejected cleanly with EREADONLY

A static vault is a read-only snapshot — ideal for published docs, reports, dashboards and view-only shares. The app detects it via sg.app.writable === false and hides its editing UI; the same HTML runs writable against the live endpoint. Same app, two backends.

The layout — path mirroring is the one hard requirement

The live API serves GET /api/vault/read/<vaultId>/<filePath>; the static host must serve the vault's encrypted bare/ tree at exactly that path under your base:

<repo root>/                    → https://my-org.github.io/my-vault/
└── api/vault/read/<vaultId>/
    └── bare/
        ├── data/      obj-cas-imm-*    # immutable — cache forever
        ├── refs/      ref-pid-muw-*    # mutable head — no-store
        ├── indexes/   idx-pid-muw-*
        └── keys/      key-rnd-imm-*

If the repo is your vault's own git remote (working tree + bare/ committed per the side-by-side pattern), the encrypted tree is already in the repo — publishing is just placing it under the right path prefix. Visitors open https://…/my-vault/#<key>: the key travels in the URL fragment (never sent to the host), the transport GETs ciphertext, the browser decrypts and renders.

Honest caveats