eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1# πŸ€– Native performance instrumentation (iOS + Mac)
2
3The answer to "the app has lots of little cuts": both native apps carry a
4small, always-there instrumentation layer (`ios/LChatCore/Sources/LChatCore/
5Perf.swift`) so a slow moment on any device leaves a record β€” without a
6third-party APM SDK, and without anything leaving the device. Streaming
7member telemetry to Datadog/Sentry/Firebase would sit badly with the E2EE
8posture (and add the kind of dependency the second rule exists to avoid), so
9the stack is Apple-native + self-owned end to end.
10
11## The three layers
12
131. **Signposts (always on, every build).** `os_signpost` intervals around
14 the seams that hurt, via `Perf.begin("name", slowMs:)` β†’ `span.end()`.
15 Cost is nanoseconds; nothing is stored or sent. Attach Instruments to any
16 build (subsystem = the bundle id, category `perf`) and the seams below
17 appear as named intervals; hangs and first-frame land in the
18 Points of Interest track.
19
202. **The slow-event ledger (consent-gated).** A span that beats its budget,
21 a main-thread hang (`HangWatchdog` β€” ClickLab's 200ms ping generalised
22 into LChatCore for both apps), or a noteworthy event (socket reconnect)
23 appends one JSON line to `Library/Logs/perf-events.jsonl` in the app's
24 container. ~512KB cap with one `.prev` rotation. Events carry a fixed
25 name, a duration, and at most a URL path or row count β€” never content,
26 names, or keys.
27
283. **MetricKit (consent-gated).** `MetricKitCollector` subscribes to the
29 OS's own APM: daily payloads (launch-time histograms, hang totals,
30 memory) and hang diagnostics **with call trees** β€” real-device stack
31 traces for hangs nobody was profiling. Raw payloads are filed under
32 `Library/Logs/perf-diagnostics/` (newest 24 kept) with a compact ledger
33 line each. The Mac delivers a subset (no hang diagnostics) β€” that's why
34 the watchdog exists on both platforms.
35
36## The gate
37
38- **Debug builds record by default** (opt out: the Settings toggle, or
39 `defaults write <bundle id> perfDiagnostics -bool NO`).
40- **Release builds record only with the consent toggle** β€” iOS: the server
41 settings cog β†’ Performance; Mac: Settings β†’ Advanced β†’ Performance
42 (Paul's call, 2026-07-20). Signposts stay on either way.
43- The extensions (NSE, share) never call `Perf.install()`, so for them the
44 layer is inert.
45
46## Instrumented seams
47
48| Span | Where | Budget |
49|---|---|---|
50| `boot.firstFrame` | launch hook β†’ RootView first appear (iOS) | recorded always |
51| `threads.hydrate` / `threads.refresh` | ThreadStore | 500ms / 2500ms |
52| `feed.openThread` | ChatFeedModel.loadHistory (open a chat β†’ rendered) | 1500ms |
53| `feed.foldPage` | ChatFeedModel.foldPageDetached (200-row decrypt+fold) | 250ms |
54| `api.get` / `api.post` | LChatAPI choke points (detail = path, never query/token) | 1000ms |
55| `socket.connect` | ChatSocket connect β†’ handshake (failed attempts unrecorded) | 3000ms |
56| `image.load` | BlobImageLoader fetch+decrypt+decode | 1000ms |
57| `main.hang` | HangWatchdog (>250ms main-thread wedge β€” Apple's responsiveness bar) | β€” |
58| `frame.hitch` | HitchRecorder (iOS only: rendered-frame gap β‰₯120ms; the felt-jank band below the watchdog β€” the Mac has no CADisplayLink and keeps just the watchdog) | β€” |
59| `socket.reconnect` | note event, 30s same-name floor | β€” |
60
61Adding one is two lines (`Perf.begin`/`end`) β€” budgets are per-call-site, so
62pick the number at which you'd actually want a report.
63
64## Getting the data off a device
65
66- **Settings β†’ Performance β†’ Prepare performance report** (iOS shares the
67 file; the Mac reveals it in Finder): one self-contained
68 `performance-report.json` β€” device model, OS, app version, the ledger,
69 and the newest MetricKit payloads (2MB cap).
70- Dev pull without the UI:
71 `xcrun devicectl device copy from --device <id> --source Library/Logs/perf-events.jsonl --domain-type appDataContainer --domain-identifier com.elevenmessenger.messenger` (same pattern as `hitches.log`).
72- Also free, zero code: **Xcode Organizer** (Hangs / Launch / Terminations
73 from TestFlight + opted-in App Store users) and the App Store Connect
74 perf-power-metrics + diagnostic-signature APIs (the existing ASC key can
75 drive a periodic pull).
76
77## What this is not (yet)
78
79The report is the streaming unit by design (`PerfExport` is
80self-contained), but nothing streams anywhere today. The intended follow-up
81is an **opt-in** upload of that JSON to a webhook-ingest feed
82(`docs/webhooks.md`) so reports land as encrypted messages in a team
83thread β€” an Aikido-style stream we own, with the server still seeing only
84ciphertext. Third-party APM stays off the table.
85
86Related: `HitchLogger.swift` (iOS, DEBUG-only frame-drop breadcrumbs β€”
87still marked temporary) and `ClickLab.swift` (Mac click-eating rig) predate
88this layer and stay as-is; fold them in only when their bugs close.
89