Build an app

Secure sandbox

Every VibeOS app runs fully isolated and can only touch the system through a narrow, host-enforced gateway. Here's exactly what keeps apps — and your users — safe.

The security model in one line

An app is untrusted HTML running inside a locked-down iframe. It has no ambient authority: the only way out is the window.vos SDK, and every call it makes is re-checked by the host against a grant the app cannot see or change. Capability is opt-in, scoped, and revocable.

Isolation guarantees

The app document is rendered in a sandboxed browser frame, separate from the desktop and from every other app. Within that frame:

  • No access to the host page. The app can't read the VibeOS desktop DOM, cookies, storage, or other apps' windows.
  • No external network for core logic. Apps can't load scripts, styles, or libraries from external URLs — all CSS and JavaScript must be inlined in the single html field, so there's no hidden phone-home channel.
  • No raw filesystem or OS handles. There is no fs, no native APIs — only the brokered window.vos surface.
  • A private home by default. Each app gets its own sandbox directory at home/.apps/<appId>/ that no other app can reach.

One file, no runtime fetch

Because everything an app runs is bundled into the manifest and reviewed before publishing, there's no way to swap in new code after approval. What review sees is what runs.

The brokered SDK boundary

The app never performs privileged work itself. It asks the host to, by calling a window.vos method. Each call is a message across the sandbox boundary; the host validates it, performs the action with its own authority, and returns a result. The app holds no keys and no file handles — it only holds promises.

javascript
// Every call crosses the boundary and is re-checked host-side.
// The app cannot widen its own scope — the grant lives outside the iframe.

await window.vos.fs.writeFile("home/Documents", "note.md", text);
// host: is "home/Documents" inside the installed write grant? ✔ allowed

await window.vos.fs.readFile("home/Pictures", "secret.png");
// host: "home/Pictures" not in read grant -> rejected, Promise rejects

await window.vos.fs.readFile(".system", "keys.json");
// host: reserved path ✗ always rejected, regardless of grant

Permission structure

Permissions are declared in the manifest and are directory-scoped for the filesystem and a simple capability flag for AI.

json
{
  "permissions": {
    "read":  ["home/Documents"],   // directories the app may read
    "write": ["home/Documents"],   // directories the app may write
    "ai": true                      // optional AI gateway access
  }
}
FieldTypeDescription
readstring[]Home-relative directories the app may read. Presence of the key (even []) declares the filesystem read capability.
writestring[]Home-relative directories the app may write. An empty [] means sandbox-only access at home/.apps/<appId>/.
aibooleanGates the AI gateway (window.vos.ai). When omitted or false, every AI call is rejected.

Declaring vs. granting

The manifest declares what an app wants. The user grants it at install time. These are different things:

  • The presence of read/write declares the filesystem capability; the array contents declare which directories.
  • An empty array ([]) declares filesystem use but scopes it to the app's private sandbox only — no shared directories, no prompt.
  • Requesting a shared directory like ["home/Documents"] triggers an install-time approval prompt the user must accept.
  • ai: true is approved at install alongside any filesystem scopes.

Reserved paths

These are blocked for every app, no matter what the manifest requests:

  • .system — OS internals.
  • .Trash — deleted items.
  • Other apps' sandboxes at .apps/<id>.
  • Any hidden/dotfile path and .. traversal segments — rejected by the manifest validator before the app is ever accepted.

How enforcement works

Declaration is not trust. The grant is stored host-side and consulted on every single call:

  • Path is normalised first. The host resolves and canonicalises the requested path, so tricks like home/Documents/../Pictures can't escape the grant.
  • Checked against the installed grant. Read calls require a covering read entry; write/delete calls require a covering write entry; AI calls require ai.
  • No widening at runtime. The app can't edit its own manifest or grant — the only way scope changes is a new install/upgrade the user re-approves.
  • Fail closed. Anything not explicitly allowed is denied, and the corresponding window.vos promise rejects.

The manifest is a request, never a guarantee

An app that lists ["home"] still only gets what the user approved. Even with a grant, every call is validated again at the moment it runs — there's no cached "trusted" state an app can exploit.

Review backs the runtime

Runtime isolation is paired with up-front review. Before an app is published, an automated AI pass plus a human reviewer check the manifest and inline code for over-broad permissions, undisclosed behaviour, and unsafe patterns — see submission & review. Combined with the sandbox, this means users approve a known, scoped, immutable app.

Build with least privilege

Request only the directories you use, skip ai if you don't call it, and prefer the private sandbox. See the manifest reference and the SDK reference.