Extend code-signing infrastructure to the Quest APK build

Closed
#14 2 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
68/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
android, shell

Research direction

Start with android-repack-apk in justfile:118-151 and compare its temporary keystore handling with certs/generate-ca.sh:26-34 and the pass guard in justfile:1699-1702. Verify the persistent keystore and passphrase are retrieved into a shred-on-exit tempfile, then run android-repack-apk twice and confirm the second APK installs as an update without uninstalling the existing app.

Written by the indexing model from the issue text.

Description

backlog enhancement

Problem

Quest APK sideloads are signed with a brand-new throwaway debug keystore on
every single repack
(android-repack-apk, justfile:133-135, commit
56c5c3e36cb9844fb57af4aaf5bd3ec6f9a07a55):

keytool -genkeypair -v -keystore "$ks" -alias nevrdebug -keyalg RSA -keysize 2048 \
    -dname "CN=NEVR Debug, OU=nevr-runtime, O=Sprock, C=US"

Android doesn't validate app signatures against a trusted root the way
Authenticode does — it only requires the same key sign every version of a
given package, or the OS refuses the install as a different publisher. A
fresh key every build means every deploy looks like a new app to Android,
which forces adb uninstall (full app-data loss: cached login, local
settings) before every single install.

Hit this directly on 2026-09-15 deploying libovrplatformloader.so's
GOT-hook build (commit 87c68d2ae73c8a12678a9eaad5fa70fdd15a5d58) to a real
Quest 2: install failed with INSTALL_FAILED_UPDATE_INCOMPATIBLE: ... signatures do not match newer version, requiring uninstall + reinstall.
Full log: docs/design/2026-09-15-quest-got-hook-basics-verification.md
(commit f863831025669196be1f791c9f54b508a72ce38c — see "Tonight's
execution log" steps 5–8).

What already exists for Windows (Authenticode) — the pattern to extend

A real 3-tier CA hierarchy already exists for signing the Windows
DLLs/EXEs, from commit 17ce26c9a6a8412adbc038a063b479bf2607821a
("feat: add code signing CA hierarchy and AI semantic release workflow"):

  • Root CA (4096-bit RSA, 10yr validity) — key generated once, stored in
    pass at services/echovrce/code-signing-root-ca-key, extracted only to
    a shred-on-exit tempfile when needed (certs/generate-ca.sh:26-34,
    :52-55). Never touches disk otherwise.
  • Intermediate CA (5yr) → code-signing cert (~2.25yr, kept under the
    CA/Browser Forum 825-day reuse limit) — both local, gitignored
    (.gitignore:94-96, same commit 17ce26c9).
  • just sign (justfile:1692-1715, commit 17ce26c9, globstar fix in
    c93530f75ba0936c15d49c7e4e1dedcbe642aa2c) Authenticode-signs everything
    in dist/ via osslsigncode + a DigiCert RFC3161 timestamp.
  • just verify-sign (justfile:1721-1722) checks the result against
    certs/root-ca.crt.

Proposed change

Extend the same discipline (persistent key, stored in pass, never
regenerated per-build) to the Quest signing path — not a literal copy of the
3-tier CA structure, since Android app-signing doesn't chain to a trusted
root at all; it only needs key-stability across versions. A 3-tier hierarchy
here would be complexity with no corresponding requirement on the Android
side.

  1. Generate one persistent Android signing keystore (JKS or PKCS12),
    one time — not per-build.
  2. Store it in pass, mirroring the root-CA-key pattern in
    certs/generate-ca.sh:26-34: e.g. services/echovrce/quest-signing-keystore
    (base64-encoded keystore blob) + a sibling entry for its passphrase.
  3. Change android-repack-apk (justfile:118-151) to pull that keystore
    from pass into a shred-on-exit tempfile and sign with it, instead of
    keytool -genkeypair-ing a fresh throwaway one every call.
  4. Optional, for parity with generate-certs/renew-certs: add a
    just generate-quest-keystore recipe with the same "ERROR: no keystore
    found, run X" guard just sign already has (justfile:1699-1702).

Effect

One remaining forced uninstall to transition off the current debug-signed
install; every repack after that installs as a normal update — no more
per-iteration app-data loss during Quest development.

Non-goals

  • Play Store distribution / Play App Signing enrollment — this is sideload
    development signing only.
  • Any change to the Windows Authenticode pipeline — it's the reference
    pattern, not something being modified.
Update 2026-10-10 (smoke #5 / main 7ed718da)

Evidence from smoke #5 (2026-10-10): the sideload was installed over the existing install with adb install -r and succeeded (smoke5/install.log: "Success"; lastUpdateTime=2026-10-10 12:17:59), with the same signer as smoke #4 (CN=NEVR Debug, OU=nevr-runtime, O=Sprock, C=US; smoke5/ready.md), so cached login survived. That worked only because the lane copied the same debug.keystore into the repack. At origin/main justfile:142 still defaults ks to build/android-arm64/repack/debug.keystore and :155-160 generates a new one when it is absent, so any clean checkout, worktree or just clean gets a new key. The pass-stored key and a generate recipe in this issue do not exist yet.

Dominant language
C++
Stars
2
Forks
5
Avg merge
5h 8m
Merged PRs (30d)
911

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from EchoTools/nevr-runtime

All issues in EchoTools/nevr-runtime

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.