- Android/rust/archy-fips-core: leaf-only fips node as a JNI cdylib (fips pinned to the fips-native fork rev with VpnService fd support), built by gradle via cargo-ndk (arm64), tested on host - ArchyVpnService: split-tunnel VpnService routing only fd00::/8 (MTU 1280, foreground specialUse); FipsManager handles the one-time VPN consent and silent auto-start — no settings surface at all - pairing QR now fully configures the mesh: fnpub/fip/fhost/fudp/ftcp plus fanchors (the node's seed-anchor list, npub@addr/transport) so the phone can rendezvous through public anchors when the LAN endpoint is unreachable - default seed anchors gain the two dual-transport join.fips.network test anchors (23.182.128.74:443/tcp, 217.77.8.91:443/tcp); anchor adverts are Nostr kind-37195 events - device token rides the password field end-to-end: backend accepts tokens wherever it accepts the password, so scan = instant login (WebSocket auth + WebView form injection unchanged); token logins skip TOTP - ServerEntry.meshIp + IPv6-bracketed URLs; WebView retries the mesh address on main-frame errors, auto-login and origin checks honor it - companion v0.5.0 (versionCode 20), arm64 abiFilter; FipsNative.available gates everything so non-arm64 still runs as a plain companion Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
97 lines
6.1 KiB
Markdown
97 lines
6.1 KiB
Markdown
# Companion app pairing QR — integration handoff
|
|
|
|
**Status:** web-UI side SHIPPED (CompanionIntroOverlay.vue, 2026-07-16). This doc is
|
|
the contract + requirements for the companion-app side (worked on separately, on
|
|
the Mac).
|
|
|
|
## What the web UI now does
|
|
|
|
The "Remote Companion" intro modal (shown once after first dashboard login, and in
|
|
the public demo) gained a second screen:
|
|
|
|
1. **Screen 1 (existing):** APK download QR (desktop) / download button, plus a new
|
|
**"I've installed it"** button to the right of the download button.
|
|
2. **Screen 2 (new, slide transition):** a **pairing QR** the companion app scans to
|
|
auto-fill the server entry, with a **Back** button returning to screen 1. On
|
|
small screens (where you can't scan your own display) an
|
|
**"Open in companion app"** deep-link button is shown above Back, using the same
|
|
URI as the QR.
|
|
|
|
## The QR payload / deep link (the contract)
|
|
|
|
A single URI, also usable as an OS deep link:
|
|
|
|
```
|
|
archipelago://pair?v=1&url=<origin>&name=<display name>[&tok=<device token>][&pw=<password>][&fnpub=…&fip=…&fhost=…&fudp=…&ftcp=…]
|
|
```
|
|
|
|
Query parameters:
|
|
|
|
| param | required | meaning |
|
|
|-------|----------|---------|
|
|
| `v` | yes | Payload version, currently `1`. Reject/ignore unknown majors gracefully — show "please update the app". |
|
|
| `url` | yes | Full origin the app should connect to, scheme included: `https://demo.archipelago-foundation.org`, `http://archipelago.local`, `http://192.168.1.228`, etc. No trailing slash guaranteed either way — normalize. |
|
|
| `name`| no | Display name for the server entry. Real nodes send the configured server name, or `My Archipelago` when it's still the factory default. |
|
|
| `tok` | no | **Device token** minted via `auth.createDeviceToken` when the QR is rendered. The app logs in with `{"method":"auth.login","params":{"token":"…"}}` — same endpoint, same rate limiter, skips TOTP (the token was minted from an authenticated session). Long-lived until re-minted (re-showing the pair screen replaces the `companion` token) or revoked (`auth.revokeDeviceToken`). Scan → instantly connected, no typing. |
|
|
| `pw` | no | Login password. **Only present in the public demo** (shared demo password `entertoexit`). Real nodes never embed a password — the frontend doesn't have it. |
|
|
| `fnpub` | no | Node's FIPS mesh identity (bech32 npub of the daemon's seed-derived key). Presence of this param means "this node speaks FIPS — mesh with it". |
|
|
| `fip` | no | Node's `fips0` ULA (IPv6). Once the phone is meshed, the node's UI stays reachable at `http://[<fip>]` from anywhere — this is the remote-access address (replaces the old WireGuard 10.44.0.1 flow). |
|
|
| `fhost` | no | Host the phone's embedded FIPS dials (same host `url` resolved to). |
|
|
| `fudp` / `ftcp` | no | Mesh transport ports on `fhost` (currently 2121/udp and 8443/tcp). |
|
|
| `fanchors` | no | Comma-joined `npub@host:port/transport` rendezvous anchors (the node's seed-anchor list, capped at 4). The phone peers with these too so it can route to the node via the public mesh when the direct endpoint is unreachable (away from home / NAT). |
|
|
|
|
Examples the web UI actually emits:
|
|
|
|
- Demo: `archipelago://pair?v=1&url=https%3A%2F%2Fdemo.archipelago-foundation.org&pw=entertoexit`
|
|
- Real node, browsed via LAN IP: `archipelago://pair?v=1&url=http%3A%2F%2F192.168.1.228`
|
|
- Real node kiosk (UI runs on localhost, so it advertises the mDNS name from
|
|
`system.get-hostname`): `archipelago://pair?v=1&url=http%3A%2F%2Farchipelago.local`
|
|
|
|
## Companion app requirements
|
|
|
|
1. **Scan entry point:** the app's action is labeled **"Scan Node's QR"**
|
|
(implemented 2026-07-17; the modal copy in CompanionIntroOverlay.vue was
|
|
updated to match).
|
|
2. **Parse the URI** (from camera scan AND from an OS deep-link intent —
|
|
register the `archipelago://` scheme so the "Open in companion app" button on
|
|
phones works).
|
|
3. On success, **create/update a saved server entry**:
|
|
- Server address = `url` exactly as given (respect the scheme — the demo is
|
|
https, LAN nodes are typically http, `.local` mDNS names must work).
|
|
- If `pw` present, prefill the password and attempt auto-login; otherwise land
|
|
on the password prompt for that server.
|
|
- If an entry with the same origin already exists, update it rather than
|
|
duplicating.
|
|
4. **Demo flow (the showcase):** scanning the demo QR should take a fresh install
|
|
to a logged-in demo session in one step — url `https://demo.archipelago-foundation.org`,
|
|
password `entertoexit`, no manual typing.
|
|
5. **Robustness:**
|
|
- Tolerate unknown extra query params (forward compat — we may add `name`,
|
|
`cert` fingerprint, etc. under `v=1`).
|
|
- Self-signed HTTPS on `.local`/LAN addresses may appear later; don't hard-fail
|
|
the parse on scheme.
|
|
- Bad/foreign QR → clear error, stay on the scan screen.
|
|
|
|
## Landed extensions (2026-07-22)
|
|
|
|
- **Device token** (`tok`) — real nodes now pair instantly; see the param table.
|
|
Minting replaces the previous `companion` token, so merely re-opening the pair
|
|
screen invalidates a previously issued token (the phone's session/remember
|
|
cookies keep working; a re-scan re-pairs).
|
|
- **`name`** — the app labels the entry with the node's server name
|
|
("My Archipelago" when unset).
|
|
- **FIPS mesh params** (`fnpub`/`fip`/`fhost`/`fudp`/`ftcp`/`fanchors`) — the
|
|
companion app embeds a leaf-only FIPS node (Android/rust/archy-fips-core)
|
|
behind a split-tunnel VpnService and dials the node + rendezvous anchors on
|
|
scan. This replaces the WireGuard install/tunnel onboarding screens entirely;
|
|
remote access = the node's fips0 ULA (`fip`), which the WebView falls back to
|
|
automatically when the LAN address stops answering.
|
|
|
|
## Testing checklist (app side)
|
|
|
|
- [ ] Scan demo QR from https://demo.archipelago-foundation.org → auto-connected demo session.
|
|
- [ ] Scan a real node's QR (LAN IP origin) → entry created, password prompt shown.
|
|
- [ ] Scan a kiosk node's QR (`http://<name>.local`) → mDNS resolution works on the phone.
|
|
- [ ] Tap "Open in companion app" on a phone browser → deep link opens the app with the same behavior.
|
|
- [ ] Re-scan same node → no duplicate entry.
|