The apk.json manifest
Field reference for apk.json, the on-disk project file that describes a native app and the rules that reject a bad publish.
apk.json is the project file the CLI reads for every i99dash apk
command. It points at your release-signed .apk and carries the durable
identity, version ordering, and signer pin the platform enforces on
publish. Scaffold one with i99dash apk init,
then edit the fields below.
Fields
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | yes | The Android applicationId (reverse-DNS, e.g. com.acme.dashcam). Durable identity — never change it after publishing. The ids i99dash, dashdoctor, and fq are reserved and rejected. |
versionCode | positive integer | yes | The monotonic ordering key. Each publish must use a value strictly greater than the package current max, or submit is rejected. |
versionName | string | yes | Display version (e.g. 1.2.0). Shown to owners; not used for ordering. |
apkPath | string | yes | Relative path to the release-signed .apk. |
signerSha256 | string | yes | SHA-256 of the APK signing certificate. TOFU-pinned per package on first publish and immutable thereafter. See Signing. |
category | string | no | Storefront category, e.g. utilities. |
requires | object | no | Compatibility requirements the car evaluates before offering the app, e.g. { "minAndroidSdk": 29 }. Other keys: abi, dilink. |
displayName | string | locale-map | no | Storefront name override. Omitted: the CLI auto-extracts the launcher label from the .apk. A bare string maps to the en fallback; a locale map is sent verbatim. See Listing. |
description | string | locale-map | no | Storefront description. APKs carry no store description, so this is the only source; omitted if unset. |
iconPath | string | no | Relative path to a PNG/WEBP/JPEG to use instead of the icon auto-extracted from the .apk. See Listing. |
Scaffold
i99dash apk init writes this template. Replace id, set apkPath to
your build, and paste the real signerSha256:
{
"id": "com.example.myapp",
"versionName": "1.0.0",
"versionCode": 1,
"apkPath": "./app-release.apk",
"signerSha256": "REPLACE_WITH_APKSIGNER_SHA256",
"category": "utilities",
"requires": { "minAndroidSdk": 29 }
}versionCode must increase
versionCode is the only ordering key. Every publish must use a
strictly greater integer than the highest versionCode already accepted
for the package. A value equal to or below the current max is rejected as
a downgrade or replay. The car uses the same rule to decide an OTA update
is newer, so a release with a lower versionCode is never offered to a
car running a higher one.
{ "versionCode": 2 }versionName plays no part in ordering — bump versionCode for every
publish, even a re-spin of the same display version.
id is immutable, and three ids are reserved
id is the Android applicationId and the package's permanent
identity. Choose it once. Changing id after your first publish creates
a different package — it does not update the existing one, and the car
tracks installs by id.
The first-party ids i99dash, dashdoctor, and fq are reserved and
rejected at submit.
signerSha256: how to get it, and the TOFU pin
signerSha256 is the SHA-256 of the certificate your .apk is signed
with. Read it from the APK itself:
apksigner verify --print-certs YOUR_APK_PATHkeytool -list against your keystore returns the same digest. Copy the
SHA-256 value into signerSha256 exactly.
On your first publish for a package, this value is pinned (trust on first
use) and becomes immutable. A later publish whose .apk is signed by a
different certificate is rejected — this is the anti-repackage backstop,
the same model as a Play upload key. i99dash apk validate checks the
on-disk .apk against the signerSha256 you declared before you publish.
This pin is "K2" in the signing model.
displayName, description, and iconPath override extraction
On publish the CLI reads the launcher label and icon out of your .apk
and sends them as storefront metadata. The three fields below override
those auto-extracted values when set — override always wins:
displayNameoverrides the extracted launcher label. Use a locale map to translate the storefront name, or when the APK default label is in the wrong language.descriptionhas no APK source, so the field is the only way to give the storefront a description.iconPathoverrides the extracted icon. Point it at a PNG/WEBP/JPEG (SVG is rejected, max 1 MB). Use it when the APK ships only an adaptive or vector icon the extractor cannot rasterise.
These three fields ride alongside the signed manifest, never inside it, so editing them never changes the signature. See Listing.