CloakBrowser Pro — launch & native license-session protocol
Recorded 2026-10-18 from the isolated lab at ~/claude/random/cloakbrowser-lab (baseline 56fcd4b). Static inspection of the installed Python wrapper plus the Pro Chromium binary (strings and, as of the 2026-10-18 update, rizin disassembly of the native license runtime), one authorized runtime capture, and controlled sandboxed test runs. Not an exhaustive packet capture of all product behavior.
Update 2026-10-19: Phase 5 documents verified private keyless copies for macOS ARM64, Linux x86-64, and Windows x86-64. The protocol sections describe the unmodified vendor binaries. Their refusal of forged/offline leases does not make a separately converted, plaintext-table browser impossible. The original binaries and saved logins were preserved.
Evidence base (provenance of every claim)
Section titled “Evidence base (provenance of every claim)”- Wrapper source —
cloakbrowser/license.pyandbrowser.pyin the installed package (~/.local/share/uv/tools/cloakbrowser/lib/python3.12/site-packages/cloakbrowser), read in full. - Binary strings — 374,217 strings from
Chromium Framework(sha256e4fdeeb97c0402e2fc2bf894ea706499611477a955d8a9781077fbc257dc9884, build154.0.8037.57.1), examined in binary order, not just grep hits. - Runtime capture — the
session/startrequest/response. Re-captured live on 2026-10-18 (Phase 2b) via a logging reverse-proxy relaying to the real server, with the lease signature independently verified against the Phase-2a pinned key; this supersedes §1’s one-shot capture for the crypto specifics (key never logged, seat released viasession/end).
Structural fact: wrapper vs. binary
Section titled “Structural fact: wrapper vs. binary”session/start is not a wrapper call. The Python wrapper only ever talks to three endpoints (literals in license.py):
| Wrapper call | URL | Purpose |
|---|---|---|
validate_license() |
/api/license/validate |
key validity, cached 24h (.license_cache) |
get_session_seats() |
/api/license/session/count |
live seat count, deliberately not cached |
get_pro_latest_release() |
/api/download/version |
version resolution, cached 1h |
/api/license/session/{start,heartbeat,end} appear only inside the native binary, in components/ungoogled/license_runtime.cc (source path confirmed in strings). On launch, two independent things happen: the wrapper prepares the environment, then the binary runs its own licensing protocol the wrapper never sees on the wire.
Phase 1 — wrapper, before the binary starts
Section titled “Phase 1 — wrapper, before the binary starts”From browser.py + license.py:
resolve_license_key()— precedence:license_key=arg →CLOAKBROWSER_LICENSE_KEYenv → cache-dirlicense.keyfile (default~/.cloakbrowser/vs a custom path tracked separately) → none.mint_denial_file()— computes a unique~/.cloakbrowser/denials/<uuid>.json, creates the directory only, sweeps denial files older than 3600s. ReturnsNone(feature skipped) if the dir isn’t writable — “the fix must never break a launch.”build_launch_env()— decides whether to injectCLOAKBROWSER_LICENSE_KEYinto the child env based on the key’s source: inject forparam/custom_file; don’t forenv(already inherited) ordefault_file(binary reads~/.cloakbrowser/license.keyitself — keeps the key out of process env). Always addsCLOAKBROWSER_LICENSE_STATUS_FILE=<denial path>. Because Playwright replaces rather than merges the child env, any customenv=forces a fullos.environcopy soHOMEand the key survive.- Playwright launches the binary.
Phase 2 — native session/start (binary, license_runtime.cc)
Section titled “Phase 2 — native session/start (binary, license_runtime.cc)”The license runtime’s symbol cluster, in binary order:
license_key install_id k_pub lease_idBlockUntilFingerprintTableKeyResolvedMakeLoader heartbeat_interval_seconds lease_ttl_seconds ScheduleHeartbeatcloakbrowser fp-table K v1|license_key_hash expires_at K_wrapped macos license.key .cloakbrowserThis is not “a license check that gates a boolean.” The grant delivers key material:
- The binary reads the API base from
CLOAKBROWSER_LICENSE_API(defaulthttps://cloakbrowser.dev) and POSTssession/startwithlicense_key,install_id,k_pub(a per-launch public key), plusplatform/version. - The response carries
K_wrapped— a key sealed to that launch’sk_pub— withlease_id,expires_at,license_key_hash,heartbeat_interval_seconds,lease_ttl_seconds. BlockUntilFingerprintTableKeyResolvedis a hard gate in the unmodified binary: startup waits for the content key needed to decrypt the fingerprint tables. Recovery confirmed that X25519/HKDF-SHA256 derives a wrapping key, ChaCha20-Poly1305 unwraps the 32-byte content key, and AES-256-GCM uses that content key to decrypt the table. Correction:cloakbrowser fp-table K v1|is the HKDF wrapping-key information prefix, not a second derivation applied to the unwrapped content key. Phase 5 records the verified layout.
Scope of the offline refusal: an unmodified Pro binary cannot complete its normal encrypted-table startup without valid key material, and the observed expired/replayed or wrong-signer leases are refused. That does not prove all offline modifications impossible. After one authorized plaintext recovery for a specific payload, a different native copy can embed that data and replace the licensing-dependent loader. This is the approach verified in Phase 5; it does not forge a vendor lease.
Phase 2a — instruction-level disassembly (update 2026-10-18)
Section titled “Phase 2a — instruction-level disassembly (update 2026-10-18)”license_runtime.cc has now been disassembled, not just string-scanned. REA’s Hopper backend was blocked (macOS denied Apple-Events automation to Hopper), so this used rizin 0.9.1 driven by an arm64 adrp/add reference scan: every reference to the license strings resolves into one tight cluster, 0x08a0c200–0x08a10170 (~16 KB, 12 functions) in Chromium Framework (same sha256 as above). That span is the compiled translation unit.
Functions, by the strings/keys each references (file offset == VA, image base 0; boundaries past 0x08a0ca78 are approximate — a couple of recovered entries are tail-jump blocks, not true prologues):
| Addr | Role |
|---|---|
fcn.08a0c3d0 |
build session/start request JSON — license_key, install_id, "macos", k_pub, lease_id |
fcn.08a0ca78¹ |
resolve API base — https://cloakbrowser.dev / env CLOAKBROWSER_LICENSE_API |
fcn.08a0cc5c |
MakeLoader + status file (env CLOAKBROWSER_LICENSE_STATUS_FILE) |
fcn.08a0d168 |
StartSession — read key (env CLOAKBROWSER_LICENSE_KEY / ~/.cloakbrowser/license.key), install_id, fire /start & /end |
fcn.08a0e3e4 |
core verifier (5 392 B, 330 blocks) — parse lease, verify signature, read lease_ttl_seconds / heartbeat_interval_seconds, unwrap K_wrapped |
fcn.08a0f988 |
ScheduleHeartbeat — send /heartbeat, verify response |
fcn.08a0c6e8 |
Completion-callback dispatcher; the earlier identification as the key-resolution gate was incorrect |
fcn.08a0c7fc |
Actual BlockUntilFingerprintTableKeyResolved entry, corrected during the keyless conversion |
Signature verification is now confirmed in the binary (previously only a single-capture claim). The verified response is passed to a primitive at 0x6ee4d80, called as verify(x0=msg, x1=len, x2=sig, x3=pubkey) with the result checked == 1, at two sites — 0x08a0ed4c (the session/start lease) and 0x08a0f164. The x3 pubkey argument is a 32-byte constant pinned in the binary at 0xe931fb7, sitting immediately after the /api/license/session/start literal:
619750ac24a66fcf6854032758859e7984870a32e4dc0151bb78f4df06a4f6b5This is the pinned key the strings-only pass could not find: it is raw bytes, not a string, so a string search misses it, and the verify primitive is shared BoringSSL with no license-scoped symbol. Its shape (32-byte key, passed as 4th arg, result-is-1 check) is consistent with ED25519_verify; the exact algorithm of 0x6ee4d80 was not walked into.
K_wrapped unwrap, confirmed: the core verifier looks up "K_wrapped" in the parsed JSON response (bl 0x6df9658, key length 9), calls local helper fcn.08a0feb8, then checks the result length == 0x20 (32). The unwrapped session key is 32 bytes — corroborating §1’s capture at the instruction level.
¹ 0x08a0ca78 is a tail-jump block into the base-URL resolver, not its entry; the URL/env references physically sit in that range.
Full annotated listing (license_runtime.annotated.asm, in the lab scratchpad): function map, pinned key, and every license-string reference annotated inline.
Phase 2b — live session/start capture + signature proof (update 2026-10-18)
Section titled “Phase 2b — live session/start capture + signature proof (update 2026-10-18)”The session/start exchange was re-captured live on this Mac and the lease signature verified end-to-end. Method: a local logging reverse-proxy relayed to the real cloakbrowser.dev (so the signed response is genuine) with CLOAKBROWSER_LICENSE_API pointed at it; the Pro 154.x binary was launched directly (a free key makes the wrapper’s ensure_binary pick the free 145.x build, which has no license runtime). The key was read by the binary from ~/.cloakbrowser/license.key and never logged (redacted in the request record), and the browser was closed cleanly so session/end released the seat. This key is a free-plan key, so the lease below is a free-plan lease — the signed-lease machinery is identical across plans.
Request — POST /api/license/session/start:
{ "install_id": "<uuid>", "instance_id": "<uuid>", "k_pub": "<32-byte hex, regenerated per launch>", "license_key": "<redacted>", "platform": "macos", "version": "154.0.8037.57.1" }Both install_id and instance_id are sent (see the overturned retractions below).
Response — 200, body is zstd-compressed JSON (Content-Encoding: zstd):
{ "valid": true, "lease_id": "lease_...", "heartbeat_interval_seconds": 60, "lease_ttl_seconds": 240, "plan": "free", "token": "<base64url(payload)>.<base64url(signature)>" }lease_ttl_seconds: 240 = 4 min, matching the wrapper’s exit-76 free/trial/solo window; heartbeat_interval_seconds: 60.
Signed payload — base64url-decode of the token’s first part:
{ "K_wrapped": "<184 hex chars = 92 bytes>", "license_key_hash": "<64 hex = 32 bytes = SHA-256(license_key)>", "lease_id": "lease_...", "issued_at": "...Z", "expires_at": "...Z (= issued_at + 240 s)", "plan": "free", "platform": "macos", "version": "154.0.8037.57.1", "install_id": "<uuid>", "instance_id": "<uuid>" }Signature verification (the proof). The token’s second part is 64 bytes = Ed25519. Verified with Python cryptography against the pinned key 619750ac24…f6b5 extracted from the binary in Phase 2a: pub.verify(sig, base64url_decode(payload)) succeeds. So the disassembled pinned key is the vendor’s signing key, the algorithm is Ed25519, and the signed message is the raw decoded payload bytes (not the base64url string — that variant fails to verify). This no longer rests on §1’s single capture.
Confirmed byte-lengths / facts: signature 64 B (Ed25519); K_wrapped 92 B (hex-encoded in the payload); k_pub 32 B; license_key_hash 32 B and equal to SHA-256(license_key) — it matches key_sha256 in ~/.cloakbrowser/.license_cache. expires_at − issued_at = lease_ttl_seconds exactly.
session/end body, captured on clean close: { install_id, instance_id, lease_id, license_key } — end carries lease_id (as predicted) plus the two ids and the key.
Phase 2c — offline refusal, seat cap, and session longevity (update 2026-10-18)
Section titled “Phase 2c — offline refusal, seat cap, and session longevity (update 2026-10-18)”The four behaviors the earlier lab could not establish were tested directly on this Mac, launching the Pro 154.x binary with CLOAKBROWSER_LICENSE_API pointed at a local server — an offline mock for the refusal tests, a logging relay to the real server for the live ones — reading exit/denial codes from CLOAKBROWSER_LICENSE_STATUS_FILE. Free-plan key throughout.
Unmodified Pro rejects the tested offline leases, confirmed behaviorally. Served the original binary a crafted lease with no server contact:
- Replaying the genuine captured lease → exit 78 (expired + bound to the old
k_pub; leases are not replayable). - A forged, non-expired, structurally perfect lease signed with a freshly generated Ed25519 key (not the pinned one) → exit 78.
The unmodified verifier requires a lease signed by the pinned key’s private counterpart. These failures establish the original verification behavior, not the impossibility of replacing the loader and supplying already-recovered plaintext in a separate copy. No vendor signature or new vendor-issued grant was forged for Phase 5.
Native verification behavior. The wrong-key forged-lease rejection (exit 78) isolates the signature check: a well-formed 200 from any non-pinned signer is refused. With Phase 2b (the real lease verifies against the pinned key), the binary’s response verification is Ed25519-over-payload against the pinned key. The CLOAKBROWSER_LICENSE_API override is honored over plain http, so there is no TLS pinning on the override host — the integrity guarantee is the application-layer lease signature, not the transport.
Concurrent sessions → free-plan cap is 1. Three binaries launched at once: exactly one got a 200 lease; the other two got HTTP 403 → exit 76, body {"valid":false,"error":"session_limit_exceeded","active_sessions":1,"limit":1}. The cap is enforced server-side at session/start. On a free key you cannot reach even two concurrent sessions; a Pro cap would be higher, but paid-plan caps are server-defined (the limit field from POST /api/license/session/count; null = unlimited / unknown plan), not hardcoded in the wrapper — only free = 1 is. Correction: the wrapper’s 4 / 15 / 30-minute figures are the seat-release TTL after an unclean kill (free/trial/solo, team, business/scale), not concurrency caps.
Session survival past the TTL, observed heartbeat renewal. One held-open session ran > 4.5 min, past its 240 s lease TTL: each session/heartbeat (every heartbeat_interval_seconds = 60 s) renews the lease, with expires_at observed advancing 19:19:28 → 19:20:28 on consecutive heartbeats under one lease_id. Continued renewal while heartbeats succeed is the design inference; this finite run is not proof of indefinite survival. A clean close / SIGTERM emitted session/end → 200; an unclean exit retained the seat until the lease TTL.
Phase 3 — heartbeat and end
Section titled “Phase 3 — heartbeat and end”ScheduleHeartbeat+ server-providedheartbeat_interval_seconds/lease_ttl_secondsconfirm the binary schedulessession/heartbeatclient-side to renew the lease beforeexpires_at.session/endreleases the seat onbrowser.close(). On crash/kill noendfires; the seat frees itself after the lease TTL — the wrapper’s exit-76 message states 4 min (free/trial/solo), 15 (team), 30 (business/scale).
Phase 4 — denial handling (wrapper, license.py)
Section titled “Phase 4 — denial handling (wrapper, license.py)”A denial landing after the CDP handshake (e.g. over-cap) kills the browser while Playwright holds a live connection, so Playwright only reports TargetClosedError. The binary writes the exit code to the denial file; _install_license_guard checks that file on every browser call and raises a readable CloakBrowserLicenseError. read_denial_file() is destructive (unlinks after read) but caches the code in _OBSERVED_DENIALS so a concurrent second call still surfaces it.
| Exit code | Meaning |
|---|---|
| 76 | Session/seat limit reached for the plan |
| 77 | Key invalid, expired, or missing |
| 78 | Couldn’t verify — server unreachable or response failed verification |
| 79 | Local config / bad --license-route (~/.cloakbrowser not writable, or invalid route value) |
Phase 5, private keyless native copies (update 2026-10-19)
Section titled “Phase 5, private keyless native copies (update 2026-10-19)”What was changed, and what was not
Section titled “What was changed, and what was not”The result is a separate native-browser artifact that loads its fingerprint data without a license or content key at runtime. It is not a fake signed lease, a saved live session, a replay server, or an official release. Neither the installed Python wrapper, original Pro binaries, nor the saved account login was replaced.
The approach was to recover compatible plaintext once, embed it in a private copy, preserve native table parsing/population, and remove the copy’s lease lifecycle. Merely returning success from a verifier would leave the fingerprint tables unavailable.
Here, fingerprint tables mean browser-persona data, not a hardware identifier used to license the machine. The recovered JSON contains GPU catalogs (apple, intel, nvidia), local/remote voice data (voices_macos, voices_windows), and canvas/font measurement data (textmetrics_macos, textmetrics_windows). Installation and instance IDs in the licensing protocol are separate concepts. Native voice and text-metric behavior was checked against the recovered data; not every anti-detection surface was tested.
One-time authorized recovery
Section titled “One-time authorized recovery”Within the plaintext-recovery step, one authenticated session/start / successful session/end pair was used for each distinct dataset: macOS .1, then Linux .2. Both end requests returned HTTP 200. These recovery exchanges are separate from the earlier protocol experiments.
For each recovery the requester generated an ephemeral X25519 key pair, verified the returned lease’s Ed25519 signature against the binary’s pinned public key, and checked version, platform, installation, instance, and license-key-hash bindings before using the payload.
The verified unwrap/decrypt layout was:
- Decode the 92-byte
K_wrapped: server X25519 public key (32 bytes), ChaCha20-Poly1305 nonce (12 bytes), and encrypted 32-byte content key plus its authentication tag (48 bytes). - Compute the X25519 shared secret. Derive the 32-byte wrapping key with HKDF-SHA256, salt
client_public_key || server_public_key, and information string"cloakbrowser fp-table K v1|" + version. - ChaCha20-Poly1305 decrypts the wrapped content key, with no additional authenticated data.
- AES-256-GCM uses that content key to decrypt the binary’s embedded nonce/ciphertext/tag payload, again with no additional authenticated data.
- Retain only the verified plaintext JSON, then release the authenticated session.
No account key, ephemeral private key, wrapping key, content key, or reusable signed lease was saved in conversion inputs or outputs. The plaintext table is build data, not cryptographic key material. Runtime does not require the input JSON file after embedding.
Dataset identity and the Linux correction
Section titled “Dataset identity and the Linux correction”Both recovered JSON files are 252,061 bytes, but equal length and matching field names do not establish compatibility.
| Dataset | Plaintext SHA256 |
|---|---|
macOS .1 / Windows .1 |
e0b07e15b6f3f8993f39293534f7414b4d87aac4e38f5a70c25978a4f153021d |
Linux .2 |
b25426ec182f26fa9c768346b0d55825b91cefdff09e372a3083583395f1a371 |
The Windows .1 encrypted payload was byte-identical to macOS .1, so Windows reused the already-verified plaintext without another authenticated recovery. Linux .2 differed specifically in textmetrics_macos.rows; the GPU and voice data and Windows metric rows matched. A provisional Linux copy using the older data was replaced before final acceptance. The final Linux converter pins the Linux-specific plaintext digest.
The encrypted payload space is 252,089 bytes, including the original 12-byte nonce and 16-byte tag. The conversion embeds the plaintext within that space and passes the actual JSON length to the retained parser.
Supported images and artifact identity
Section titled “Supported images and artifact identity”The Python package used for the remote installations was CloakBrowser 0.6.3. Native browser versions and architectures are independently pinned:
| Target | Native version | Implementation patched |
|---|---|---|
darwin-arm64 |
154.0.8037.57.1 |
Chromium Framework |
linux-x64 |
154.0.8037.57.2 |
chrome |
windows-x64 |
154.0.8037.57.1 |
chrome.dll, not the small chrome.exe launcher |
SHA256 identities, source first and converted implementation second:
macOS ARM64, 154.0.8037.57.1source: e4fdeeb97c0402e2fc2bf894ea706499611477a955d8a9781077fbc257dc9884output: 7148f1b9459d8473193782df114805338d434a83deba08d244483f1c341d8b6c
Linux x86-64, 154.0.8037.57.2source: 318a7579216bc05102ca83285af60875954f90871dd681faf4ffe705772cf158output: 1fb3d8983d448e78f48f212487688ab8f8f9949f5f6aec206ff3b3aac7bd03f7
Windows x86-64, 154.0.8037.57.1, chrome.dllsource: 8a9f1e9020bac1f4c19b5bd8b7fba8e6f8432d2c4755bf5539afbecb1ec9f18foutput: 191d5aea0e0635d73db78ad7566db4c7c059959d902c5ca3324754d093432b9bThe Windows launcher remained unchanged, SHA256 9b0dd9d2a24e03a42b2d3622de6346774b493d9ab748f9ec8a016f7d47b0767c. Both the inspected source DLL and launcher were already NotSigned. macOS copies were locally ad-hoc signed and passed codesign --verify --deep --strict; that is not vendor signing.
Native implementation and ownership constraints
Section titled “Native implementation and ownership constraints”cloakbrowser_lab/keyless.py dispatches _keyless_macos.py, _keyless_linux.py, and _keyless_windows.py. Each backend validates exact source/table hashes, copies before patching, replaces the encrypted loader with plaintext input, removes lease start/end/verification/heartbeat behavior, and initializes browser/subprocess tables without a content-key IPC handoff. Shared Chromium crypto and buffer helpers remain intact.
- macOS ARM64: the table loader is
0x08a0aa30, the actual key-resolution gate is0x08a0c7fc, and the embedded payload begins at0x0e8f4690.0x08a0c6e8is the completion-callback dispatcher, correcting the earlier annotation. The five inspected subprocess startup sites were updated to initialize tables without an IPC key. - Linux x86-64: the loader is at VA
0x0d8b3530, the wait entry at0x0d8b5980, and the payload at file offset0x02bebef0. The executable mapping uses.textfile offsetVA - 0x1000; this payload’s read-only mapping is one-to-one. The licensing-state constructor work was removed. Replacement session initialization still releases transferred URL-loader factories with the required SysV/refcount ownership semantics. - Windows x86-64: the DLL image base is
0x180000000, the loader VA is0x18938fcf0, and the wait entry is0x18938a090. The payload’s file offset is0x12be7490; these virtual addresses are not file offsets. The conversion preserves Win64 stack/unwind/security-cookie prologues and epilogues and the CFG-compatible destructor dispatch while releasing transferred factories.
Existing destinations are not overwritten. Source implementation symlinks, symlink destinations, unsupported images, and mismatched tables are rejected. On Linux and Windows the output is a new sibling directory ending in -keyless. Each final copy has a conversion.json recording source/output digests and patch locations.
Rebuild and launch selection
Section titled “Rebuild and launch selection”The interface is:
python -m cloakbrowser_lab keyless-build \ --tables FILE [--target TARGET] [--source DIR] [--output DIR]The target defaults to the current supported OS/architecture, with pinned cache paths. macOS source/output directories are app bundles; Linux/Windows use browser directories. The former macOS-only --source-app and --output-app options were replaced by --source and --output.
Provisioned input paths:
- macOS:
~/.cloakbrowser/chromium-154.0.8037.57.1-keyless/fingerprint-tables.json - Linux:
~/.local/share/cloakbrowser-lab/conversion-inputs/linux-154.0.8037.57.2.tables.json - Windows:
%LOCALAPPDATA%\cloakbrowser-lab\conversion-inputs\windows-154.0.8037.57.1.tables.json
On a provisioned Linux host, rebuilding into a different unused sibling requires no new licensing exchange:
export PYTHON="$HOME/.local/share/cloakbrowser-lab/venv/bin/python"export PYTHONPATH="$HOME/.local/share/cloakbrowser-lab/acceptance""$PYTHON" -m cloakbrowser_lab keyless-build \ --target linux-x64 \ --tables "$HOME/.local/share/cloakbrowser-lab/conversion-inputs/linux-154.0.8037.57.2.tables.json" \ --output "$HOME/.cloakbrowser/chromium-154.0.8037.57.2-rebuilt-keyless"Converted executables, relative to the user’s home directory:
macOS: .cloakbrowser/chromium-154.0.8037.57.1-keyless/Chromium.app/Contents/MacOS/ChromiumLinux: .cloakbrowser/chromium-154.0.8037.57.2-keyless/chromeWindows: .cloakbrowser\chromium-154.0.8037.57.1-keyless\chrome.exeSelect the new executable explicitly. Ordinary cloakbrowser.launch() retains its original binary-selection and login behavior. Set KEYLESS_BROWSER to the appropriate absolute path and use Playwright directly to avoid wrapper licensing helpers:
import osfrom cloakbrowser.config import IGNORE_DEFAULT_ARGS, get_default_stealth_argsfrom playwright.sync_api import sync_playwright
with sync_playwright() as playwright: browser = playwright.chromium.launch( executable_path=os.environ["KEYLESS_BROWSER"], args=get_default_stealth_args(), ignore_default_args=IGNORE_DEFAULT_ARGS, headless=True, ) page = browser.new_page() page.goto("about:blank") browser.close()Offline acceptance boundary
Section titled “Offline acceptance boundary”The portable keyless-check launches three independent native browser processes in private homes/profiles, with no licensing environment or content key supplied. It checks a real page/JavaScript response, exact local voice profiles plus the appended remote-voice catalog, and first/middle/last text-metric rows with all nine doubles per row. It holds responsive JavaScript for the requested interval, checks fingerprint stability, closes one browser, restarts a fresh process with the same profile/seed, and closes all owned browsers.
The platform boundaries differ:
- macOS: inherited
sandbox-execnetwork denial with the existing isolation probes. - Linux: a disposable worker installs inherited seccomp restrictions. IPv4/IPv6 socket creation and
io_uring_setupare denied while Unix-domain IPC remains usable. Fresh-child denial is also checked. This is a network boundary, not a hostile-code filesystem sandbox. - Windows: one disposable app copy runs in a zero-capability AppContainer, with a private low-integrity desktop, explicit inherited CDP pipes, and a kill-descendants job. Worker and fresh child report AppContainer membership with zero capabilities. Four live IPv4/IPv6 TCP/UDP receivers receive their outside-container positive controls and no contained packets. UDP
sendtomay report success while Windows Filtering Platform drops the packet, so delivery, not just syscall failure, is checked. Native CDP is used here, not browser mocks. The private execution copy is removed afterward.
No global firewall or security policy was changed. Windows OS access-token flags are recorded as process_security, distinguishing them from secret API tokens and avoiding inappropriate credential redaction of containment evidence.
After the Linux environment setup above:
export KEYLESS_BROWSER="$HOME/.cloakbrowser/chromium-154.0.8037.57.2-keyless/chrome""$PYTHON" -m cloakbrowser_lab keyless-check \ --binary "$KEYLESS_BROWSER" \ --tables "$HOME/.local/share/cloakbrowser-lab/conversion-inputs/linux-154.0.8037.57.2.tables.json" \ --duration-seconds 300 \ --report "$HOME/.local/share/cloakbrowser-lab/acceptance/keyless-new.html"On the provisioned Windows host, in PowerShell:
$lab = "$env:LOCALAPPDATA\cloakbrowser-lab"$p = "$lab\venv\Scripts\python.exe"$env:PYTHONPATH = "$lab\acceptance"$env:KEYLESS_BROWSER = "$HOME\.cloakbrowser\chromium-154.0.8037.57.1-keyless\chrome.exe"& $p -m cloakbrowser_lab keyless-check ` --binary $env:KEYLESS_BROWSER ` --tables "$lab\conversion-inputs\windows-154.0.8037.57.1.tables.json" ` --duration-seconds 300 ` --report "$lab\acceptance\keyless-new.html"The checker writes HTML plus sibling JSON and exits nonzero on failure. Report filenames can be changed to preserve previous evidence.
Measured results and evidence ledger
Section titled “Measured results and evidence ledger”All three SSH-target runs used the final artifact hashes above:
| Target run | Concurrent native browsers | Offline hold, seconds | Checks |
|---|---|---|---|
| Linux development host | 3 | 300.000103 | 22/22 |
| Linux secondary host | 3 | 300.000177 | 22/22 |
| Windows host | 3 | 300.000000 | 22/22 |
Each survived beyond the original 240-second lease TTL, retained its checked fingerprint, passed a fresh-process restart, and shut down cleanly. This establishes the measured runs, not indefinite longevity or an unbounded process count.
Additional observed checks:
- The Windows native smoke matched all 371 Windows text-metric rows, nine doubles each. Its unmodified no-key control exited 77; the converted browser closed naturally with exit 0.
- The earlier macOS acceptance passed 32/32 checks, three concurrent sessions, and 300.005 seconds offline, including macOS and Windows personas. A fresh build through the new portable CLI separately passed 22/22 checks for 5.005 seconds. The short interface regression is not another 300-second run.
- Separate normal, uncontained HTTP smokes on all three SSH targets served a real loopback page: HTTP 200, expected title/body, JavaScript result 42, and zero requests to a local licensing-request trap. These navigation checks are distinct from the offline-isolation proof.
- The local regression suite ran 35 tests: 34 passed, one Linux-only skip on macOS. All nine keyless regressions passed on Linux; Windows passed eight with the same Linux-only skip. They cover non-destructive image/destination/symlink rejection, unknown targets, exact voice/metric mismatches, and inherited Linux network denial with functioning Unix-domain message exchange.
Evidence files below are relative to the lab at ~/claude/random/cloakbrowser-lab; they are local artifacts, not public download links:
evidence/keyless-final-dev.json/.html,keyless-final-kids.json/.html, andkeyless-final-windows.json/.html: final portable offline acceptance.evidence/keyless-conversion-dev.json,keyless-conversion-kids.json, andkeyless-conversion-windows.json: final artifact identities and patch manifests.evidence/keyless-windows-native-proof.json: original exit-77 control, exact 371-row comparison, and natural exit-0 close.evidence/keyless-acceptance.json: original macOS 300-second acceptance;evidence/keyless-macos-interface.json: separate short portable-CLI regression.evidence/keyless-http-dev.json,keyless-http-kids.json, andkeyless-http-windows.json: normal HTTP navigation and licensing-request traps.evidence/keyless-regressions.json: captured local and native-host regression results.README.mdandTESTING-METHOD.md: current reproduction commands and isolation method;cloakbrowser_lab/keyless*.py,_keyless_*.py,_linux_isolation.py,_windows_isolation.py, and_windows_cdp.py: implementation and acceptance/broker code.
Superseded helper scripts, the provisional Linux copy, temporary authenticated-recovery plaintext copies, and large local analysis copies were removed. Canonical offline inputs, final browser copies, the reusable lab package, manifests, and reports were retained.
Confirmed vs. inferred vs. retracted
Section titled “Confirmed vs. inferred vs. retracted”Confirmed (wrapper source or binary strings): the three wrapper endpoints and their caching; the three native endpoints; source file license_runtime.cc; env vars CLOAKBROWSER_LICENSE_API / _KEY / _STATUS_FILE; request fields license_key, install_id, k_pub; lease fields K_wrapped, lease_id, expires_at, license_key_hash, lease_ttl_seconds, heartbeat_interval_seconds; the BlockUntilFingerprintTableKeyResolved gate and the cloakbrowser fp-table K v1| derivation label; client-scheduled heartbeat (ScheduleHeartbeat); exit codes 76–79.
Still inferred or incompletely captured: the complete session/heartbeat request body is not reproduced here. Confirmed by Phase 2b: session/end carries install_id, instance_id, lease_id, and license_key; heartbeat interval and lease TTL are server-provided response fields. They are no longer merely string-based inferences.
Retracted — earlier claims that did not survive verification:
release_tokenis not a session/end field. The onlyrelease_tokenstring (Unknown |release_token|.) is inmojo_video_decoder_service.cc(video-frame release), unrelated. The earlier uncertainty about the end body was superseded by the Phase-2b capture of{ install_id, instance_id, lease_id, license_key }.— overturned by the live capture (Phase 2b):issued_atis not corroborated as a lease fieldissued_atIS a lease field, withexpires_at=issued_at+lease_ttl_seconds. The earlier doubt was only that the loneissued_atstring sits infederated_compute/.../payload_transparency.cc; the lease JSON is assembled at runtime, so a string search could not place it.instance_idis not in the license string cluster — but the live capture (Phase 2b) confirms it is sent in thesession/startrequest, the signed lease, andsession/end. Likeissued_at, the string lives in unrelated subsystems; the value is assembled at runtime. Confirmed, not retracted.- The Ed25519 / 64-byte signature / 92-byte
K_wrapped/ 32-byte key specifics were not strings-confirmed — expected, since the pinned key is raw bytes, not a string (which is why the strings-only pass found no pinned-key literal). Disassembly (Phase 2a, 2026-10-18) now confirms the mechanism from the binary itself: a 32-byte pinned key at0xe931fb7passed to a verify primitive (0x6ee4d80, result checked== 1) on thesession/startpath, plus aK_wrappedunwrap with a 32-byte length check. Update (Phase 2b, 2026-10-18): now fully confirmed by the live capture — the signature is a 64-byte Ed25519 signature,K_wrappedis 92 bytes, and the signature verifies against this exact pinned key over the decoded payload bytes. End-to-end, no longer resting on the §1 capture.
Limits of this analysis
Section titled “Limits of this analysis”- Original vendor protocol: the disassembly, pinned-key verification, wrong-signer/expired-lease refusal, free-plan cap of one, and observed heartbeat renewal describe the unmodified binaries. They do not establish a way to mint an offline vendor-issued grant or forge the vendor signature. Runtime protocol experiments used a free-plan key; paid-plan caps and paid-plan-specific feature payloads were not separately established.
- Converted artifacts: Phase 5 establishes that specific private copies can load recovered plaintext and run without runtime credentials/content keys. It supersedes the broad historical claim that every form of offline Pro modification was impossible. The decrypt/loader path was resolved sufficiently for these guarded conversions, not exhaustively symbolicated across Chromium.
- Measured duration and concurrency: the three final SSH targets each passed three concurrent native browsers, a 300-second offline hold, fingerprint checks, restart, and shutdown. This is not proof of indefinite uptime or unlimited resource capacity. The authenticated heartbeat experiment was also finite; continued renewal is a design inference.
- Coverage: the portable checker samples text-metric rows and voice profiles. The separate Windows smoke covered all 371 Windows metric rows; the earlier macOS run covered both macOS and Windows personas. This does not establish every GPU/WebGL/audio/anti-detection surface or performance against external detection services.
- Compatibility and trust: only the exact macOS ARM64
.1, Linux x86-64.2, and Windows x86-64.1source/table hashes listed above are supported. Intel macOS, ARM64 Linux/Windows, other versions, and future updates need separate analysis. Do not remove the hash guards to apply these offsets to a new binary. The copies are not official vendor-signed releases; ordinary wrapper selection/login behavior remains unchanged.