Home / SG/Vault / Sub-vaults

Sub-vaults: vaults inside vaults

A vault can point at other vaults. A pointed-to vault shows up inline in the file tree, exactly like a folder — expand it and browse. A clinic's parent vault can hold many per-patient vaults; a demos vault can gather live project vaults — without copying their contents.

The mechanism is a convention — like git submodules

Nothing is baked into the vault format. A sub-vault is a small pointer file plus UI behaviour: any file named *.link.json is treated as a link, and renders as an expandable folder at its own path. It really is "just create some files" — write the pointer, sgit commit, sgit push.

# demos/acme.link.json → renders as folder demos/acme
{ "vault_id": "75f1c88be33d", "ref_id": "lk-37939d3b9b1a", "label": "ACME PoC" }

Two levels of setup:

The same pointer convention covers external resources (a YouTube video, an image, a page) — with a privacy default worth advertising: external embeds are click-to-load, showing a placeholder until the user acts, so opening a vault never phones home on its own; and external frames get no bridge and a visible "cannot read this vault" banner.

Apps read across the boundary

To an app, an inner-vault file is just another path: sg.vfs.readText('patients/alice/score.json') auto-opens the child read-only with the stored key — no prompt fires during a read, and if no key is available the read simply fails: the zero-knowledge boundary holds. This is the workflow that lets one dashboard read across many per-user vaults without anyone seeing raw vault UI.

Extract & embed — one primitive, three techniques

The reverse direction: pull a folder out into its own vault (separate lifecycle, separate sharing, separate history) and bring it back into the parent's app. Extraction is one call from inside a vault app:

await sg.vault.create({
  label: 'Q3 Report', seedFrom: 'self:reports/q3',
  returnKey: true, custody: true, link: { path: 'sub/q3-report' }
})

The new vault gets a strong random passphrase; the seed copy structurally cannot carry .vault/** — so a child can never inherit the parent's owner secrets or tokens. Then choose how to surface it: a read-only sub-vault link (the default, and the recommended one today), a live embedded app via sg.vault.embed (host-managed key handshake — the key never touches the URL or storage; the frame is sandboxed to scripts only, and the host refuses escape-hatch sandbox tokens even if asked), or a cross-vault mount where the child performs operations under its own grants — your app never gains cross-vault write authority itself.

Honesty notes: the mount technique's credential resolution is explicitly a trial-only stub in current code — prefer the link or embed forms for real users today. And treat an extract-and-link split as an organisational boundary (lifecycle, sharing, history), not a confidentiality boundary from the parent's own readers. Sub-vault access from the sgit CLI is proposed, not shipped — today sub-vaults are a browser-side capability.