How we verify
Every build, against real Safari
We do not ask you to take our word for it. Every change to Lumen is built on a Mac with real Safari: a profile is compared with it, every request around the proxy is looked for, and the results are recorded.
A profile against Safari on the same Mac
The profile is checked as you get it: a process of its own, Safari’s window and a proxy in its settings. Next to it, real Safari on the same Mac. One page measures both, and a server on this Mac reads their TLS and request headers. The proxy in this run does not answer, and the page is on the Mac itself and loads directly - so the profile keeps the same language and time zone as Safari.
User-Agent, platform, language and Intl, time zone, core count. Only the number of navigator’s properties differs: a profile with a proxy has no navigator.credentials - below.
Screen size and its available part, colour depth, pixel density, pointer and touch.
A page sees it as the window’s outer size minus its inner size. The window’s own size and place are each window’s own: these 8 values are shown, not compared.
The same headers in the same order as the server got them from Safari.
What any https server sees first: JA3, JA4, ciphers and extensions.
Permission states, storage quota, speech voices and hundreds of WebKit’s interfaces. The twelve differences - interfaces a profile with a proxy turns off, and the window’s property count - are below.
Canvas - the same picture byte for byte, WebGL - vendor, renderer, extensions and parameters, audio, font metrics.
A profile has ApplePaySession, PaymentRequest and PushManager, and each of the 17 calls checked answers as it does in Safari on this Mac.
What differs - and why
Of 507 values, 14 differ. We show all of them. Eleven are interfaces a profile turns off because their traffic or their checks would go around the proxy. Two are property counts that change with them. One is the window’s bars.
| Signals | Count | When | Why |
|---|---|---|---|
WebRTCRTCPeerConnection, RTCDataChannel, RTCEncodedVideoFrame, RTCRtpScriptTransform and the RTCPeerConnection function itself |
5 | without one too | With a proxy WebRTC is off: its UDP would go around the proxy. Without a proxy it is off by default. |
Web Authentication (passkeys)PublicKeyCredential, Credential, navigator.credentials, the Web Authentication check |
4 | with a proxy | Passkeys live in the owner’s iCloud Keychain, one set for Safari and every profile, and the system’s services check them, not the profile. Without a proxy these interfaces are there. |
WebTransportWebTransport |
1 | with a proxy | It runs over HTTP/3, that is QUIC over UDP, which neither an HTTP nor a SOCKS5 proxy carries: the connection would go around the proxy. |
| Private Click Measurementad attribution | 1 | with a proxy | WebKit sends attribution reports later, outside the page’s session. With a proxy it is off, and reports already stored are removed at launch. |
Number of window’s own propertieswindowOwnCount |
1 | without one too | Follows from the rows above: 1005 for a profile with a proxy, 1044 for Safari. Two more interfaces are missing without a proxy too - DigitalCredential and MediaSessionCoordinator: WebKit turns them on only for an app with special Apple entitlements, which Safari has. |
Number of navigator’s propertiesnavigatorProtoCount |
1 | with a proxy | Follows from it: a profile with a proxy has no navigator.credentials. |
The window’s barsgeometry.chrome |
1 | without one too | How much of the window’s height the bars take: for a profile in Safari’s window this number differs from Safari’s own on this Mac. |
Leak checks
Separate checks run a profile behind real proxies on the CI machine and look for any way around them. A check fails at the first request that goes around.
Without a proxy, cloudflare-quic.com answers a profile over HTTP/3; through an HTTP or SOCKS5 proxy, over HTTP/2, and the proxy sees every request. UDP is never even asked of the proxy.
leak_check · every build
No RTCPeerConnection in the page, its frames or its workers. A control without a proxy does get its public address over STUN - so the check sees a leak when there is one.
leak_check and the self-test
The test page asks for names to be resolved early: dns-prefetch, preconnect, a Link header. The packet capture holds no DNS question for any of them.
egress_check
A page, a frame and a worker reach for 127.0.0.1, localhost, ::1 and 0.0.0.0. Each request is refused; a loopback listener takes no connection.
self-test
A test server presents a certificate the system would go to the network to complete. In a proxied profile it is refused at the handshake - through the proxy.
self-test
While a profile opens a page that makes every kind of request, CI records every packet leaving the machine with the process that sent it: 2,410 packets in the latest run.
egress_check · pktap
Every write of the app and WebKit is traced: a profile writes nothing where the real Safari keeps its data. In the latest run, a trace of 606,017 lines.
disk_check · fs_usage
Search suggestions, Safe Browsing, translation, Apple Pay and the other services that go to the network from processes of their own are not called from a profile’s process: connections to them are refused.
self-test and packet capture
What every build checks
These steps run on every change to the code. If any required one fails, the build is red.
Every step of a build
| Step | What it checks | Latest run |
|---|---|---|
| Core unit tests | Encryption, the vault, sync, cookie import, warm-up - on Linux and on macOS | 223 tests on macOS, 0 failures |
| Scripts in two engines | The scripts a profile adds to a page behave like built-in functions - in V8 and in JavaScriptCore, Safari’s engine | passed |
| Self-test on real WebKit | Profile isolation, the proxy and fail-closed, sealing on stop, passwords, cookies, sync between two Macs, warm-up, Safari’s window in a profile’s process | 274 checks, 0 failures |
| Safari’s classes | That Safari’s interface in a profile finds everything it relies on: if a macOS update removes something, the build shows it | passed |
| Compared with Safari | A probe of 448 signals: a profile and Safari on one machine | 434 of 448; 0 must-match differences |
| Leaks | QUIC, WebRTC, DNS, requests to the computer itself | passed |
| Packets and disk | Every packet off the machine and every write to disk around the self-test | no packet around the proxy |
| Logs | Nothing of yours reaches the system log in the clear | passed |
| Languages | The Russian and English interfaces hold the same strings | passed |
Check it yourself
All you need is your Mac and any proxy.
The address
In a profile with a proxy, open a page that shows your IP address. It must be the proxy’s.
Fail-closed
Switch the proxy off and reload. The page must not open - let alone show your real address.
The fingerprint
Open the same fingerprint test page in Safari and in a profile on this Mac. The values match - apart from those listed above, which a profile turns off so that nothing goes around the proxy.
If the test page says “Canvas: Spoofed”, open it in plain Safari 26: deviceinfo.me says the same there. Its test knows only an older PNG length, and today’s WebKit encodes the same picture differently.
Reports
The raw data of the comparison with Safari and the leak checks - JSON you can open and compare yourself. No build logs and no build machine paths: only what was measured.
A question about our checks?
Write to us: if there is something we do not measure, we want to know.