SDK reference
vos.app & events
Metadata about the running instance — its name, id, private sandbox path and the permissions actually granted — plus the two events VibeOS dispatches into your window.
app.getInfo
app.getInfo
none — always availablegetInfo(): Promise<{
name: string;
id: string;
closeBehavior: "close" | "minimize";
sandboxPath: string;
permissions: {
hasRead: boolean;
hasWrite: boolean;
hasAi: boolean;
hasNet: boolean;
network: string[];
};
}>returns metadata for the running instance
The one capability every app has without declaring anything. It is also the correct way to discover your sandbox directory, and calling it lazily creates that directory if it does not exist yet — so call it during boot before your first write.
const info = await window.vos.app.getInfo();
// {
// name: "Markdown Notes",
// id: "markdown-notes",
// closeBehavior: "close",
// sandboxPath: "home/.apps/markdown-notes",
// permissions: {
// hasRead: true, hasWrite: true, hasAi: false, hasNet: true,
// network: ["https://api.openlibrary.org/"]
// }
// }The info object
| Field | Type | Description |
|---|---|---|
| name* | string | The app's display name as installed. |
| id* | string | Stable identifier for this installed app. Useful as a storage key. |
| closeBehavior* | "close" | "minimize" | What the window does when the user closes it, as declared in your manifest. |
| sandboxPath* | string | Home-relative path to your private directory, e.g. home/.apps/<appId>. Created on demand the first time you ask for it. |
| permissions.hasRead | boolean | Whether filesystem reads are available at all. |
| permissions.hasWrite | boolean | Whether filesystem writes are available. |
| permissions.hasAi | boolean | Whether window.vos.ai may be used. |
| permissions.hasNet | boolean | Whether window.vos.net may be used. |
| permissions.network | string[] | The network origin patterns the user approved, as declared. |
These are the real, installed permissions
The values come from the grant the user approved — not from the manifest embedded in your HTML. If a user revokes an origin, permissions.network reflects that on the next call.
Adapting to permissions
A well-behaved app never assumes it got what it asked for. Users can decline directories and revoke network origins, and older installs may predate a permission you added later. Read the granted capabilities once at boot and shape the UI around them.
const { permissions, sandboxPath } = await window.vos.app.getInfo();
// Build the UI around what was actually granted, not what you asked for.
if (permissions.hasAi) enableSummariseButton();
if (!permissions.hasWrite) enterReadOnlyMode();
if (permissions.hasNet) showSyncPanel();
// Always prefer the reported sandbox path over a hard-coded one.
const dataDir = sandboxPath;- Hide controls you cannot fulfil rather than letting them fail with an error.
- If your update needs a new permission, users are re-prompted on upgrade — until they accept, the old grant applies.
- Full details in permissions.
Window events
VibeOS dispatches exactly two events into your document.
| Event | Type | Detail | When |
|---|---|---|---|
vos-ready | Event | none | Once, immediately after window.vos is installed. Does not replay. |
vos-fs-change | CustomEvent | { affectedPath?: string } | Whenever the host observes a filesystem change, debounced by roughly 200 ms. |
vos-ready
// Fired once on window as soon as window.vos exists. No detail payload.
if (window.vos) start();
else window.addEventListener("vos-ready", start, { once: true });vos-fs-change
// A CustomEvent whose detail may name the path that changed.
window.addEventListener("vos-fs-change", (event) => {
const path = event.detail && event.detail.affectedPath;
if (!path || path.startsWith(myDir)) reload();
});
// Equivalent, and usually nicer, through the fs helpers:
window.vos.fs.watch(myDir, () => reload());Prefer fs.watch
fs.watch is built on this event and adds path filtering, debouncing and an unsubscribe function. Use the raw event only when you want the unfiltered stream.
There are no theme, resize, focus or close events, and no API for opening windows, showing notifications, reading the clipboard or changing permissions. Use standard web platform events — resize, visibilitychange, prefers-color-scheme — for those needs.
Lifecycle & close
Your window can be closed or minimised at any moment, and you cannot intercept it. closeBehavior: "minimize" keeps the document alive in the background — useful for players and timers — while "close" discards it entirely.
// Save eagerly — you cannot block or intercept a window close.
window.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") flushPendingSave();
});
window.addEventListener("pagehide", flushPendingSave);- Save on a short debounce, not on close — a pending write may never run.
- Keep everything you need to restore state in a file under
sandboxPath; in-memory state does not survive a close. - Choose
minimizeonly when background continuation genuinely benefits the user — it keeps consuming resources.