Skip to documentation
VibeMarket OSDEVELOPER DESKBack to your desktop ↗
VibeMarket OS — SDK documentation×
Getting startedReferenceSecuritySDK 0.2.0 · Developer preview
Addressvibechain.com/os/docsDesktop →
VIBEMARKET OS SDK · DEVELOPER PREVIEW

Your app.
A window of its own.

Make a tiny game, a useful tool or a weird desktop toy. Start with a complete TypeScript project and test it in a sandboxed local window.

▤ Static web app▣ Isolated runtime↗ Bring your renderer
01 / GET STARTED

One command. Your own app.

Install @vibechain/os from npm. Use Node.js 22.23.4, or a supported patched Node 24/26 release, and npm. The MIT-licensed SDK generates a strict TypeScript project with its own build tools and a pinned SDK version.

View the npm package ↗ · Download SDK 0.2.0

Terminal · create and run
npx --yes @vibechain/os@0.2.0 init my-app
cd my-app
npm install
npm run build
npm run dev

Open the local URL printed by npm run dev. The starter, Stamp Club, lets you add stamps, minimize and restore the app, and reset it.

Change → build → restart preview.

The preview serves one immutable build. Run npm run build again and restart npm run dev after editing. It doesn’t silently swap code under an open session.

02 / CONNECT

A small API. Your own UI.

Import the SDK in your app’s TypeScript entry point. Connect inside the supplied preview or OS app host. Paint your initial UI, then call ready() to dismiss the host’s loading state.

This example expects a button with id="stamp" and a status element with id="status".

TypeScript · acknowledged state changes
import { connectVibeMarketApp } from "@vibechain/os";

const app = await connectVibeMarketApp();
const saved = await app.checkpoint.read();
// Validate your own checkpoint shape before using it.
let count = saved !== null && typeof saved === "object"
  && !Array.isArray(saved) && typeof saved.count === "number"
  && Number.isSafeInteger(saved.count) ? saved.count : 0;

const button = document.querySelector<HTMLButtonElement>("#stamp");
const status = document.querySelector<HTMLElement>("#status");
if (!button || !status) throw new Error("Missing starter controls");
button.textContent = "Stamps: " + count;

button.addEventListener("click", () => {
  button.disabled = true;
  void app.checkpoint.write({ count: count + 1 }).then(() => {
    count += 1;
    button.textContent = "Stamps: " + count;
    status.textContent = "Saved for this open app window.";
  }).catch(() => {
    status.textContent = "Couldn't save. Try again.";
  }).finally(() => { button.disabled = false; });
});

await app.ready();
window.addEventListener("pagehide", () => app.destroy(), { once: true });

The app facade exposes readiness, lifecycle, game completion and a small app-owned checkpoint. Existing Market games have a separate permissioned API; an OS app cannot call those methods.

03 / FIT THE WINDOW

Make a good desktop neighbor.

Use the surface size for responsive layout. Stop animation, timers and audio when inactive. Do useful work in response to input, and avoid continuous polling.

TypeScript · lifecycle subscription
const unsubscribe = app.lifecycle.onChanged(({ active, width, height }) => {
  // Resize your canvas or layout to the available surface.
  // Stop animation and audio when inactive.
});

const current = app.lifecycle.current();
// current: { active: boolean, width: number, height: number }

// When your app is finished:
unsubscribe();
app.destroy();

Suspending an untrusted app removes its frame. Restoring creates a fresh frame and SDK connection, then restores the last checkpoint accepted by the host for that open window. Don’t rely on an asynchronous unload handler to save changes.

04 / FINISH THE ROUND

Your splash. Shared results.

Give players your own introduction and loading artwork. Call ready() after the first usable frame is ready. When the round ends, call game.finish({ score }) to hand the final screen to VibeMarket OS.

TypeScript · complete a game
import { connectVibeMarketApp } from "@vibechain/os";

// Render your own loading splash, then connect and prepare the game.
const app = await connectVibeMarketApp();
await app.ready();

// When the round ends, report its final score once.
await app.game.finish({ score: 125 });
// The host owns the results screen and any confirmed Cash award.

The results screen shows the game score and any existing Cash reward confirmed by the host. Guest practice Cash is labeled separately. While a reward is being confirmed, the host keeps the same run available for recovery.

A reported score does not authorize a payout. Cash requires an eligible host-managed run and its existing server scoring, funding and daily-limit rules. Local previews and games without a registered reward policy show the score without a Cash award.

Report one nonnegative integer score per game session, up to 1,000,000,000. A retry with the same score is acknowledged without completing the game twice; a different second score is rejected. Use the host’s play-again control for a new round. The same completion method is available on connectGameSdk().

05 / REMEMBER A LITTLE

Save progress as you go.

checkpoint.read() returns null on the first open. Validate your own data shape before using it. checkpoint.write(value) resolves when the host’s acknowledgment arrives. If suspension interrupts that reply, the host may already have accepted the state.

The checkpoint lives in host memory for that app installation, version, identity and open window. Closing, resetting or revoking the app, or changing its package or identity, clears it. It is not a disk save, cloud save or browser-restart guarantee.

Checkpoint limits
BoundaryLimit
UTF-8 JSON size32 KiB
Nested depth8
Total values2,048
Keys per object / items per array64 / 256
Individual string8 KiB UTF-8

Use plain JSON values and finite numbers. Functions, accessors, cycles, sparse arrays and prototype-manipulation keys are rejected. Store state, not image bytes or a growing event log.

06 / PACKAGE IT

Bundle the app. Keep it self-contained.

The starter generates a manifest for you. Its reserved category identifies the app facade. Keep collectionId: null and permissions: [].

vibemarket.json · app example
{
  "schemaVersion": 1,
  "sdkVersion": 1,
  "id": "stamp-club",
  "version": "0.1.0",
  "name": "Stamp Club",
  "tagline": "A tiny stamp collection for your desktop.",
  "entry": "index.html",
  "category": "vibexp-app",
  "collectionId": null,
  "permissions": []
}

Bundle dependencies and use relative asset paths. Deliver built output—not a server, install script or node_modules. Remote scripts, fonts, APIs, workers and wallet libraries aren’t part of this app contract.

Terminal · build a submission archive
npm run build
npm run pack

This produces app.zip for the development publisher. Packing an app does not publish it to the live OS.

07 / TRUST BOUNDARIES

Your app owns its content.

The host owns the surrounding desktop. Apps run in an opaque-origin, script-only sandbox and connect through a private session port. The host validates the app methods and bounds accepted checkpoint and message work.

Available

  • Render your own static web app.
  • Read the window’s activity and size.
  • Read and write your own bounded checkpoint.
  • Tell the host your UI is ready.
  • Report one completed game’s score.

Outside the app API

  • Account profiles and inventory.
  • Setting rewards, purchases, trades and wallet calls.
  • Steam commands and native IPC.
  • Parent DOM, arbitrary storage and HTTP relay.

Don’t include secrets in an app bundle or checkpoint. The iframe boundary and CSP do not provide a hard CPU, GPU or memory quota, or a complete outbound-network firewall. An immutable package identifies the admitted package; it does not attest every later instruction executed by a malicious app.

The host drops the frame on suspension and revokes its connection on terminal faults. Native Steam and mobile execution of third-party apps remains disabled pending separate host qualification.

08 / API REFERENCE

Everything in the app facade.

MethodResult
connectVibeMarketApp(signal?)Promise<VibeMarketAppSdk>. Connect to the current host session.
app.ready()Promise<void>. Acknowledge initial UI readiness.
app.game.finish({ score })Promise<void>. Report completion and show host-owned results. Does not set a Cash award.
app.checkpoint.read()Promise<AppValue>. Read this open app’s state.
app.checkpoint.write(value)Promise<void>. Await the host’s acceptance.
app.lifecycle.current()Read-only { active, width, height }.
app.lifecycle.onChanged(listener)Returns an unsubscribe function.
app.destroy()Close the connection and clear subscriptions.

Requests are limited to 10 per second, 4 in flight and 5,000 over an open-window session, including remounts. The session lasts up to 15 minutes; an individual request times out after 10 seconds. Treat a closed connection as terminal and use the host’s reopen/reset controls.

09 / WHAT YOU CAN SHIP TODAY

Build and test locally.

This is a developer preview. Create, build, package and test your app locally. Production app submissions and approval are not available yet.

A public app directory, developer approval and production publishing are separate release work. The preview does not grant inventory, payments, rewards or native privileges. Existing Market games keep their existing host contract.

Start building ↑
▣ SDK documentationDeveloper preview · browser apps