i99dash docs
Recipes

App icons + cover image + screenshots

How to ship a real icon (and optional cover image / screenshots) with your bundle so the catalog renders them at a versioned CDN URL.

You scaffold a project, you publish, you open the catalog — and the icon is the i99dash placeholder. This recipe is the fix.

TL;DR

// manifest.json
{
  "icon": "./assets/icon.svg",
  "coverImage": "./assets/cover.jpg",
  "screenshots": ["./shots/01.png", "./shots/02.png"]
}

Every path is relative to your bundle's dist root (whatever the catalog API ends up serving). The publish flow rewrites these to absolute, versioned CDN URLs at submit time:

./assets/icon.svg
  → https://miniapps.i99dash.app/<your-app>/<version>/assets/icon.svg

You don't upload icons separately. They ship in the same tarball as your code.

Where the file goes (per framework)

The manifest path ./X must resolve to dist/X after build. Different frameworks lay out static files differently:

FrameworkPut your icon atManifest reads
Vanilla (i99dash init)src/assets/icon.svg./assets/icon.svg
Next.jspublic/assets/icon.svg./assets/icon.svg
Vitepublic/assets/icon.svg./assets/icon.svg
Nuxtpublic/assets/icon.svg./assets/icon.svg

The rule: files inside the framework's "static-assets" folder (the one whose contents end up at the dist root) match the manifest path verbatim. Next.js's public/X becomes dist/X, so manifest reads ./X.

Locked specs

Validated by i99dash validate (CLI side, dimension-sniff via image-size) and re-validated by the publish backend (magic-byte check on the extracted bundle — catches a bash script renamed to icon.png that the CLI's extension-only check misses).

AssetFormatDimensionsMax sizeRequired
iconPNG, SVG256×256 (square)100 KByes
coverImagePNG, JPG, WebP1280×720 (16:9)500 KBno
screenshots[]PNG, JPG, WebP≤ 1920×1080800 KB eano, ≤ 8

SVG icons skip the dimension check (they're scalable).

What the publish flow does

  1. i99dash build — copies your tree (or runs the framework build); the asset-presence check verifies each manifest path resolves inside dist/.
  2. i99dash publish — uploads the tarball, the backend extracts it to https://miniapps.i99dash.app/<app>/<version>/....
  3. Magic-byte check — server reads the first 16 bytes of every declared asset (captured inline during extraction; no extra roundtrip) and verifies the signature matches the declared format. Submit fails with a clear error citing the field if not.
  4. URL rewrite — relative paths in the manifest become absolute https://miniapps.i99dash.app/<app>/<version>/... URLs, and that's what gets stored in mini_app_bundles.manifest_json and served by GET /api/v1/mini-apps/catalog.

The version is in the URL, so a v1.2.0 → v1.2.1 publish gets a fresh URL even if the icon bytes are the same — cache invalidation without a server-side hash dance.

Errors you might see

ErrorWhat's wrong
icon must be a relative path…Don't put a full URL — use ./assets/icon.svg
icon extension must match …Icon must be PNG or SVG (cover/screenshots: JPG/PNG/WebP)
icon: file not found at … (build)The framework didn't include the file — wrong folder
<field> magic-byte check failedThe file has the right extension but the wrong contents — likely a Git-LFS pointer or a copy gone wrong
extension must match (cover)WebP / JPEG ok for cover; SVG isn't (size cap is on bytes, not vectors)

Updating an icon

Bundle paths are version-pinned. To replace an icon:

  1. Update the file (src/assets/icon.svg or wherever).
  2. Bump manifest.version (e.g. 0.1.4).
  3. i99dash publish.

The new version's URL is fresh, the old version's URL still serves the old icon. No "edit storefront without rebuild" flow yet — that ships in a follow-up. For v1, ship a version bump.

On this page