| 1 | `Call` and `MediaEngine` are main-actor owned. The Go listener dispatches in |
| 2 | arrival order onto that actor. Each peer serializes asynchronous SDP operations; |
| 3 | late callbacks check the peer's closed flag, and the Go sender checks generation |
| 4 | before accepting a signal. Candidate queues are bounded and drained only after a |
| 5 | remote description. A call epoch discards callbacks from an earlier room. |
| 6 | |
| 7 | `@preconcurrency import CallsBridge` describes an Objective-C boundary whose |
| 8 | thread safety is supplied by the Go mutexes and tested with the race detector. |
| 9 | It is not an assertion that WebRTC objects can be used from arbitrary threads. |
| 10 | |
| 11 | |
| 12 | Native sign-in opens the default system browser. The shared Go client creates a |
| 13 | random verifier, sends its SHA-256 challenge to `/native/request`, and polls |
| 14 | `/native/exchange` with the verifier. `/native/start` keeps a short-lived pending |
| 15 | cookie while the existing sign-in and invitation continuation run. The browser |
| 16 | then shows the signed-in account, a code matching the app, and an explicit |
| 17 | same-origin protected approval form. JavaScript is unnecessary. |
| 18 | |
| 19 | The server retains bounded requests in memory for five minutes. Exchange is |
| 20 | single-use and revalidates the approving browser session and current identity |
| 21 | before minting a separate normal framework session. Home subject, method and |
| 22 | authentication time are preserved. A browser URL contains neither the verifier |
| 23 | nor a session token. Request/exchange honor no ambient cookie, forbid redirects |
| 24 | in the client and expose no CORS access. Cancellation stops the app's polling; |
| 25 | the unconsumed server request expires. This is a Calls session handoff, not an |
| 26 | OAuth authorization server or a replacement for Home assertion verification. |
| 27 | |
| 28 | The web's **Join in the app** starts the same handoff from the other end |
| 29 | (2026-10-02). `GET /m/{sqid}/app`, with the browser's session and standing, |
| 30 | makes an *offer*: random, five minutes, one per browser session, bound to that |
| 31 | session's cookie hash and the meeting. The app link carries it. The app's |
| 32 | `/native/request` names it (`BeginOfferAuthentication`), which binds that one |
| 33 | request to it — a second claim is 410 — and the waiting page in the browser |
| 34 | that made the offer (`/native/offer?o=…`, readable only by that session) turns |
| 35 | into `/native/authorize` for the request. Approval and exchange are unchanged. |
| 36 | The offer is not a credential: a claim yields only a request id, and only the |
| 37 | offering browser can see which request claimed it or approve it. |
| 38 | |