Phase 1 of the in-app update flow — server side. Endpoints serve the
bundled Android APK + sidecar version file from /app/client/.
Returns 404 gracefully when files aren't present, so dev environments
and pre-CI-wiring images degrade cleanly to "no update available."
- internal/api/client_assets.go: handleClientVersion + handleClientAPK.
Both unauthenticated (matches /healthz) so install flow doesn't
depend on a live session. APK served with proper
application/vnd.android.package-archive Content-Type +
http.ServeContent so Range requests work for resumable downloads
on flaky networks.
- Path resolves to /app/client/ by default; MINSTREL_CLIENT_APK_DIR
env var overrides for dev.
- Dockerfile creates /app/client/ + commented COPY hooks for the CI
sequencing phase.
- Tests cover all four states: missing apk, apk-but-no-version,
both present (200 with correct shape), apk stream (200 with
correct Content-Type + body bytes).
Phases 2 (Flutter client provider + banner + install intent + Android
manifest changes) and 3 (CI sequencing to bake the APK into the image)
land in follow-up commits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>