PinnedCert.kt no longer carries a pasted certificate. The build reads $XDG_CONFIG_HOME/ai-app/certs/ca.pem (AI_APP_CA overrides) and generates the constant, so the trust anchor follows the build machine: an APK built on the backend host pins that host, and one built in the dev VM pins the VM's throwaway CA and is good only for its emulator. That removes the reason to add a second trust anchor for development -- there is nothing to add and then forget to remove -- and it means the private key never has to exist near this repo, which the VM can write. Regenerating a CA now needs a rebuild instead of a paste, so a stale constant can't quietly disagree with the server. build-apk.sh is the missing counterpart to run-android.sh: it produces the APK to install through Local Updater and touches no emulator. It finds the SDK from ANDROID_HOME/ANDROID_SDK_ROOT before falling back to ~/Android/Sdk, since the host doesn't share the VM's layout, and prints the fingerprint of the CA being pinned so a wrong one is visible there rather than as a handshake failure on the phone. Verified end to end on the emulator against a server using a freshly generated CA -- which is how the first attempt was caught: the generated constant began with a newline, so CertificateFactory lost the "-----BEGIN" sniff, tried DER, and failed at runtime with an ASN.1 decode error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
69 lines
2.9 KiB
Bash
Executable File
69 lines
2.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# Builds the app's APK, ready to install on a phone through Local Updater.
|
|
#
|
|
# ./build-apk.sh
|
|
#
|
|
# The APK pins the CA on *this* machine ($XDG_CONFIG_HOME/ai-app/certs/ca.pem,
|
|
# or AI_APP_CA), so build it on the machine that runs the backend: an app
|
|
# built somewhere else trusts a CA that backend can't present, and simply
|
|
# won't connect. Run ../gen-dev-cert.sh first if there are no certificates
|
|
# yet; the build stops with that instruction if it can't find one.
|
|
#
|
|
# Unlike ./run-android.sh, this touches no emulator: it only produces the
|
|
# file. Installing on a real phone goes through Local Updater, which serves
|
|
# whatever is under this project's build directory.
|
|
set -eu
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# Prefer an SDK this machine has already configured -- the host and the dev
|
|
# VM don't keep it in the same place, and android-env.sh is written for the
|
|
# VM's layout (it also installs missing packages, which isn't wanted here).
|
|
if [ -n "${ANDROID_HOME:-}" ] && [ -d "${ANDROID_HOME}" ]; then
|
|
echo "==> Using ANDROID_HOME=$ANDROID_HOME"
|
|
elif [ -n "${ANDROID_SDK_ROOT:-}" ] && [ -d "${ANDROID_SDK_ROOT}" ]; then
|
|
ANDROID_HOME="$ANDROID_SDK_ROOT"
|
|
export ANDROID_HOME
|
|
echo "==> Using ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT"
|
|
elif [ -d "$HOME/Android/Sdk" ]; then
|
|
ANDROID_HOME="$HOME/Android/Sdk"
|
|
ANDROID_SDK_ROOT="$ANDROID_HOME"
|
|
export ANDROID_HOME ANDROID_SDK_ROOT
|
|
echo "==> Using $ANDROID_HOME"
|
|
else
|
|
echo "No Android SDK found. Set ANDROID_HOME to it, or install one" >&2
|
|
echo "(Android Studio's default location is ~/Android/Sdk)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
CA="${AI_APP_CA:-${XDG_CONFIG_HOME:-$HOME/.config}/ai-app/certs/ca.pem}"
|
|
if [ -f "$CA" ]; then
|
|
# Printed so a wrong or stale certificate is visible here rather than as
|
|
# a handshake failure on the phone. Compare with the server's own
|
|
# ca-sha256.txt, which gen-dev-cert.sh writes beside it.
|
|
FINGERPRINT=$(openssl x509 -in "$CA" -pubkey -noout 2>/dev/null \
|
|
| openssl pkey -pubin -outform der 2>/dev/null \
|
|
| openssl dgst -sha256 -binary 2>/dev/null \
|
|
| openssl base64 2>/dev/null || echo "(openssl unavailable)")
|
|
echo "==> Pinning the CA at $CA"
|
|
echo " fingerprint: $FINGERPRINT"
|
|
else
|
|
echo "No CA certificate at $CA -- run ../gen-dev-cert.sh on this machine" >&2
|
|
echo "first, or set AI_APP_CA to one. The APK has to embed it at build time." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> Building"
|
|
./gradlew :androidApp:assembleDebug
|
|
|
|
APK="$SCRIPT_DIR/androidApp/build/outputs/apk/debug/androidApp-debug.apk"
|
|
echo
|
|
echo "==> Built $APK"
|
|
[ -f "$APK" ] && ls -lh "$APK" | awk '{print " " $5}'
|
|
echo
|
|
echo "To get it onto the phone: add this project to Local Updater (or hit"
|
|
echo "Update on it if it's already there) and install from there."
|
|
echo "Then start the backend and scan the enrollment QR it prints:"
|
|
echo " ./server/target/release/ai-server --rotate-token"
|