Publish an app
Four steps, no sign-up.
The store is an automatic index of public GitHub repositories. There is no registration and no review queue; listing means a repository tagged itself, not that PicoDeck vetted it.
- Push your app to a public GitHub repository (not a fork) with
app.jsonat the root. - Add the repository topic
picodeck-app(Settings → Topics on GitHub). - Create a GitHub Release with exactly one
.zipasset containing your app files at the archive root. - Wait up to 30 minutes, then check status if it is not listed.
app.json
{
"id": "com.example.snake",
"name": "Snake",
"version": "1.2.0",
"description": "One line for the list",
"long_description": "Optional detail text",
"author": "Your name",
"category": "games",
"min_firmware": "0.1.0",
"requirements": ["audio"],
"keywords": ["arcade", "retro"],
"icon": "icon.png",
"screenshots": ["shot1.png"],
"dirname": "snake",
"homepage": "https://example.com",
"asset": "snake.zip"
}
id, name and version are required. The index reads app.json at the release tag, so tag after bumping the version. category is one of games, tools, system, demos, emulators, network, shown in the store as Games, Tools and so on. asset is only needed when the release has more than one ZIP. dirname defaults to the last segment of the id.
Icons, screenshots and keywords
All three are optional. icon and screenshots are paths to images committed in your repository, relative to its root, read at the release tag so they are versioned with the app. Use .png, .jpg, .gif or .webp; a square icon of about 128 pixels looks best, and at most four screenshots are listed. keywords is a list of up to eight short words that the store searches alongside the name, description and author. A value the index cannot read is dropped with a note on the status page rather than rejecting the app.
The ZIP
Files must sit at the archive root: app.json plus main.lua (Lua app) or main.elf (native app), and any assets. Do not wrap them in a folder. The ZIP must be at most 16 MB, and the id in the ZIP's app.json must equal the one in the repository. The index computes the SHA-256 itself; you do not publish a checksum.
Rejection reasons
The status page shows one of these codes next to any repository that is not listed.
All rejection reasons
- Code
- What it means
no-release- No published (non-draft, non-prerelease) GitHub Release.
github-error: <message>- GitHub returned an error for this repository during indexing; retried on the next refresh.
no-app-jsonapp.jsonis missing at the release tag.app-json-invalidapp.jsonis not a JSON object.missing-field:<name>- A required field is absent or not a string.
bad-id- Id must be reverse-DNS: lower-case letters and digits in 2–5 dot-separated segments of at most 32 characters each, e.g.
com.example.snake. bad-dirnamedirnamemust be 1–32 characters of letters, digits,_or-; no slashes or dots.no-zip-asset/multiple-zip-assets- The release needs exactly one ZIP, or name it in
asset. asset-not-found:<name>- The
assetnamed inapp.jsonis not on the release. asset-not-zip:<name>- The asset named in
app.jsonexists but is not a.zip. asset-too-large- ZIP over 16 MB.
zip-invalid- The ZIP could not be parsed (corrupt archive, or ZIP64 which is not supported).
zip-layout- ZIP root lacks
app.jsonormain.lua/main.elf. zip-app-json-invalid- The
app.jsoninside the ZIP is unreadable or has a bad id. id-mismatch:<id>- The ZIP's id differs from the repository's.
id-claimed-by:<repo>- Another repository already publishes this id.
dirname-claimed-by:<repo>- Another repository already publishes this
dirname. The store installs into/apps/<dirname>and clears it first, so a dirname belongs to one repository. Set a differentdirnameinapp.json. dirname-reserved- The
dirnameis one PicoDeck itself ships:store,updater,filemanager,editor,terminal_example,calculator,system,data. Pick another. pending-digest- New release queued for hashing; it will appear on a later refresh.
asset-unreachable:<status|error>- The ZIP could not be downloaded — an HTTP status, or the name of the network error; retried next refresh.
digest-error:<name>- The index hit an unexpected error while reading the ZIP; it will retry on the next refresh.
blocked- Delisted by the maintainer.
Updating
Bump version in app.json, tag a new Release with a new ZIP. Devices see the update on their next catalog fetch. Removing the topic delists the app on the next refresh.