Real Groth16 prover + camera scan + smoke runbook#64
Merged
Conversation
added 6 commits
May 29, 2026 17:17
Three pieces that together move the ADR 0023 registration ceremony
from "plumbing demo" to "drive end-to-end against a real phone":
1) RealRegistrationProver.kt — implements ProofGenerator using the
existing IsolatedMobileProver (snarkjs in the sandboxed :prover
process per ADR 0010). Builds a RegistrationUnlockedCredential
from the per-install 32-byte secret with:
biometricSecret = secret as decimal field element
salt = 0 (reserved for StrongBox salt per ADR 0018)
commitment = Poseidon(secret, salt)
didHash = Poseidon(commitment) single-arg, binds DID
to commitment in the proof's hash chain
The prover runs against the same identity_proof v1.2 circuit as
the W3 sign-in flow — no circuit change needed. publicSignals[1]
is NOT bound to the issued challenge_nonce at the circuit layer
(V1 limitation per ADR 0023); replay defence remains the single-
use verify_code chain + 15-min TTL + per-IP rate-limit. Circuit
v1.3 in Phase 1 Sprint 4 will bind the challenge in the public
inputs.
Wired in RegistrationScreen's viewModelFactory so production
uses the real prover. StubProofGenerator stays as a test seam.
2) RegistrationQrCamera.kt — lean CameraX + ML Kit composable
focused on registration deeplinks (zeroauth://reg?...). Handles
the permission_pending / permission_denied / scanning states.
First non-empty barcode latches and calls onResult; subsequent
frames are dropped. Wired into RegistrationScreen as a "Scan
with camera" button next to the existing paste field — the
operator picks either input mode.
The composable is intentionally NOT a refactor of ScanScreen.kt
— that one carries the W3 proof-pairing state machine and isn't
trivially reusable. The duplication is ~150 lines and
compartmentalises the regression risk.
3) docs/operations/three-qr-signup-deployment.md +
scripts/smoke-registration.sh — runbook + executable smoke
test that validates the four documented HTTP round-trips
against a deployed backend in <10s. The smoke checks:
- POST /v1/registrations issues a well-shaped pair_code
- pair-device returns next.step=enroll + valid enroll_code
- submit-commitment returns next.step=verify + 32-hex challenge
- complete with stub proof correctly surfaces 404 verify_failed
(server's verifier rejected — plumbing reaches the verifier)
The script is documented as pilot-grade — the real-proof verify
step requires a real phone running the APK with the camera flow
from item (2) above.
Verify:
- Bash syntax check: bash -n scripts/smoke-registration.sh OK
- Android compile + emulator smoke run on CI for items 1 + 2.
- Backend tests are unchanged (this commit only adds Android +
docs + a bash script).
ImageProxy.image accessor is annotated @ExperimentalGetImage; the inner lambda inside setAnalyzer transitively uses it. Add the file-level import + an @OptIn on CameraScanLayer to silence the opt-in propagation error.
The function-level @OptIn wasn't propagating into the inner setAnalyzer lambda. File-level annotation is the cleanest fix and matches the file-scope semantics — this whole file is the camera scanner so opting in at the top is honest.
CameraX @ExperimentalGetImage is androidx's lint-style opt-in, not Kotlin's @RequiresOptIn — Kotlin's @OptIn has no effect on it. The clean fix is the same pattern ScanScreen.kt uses: extract the ImageAnalysis.Analyzer into a private class with the annotation directly on the class declaration. The lint resolver picks it up at the use site and the build goes green.
@ExperimentalGetImage on the class makes the constructor call site require opt-in too; on the override method it scopes only to the proxy.image access. Matches ScanScreen.kt's QrPayloadAnalyzer pattern verbatim.
Lint's ParcelCreator check prefers @JvmField val CREATOR over the `companion object CREATOR` shorthand. Refactoring would ripple through the IPC fixtures and the WebViewMobileProverTest + IsolatedMobileProverTest helpers; suppress on the two creator sites and track the @JvmField cleanup as Phase 1 Sprint 4 work alongside the prover-witness refactor.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Moves the ADR 0023 ceremony from "plumbing demo" to "drive end-to-end against a real phone":
RealRegistrationProver.kt — wires the existing IsolatedMobileProver (snarkjs in the sandboxed :prover process per ADR 0010) into the registration verify step. Builds a RegistrationUnlockedCredential from the per-install 32-byte secret. The proof runs against the existing identity_proof v1.2 circuit (no circuit change needed). publicSignals[0] is the commitment the server checks against; publicSignals[1]/[2] are circuit-internal. ADR 0023 V1 replay defence (single-use verify_code + 15-min TTL + per-IP rate-limit) is unchanged; circuit v1.3 in Phase 1 Sprint 4 will bind the challenge in the public inputs.
RegistrationQrCamera.kt — lean CameraX + ML Kit composable for scanning
zeroauth://reg?...deeplinks. Permission flow + cancel CTA + first-result latch. Wired into RegistrationScreen as a "Scan with camera" button next to paste.docs/operations/three-qr-signup-deployment.md + scripts/smoke-registration.sh — runbook for the end-to-end deploy + a <10s smoke script that validates all four HTTP round-trips against a deployed backend.
Test plan
bash -n scripts/smoke-registration.shclean