What is a mini-app?
The mental model — host, bridge, manifest, bundle. Read once before you start; everything else clicks faster.
In one paragraph
A mini-app is a static web bundle (HTML/JS/CSS) that the i99dash car host loads inside a sandboxed web view. It runs on a head-unit screen mounted in a car. It can read context (which user, which car, which locale, dark mode on/off), it can call backend APIs through a typed proxy, and — if it's a privileged app — it can invoke device-side operations through a separate admin bridge. It can't drive the car or break out of the sandbox.
Architecture
┌─────────────────────┐ ┌───────────────────────────┐
│ your mini-app │ JS │ i99dash host │
│ (HTML / JS / CSS) │──────▶│ sandboxed web view │
│ │ │ + getContext + callApi │
│ @i99dash/sdk │ └───────────────┬───────────┘
└─────────────────────┘ │
▲ │
│ fetches / HTML served by │
│ ▼
┌──────┴───────────┐ ┌──────────────┐
│ @i99dash/ │ mocks/ │ backend │
│ sdk-dev-server │ (./mocks/*.json)│ (production)│
└──────────────────┘ └──────────────┘Locally, sdk-dev-server attaches the host bridge to your running page
so the same code runs in dev and inside the real host. No
if (dev) { ... } branches in your app.
The five things you need to know
1. Your app is just static files
Anything that compiles to static files works: plain HTML, Next.js with
output: 'export', Vite, Vue, Svelte, anything else. The CLI tarballs
your build directory and uploads it to a CDN. There is no Node runtime
in the head-unit; there is no SSR.
2. The bridge is the contract
Every interaction with the outside world goes through MiniAppClient:
import { MiniAppClient } from '@i99dash/sdk';
const client = MiniAppClient.fromWindow();
const ctx = await client.getContext(); // who, where, how
const r = await client.callApi({ // backend proxy
path: '/api/v1/fuel-stations',
method: 'GET',
});You never call fetch() for backend data — callApi() runs through the
host's allow-list. You never read window.location for context — the
host injects getContext(). The bridge is the single seam that's
testable, mockable, and stable across host versions.
3. The manifest is your catalog row
manifest.json lives at the project root. Three rules the backend
won't budge on:
idis forever. Pinned home-screen shortcuts on every user's device hold this string. Rotating it orphans every shortcut.versionmust increment on every publish. Same(id, version)is rejected.urlandiconUrlmust be on the host allow-list. Coordinate with ops before publishing — they map your origin into the host's allow-list config.
Full reference: MiniAppManifest.
4. There are two SDKs
| Package | When to reach for it |
|---|---|
@i99dash/sdk | Read context, call backend APIs, subscribe to car status. Default. |
@i99dash/admin-sdk | Run device-side ops (pm.*, sys.*, diag.*, fs.*). Restricted distribution. |
If your app only reads context and proxies API calls, @i99dash/sdk is
the whole story. Most apps stop there. Privileged apps need an admin
grant on the developer account before they can even install
@i99dash/admin-sdk — it's gated, not just scoped. See
Privileged mini-apps.
5. The dev-server makes it the same as production
sdk-dev-server (run via pnpm dev) boots a tiny web server that
serves your app, attaches the host bridge shim, and routes callApi()
to local fixture files in mocks/*.json. Same code path as production:
context comes from the bridge, API calls go through the bridge. You're
testing the actual production code, not a mocked subset.
The dev-server's control panel (/_sdk/ui) lets you toggle driving
state, change the VIN, switch locale, flip dark mode — every input
the real host might inject. Full reference:
Local development.
What a mini-app cannot do
- Actuate the car. No
lockDoors(), nosetAcOn(true). The SDK is read-only by construction. - Persist state on the device. Use
localStoragefor ephemeral UI state if you must, but treat the bundle as if every launch is the first one. - Read other apps' data. Each mini-app is a separate sandboxed web
view; there is no DOM access between apps and no shared
localStorage. - Bypass the manifest.
safeWhileDriving,permissions,minHostVersion— the host enforces every one of them. There is no override.