Publisher guide
Develop on your machine.
You do not need the platform while you build. The devkit runs your app in the exact sandbox the store uses, with every host flag at your fingertips, and it comes two ways: as a folder of static files for any web server, or served by vibe, the command line that adds the server's own validator and packer. No account, no backend.
Two ways to run locally
Both run the same dev host: the same loader page and inline SDK byte for byte, the shell's own frame host, the same toolbar, simulated players and data inspector. They differ in what sits behind the page.
- The devkit is a zip of static files. Unzip it, serve the folder with a web server you already have, open
/devkit/. Nothing to install if you have Node, Python or an editor with a live server. vibe devis the command line serving those same files and your folder from one local server. It opens the browser for you, and because the server's rules are built into the tool, the Package tab and the terminal show the real validator's verdict after every save, and Download .zip refuses a package the server would reject.
| Devkit (static files) | vibe dev (command line) | |
|---|---|---|
| Setup | Unzip, serve the folder with a static server (npx serve ., python -m http.server, VS Code Live Server), open /devkit/. | Download one executable, run vibe dev ./app. It serves and opens the browser. |
| Needs | A static server you already have: Node, Python or an editor extension. | Nothing else: one self-contained file of about 50 MB for Windows, macOS or Linux. |
| Sandbox and host flags | The production loader and SDK, every host flag, two players, the State tab, reload on save. | The same files, embedded in the tool. Identical behaviour. |
| Validation | Dev checks for the common blockers; the server validates at upload. | The server's full validator in the Package tab and in the terminal after every save, with the same codes, files and lines. |
| Packaging | Download .zip zips the folder. | Download .zip and vibe pack validate first and refuse a package the server would reject. The zip is deterministic. |
| File discovery | Through references and folder listings; a root file that nothing references by name is missed. | Every file: the tool lists the folder itself. |
| Other devices | Serve on your network yourself and open the URL on the phone. | --host 0.0.0.0 serves phones and tablets; the terminal prints the addresses. |
| CI | Not meant for it. | vibe validate as a gate, vibe pack for the artefact. |
Which one? Start with the devkit when you already have a static server or want nothing new on your machine; everything in the next sections applies to both. Take vibe when you want the server's verdict before you upload, assets with computed paths, phones on your network, or a check in CI. Switching is free: both read the same folder, and the host keeps the same settings in your browser.
The devkit
A guest app cannot run on its own: its files are rewritten at publish time, the SDK only wakes up after a handshake with the host, and every call goes through a message port. The devkit is that host, reduced to what you need locally. It bundles your folder the way the publish pipeline does, creates the same two-frame sandbox from the same loader page and the same inline SDK, and answers window.vibe from your browser instead of the platform.
- Same sandbox. The loader page and the SDK are byte for byte the production files, so the Content Security Policy, the opaque origin and the tripwires are the real ones.
- No account, no network. Storage lives in your browser's IndexedDB, players are simulated, messages travel between frames of the same page.
- Reload on save. The host watches your files and relaunches the app with fresh frames, like a real launch.
- Starter included. The zip contains a minimal starter app in
app/to replace with your game, typings and an editor config.
Get started with the devkit
# 1. unzip vibe-devkit.zip into a new folder, then serve that folder over HTTP
npx serve . # http://localhost:3000
# or: python -m http.server 8000
# or: the "Live Server" extension in VS Code
# 2. open the dev host
http://localhost:3000/devkit/
# 3. edit app/ — every save relaunches the appThe devkit cannot run from file://; any static server works. With the command line, vibe dev ./app replaces steps 1 and 2: it serves this same page and your folder and opens the browser. The unzipped folder looks like this:
my-game/
devkit/ the local host (do not edit)
index.html the page you open
devkit.js bundles your files, runs the sandbox, answers window.vibe
guest-loader.html the production loader page with the sandbox policy
vibe-sdk.js the production guest SDK, injected exactly as in the store
vibe-sdk.d.ts typings for window.vibe
app/ your package: vibe-app.json, index.html, scripts, assets
jsconfig.json editor support for window.vibe in app/Keep your game in app/, or type another folder into the "App folder" field (for example ../my-game/). Only that folder goes into the package you upload, so typings, notes and the devkit itself stay out of it.
Host flags
The toolbar is grouped into App, Device, Context and Players, with Reset data and Download .zip on the right; each frame carries its own player, online switch and Pause. Together they drive everything the real host can do to your app, and each flag arrives the same way it does in the store:
| Control | What your app sees |
|---|---|
| Theme | ctx.theme at start, then the theme event with 'dark' or 'light'; data-theme on <html> and the page's color-scheme follow too. The devkit's own styling follows the switch; the first run takes the operating system's setting. |
| online (per frame) | ctx.online and the online / offline events. Offline, players.search fails with OFFLINE and messages.send returns queued: true; queued messages go out when the frame is back online. The frame's state chip shows live, offline, paused or stopped. |
| Pause / Resume (per frame) | pause / resume events. Switching browser tabs sends them too, like the shell. |
| Phone / Tablet / Desktop, Rotate | the frame size, ctx.viewport and the resize event with viewport, safeArea and hostControls |
| Safe area | ctx.safeArea insets of a notch and home indicator, drawn as hatched bands over the frame. Keep controls out of them. |
| Menu handle (the tab at the top of each frame) | the shell's own control and the area it covers (dashed), ctx.hostControls. Keep controls out of it. Tapping it, or holding Esc in your app, opens the shell menu in the real shell; here it sends pause until you press Resume. |
| Locale | ctx.locale, applied at launch |
| Player (per frame), Two players | each frame's header selects the simulated player running there; "Two players" adds a second frame. Picking the player of the other frame swaps the two. |
| Reset data | clears vibe.cloud, vibe.local, vibe.db and pending messages of the app for every player |
| Download .zip | zips the app folder for upload in the publisher console; with vibe dev it validates first |
const ctx = await vibe.ready; // theme, online, locale, safeArea, viewport, player: all set from the toolbar
vibe.on('theme', (theme) => applyTheme(theme)); // Theme button
vibe.on('offline', () => showBanner('Offline')); // "online" switch of a frame
vibe.on('online', () => hideBanner());
vibe.on('pause', () => stopLoop()); // Pause button, or switching browser tabs
vibe.on('resume', () => startLoop());
vibe.on('resize', ({ viewport, safeArea }) => layout()); // Device, Rotate, Safe areavibe.fullscreen.enter() makes the frame fullscreen. External links (vibe.openUrl or a link with target="_blank") ask for confirmation first, as the real host does. vibe.exit() stops the app and shows a Relaunch button.
Players and messaging
Two simulated players exist out of the box, Alex and Sam; add more with the Players group's Add button. Each has a stable, app-scoped id exactly like on the platform. vibe.players.pick() shows a picker of the others, all of them as friends (vibe.players.addFriend() only asks you to confirm), and vibe.players.search('sa') finds them by name; vibe.messages.send({ to }) delivers to them: live into the second frame when it is open and online, otherwise into their inbox, where vibe.messages.pending() picks it up on the next launch.ack() removes a message.
Enable "Two players" to test a turn-based game end to end on one screen. On the platform that scenario needs two accounts and two devices, so this is where the devkit saves the most time.
Data
vibe.cloud, vibe.local and vibe.db are kept in IndexedDB per app and player with the limits the server enforces: 64 KB per cloud value, 200 keys and 4 MB per player and app, 25 MB of device storage per app. Exceeding them produces the same QUOTA errors. The State tab shows every key and pending message and updates as the app writes; delete single keys there or reset everything from the toolbar.
Checks and packaging
The Package tab lists the files that would go into the zip, the entry scripts and styles, the module order and a set of dev checks: the mistakes that keep an app from running at all, such as a missing vibe.exit, inline scripts, references to files that are not in the folder, circular imports or external URLs. When the app behaves, Download .zip packages the folder for the publisher console. The badges on the Console and Package tabs count the warnings and errors you have not looked at yet.
vibe, the command line
vibe is a single executable that runs the platform's own validator on your machine, packs upload-ready zips, and serves the devkit with one command. It is the same code the server runs when you upload, so a clean run means the upload will pass. No .NET, no Node, no account needed.
Download and install
One self-contained file per platform, about 50 MB.
Download vibe for Windows (x64)
# PowerShell: put vibe somewhere on your PATH, for example
mkdir ~\bin -Force; Move-Item ~\Downloads\vibe-win-x64.exe ~\bin\vibe.exe
vibe --versionWindows SmartScreen may warn about a new download the first time. Choose More info, then Run anyway; compare the SHA-256 in manifest.json if you want to be sure.
manifest.json lists the current version with size and SHA-256 per file.
validate
vibe validate <folder|file.zip> runs every rule the server applies at upload: archive safety, file types, manifest, index.html, CSS, JavaScript, SVG, references and module order. Findings come with the same codes, files and lines the publisher console shows.
$ vibe validate ./app
vibe 1.0.0 checking ./app (folder, 5 files)
error JS_EXIT_MISSING No script calls vibe.exit(). Every app must give the player a way to leave …
error JS_FORBIDDEN_API app.js:12 'localStorage' is not available to guest apps.
warning REFERENCE_UNUSED helper.mjs 'helper.mjs' is not referenced by index.html or any module. It will still be shipped.
REJECTED: 2 errors, 1 warning. The server would refuse this upload.| Exit code | Meaning |
|---|---|
0 | The server would accept this package (warnings may exist). |
1 | The server would reject it; the findings say why. |
2 | Usage or I/O error: unknown option, missing folder, unreadable file. |
--json prints the same report as JSON for editors, scripts and the devkit. The codes are documented under Validation & sandbox.
$ vibe validate ./app --json
{
"tool": "vibe", "version": "1.0.0", "input": "./app", "kind": "folder", "files": 5,
"valid": false, "errors": 2, "warnings": 1,
"issues": [
{ "severity": "error", "code": "JS_FORBIDDEN_API", "file": "app.js", "line": 12, "message": "'localStorage' is not available to guest apps." }
]
}pack
vibe pack <folder> zips the folder the way the server wants it: hidden files and folders, node_modules and OS clutter are left out, entries are sorted and timestamps fixed so the same files always give the same zip and hash. The zip is validated before it is written, and nothing is written when the rules fail unless you pass --force.
$ vibe pack ./app
…
OK My Game 1.2.0: 0 errors, 0 warnings. The server would accept this upload.
Wrote /work/my-game-1.2.0.zip (5 files, 38.2 KB, sha256 4791…3088).
$ vibe pack ./app -o build/my-game.zip # choose the file name
$ vibe pack ./app --force # write even when the rules fail (the server will still reject it)dev
vibe dev <folder> is the devkit with the tool behind it. It serves the devkit page and your app from one local server and opens the browser. Compared with a plain static server you get:
- The real validator in the Package tab. The server's verdict with every finding, refreshed on each reload, and the same verdict in the terminal after every save.
- Download .zip packs with the server rules, exactly like
vibe pack, and refuses a package the server would reject. - Every file is found. The server lists folders even when they contain
index.html, so assets with computed paths are never missing. - Phones and tablets.
--host 0.0.0.0serves the devkit to other devices on your network; the terminal prints the addresses.
$ vibe dev ./app
vibe 1.0.0 dev server
App folder: /work/my-game/app
Listening on http://localhost:4300
Devkit: http://localhost:4300/devkit/?app=../app/
The validator runs on every change; press Ctrl+C to stop.
[14:02:11] OK My Game 1.2.0: 0 errors, 0 warnings. The server would accept this upload.
$ vibe dev ./app --port 5000 --no-open # another port, do not open a browser
$ vibe dev ./app --host 0.0.0.0 # also reachable from phones and tablets on your networkIn CI
The exit codes make validate a natural gate in a pipeline, and pack produces the artefact you upload. The Linux binary runs on the usual CI images without extra setup.
# GitHub Actions (Linux runner)
- run: curl -fsSL /downloads/vibe-linux-x64 -o vibe && chmod +x vibe
- run: ./vibe validate ./app # fails the job when the server would reject the package
- run: ./vibe pack ./app -o my-game.zipLimits
Devkit
- Files are found through every reference in
index.html, CSS and scripts, the manifest icon and the server's directory listings. Static servers answer a folder that containsindex.htmlwith that file, so the app root itself is never listed: a root file that nothing references by name is not included. Subfolders are listed as soon as one file inside them is referenced, solevels/1.jsonnext tovibe.asset(`levels/${n}.json`)brings the whole folder along. The Package tab says what was found and why. Withvibe devevery file is found, because the tool serves the folder itself. - Push notifications and the store listing are platform features; the devkit has neither.
- The devkit runs in the browser you open it with. Test on a phone by serving your folder on the local network and opening the same URL there (
vibe dev --host 0.0.0.0does that for you).
Command line
- The tool does not upload or publish; that stays in the publisher console. Uploading from the command line with an API token is planned.
- Validation is the server's static validation. The runtime sandbox is what the devkit shows you; the two together are what the platform does.
- Binaries are not yet code-signed; see the notes per platform above. The SHA-256 in manifest.json is the way to verify a download.
File rules, the manifest, index.html, assets and modules.
Everything on window.vibe: storage, players, messages, events, errors, quotas.
Every rule and error code the validator applies.
Upload the zip, go live, update installed players.