VibeGames

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

CodeSeverityMeaningFix
ARCHIVE_INVALIDErrorThe upload is not a readable zip file.Re-create the zip with a standard tool; do not rename other archive formats.
ARCHIVE_TOO_LARGEErrorThe zip or its uncompressed content exceeds the size limit.Compress audio and images; drop unused files.
ARCHIVE_TOO_MANY_FILESErrorMore than 2000 entries.Combine sprite sheets, remove build leftovers.
ARCHIVE_UNSAFE_PATHErrorAn entry uses an absolute path, '..', a backslash or is a symlink.Zip the folder content with relative paths only.
ARCHIVE_NESTED_ARCHIVEErrorA zip (or other archive) inside the package.Remove it; everything must be plain files.
FILE_NAME_INVALIDErrorCharacters outside letters, digits, '.', '_', '-', or more than 8 folder levels.Rename the file; avoid spaces and unicode in names.
FILE_TYPE_NOT_ALLOWEDErrorAn extension outside the allow-list (.htm, .xml, .exe, .wasm …).Convert or remove it. Only index.html may be HTML.
FILE_CONTENT_MISMATCHErrorA binary file's content does not match its extension.Export the file again in the right format.
FILE_NOT_UTF8ErrorA text file is not valid UTF-8.Save as UTF-8 without a byte-order mark.
FILE_TOO_LARGEErrorA single file exceeds the per-file limit.Split or compress the file.
MANIFEST_MISSINGErrorNo vibe-app.json at the root of the package.Add the manifest next to index.html.
MANIFEST_INVALIDErrorThe manifest is not valid JSON or a field breaks the rules.The message names the field; see Package & manifest.
ICON_INVALIDErrorThe icon is not a PNG inside the package or not 32 to 512 pixels square.Export a square PNG, for example 256x256.
ENTRY_MISSINGErrorNo index.html.Add the entry fragment.
ENTRY_STRUCTUREErrorThe 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_ELEMENTErrorhtml, head, body, meta, base, title, iframe, object, embed, non-stylesheet link …Remove the element; the host provides the document.
HTML_FORBIDDEN_ATTRIBUTEErrorAn on* handler, javascript: URL, ping, or a form action.Attach handlers from JavaScript.
HTML_INLINE_SCRIPTErrorA <script> with inline content.Move the code to a file and reference it with src.
HTML_EXTERNAL_REFERENCEErrorA src/href/srcset/poster that points outside the package.Ship the file inside the package.
HTML_LINK_TARGETErrorA 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_SYNTAXErrorA script does not parse.Fix the syntax; the message includes the line.
JS_FORBIDDEN_APIErrorlocalStorage, cookies, WebSocket, Worker, RTCPeerConnection, window.open, top/parent …Use the vibe.* equivalents; see The runtime sandbox.
JS_NAVIGATIONErrorAssigning location or calling location.assign/replace/reload.Use vibe.openUrl() for external links.
JS_EXTERNAL_URLErrorfetch, XMLHttpRequest, import() or a module specifier with an absolute URL.Ship the data in the package and load it with a relative path.
JS_EVALErroreval, new Function, or code passed as a string to setTimeout/setInterval.Use plain functions.
JS_EXIT_MISSINGErrorNo script references vibe.exit.Add a button or menu entry that calls vibe.exit().
JS_DYNAMIC_URLWarningfetch()/XMLHttpRequest/import() with a computed URL.Fine if it resolves to vibe.asset(); anything else fails at runtime.
CSS_EXTERNAL_REFERENCEError@import or url() pointing outside the package.Bundle fonts and images; reference them relatively.
CSS_FORBIDDENErrorexpression(), behavior: or -moz-binding.Remove the legacy construct.
SVG_FORBIDDENErrorScript, event handler, foreignObject, <a>, external href or external url() in an SVG.Clean the SVG (export as plain SVG) or rasterize it.
JSON_INVALIDErrorA .json file does not parse.Validate the JSON.
REFERENCE_MISSINGErrorA 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_UNUSEDWarningA file is not referenced by anything.Harmless; remove the file to shrink the package.
MODULE_CYCLEErrorES 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:

CapabilityStatusUse instead
Network requests to any origin, including the VibeGames APIblockedShip data in the package; use vibe.cloud for server state.
localStorage, sessionStorage, indexedDB, Cache API, cookiesthrowvibe.local, vibe.db, vibe.cloud
window.open, target="_blank" without the host, popupsblockedvibe.openUrl() or a plain external link; the player confirms.
Navigation of the frame, the parent or the top windowblockedYour app is a single screen; manage views yourself.
Nested iframes, Workers, service workers, WebTransportblockedKeep the work on the main thread or ask the operator for a feature.
Form submissionblockedHandle the submit event; it never leaves the frame.
WebRTCtripwireNot blockable by policy in today's browsers; the SDK reports its use, the host stops the app and flags the version.
Fullscreen, pointer lockhost-mediatedvibe.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:

AreaBlockedWorks
Networkfetch, XMLHttpRequest, WebSocket and sendBeacon to any origin, also to the VibeGames API; images, scripts and import() from other sitesYour own files: relative imports, images and fonts, CSS url(), fetch(vibe.asset(...))
StoragelocalStorage, sessionStorage, IndexedDB, cookies, the Cache APIvibe.cloud, vibe.local and vibe.db
Navigationwindow.open, navigating the top or parent page, nested frames, form submissionLinks through vibe.openUrl() (the player confirms)
Codeeval and new Function, Workers, WebRTCYour scripts and ES modules
DevicesCamera and microphone, location, USB, Bluetooth and similarGamepads, autoplaying audio, pointer lock (when declared)