Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/build-apk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Build APK

on:
push:
workflow_dispatch:

jobs:
build:
name: Build obfuscated release APK
runs-on: ubuntu-latest
timeout-minutes: 45

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.1'
channel: stable
cache: true

- name: Install dependencies
run: flutter pub get

# --obfuscate scrambles Dart symbol names in the AOT snapshot;
# --split-debug-info writes the symbol maps needed to de-obfuscate
# crash stack traces (flutter symbolize). Without those maps a release
# crash is unreadable, so they are uploaded as a separate artifact.
- name: Build obfuscated release APK
run: flutter build apk --release --obfuscate --split-debug-info=build/symbols

- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: seeonce-release-apk
path: build/app/outputs/flutter-apk/app-release.apk
if-no-files-found: error

- name: Upload de-obfuscation symbols
uses: actions/upload-artifact@v4
with:
name: obfuscation-symbols
path: build/symbols
if-no-files-found: error
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Senders compress → encrypt → chunk → send: `image_sender` (+ `image_compre

Handshake/wire changes require both devices to run a matching build and may force re-pairing. Existing on-wire choices that are load-bearing: RSA-OAEP uses SHA-256 (not the SHA-1 default), reconnect key-hash IDs are the full 256-bit hash, and reconnect includes an `auth_challenge`/`auth_response` step. Changing any of these is a breaking protocol change.

The reconnect auth handshake is **v2** (`_authProtocolVersion` in `local_reconnect_service.dart`). v2 signs `nonce || channelBinding` rather than the bare nonce, where `channelBinding` is a SHA-256 over the two DTLS certificate fingerprints (sorted) pulled from the local/remote SDP. This binds the proof-of-key-possession to the specific DTLS connection, so a LAN relay/MITM — which must terminate DTLS with its own certificate on each leg — is detected (the fingerprints, and therefore the binding, won't match). v2 peers refuse to authenticate with v1 peers, so upgrading one side forces a re-pair. Bump the version on any further change to the signed material.

## Reference

`IMPLEMENTATION_PLAN.md` (repo root) and the "Security notes" section of `README.md` contain deeper rationale for the trust model, platform differences, and ephemerality guarantees.
1 change: 1 addition & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
gradle-wrapper.jar
/build/
/.gradle
/captures/
/gradlew
Expand Down
Loading
Loading