Appearance
Keryx Android production signing
Approved: 26 August 2026
This document is the P3-01 signing packet for Keryx for SafeCall on Google Play. It records the Play App Signing model, upload-key custody, Gradle fail-closed wiring, AAB verification, recovery drill procedure, and evidence handoff.
It does not claim iOS distribution signing (#110), CI signing gates (#112), store record verification (#114), Play Console form paste (#125), or that FGS/FCM delivery architecture is implemented in the release binary (#96 policy only).
Purpose and non-goals
Purpose
- Approve production Android signing identity for
com.sourcectl.keryx. - Require release builds to use an upload keystore outside Git — never debug signing.
- Document custodians, backup, rotation triggers, and recovery drill steps.
- Provide verification commands and evidence archive requirements for #109 close.
Non-goals
- Committing keystores, passwords, or Play service-account JSON.
- Replacing SafeCall Nav (
mobile/) signing — out of scope unless requested. - Automating CI secret injection (#112).
- Store upload or Internal-track promotion without separate release authority.
Package identity
| Field | Value |
|---|---|
| Application ID | com.sourcectl.keryx |
| Publisher | sourcectl (P0-01) |
| Store product name | Keryx for SafeCall |
| Default Play language | English + Czech listing parity (P0-02) |
Play App Signing model
Google Play App Signing holds the app signing key (Google-managed). Keryx operators hold the upload key used to sign AABs before upload.
| Key | Custody | In Git |
|---|---|---|
| App signing key | Google (Play App Signing) | Never — public cert/fingerprint only |
| Upload key | Secret store (secrets/ on NAS) + backup custodian | Never — *.jks gitignored |
Enrollment (Play Console, current Help as of August 2026)
Official pages: Create and set up your app, Use Play App Signing, Prepare and roll out a release, Countries/regions, Category. May 2026 Console change: App signing lives under Protected with Play (not “App integrity”).
- Create the app: Home → Create app. Name Keryx for SafeCall, default language English, App, Free. Accept Developer Program Policies, US export laws, and Play App Signing Terms of Service. Package
com.sourcectl.keryxis set by the first AAB, not this form. - Category: left menu Grow users → Store presence → Store settings → App category → Business. Do not paste listing copy.
- Countries: select the app → Test and release → Production → Countries/regions tab → add Czech Republic only (remove others). Testing tracks sync with production unless you unsync. If Production has no countries yet, set the same on Test and release → Testing → Internal testing → Countries/regions.
- Play App Signing: new apps are enrolled when you accept the ToS and upload the first AAB. To inspect keys: left menu Protected with Play, then Play Store distribution / Play Store protection → Play app signing (Google renamed this in May 2026). Confirm upload-cert SHA-256
0D:AC:61:B1:5F:85:91:3F:2B:21:20:5E:46:14:81:90:20:13:85:8B:76:D2:00:91:36:FD:30:9D:18:D8:0D:AA. - First AAB: Test and release → Testing → Internal testing → Create new release (or Create new release on that page). Follow the on-screen Play App Signing prompt. Upload
keryx/build/app/outputs/bundle/release/app-release.aab. Save as draft is enough; do not start a production rollout. If Create new release is disabled, finish the Dashboard setup tasks first.
Archive a screenshot of the Play app signing page (no secrets) in ~/sourcectl/keryx-release-evidence/. Operator verified 30 August 2026: upload SHA-256 matches GLaDOS; Czech Republic only; internal-testing AAB saved as draft (OA-109-play-record, OA-109-play-signing).
Upload-key loss without Play reset is a launch blocker. Document reset procedure in controlled store records, not in this repository.
Upload keystore creation
Generate outside routine commits; store only in NAS secrets/ (secret-store.md). Do this on GLaDOS (primary). MiniVan-3 gets a copy of the same .jks for OA-109-recovery — do not generate a second keystore.
Operator steps (OA-109-keystore)
From the repo root. storeFile=../upload-keystore.jks is relative to keryx/android/app/ (Gradle), so the file must live at keryx/android/upload-keystore.jks.
- Confirm
keytoolexists (keytool -help). It ships with a JDK (Android Studio’s JBR is fine). - Create the keystore (prompts for store password, then key password — use the same strong password for both unless you have a reason not to):
bash
cd keryx/android
keytool -genkey -v \
-keystore upload-keystore.jks \
-storetype JKS \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-alias upload \
-dname "CN=Keryx for SafeCall, OU=Keryx, O=SOURCE CONTROL SINGLE MEMBER P.C., C=GR"
cd ../..- Create gitignored
key.properties:
bash
cp keryx/android/key.properties.example keryx/android/key.propertiesEdit keryx/android/key.properties so it looks like this (passwords filled, storeFile unchanged):
properties
storePassword=<the store password from keytool>
keyPassword=<the key password from keytool>
keyAlias=upload
storeFile=../upload-keystore.jks- Confirm Git will not see them:
bash
git check-ignore -v keryx/android/key.properties keryx/android/upload-keystore.jks
git status --short keryx/android/Both files must be ignored. Never git add them.
- Copy into NAS
secrets/(secret-store.md; separate files from checksums inevidence/):- file
upload-keystore.jks - store password
- key password
- alias
uploadDo not put passwords in git, Slack, or~/sourcectl/keryx-release-evidence/.
- file
- Build and verify on GLaDOS:
bash
cd keryx && flutter build appbundle --release && cd ..
bun run verify:keryx-aabCopy the printed AAB SHA-256 and upload-cert fingerprint into ~/sourcectl/keryx-release-evidence/ (checksums only).
Never commit upload-keystore.jks, passwords, or key.properties. If you lose the upload key without a Play reset, you cannot update the Play listing. Do not create a second keystore “just in case.”
Local and CI signing inputs
Copy the committed example:
bash
cp keryx/android/key.properties.example keryx/android/key.properties
# Edit passwords and storeFile path — file is gitignoredExample shape (keryx/android/key.properties.example):
properties
storePassword=
keyPassword=
keyAlias=upload
storeFile=../upload-keystore.jksGradle loads keryx/android/key.properties from the Android root project (keryx/android/). Release builds fail closed when the file or keystore is missing — see keryx/android/app/build.gradle.kts.
Optional CI overrides (future #112): mount secrets and set paths via env or copy key.properties into the build workspace from CI variables (KERYX_KEY_* naming reserved; not wired until #112).
Custody matrix
Personal contact values live in controlled store records (#114); this packet defines roles only.
| Role | Responsibility |
|---|---|
| Primary custodian | Creates upload keystore; maintains key.properties locally; runs first signed build and Play enrollment |
| Backup custodian | Holds duplicate keystore export in NAS secrets/ (secret-store.md); performs recovery drill |
| Release authority | Approves rotation/revocation and Play upload-key reset |
| Audit log | NAS evidence/ — build commit, fingerprint, checksum, operator, date |
Rotation / revocation triggers
- Suspected keystore or password compromise.
- Custodian offboarding without backup transfer.
- Play Console upload-key mismatch or failed verification.
- Google Play upload-key reset request.
After rotation: new upload keystore → update Play upload certificate → archive new SHA-256 → recovery drill on backup path.
Build and verify release AAB
bash
cd keryx
flutter analyze && flutter test
flutter build appbundle --release
cd ..
bun run verify:keryx-aabOptional fingerprint gate:
bash
bun run verify:keryx-aab -- --expect-fingerprint=<SHA-256>Default AAB path: keryx/build/app/outputs/bundle/release/app-release.aab.
Verification checklist
Archive in controlled evidence (not git):
- [ ]
jarsigner -verifypasses (verify:keryx-aabexit 0) - [ ] SHA-256 upload certificate fingerprint recorded
- [ ] AAB SHA-256 checksum recorded
- [ ] Source commit hash and build timestamp recorded
- [ ]
debuggable=falseon release manifest (Flutter release default) - [ ] Package
com.sourcectl.keryx, version code/name match intended release - [ ] Play Console upload cert matches local fingerprint
- [ ] Play App Signing enrollment noted (screenshot or ticket reference)
Recovery drill procedure
Second authorized environment (different machine or restored secret mount):
- Obtain upload keystore + passwords from NAS
secrets/(secret-store.md) — not from git. - Create
keryx/android/key.propertiesfromkey.properties.example. - Check out the same commit (or current release tag per versioning.md).
- Run
flutter build appbundle --release. - Run
bun run verify:keryx-aab— fingerprint must match primary build. - Record: date, operator, commit, fingerprint, checksum, environment ID in controlled audit log.
Failure to reproduce fingerprint blocks #109 close.
Evidence handoff
| Remaining work | Owner issue |
|---|---|
| Reproducible AAB/IPA pipeline command | #111 — recorded 1 September 2026 (bun run release:keryx -- --contracts) |
| CI signing secrets + verify gate | #112 |
| Store records, roles, evidence archive | #114 |
| Play Console paste and submission | #125 |
| iOS distribution signing | #110 — ios-signing.md (App ID com.sourcectl.keryxapp; GLaDOS + MiniVan-3 IPAs verified; .p12 on NAS) |
Launch posture
Closing P3-01 / #109 means production upload signing is wired fail-closed, a verified signed release AAB exists, Play App Signing is enrolled, custody is documented, and recovery is proven on a second authorized environment.
It does not mean Internal/Production track promotion, CI automation, iOS signing, or that every store form is submitted.
Related documents
- Publishing Keryx — Google Play runbook
- Secret store — NAS CT 500 LAN share;
secrets//evidence/ - NAS unlock after reboot — gocryptfs after CT 500 restarts
- Play Data Safety — release AAB inspection handoff
- SafeCall Nav Android signing — reference pattern (Nav not wired)