Core concepts

Permissions

Every capability an app has is declared in its manifest, approved by the user at install time, and re-checked by the host on every single SDK call. This page is the complete model.

readwriteainetwork

The permissions object

Four keys, all optional. Anything else you add is ignored with a warning, so a typo silently removes a capability — check spelling carefully.

json
{
  "permissions": {
    "read":    ["home/Documents", "home/Pictures"],
    "write":   ["home/Documents"],
    "ai":      true,
    "network": ["https://api.example.com/v1/"]
  }
}
FieldTypeDescription
readstring[]Home-relative directories the app may read. [] grants only the private sandbox.
writestring[]Home-relative directories the app may write, create files in and delete from. [] grants only the private sandbox.
aibooleantrue enables window.vos.ai. Anything else disables it.
networkstring[]Allowed https:// origins, optionally with a path prefix. Up to 32 entries.

Presence vs. value

The most important rule in the model: the presence of a key declares the capability; its value scopes it.

DeclarationResult
key omittedCapability off. Every related SDK call rejects with “app lacks … permission”.
"read": []Capability on, scoped to the app's private sandbox only. No user prompt for shared folders.
"read": ["home/Documents"]Capability on, sandbox plus the listed folders — the user approves them at install.
"network": []Capability declared with no targets — every request is blocked. Omit the key instead.

Nothing is granted implicitly

Requesting write does not imply read. Most apps need both keys.

Filesystem scopes

Filesystem access is directory-scoped. A grant on home/Documents covers that folder and everything beneath it, but nothing above it.

json
// The most common configuration: the app stores its own data and
// nothing else. Grants home/.apps/<appId>/ with no directory prompt.
"permissions": { "read": [], "write": [] }
  • Private sandbox: home/.apps/<appId>/ — always available once the capability is declared. Read the exact path from app.getInfo().sandboxPath.
  • Shared folders: use these only when the user should see and manage the files with other apps — documents, images, exports.
  • No traversal: paths containing .. are rejected outright.
json
// A game, a calculator, a clock: no capabilities at all.
// Omit the key entirely — every window.vos.fs / ai / net call is rejected,
// but window.vos.app.getInfo() still works.
{ "vibe": 1, "name": "Calculator" }

Reserved paths

Three areas are owned by the operating system. They are stripped from any allow-list and blocked at call time even if a grant somehow contains them:

PathWhy
home/.systemOS state and configuration.
home/.TrashDeleted items belong to the user and the OS.
home/.appsEvery app's private data. You reach your own sandbox through sandboxPath and can never read another app's.

AI

"ai": true enables window.vos.ai. The user approves it at install time alongside filesystem scopes, and the actual model usage is charged against their plan — you need no keys and no billing of your own.

Permission is not entitlement

Having the permission does not guarantee AI works: the user may be signed out, on a plan without AI, or out of credits. Check with ai.canUse() and always handle ai.chat rejections.

Network

Each entry in network is an https:// origin with an optional path prefix; the host builds a CSP from the list and re-validates every net.fetch URL against it. Wildcard hostnames, credentials in URLs, and private or internal addresses are never allowed. Full details in the vos.net reference.

Install & upgrade

  • At install the user sees exactly what you requested and approves or declines. Sandbox-only apps install with no directory prompt.
  • Publishing an update that widens the scope re-prompts the user on upgrade. Until they accept, the previous grant applies — so ship capability checks, not assumptions.
  • Users can revoke network origins after install from Web Apps → app details → Permissions.
  • The manifest inside your HTML is never trusted at runtime; the host always reads the stored grant. There is no SDK call that changes permissions.

Read what was actually granted at boot and adapt:

javascript
const { permissions } = await window.vos.app.getInfo();
// { hasRead: true, hasWrite: false, hasAi: false, hasNet: true, network: [...] }
if (!permissions.hasWrite) enterReadOnlyMode();

Common configurations

App typePermissions
Game, calculator, clock — no data at allomit permissions
Notes, to-do, settings-heavy app storing its own data{ "read": [], "write": [] }
Editor working on the user's documents{ "read": ["home/Documents"], "write": ["home/Documents"] }
Image viewer — reads photos, saves nothing shared{ "read": ["home/Pictures"], "write": [] }
AI writing assistant with local drafts{ "read": [], "write": [], "ai": true }
API-backed client with an offline cache{ "read": [], "write": [], "network": ["https://api.example.com/"] }

Least privilege wins reviews

Reviewers compare what you request against what your code actually does. An unused home grant or an unused AI flag is a common reason changes are requested. Ask for the minimum, and explain in your description why you need anything broad.

Legacy formats

Very old manifests declared permissions as a string array. The host still accepts these and converts them, with a warning to migrate.

json
// Deprecated string form — still accepted, migrate when you next update.
"permissions": ["fs.home"]    // → read: [], write: []
"permissions": ["fs.read"]    // → read: []
"permissions": ["fs.write"]   // → write: []

// Hard error — this one is no longer valid at all:
"permissions": ["fs.documents"]
  • "fs.documents" is rejected with a hard error — replace it with the structured object.
  • Unknown keys inside permissions are ignored with a warning; a wrong type (for example a string where an array is expected) rejects the manifest.