Repository navigation
Android: fix localhost launch, login→Menu Studio, edge-to-edge, native print bridge (BT Classic/USB) - #350
Conversation
…/localhost:3100
Behind Nginx the standalone server listens on HOSTNAME=127.0.0.1 PORT=3100 and Next
derives request.url from that listener, so new URL('/app', request.url) emitted
Location: https://localhost:3100/app and the Play TWA left the verified origin.
Reproduced on a Next 16.4.5 standalone server and observed live on production.
A relative Location is resolved by the client against the origin it requested.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
Signed-in merchants with an outlet land on /dashboard/menu; signed-out users sign in and return to /app. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
viewport-fit=cover, side and top safe-area padding, a status-bar scrim in installed display modes and sticky headers that stick below the top inset. No effect in a normal browser tab (insets are 0). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
…printing Short-lived HMAC grant bound to user, session, outlet, sale and paper width. Redemption re-checks the live session and current membership and returns ESC/POS bytes from the same receipt mapping the page uses (extracted to thermal-view.ts). Adds the receipt-page entry point for the Android print bridge. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
…bridge Adaptive + themed icon from the approved brand mark, splash, system bar colours, notification small icon, short launcher label, wider merchant App Links, notification permission, and PrintBridgeActivity with Bluetooth Classic (SPP) and USB transports. Not yet verified on physical printers. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
… a crash Adds a deterministic launch-contract script (redirect, cookie, login page, assetlinks) and a UI classifier for the emulator smoke test. Strict on manual and daily runs, warning on pull requests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
…fixture finding Gitleaks flagged a fake AUTH_SECRET literal in the native-print grant test (not a credential). Follows the existing .gitleaksignore convention: ignore that fingerprint, generate it at runtime after. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
The repo's API boundary guard forbids unbounded req.json(). Read at most 256 bytes via boundedBytes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
…the login page The first CI emulator run reported 'good:sign in' on Chrome's own 'Welcome to Chrome / Sign in to browse easier' screen. Handle the first-run screen first (dismiss it), and accept only MenuMinute- specific login text. Regression test uses the exact screen captured in CI. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe
|
CI status on One flake, not from this PR: Emulator evidence (useful for review): the Android smoke job now reports its destination verdict. On this run it dismissed Chrome's first-run screen, followed the app's real launch URL and reported Generated by Claude Code |
Outcome
Fixes the Android app opening
localhostand moves the app toward a launch-ready build, following the owner's decisions of 2026-11-10: launch login then Menu Studio, edge-to-edge layout, and Bluetooth Classic + USB printers as a launch requirement. Related: #340.GET /api/app/launchreturnedLocation: https://localhost:3100/app(Next derivesrequest.urlfrom the internalHOSTNAME=127.0.0.1:3100listener behind Nginx). It now returns a relative/app. Reproduced on a standalone Next 16.4.5 server and observed live./appsends signed-in merchants with an outlet to/dashboard/menu; signed-out users go to/login?next=%2Fappand return.viewport-fit=cover, side and top safe-area padding, status-bar scrim in installed display modes, sticky headers below the top inset (13 CSS blocks + 3 Tailwind classes). Insets are 0 in a normal browser tab.MenuMinute, notification permission, merchant routes added to App Links (diner/m/...stays in the browser).PrintBridgeActivitywith Bluetooth Classic (SPP) and USB transports. The web session mints a 120 s HMAC grant bound to user/session/outlet/sale/paper; the app redeems it at a fixed HTTPS endpoint for ESC/POS bytes produced from the same receipt mapping as the page (extracted tothermal-view.ts). Redemption re-checks the live session and current membership. Printing needs an explicit tap. Design and threat model:docs/android-release/print-bridge.md.scripts/android/verify-launch-contract.sh(redirect, cookie, login page, assetlinks) plus an emulator UI classifier so the smoke test can no longer pass on an error page. Strict on manual and daily runs; a warning on PRs because PRs run against the currently deployed server.mobile-production-plan.md(analysis, phased plan, status table incl. the external audit),print-bridge.md, README and device matrix updates.Contract and ownership
Paths:
src/app/api/{app/launch,pos,native-print},src/modules/pos/{native-print,thermal-view}.ts,src/lib/pos, receipt page,src/styles/globals.cssand sticky-header CSS,android/**,scripts/android/**, one workflow, docs. No schema, migration, lockfile or env-var change.AUTH_SECRETis reused with a domain-separated derivation. Triggering a native change means the signed-AAB workflow will build a new candidate after merge.Correctness and recovery
POST /api/native-print/receiptis cookie-less by design (bearer grant, 4 KiB body cap). No dedicated rate limit yet; the grant expiry and a single-sale scope bound the exposure.Evidence
tsc --noEmit, ESLint, Prettier,git diff --checkclean. 10 suites / 50 tests pass DB-free (grant sign/verify/expiry/tamper/revocation, intent URL, Android manifest contract, UI classifier, launch route, PWA, thermal receipt). The DB-backed suite was not run locally (no Postgres); CI covers it.assembleDebug+bundleReleasesucceed (compileSdk 36);aapt2 dump badgingconfirms label, adaptive icon, permissions and activities. Icons rendered and visually checked, including a fixed monochrome cut-out bug.verify-launch-contract.shfails on it today and passes the other checks.Not verified (honest limits)
No physical printer, phone or emulator run. Bluetooth Classic and USB printing, the splash, bar colours, edge-to-edge behaviour in a TWA, notification permission and App Links are unverified on a device.
Whether Chrome draws TWA content behind the system bars depends on its version; the web layer is ready, the behaviour needs checking on Android 10/13/15/16.
Branch protection on
main(audit finding) is an owner action. Play Billing remains disabled.Scope and invariants match the issue; current versus target behavior is explicit.
Changed docs formatting, local links and git diff --check pass.
Applicable runtime/browser/Android checks pass on final SHA: rely on CI.
Independent review evidence recorded.
Delivery
Merge is not a deployment. After the gated web deploy: run
scripts/android/verify-launch-contract.sh(must pass), then install the new signed Play candidate on a real device and walkdevice-test-matrix.md.🤖 Generated with Claude Code
https://claude.ai/code/session_018caQbzeYyb3uEGoW7Sbthe