# Default server, Sign in, guest joining, server setting — 2026-09-24

Branch `feat/default-server` off `f91de82`. The signed-out home names the
server (calls.oficina.works by default) with one **Sign in** and a secondary
**Join a call as a guest**; the server is a setting (Mac Settings scene,
iPhone gear sheet). M1 Max, macOS 27.

| Check | Result |
| --- | --- |
| `build-bridge.sh` | Rebuilt; RNNoise and DeepFilter cache hits at their pins |
| `swift test` | 133 tests, 0 failures, 1 skipped (keychain); adds `DefaultServerTests` (17) |
| `GOTOOLCHAIN=go1.26.5 make ci`, `make native-test` | Passed with `GOFLAGS=-count=1` |
| `make native-mac` | Built; launched and stayed up |
| `make iphone` | Simulator build succeeded |
| `CGO_ENABLED=0 make browser` | 33 of 34 in the full run while an Xcode build shared the machine; `mutualshare` (a frame-flow window stalled) passed alone on an idle machine. No web code changed |
| `make native-browser` | Passed; `TestNativeBrowserAuthentication` gains a `server` case (Sign in's instance-root handoff) |
| iPhone UI tests | `testSignedOutHomeIsSignInAndGuest`, `testInvalidGuestLink` passed on the simulator |
| `TestIPhoneSafariAuthentication` | Member (Sign in, `-CallsServer`) and guest (guest screen) passed. The guest case was already failing on main: the web's guest screen needs its "sign in" link followed, and Safari's autofill bubble could take the tap on the submit button |
| Snapshots | `CallsInterop --lobby-snapshots` renders the new states (see DESIGN.md) |

# Per-call device routing on LiveKit's module — 2026-09-23

Branch `merge/native-call-devices`: `native-call-devices` (`410ddcb`, the
source of Calls 0.2.1 build 3) merged into `origin/main` at `1ab8a2e`. The
Mac stays on LiveKit's AVAudioEngine module with DeepFilterNet 3; build 3's
CoreAudio HAL device (`CallsAudio`) was dropped, because WebRTC's
custom-audio-device constructor takes no audio processing module. Its routing
rules were carried onto the module (`AudioRoutes`), and its camera discovery,
refresh and restore-on-refusal kept. M1 Max, macOS 27.

| Check | Result |
| --- | --- |
| `build-bridge.sh`, `build-rnnoise.sh`, `build-deepfilter.sh` | Built fresh in a new worktree (no cache) |
| `deepfilter-notices.py` after that build | Regenerates `LICENSE.deepfilternet-crates` byte for byte (90 crates) |
| `swift test` | 116 tests, 0 failures, 1 skipped (keychain test, as before); includes the seven `CallAudioSelectionTests` and a non-default choice on the real module |
| `GOTOOLCHAIN=go1.26.5 make ci`, `make race`, `make native-test` | Passed with `GOFLAGS=-count=1` (no cached results) |
| `make native-mac` | Built; signature verifies; bundle carries LiveKitWebRTC, the DeepFilterNet model, all five notices and `PATENTS.webrtc`; launched and stayed up |
| `make iphone` | Simulator build succeeded |
| `CGO_ENABLED=0 make browser` | 34 of 34 scenarios passed |
| `make native-browser` | Passed (auth, member, guest, chat); CallsInterop rebuilt by the target; the new per-call audio step ran and passed, DeepFilterNet 3 processed the synthetic microphone |

Found while porting: LiveKit's engine module (M150) never reports which device
it is on (`inputDevice`/`outputDevice` read back nil — confirmed on this
machine), and `trySetInputDevice`/`trySetOutputDevice` return YES when the
native call fails (read in the source). Main's pickers read the device back,
so a non-default choice would have been reported as refused; the choice is
now kept by `AudioRoutes`.

Not verified here: this Mac lists only the default microphone and speaker,
so switching between two real devices, unplugging a chosen one, and the
module following a new system default or sample rate need a hands-on pass
with a USB or Bluetooth device.

---

# LiveKit WebRTC and neural noise reduction — 2026-09-23

Branch: `feat/livekit-webrtc-audio`, based on `origin/main` at `3846ca4`. Not
merged, not deployed. Machine: M1 Max, macOS 27, Xcode 26.6.

| Check | Result |
| --- | --- |
| `./native/scripts/build-bridge.sh` | Passed |
| `native/scripts/build-rnnoise.sh`, `build-deepfilter.sh` | Built both xcframeworks from their pins (DeepFilter: Rust 1.92.0 via rustup, cbindgen 0.29.2) |
| `swift test` (native/apple) | 48 tests, 0 failures, including the suppressor fixture tests |
| `make native-test` (uncached, `GOFLAGS=-count=1`) | Passed |
| `make native-mac` | Built; ad hoc signature verifies; bundle carries LiveKitWebRTC.framework, the DeepFilterNet model and the RNNoise/DeepFilterNet notices; app launches |
| `make iphone` + `build-for-testing` (simulator) | Build and test build succeeded; RNNoise linked, DeepFilter not |
| `make native-browser` | Passed (member, guest, chat); asserts DeepFilterNet 3 processed the synthetic microphone and WebRTC reports its suppressor and gain off, echo cancellation on |
| `CGO_ENABLED=0 make browser` | 33 of 34 passed; `switching` (web camera switch) failed once under load from a concurrent simulator run and passed alone. This branch does not touch the web client |
| `make iphone-browser` | Failed, and fails identically on a clean `origin/main` worktree (both peers stuck `reconnecting`, 0 frames); the Safari UI test flaked differently on each run. Environmental, not this branch; the first run's media test did pass on this branch |
| `GOTOOLCHAIN=go1.26.5 make ci` | Passed |

Fixture (synthesised speech at -23 dBFS with seeded hiss and key clicks, each
suppressor alone): RNNoise takes the noise-only lead-in from -41 to -71 dBFS
and DeepFilterNet 3 to -69 dBFS (both at their 30 dB limit); speech holds at
-24.5 → -24.6 dBFS; 99% of voiced frames kept by each. Per 10 ms frame:
RNNoise 0.69–0.81 ms, DeepFilterNet 1.16–1.23 ms (about 7–8% and 12% of one
core). DeepFilterNet's model loads in 0.26–0.35 s. In the native/browser
gate, as wall-clock share of the audio thread with Chromium on the same
machine: DeepFilterNet median 15.1–15.3% (13–27%), RNNoise median 12.9%.

Needs a real call or device: echo with speakers (software AEC3 on Mac, now on
the AVAudioEngine module), typing, wind and room echo against both models;
that SpeechGain lifts a quiet talker without audible pumping; switching Mac
microphone and speaker mid-call (only one of each on the test machine);
Bluetooth at 16 kHz handing back to WebRTC's suppressor; Voice Isolation on
iPhone standing ours aside; whether iPhone accepts the platform-mode options
(the readout says if not); DeepFilterNet's cost on an iPhone. The Mac audio
bug (growling) is not addressed and the module change may move it either way.
A test process cannot get microphone permission, so Apple voice processing's
live state on Mac and the Mic Mode menu could not be observed.

---

# Per-call device routing — 2026-09-21

Superseded for audio when this branch was merged into main on 2026-09-23:
the CoreAudio HAL device (`CallsAudio`) it describes was not kept, and its
routing rules now run on LiveKit's audio device module (see the README).

Worktree: `meet-call-devices`, branch `native-call-devices`, based on fetched
`origin/main` at `3846ca4`. Calls 0.2.1 (build 3) is signed with Team Tito Limited, notarized, and published.
Download: https://raw.amadan.net/oficina/native-releases/deb0bac4cdf6ef23a8edca9094f41260fc02143d/calls/releases/net.amadan.oficina.calls-3.zip

Independent release review: GPT-6 Astra (`release_review`), approved with the
physical-device acceptance limitations below. The extracted, notarized app also
passed Gatekeeper and a relocated launch smoke test.

- Mac app builds and its development signature verifies; iPhone simulator builds.
- All 29 Swift tests pass, including independent per-call selections, reset on
  leave, refused changes, disconnection fallback, following system defaults,
  and reopening devices after sample-rate changes.
- Native/browser member and guest calls pass with the new HAL audio path.
  Reopening both audio routes and refusing a missing microphone preserve
  increasing audio counters and the peer connection. Camera switching, queued
  camera changes, refusal, stream renewal and leaving also pass.
- Native/browser chat passes. The shipping DevicePanel was rendered and visually
  inspected in `native/build/call-devices.png`.
- `GOTOOLCHAIN=go1.26.5 make browser` passes all 34 Chromium scenarios.
- `GOTOOLCHAIN=go1.26.5 make ci` passes. The repository pins this Go version for
  its dependency-notice inventory.

The live gate uses the system's audio route and a synthetic camera. Audio packet
counters establish transport continuity, not audible quality. Physically changing
between USB/Bluetooth devices, unplugging them, and checking echo cancellation
remain hands-on acceptance checks; unit tests cover the routing policy.

---

# Native completion verification — 2026-09-12

Branch: `codex/iphone-and-mirrored-preview`, based on `86ed9f3`.

- Final `make ci`, server race gate, and shared-core race gate pass.
- Full Chromium call gate passes (539 seconds) for this web snapshot.
- Final native/browser gate passes (81 seconds), including member/guest media,
  browser authorization, and bidirectional chat.
- iPhone Safari member/guest authorization and native peer media tests pass
  on iOS 26.5 Simulator (154 seconds). Peers retain media through stream renewal
  and leave cleanly. Simulator media uses synthetic video and disables audio.
- Mac app builds, its ad hoc signature verifies, and it is installed and
  relaunched at `/Applications/Calls.app` with the approved icon and license.
- iPhone development build signs successfully with the personal development
  team and is installed on Paul's iPhone 16 Pro. Initial launch was blocked by
  the phone's lock screen; physical launch/permissions and audible audio await
  an unlocked-device acceptance pass.
- Independent GPT-6 Astra source review approved the local-build milestone
  after fixing cancellation outcomes for queued chat and obsolete join/leave
  operations. A regression test verifies all accepted queued chat gets an
  explicit failure on re-registration; it passes under the race detector.

The server update includes member-only repeat sharing of encrypted invitations
and its additive migration. Older invitations remain valid. Native credentials
remain in memory; calls are foreground-only on iPhone. Developer ID notarization,
TestFlight, physical audio/device routing, background calling and native screen
sharing remain outside this local-install milestone.

The earlier milestone record follows for historical context.

---

# Mac milestone verification — 2026-09-11

Base: fetched `origin/main` at `a283c53`, then fast-forwarded the working branch
to the newer `origin/integrate/home-sso-release` at `eee686a`. Working branch:
`codex/native-mac-milestone`. No production deployment was performed.

| Check | Result |
| --- | --- |
| `CGO_ENABLED=0 make ci` | Passed: vet, formatting, all Go tests and migration agreement |
| `make race` | Passed across `internal/...` |
| `CGO_ENABLED=0 make browser` | Passed all 31 Chromium scenarios; 541 seconds for the browser suite |
| `make native-test` | Passed shared core, bridge and core race tests |
| `make native-mac` | Built the local Mac app with a macOS 14 deployment target |
| `codesign --verify --deep --strict native/build/Calls.app` | Passed for the ad hoc development signature |
| `TestNativeMacCallsBrowser` | Passed member and guest cases; final run 26.49 seconds |
| App launch and opening-window inspection | Passed |

The native/browser test observed video decoded in both directions, audio bytes
received in both directions, no incoming guest media before admission, increasing
media counters after repeated three-second stream renewals, preservation of the
native participation ID, and removal of the browser's remote tile after the
native client left. It uses a synthetic native camera implemented only in the
separate `CallsInterop` executable, Chromium's fake media device, and real
magic-link sessions on an isolated local Calls server. Audio traffic counters do
not prove audible microphone/speaker quality.

`TestNativeCoreUsesHomeSessionAndResumesCall` separately runs the shared Go
client through the real Home assertion/linking fixture, stream renewal and
meeting end. `TestNativeCoreCannotUseAgentBearerAsHumanSession` rejects agent
credentials for calls. Metadata isolation tests cover an actual second meeting
the guest cannot access. Unit tests exercise SSE bounds and framing, redirect
refusal, retry sequence preservation, duplicate delivery and stale native
callbacks after a pair generation changes.

Unverified physical-device acceptance: camera/microphone permission prompts,
audio-device changes, sleep/wake, and a live Home passkey ceremony.
Distribution signing/notarization,
iOS, screen sharing and full long-call TURN renewal parity remain outside this
first milestone. The result is a local development app, not a public release.


The webview sign-in implementation was replaced with system-browser approval.
`TestNativeBrowserAuthentication` covers member and guest sign-in in a fresh
Chromium context with JavaScript disabled: the email opens in a separate tab,
approval hands a session to the shared Go client, and that session can read the
meeting metadata. Server tests cover Home identity/authentication-time
preservation, distinct session tokens, explicit POST approval, CSRF rejection,
wrong proof, replay, expiration, revoked source sessions, and unsafe targets.

Handoff revision checks (2026-09-11): CI and migration agreement passed; core
and bridge tests passed with the core race detector; server authentication race
tests passed; member/guest browser handoff and code comparison passed (65.13 s);
native/browser media and departure checks passed (84.33 s). The rebuilt app's
ad hoc signature verified, and the app was reopened locally. Production sign-in
requires deployment of the new server routes; no deployment was performed.

The full `CGO_ENABLED=0 make browser` gate also passed all 31 scenarios
after the handoff change (797.94 s for Chromium).
