sideloadplay
v0.1.1
Published
The Sideload developer CLI: sign in with an invite token and publish a game.
Readme
sideloadplay
The developer CLI for Sideload. Validate a game, preview its listing, test it in the Launcher, and publish it.
Validation and local testing work without an account or invite token. Publishing requires an invite token. Sideload is in developer alpha with no self-service sign-up; tokens are issued by the operator.
Running it
Nothing to install:
npx sideloadplay --helpNode 22 or newer.
If you would rather have it on your path, npm install -g sideloadplay gives
you both sideloadplay and the shorter sideload.
Validating
npx sideloadplay validate <dir>validate runs every check publish runs against <dir> — the same
sideload.json, the same artwork — with no token, no network, and no
upload. There is nothing to sign in for: it works before you have an invite
token, and its verdict is exactly the one publish will reach for the same
directory.
Signing in
npx sideloadplay loginlogin reads your invite token from standard input, and there is no flag
and no environment variable that will pass it instead. That is deliberate: a
flag ends up in your shell history and in CI logs, and standard input does
not. Paste the token when prompted, or pipe it:
npx -y sideloadplay login < token.txt-y matters when you pipe: without it, npx may ask to confirm the install and
read the answer from the same standard input your token is on.
The token is verified against the API and stored in your operating system's keychain. Where there is no keychain, it is written to a file only you can read. A token that does not match a developer is reported and stored nowhere.
Running it in the launcher
Preview a prepared build's listing before playing:
npx sideloadplay dev <dir> --listingThis opens a read-only preview of its Markdown description, creator credits and artwork. Choose Play to test the game. To launch gameplay directly:
npx sideloadplay dev <dir>Both modes run the same checks as validate, serve the prepared directory, and
open the Sideload Launcher. They do not compile or publish the game and require
no account or invite token. <dir> must contain the static build, sideload.json
and its referenced files; the manifest's entry_file selects the game entry.
The build is served under /g/<localGameId>/dev/, matching the nested path shape
of a published build (/g/<gameId>/<buildId>/). Root-absolute asset paths such as
/assets/thing.glb can therefore fail here just as they would after publishing.
Use paths relative to the entry HTML or module instead.
Local launch currently requires macOS and a compatible installed Sideload Launcher. Keep the command running while previewing or playing; Ctrl-C or SIGTERM stops its local server. JSON mode reports validation, port conflicts, opener failures, and launcher rejection or compatibility timeouts.
The launcher also offers Settings → Run a local game: select the prepared folder, review its listing, then Play. Recent folders can be reopened using the controller; the native folder picker uses the operating system's keyboard/mouse interaction.
CLI and Settings use the same canonical folder identity and persisted loopback
port, so returning to the same folder preserves its save origin through rebuilds.
Separate folders have separate saves even if their manifests use the same slug.
Moving a folder creates a new identity; old random-port development saves are not
automatically migrated. Assignments live in
~/Library/Application Support/Sideload/local-builds.json; forgetting a recent
folder does not delete its assignment or saves.
If its assigned port is occupied, stop the other CLI/launcher local session and
retry. Sideload never silently changes the port. A compatible updated launcher is
required to acknowledge the new handoff. Older --dev-url launcher callers remain
a legacy game-only path. Both local entry points enforce the hosted-game network
policy: requires.network: true alone does not grant arbitrary network access.
Publishing
npx sideloadplay publish <dir><dir> holds your built game, a sideload.json describing it, and your store
artwork. The build must be servable as static files with an entry point —
index.html unless sideload.json says otherwise.
Everything is validated locally first, and nothing is uploaded until it all passes. Then the build and artwork upload, and your listing is created or updated.
A candidate with a cover and at least three screenshots is submitted for review. Without them the candidate stays a draft, and the output names what is missing. Publishing again uploads a new build; earlier builds are never modified.
The command reports candidate review status separately from live availability. When the API supplies them, JSON and prose include the game ID, candidate build ID, live build ID and live status. Older servers may omit these additive fields; the CLI reports live availability as unknown rather than guessing that an update is live or remains live.
Uploading submits a candidate for review. An existing approved version remains live until the operator approves the update. Keep the returned game and build IDs to coordinate review with the operator.
sideload.json
{
"$schema": "https://sideloadplay.com/schema/sideload.schema.json",
"slug": "your-game",
"title": "Your Game",
"short_description": "One line, at most 140 characters.",
"description": "./DESCRIPTION.md",
"version": "1.0.0",
"entry_file": "index.html",
"engine": "threejs",
"renderer": "webgl2",
"tags": ["Racing"],
"input": ["keyboard", "mouse", "gamepad"],
"controller_support": "full",
"lifecycle": "unmanaged",
"estimated_playtime_min": 90,
"languages": ["en"],
"content_rating": "everyone",
"developer": { "name": "Your Studio" },
"requires": {
"pointer_lock": true,
"fullscreen": true,
"network": false,
"shared_array_buffer": false
},
"size": { "initial_load_mb": 8, "total_mb": 24 },
"assets": {
"cover": "./store-assets/cover.png",
"screenshots": ["./store-assets/1.png", "./store-assets/2.png", "./store-assets/3.png"]
}
}The $schema line gives you completion and validation in any editor that
understands JSON Schema, including the list of tags you can use.
description may be markdown inline, or a path to a .md file — a
4,000-character description is not something to hand-edit inside a JSON
string.
developer credits the game's creator; it does not set the listing's owner.
The authenticated account supplies the publisher identity. Use a nonempty
developer.name and, when available, these optional HTTP(S) links:
website_url: the creator's website.source_url: the repository for the version you ship.upstream_url: the original repository when your version is an adaptation.
Preserve original creator credits and license notices. Changing creator metadata does not transfer publishing ownership. The Launcher labels a distinct publisher separately; local previews do not invent publisher information.
requires.network must be honest. Declaring false and then making network
requests at runtime breaks the developer agreement.
Artwork
Cover, Hero and Screenshots must be exactly 1920×1080 (16:9), so a gameplay screenshot at those dimensions is a valid cover. Logo has separate bounds.
| Asset | Size | Required | |---|---|---| | Cover | 1920×1080 | yes | | Screenshots | 1920×1080, gameplay only | 3 to 8 | | Hero | 1920×1080 | optional | | Logo | transparent PNG within 1200×400 | optional |
Each artwork file is capped at 5 MB. Cover, Hero and Screenshots use PNG or JPEG; Logo uses transparent PNG.
--json
Every command takes --json. Failures are reported as structured objects
rather than prose, so a script or an agent can act on them:
{ "id": "screenshot_dimensions", "actual": "1920x1200", "expected": "1920x1080" }License
MIT.
