Publisher guide
Validation & sandbox
Two layers protect players: a static validation of every upload, and a browser sandbox at runtime. Validation gives you fast feedback; the sandbox is the real boundary.
How validation works
After an upload the version is Validating. A background worker reads the archive, types every file, parses the manifest, the entry fragment, every stylesheet, script, SVG and JSON file, resolves every reference and orders the module graph. The result appears on the version page with one line per issue, including file and line number.
- Errors reject the version. Fix them and upload again.
- Warnings are informational. The version is published with them.
- Archive-level problems stop the run early, so a second upload may reveal further issues.
Validation is a lint, not the security boundary: it catches mistakes early and explains them. The sandbox below blocks the same things at runtime even if a script hides them from the validator.
The exit rule
Put the exit where players expect it: a pause menu, a title screen, a close button in the top bar of your own UI. The host keeps its own fallback, but it is meant as a safety net, not as your app's exit.
Error codes
| Code | Severity | Meaning | Fix |
|---|---|---|---|
ARCHIVE_INVALID | Error | The upload is not a readable zip file. | Re-create the zip with a standard tool; do not rename other archive formats. |
ARCHIVE_TOO_LARGE | Error | The zip or its uncompressed content exceeds the size limit. | Compress audio and images; drop unused files. |
ARCHIVE_TOO_MANY_FILES | Error | More than 2000 entries. | Combine sprite sheets, remove build leftovers. |
ARCHIVE_UNSAFE_PATH | Error | An entry uses an absolute path, '..', a backslash or is a symlink. | Zip the folder content with relative paths only. |
ARCHIVE_NESTED_ARCHIVE | Error | A zip (or other archive) inside the package. | Remove it; everything must be plain files. |
FILE_NAME_INVALID | Error | Characters outside letters, digits, '.', '_', '-', or more than 8 folder levels. | Rename the file; avoid spaces and unicode in names. |
FILE_TYPE_NOT_ALLOWED | Error | An extension outside the allow-list (.htm, .xml, .exe, .wasm …). | Convert or remove it. Only index.html may be HTML. |
FILE_CONTENT_MISMATCH | Error | A binary file's content does not match its extension. | Export the file again in the right format. |
FILE_NOT_UTF8 | Error | A text file is not valid UTF-8. | Save as UTF-8 without a byte-order mark. |
FILE_TOO_LARGE | Error | A single file exceeds the per-file limit. | Split or compress the file. |
MANIFEST_MISSING | Error | No vibe-app.json at the root of the package. | Add the manifest next to index.html. |
MANIFEST_INVALID | Error | The manifest is not valid JSON or a field breaks the rules. | The message names the field; see Package & manifest. |
ICON_INVALID | Error | The icon is not a PNG inside the package or not 32 to 512 pixels square. | Export a square PNG, for example 256x256. |
ENTRY_MISSING | Error | No index.html. | Add the entry fragment. |
ENTRY_STRUCTURE | Error | The top level is not stylesheet links, scripts and exactly one <div data-vibe-root>. | Wrap all markup in one root div; move other tags inside it. |
HTML_FORBIDDEN_ELEMENT | Error | html, head, body, meta, base, title, iframe, object, embed, non-stylesheet link … | Remove the element; the host provides the document. |
HTML_FORBIDDEN_ATTRIBUTE | Error | An on* handler, javascript: URL, ping, or a form action. | Attach handlers from JavaScript. |
HTML_INLINE_SCRIPT | Error | A <script> with inline content. | Move the code to a file and reference it with src. |
HTML_EXTERNAL_REFERENCE | Error | A src/href/srcset/poster that points outside the package. | Ship the file inside the package. |
HTML_LINK_TARGET | Error | A link that is not an absolute http(s) URL with target="_blank" (or a #anchor). | The sandbox cannot navigate; use external links or anchors only. |
JS_SYNTAX | Error | A script does not parse. | Fix the syntax; the message includes the line. |
JS_FORBIDDEN_API | Error | localStorage, cookies, WebSocket, Worker, RTCPeerConnection, window.open, top/parent … | Use the vibe.* equivalents; see The runtime sandbox. |
JS_NAVIGATION | Error | Assigning location or calling location.assign/replace/reload. | Use vibe.openUrl() for external links. |
JS_EXTERNAL_URL | Error | fetch, XMLHttpRequest, import() or a module specifier with an absolute URL. | Ship the data in the package and load it with a relative path. |
JS_EVAL | Error | eval, new Function, or code passed as a string to setTimeout/setInterval. | Use plain functions. |
JS_EXIT_MISSING | Error | No script references vibe.exit. | Add a button or menu entry that calls vibe.exit(). |
JS_DYNAMIC_URL | Warning | fetch()/XMLHttpRequest/import() with a computed URL. | Fine if it resolves to vibe.asset(); anything else fails at runtime. |
CSS_EXTERNAL_REFERENCE | Error | @import or url() pointing outside the package. | Bundle fonts and images; reference them relatively. |
CSS_FORBIDDEN | Error | expression(), behavior: or -moz-binding. | Remove the legacy construct. |
SVG_FORBIDDEN | Error | Script, event handler, foreignObject, <a>, external href or external url() in an SVG. | Clean the SVG (export as plain SVG) or rasterize it. |
JSON_INVALID | Error | A .json file does not parse. | Validate the JSON. |
REFERENCE_MISSING | Error | A path in HTML, CSS or JavaScript names a file that is not in the package. | Check the spelling and the case of the path. |
REFERENCE_UNUSED | Warning | A file is not referenced by anything. | Harmless; remove the file to shrink the package. |
MODULE_CYCLE | Error | ES modules import each other in a cycle. | Break the cycle or bundle the modules into one file. |
The runtime sandbox
Your app runs in an iframe with an opaque origin and a strict Content Security Policy. Whatever the validator missed, the browser blocks:
| Capability | Status | Use instead |
|---|---|---|
| Network requests to any origin, including the VibeGames API | blocked | Ship data in the package; use vibe.cloud for server state. |
| localStorage, sessionStorage, indexedDB, Cache API, cookies | throw | vibe.local, vibe.db, vibe.cloud |
| window.open, target="_blank" without the host, popups | blocked | vibe.openUrl() or a plain external link; the player confirms. |
| Navigation of the frame, the parent or the top window | blocked | Your app is a single screen; manage views yourself. |
| Nested iframes, Workers, service workers, WebTransport | blocked | Keep the work on the main thread or ask the operator for a feature. |
| Form submission | blocked | Handle the submit event; it never leaves the frame. |
| WebRTC | tripwire | Not blockable by policy in today's browsers; the SDK reports its use, the host stops the app and flags the version. |
| Fullscreen, pointer lock | host-mediated | vibe.fullscreen; declare the pointer-lock permission in the manifest. |
Everything your app can do is exactly what the host lets it do. This is what allows players to install any game from the store without trusting its author.
Testing your app
- Preview. The publisher console can run any version that passed validation (draft, in review, published or withdrawn) in the same sandbox players use. Open the browser developer tools to see your app's console output.
- Devkit. The devkit runs your app in the same sandbox on your machine: what fails there fails in the store.
- Local stack. The platform runs locally with the API and the shell; see Publishing → Local testing.
What the sandbox blocks at runtime, also when a static check missed it, and what works instead:
| Area | Blocked | Works |
|---|---|---|
| Network | fetch, XMLHttpRequest, WebSocket and sendBeacon to any origin, also to the VibeGames API; images, scripts and import() from other sites | Your own files: relative imports, images and fonts, CSS url(), fetch(vibe.asset(...)) |
| Storage | localStorage, sessionStorage, IndexedDB, cookies, the Cache API | vibe.cloud, vibe.local and vibe.db |
| Navigation | window.open, navigating the top or parent page, nested frames, form submission | Links through vibe.openUrl() (the player confirms) |
| Code | eval and new Function, Workers, WebRTC | Your scripts and ES modules |
| Devices | Camera and microphone, location, USB, Bluetooth and similar | Gamepads, autoplaying audio, pointer lock (when declared) |