Raw Markdown โ ยท Manifest generator โ
Game-owned releases
The Cubby registry lists a stable manifest_url. A game's own repository and HTTPS domain own that release manifest and its files. A game release does not require a Cubby registry edit or a second GitHub release to insert checksums.
File release manifest: schema 2
{
"schema_version": 2,
"id": "example-game",
"version": "1.0.0",
"runtime": "web",
"entrypoint": "index.html",
"artwork": "cover.svg",
"files": {
"index.html": {
"url": "releases/1.0.0/index.html",
"size": 15,
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
},
"cover.svg": {
"url": "releases/1.0.0/cover.svg",
"size": 6,
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
}
}
This example uses **illustrative** byte sizes and hashes. Generate actual values from the exact build files before uploading; the manifest is not self-hashed.
schema_version: 2, stable registry-matchingid, distinctversion, andruntime: "web"identify the release. IDs contain lowercase letters, digits, and hyphens, beginning with a letter/digit (maximum 80 characters).entrypointandartworkare required relative installed file paths. Optionalattractnames the trailer file; optionalscreenshotsis an array of up to 12 relative file paths. All must occur infiles.- Every
filesvalue has a downloadurl, exact nonnegative integer bytesize, and lowercase hexadecimal SHA-256. URLs may be relative to the requested manifest URL, root-relative on its host, or absolute HTTPS URLs. For a manifest athttps://game.example/store/demo/manifest.json,1/files/a.jsresolves tohttps://game.example/store/demo/1/files/a.js, while/releases/1/a.jsresolves tohttps://game.example/releases/1/a.js. Parent-relative paths work using normal URL resolution, not installed-file path rules. Absolute download hosts may differ from the manifest host. The resolved transport must be HTTPS. Protocol-relative URLs (//host/path), redirects, URL credentials/fragments, backslashes, whitespace/control characters, malformed escapes, and encoded controls/backslashes fail. URL-encode spaces and Unicode in remote references. Relative references require the source manifest URL; they never use the catalog origin or process working directory. Legacy schema-1package.urlsupports the same download-reference rules. - Files are regular files only: no absolute/traversal/backslash/NUL/empty path segments, file/directory prefix collisions, or
manifest.jsonpayload. That filename is reserved for Cubby's installed inventory. Paths also reject%,?,#, ASCII control characters, and a trailing space because installed asset URLs cannot represent them unambiguously. Ordinary spaces and Unicode are supported. Subdirectories are implicit, not extra download entries. - Limits: 256 KiB release manifest; 1,000 payload files; 128 MiB aggregate payload bytes. Each file must match both its declared size and hash. A schema-2 manifest must not also contain a ZIP
packagefield. - Keep file URLs immutable and versioned. Any changed payload or game metadata requires a distinct release version. Changing only download URLs does not change the installed game. Keep the game ID stable to preserve saves.
One release workflow
- Build all offline game files, using local relative references for assets.
- Generate the manifest's sizes and hashes directly from that build directory.
- Upload those exact files under immutable versioned HTTPS paths.
- Verify they are available, then replace the stable manifest **last**.
- Publish the GitHub release in the same workflow. No second release, manually entered hash, or Cubby release is needed for subsequent game updates.
The registry's artwork/trailer/screenshots are discovery URLs, separate from installed relative asset paths. Browsing does not follow the release manifest. An adult metadata title is allowed; this does not change the wordless child UI.
Native installation and transition
The desktop installer accepts both the existing schema-1 ZIP release and the new schema-2 file release at the same registry manifest_url. Downloads and updates remain free; the native launcher charges only for game launches.
The hub every host mounts (browser, desktop and handheld) checks installed games' stable manifests on hub entry, every five minutes in the hub, and on visibility/connectivity recovery. Unchanged identities fetch no payload files. Browser updates download the complete new file inventory into a private Cache Storage release and atomically select it in IndexedDB; retained release URLs keep games already open in other tabs pinned to their original bytes. Navigating away cancels the hub's update work. Offline or failed checks retain the active release and retry at a later check. Browser timers can be throttled, and there is no updater running after the hub closes.
For file updates it reuses matching active-version files only after checking their actual size/hash and rejecting symlinked sources. Reused bytes are copied, not hard-linked. Missing, changed, or corrupt files download independently. The entire new version is staged before atomic activation, and activation waits until no game is running. Failed or interrupted installation leaves the active version and saves unchanged; a retry may redownload staged files from the failed attempt. Previous versions remain on disk.
On disk, Cubby generates its existing schema-1 digest-only manifest.json so the offline host and save namespaces do not change. A same-version ZIP-to-files migration is accepted when its payload inventory and game metadata are identical. Different bytes/metadata at an existing version are rejected. URL relocation alone is accepted. No version-ordering/downgrade policy is introduced by this contract.
Existing desktop bundles still seed from their ZIP archives without a network dependency. Games publishing schema 2 require an updated native installer; old desktop clients support schema 1 only. Desktop 0.3.0 supports schema 2 with absolute URLs but rejects relative download URLs; install **desktop 0.4.0 or newer** before changing a stable manifest to use them. The format remains schema 2, and absolute URLs stay supported. Cubby's legacy manifest forwarding endpoint resolves relative references at the upstream publisher URL before forwarding, so moving metadata does not change its meaning. Checksums prove byte integrity, not publisher identity; the registry remains a trusted-developer list.
Generate in the game's repository
runtime/file_manifest.py is a standalone Python 3.11+ standard-library tool. Copy that one file into a game's release tooling (for example tools/cubby_manifest.py); it does not import Cubby or need its registry, installed application, ZIP tooling, or Python dependencies.
After building your game's offline output into dist/game/, a single release job can run:
uv run python tools/cubby_manifest.py dist/game \
--id line-cutter --version "$RELEASE_VERSION" \
--entrypoint play.html --artwork cover.png \
--attract trailer.webm \
--base-url "/releases/$RELEASE_VERSION/" \
--output dist/release-manifest.json
Add --screenshot screenshots/example.png for each screenshot. Hashes, sizes, and safely URL-encoded file names come from the exact build output. The output manifest must be **outside** the payload directory; manifest.json is reserved inside a game installation. Existing payload manifests, symlinks, special files, unsafe paths, and oversized releases fail before publication.
--base-url is optional and defaults to ./: generated file references then point beside the hosted manifest. Supply a relative version directory when publishing a stable manifest elsewhere, or a root-relative version directory when identical metadata is served at several paths on the same domain. No deployment hostname is needed. An explicit absolute HTTPS base is still allowed.
Then, in that same CI job, use the game's hosting deploy command to upload dist/game/ to the versioned URL, verify the files, and publish dist/release-manifest.json at the stable store manifest URL **last**. Finally create the GitHub release with that same version/tag (or use a GitHub release as the job trigger). The hosting command is game-specific; this generator does not upload or create a second release. Do not rebuild files between hashing and uploading. Keep previous versioned files available.
The adult runtime.catalog CLI also accepts this schema through existing registry entries. Its update command is an explicit administrator operation: stop that game first. Unlike the native desktop scheduler, the standalone CLI cannot observe whether gameplay is active. Legacy ZIP commands remain available for existing releases; new game repositories do not need to use them.