i99dash docs
Native apps

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

FieldTypeRequiredNotes
idstringyesThe 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.
versionCodepositive integeryesThe monotonic ordering key. Each publish must use a value strictly greater than the package current max, or submit is rejected.
versionNamestringyesDisplay version (e.g. 1.2.0). Shown to owners; not used for ordering.
apkPathstringyesRelative path to the release-signed .apk.
signerSha256stringyesSHA-256 of the APK signing certificate. TOFU-pinned per package on first publish and immutable thereafter. See Signing.
categorystringnoStorefront category, e.g. utilities.
requiresobjectnoCompatibility requirements the car evaluates before offering the app, e.g. { "minAndroidSdk": 29 }. Other keys: abi, dilink.
displayNamestring | locale-mapnoStorefront 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.
descriptionstring | locale-mapnoStorefront description. APKs carry no store description, so this is the only source; omitted if unset.
iconPathstringnoRelative 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_PATH

keytool -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:

  • displayName overrides the extracted launcher label. Use a locale map to translate the storefront name, or when the APK default label is in the wrong language.
  • description has no APK source, so the field is the only way to give the storefront a description.
  • iconPath overrides 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.

On this page