@@ -6,7 +6,9 @@ your Meta developer identity — neither can (or should) be done by an agent.
66
77> Native principle (F.126): the app icon, ` build.gradle.kts ` , and ` AndroidManifest.xml ` are
88> ** ` @generated ` from ` scanner.holo ` ** by the quest compiler. To change app metadata (version, icon
9- > colors, name), edit ` scanner.holo ` and recompile — never hand-edit ` android-mr/ ` .
9+ > colors, name), edit ` scanner.holo ` and recompile — never hand-edit ` android-mr/ ` . The canonical
10+ > signed-build command compiles ` scanner.holo ` plus ` worlds/*.holo ` , verifies the materialization
11+ > independently, and only then accesses signing custody or invokes Gradle.
1012
1113---
1214
@@ -31,17 +33,17 @@ your Meta developer identity — neither can (or should) be done by an agent.
3133
3234Review runs ** Technical → Content → Publishing** . Mandatory technical VRCs and our status:
3335
34- | VRC | Requirement | Status |
35- | ---| ---| ---|
36- | Packaging.2 | APK v2 signature | ✅ verified v2 |
37- | Packaging.6 | 64-bit (arm64-v8a) only | ✅ fixed — arm64-only |
38- | Packaging.1 | Manifest conforms (VR category, version) | ✅ emitted |
39- | Packaging.5 | APK < 1 GB | ✅ ~ 120 MB |
40- | Functional.14 | Passthrough app launches in passthrough | ✅ ` enablePassthrough(true) ` on scene-ready |
41- | Functional.1 / 5 | No crashes; responds to head tracking | ▶ playtest (BETA channel) |
42- | Performance.1 / 3 | Hits refresh rate; graphics ≤ 4 s or VR loader | ▶ playtest (lightweight panel + passthrough) |
43- | Security.2 | Minimum permissions | ⚠ manifest declares ` HAND_TRACKING ` /` RENDER_MODEL ` (Spatial-SDK starter inheritance) the scanner may not use — trim after a headset test confirms controller input still works; not a hard blocker |
44- | Security.1 | Entitlement check | ➖ ** recommended, NOT required** — no Platform SDK integration needed |
36+ | VRC | Requirement | Status |
37+ | ----------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
38+ | Packaging.2 | APK v2 signature | ✅ verified v2 |
39+ | Packaging.6 | 64-bit (arm64-v8a) only | ✅ fixed — arm64-only |
40+ | Packaging.1 | Manifest conforms (VR category, version) | ✅ emitted |
41+ | Packaging.5 | APK < 1 GB | ✅ ~ 120 MB |
42+ | Functional.14 | Passthrough app launches in passthrough | ✅ ` enablePassthrough(true) ` on scene-ready |
43+ | Functional.1 / 5 | No crashes; responds to head tracking | ▶ playtest (BETA channel) |
44+ | Performance.1 / 3 | Hits refresh rate; graphics ≤ 4 s or VR loader | ▶ playtest (lightweight panel + passthrough) |
45+ | Security.2 | Minimum permissions | ⚠ manifest declares ` HAND_TRACKING ` /` RENDER_MODEL ` (Spatial-SDK starter inheritance) the scanner may not use — trim after a headset test confirms controller input still works; not a hard blocker |
46+ | Security.1 | Entitlement check | ➖ ** recommended, NOT required** — no Platform SDK integration needed |
4547
4648** Founder/submission-side (not build):** Data Use Checkup (declare the passthrough camera — "frames decoded on-device, not stored/transmitted"), Content Guidelines, IARC age rating, and the listing assets. Use a ** Release Channel (ALPHA/BETA)** to install on-headset with no review before the public submission.
4749
@@ -59,42 +61,55 @@ time — never in a plaintext file. `build-release.mjs` resolves them, materiali
5961private tmp file, builds, and ** deletes the keystore after the build** .
6062
6163### 1a. Generate the keystore (once, ever)
64+
6265``` bash
6366keytool -genkeypair -v \
6467 -keystore release.keystore \
6568 -alias quest_qr \
6669 -keyalg RSA -keysize 2048 -validity 10000 \
6770 -dname " CN=Joseph Krzywoszyja, OU=HoloScript, O=HoloScript, L=YourCity, S=YourState, C=US"
6871```
72+
6973** Back up ` release.keystore ` to two secure locations (offline). Record the passwords.** HoloKey holds
70- the * operational * copy; your offline backup is the custody master. Losing the HoloKey KEK ≠ losing the
74+ the _ operational _ copy; your offline backup is the custody master. Losing the HoloKey KEK ≠ losing the
7175keystore as long as that offline backup exists.
7276
7377### 1b. Store the four secrets in HoloKey (once)
78+
7479The vault needs a KEK + Postgres in env: ` HOLOKEY_PROD_KEK_CURRENT ` + ` HOLOKEY_PROD_KEK_<ID> `
7580(` node scripts/holokey.mjs gen-kek ` prints them) and ` DATABASE_URL ` .
81+
7682``` bash
7783node scripts/holokey.mjs set KEYSTORE_PASSWORD ' <store-password>'
7884node scripts/holokey.mjs set KEY_PASSWORD ' <key-password>'
7985node scripts/holokey.mjs set KEY_ALIAS ' quest_qr'
8086node scripts/holokey.mjs set ANDROID_KEYSTORE_B64 " $( base64 -w0 release.keystore) "
8187```
88+
8289(I can run these for you secrets-safely — the values go only into the encrypted vault, never into chat
8390or a committed/logged file.) Secrets are owner-bound under ` HOLOKEY_OWNER ` (default ` infra ` ); the build
8491must resolve under the same owner.
8592
8693### 1c. Build the signed APK
94+
8795``` bash
8896# env: JAVA_HOME (JDK 17) + ANDROID_HOME, plus the HoloKey KEK + DATABASE_URL (so the vault is ON)
89- node scripts/ build-release.mjs
97+ pnpm holoqr: build-release
9098# → android-mr/app/build/outputs/apk/release/app-release.apk (signed v2)
9199```
92- ` build-release.mjs ` resolves the four secrets from HoloKey (or, if the vault is OFF — no KEK — from
93- matching env vars ` KEYSTORE_PASSWORD ` /` KEY_PASSWORD ` /` KEY_ALIAS ` /` ANDROID_KEYSTORE_B64 ` ), writes the
94- keystore to a ` 0o600 ` tmp file, runs ` gradlew assembleRelease ` , and unlinks the tmp keystore. ** No
95- gradle change was needed** — ` android-mr/app/build.gradle.kts ` already reads ` KEYSTORE_FILE ` /` … ` from
96- env. A plaintext ` keystore.properties ` still works as a * local-only optional override* but is
97- gitignored and unnecessary with HoloKey.
100+
101+ ` holoqr:build-release ` first runs the real HoloCompositionParser and QuestCompiler over
102+ ` scanner.holo ` and every bundled world, then runs the independent Quest golden-diff gate. A parser,
103+ compiler, or generated-output mismatch blocks the build before signing secrets are resolved. For a
104+ safe proof with no signing or Gradle, run ` pnpm check:holoqr-born-from-source ` .
105+
106+ After the source gate passes, ` build-release.mjs ` resolves the four secrets from HoloKey (or, if the
107+ vault is OFF, from matching env vars
108+ ` KEYSTORE_PASSWORD ` /` KEY_PASSWORD ` /` KEY_ALIAS ` /` ANDROID_KEYSTORE_B64 ` ), writes the keystore to a
109+ ` 0o600 ` temporary file, runs ` gradlew assembleRelease ` , and unlinks the temporary keystore. The
110+ ` android-mr/app/build.gradle.kts ` file already reads ` KEYSTORE_FILE ` and the other values from env. A
111+ plaintext ` keystore.properties ` still works as a local-only optional override but is gitignored and
112+ unnecessary with HoloKey.
98113
99114> Verified (2026-06-22): the full resolve → materialize → ` assembleRelease ` → v2-signed APK → cleanup
100115> chain builds GREEN on-device via the env-fallback path (same resolver code the vault uses).
@@ -115,12 +130,13 @@ review-skip path, but a clean utility passes the basic technical/content/privacy
115130 to install on your headset and verify before public submission.
1161315 . ** Fill listing + declarations** (assets list below).
1171326 . ** IARC age-rating questionnaire** (in-dashboard).
118- 7 . ** Data Use questionnaire** — declare the passthrough camera: * "camera frames are processed on-device
119- to decode QR codes; frames are not stored or transmitted."* A ** Privacy Policy URL is required**
133+ 7 . ** Data Use questionnaire** — declare the passthrough camera: _ "camera frames are processed on-device
134+ to decode QR codes; frames are not stored or transmitted."_ A ** Privacy Policy URL is required**
120135 (see ` PRIVACY.md ` ).
1211368 . ** Submit for review** — Technical → Content → Publishing. Submit ≥ 2 weeks before any target date.
122137
123138### Listing assets to provide (24-bit PNG unless noted)
139+
124140- App icon ** 512×512** (no transparency) · Spatialized icon ** 180×180** (transparent)
125141- Hero ** 3000×900** · Cover landscape ** 2560×1440** · square ** 1440×1440** · portrait ** 1008×1440** · mini ** 1080×360**
126142- Logo (transparent) up to ** 9000×1440** (32-bit)
@@ -133,6 +149,7 @@ I can produce the icon/cover/logo art set from the emitted `ic_launcher.xml` bra
133149---
134150
135151## Sources
152+
136153Meta Horizon publishing docs (upload, manifest, signing, submit, asset guidelines, release channels)
137154— verified June 2026. Key facts: APK + v2 signing, ` quest3|quest3s ` supportedDevices, monotonic
138155` versionCode ` , App-Lab-merged-into-Store.
0 commit comments