Skip to content

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.

  • Wrapper source — cloakbrowser/license.py and browser.py in 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 (sha256 e4fdeeb97c0402e2fc2bf894ea706499611477a955d8a9781077fbc257dc9884, build 154.0.8037.57.1), examined in binary order, not just grep hits.
  • Runtime capture — the session/start request/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 via session/end).

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:

  1. resolve_license_key() — precedence: license_key= arg → CLOAKBROWSER_LICENSE_KEY env → cache-dir license.key file (default ~/.cloakbrowser/ vs a custom path tracked separately) → none.
  2. mint_denial_file() — computes a unique ~/.cloakbrowser/denials/<uuid>.json, creates the directory only, sweeps denial files older than 3600s. Returns None (feature skipped) if the dir isn’t writable — “the fix must never break a launch.”
  3. build_launch_env() — decides whether to inject CLOAKBROWSER_LICENSE_KEY into the child env based on the key’s source: inject for param/custom_file; don’t for env (already inherited) or default_file (binary reads ~/.cloakbrowser/license.key itself — keeps the key out of process env). Always adds CLOAKBROWSER_LICENSE_STATUS_FILE=<denial path>. Because Playwright replaces rather than merges the child env, any custom env= forces a full os.environ copy so HOME and the key survive.
  4. 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_id
BlockUntilFingerprintTableKeyResolved
MakeLoader heartbeat_interval_seconds lease_ttl_seconds ScheduleHeartbeat
cloakbrowser fp-table K v1|
license_key_hash expires_at K_wrapped macos license.key .cloakbrowser

This is not “a license check that gates a boolean.” The grant delivers key material:

  • The binary reads the API base from CLOAKBROWSER_LICENSE_API (default https://cloakbrowser.dev) and POSTs session/start with license_key, install_id, k_pub (a per-launch public key), plus platform/version.
  • The response carries K_wrapped — a key sealed to that launch’s k_pub — with lease_id, expires_at, license_key_hash, heartbeat_interval_seconds, lease_ttl_seconds.
  • BlockUntilFingerprintTableKeyResolved is 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:

619750ac24a66fcf6854032758859e7984870a32e4dc0151bb78f4df06a4f6b5

This 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.

  • ScheduleHeartbeat + server-provided heartbeat_interval_seconds / lease_ttl_seconds confirm the binary schedules session/heartbeat client-side to renew the lease before expires_at.
  • session/end releases the seat on browser.close(). On crash/kill no end fires; 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)”

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.

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:

  1. 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).
  2. 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.
  3. ChaCha20-Poly1305 decrypts the wrapped content key, with no additional authenticated data.
  4. AES-256-GCM uses that content key to decrypt the binary’s embedded nonce/ciphertext/tag payload, again with no additional authenticated data.
  5. 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.

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.

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.1
source: e4fdeeb97c0402e2fc2bf894ea706499611477a955d8a9781077fbc257dc9884
output: 7148f1b9459d8473193782df114805338d434a83deba08d244483f1c341d8b6c
Linux x86-64, 154.0.8037.57.2
source: 318a7579216bc05102ca83285af60875954f90871dd681faf4ffe705772cf158
output: 1fb3d8983d448e78f48f212487688ab8f8f9949f5f6aec206ff3b3aac7bd03f7
Windows x86-64, 154.0.8037.57.1, chrome.dll
source: 8a9f1e9020bac1f4c19b5bd8b7fba8e6f8432d2c4755bf5539afbecb1ec9f18f
output: 191d5aea0e0635d73db78ad7566db4c7c059959d902c5ca3324754d093432b9b

The 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 is 0x08a0c7fc, and the embedded payload begins at 0x0e8f4690. 0x08a0c6e8 is 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 at 0x0d8b5980, and the payload at file offset 0x02bebef0. The executable mapping uses .text file offset VA - 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 is 0x18938fcf0, and the wait entry is 0x18938a090. The payload’s file offset is 0x12be7490; 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.

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:

Terminal window
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/Chromium
Linux: .cloakbrowser/chromium-154.0.8037.57.2-keyless/chrome
Windows: .cloakbrowser\chromium-154.0.8037.57.1-keyless\chrome.exe

Select 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 os
from cloakbrowser.config import IGNORE_DEFAULT_ARGS, get_default_stealth_args
from 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()

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-exec network denial with the existing isolation probes.
  • Linux: a disposable worker installs inherited seccomp restrictions. IPv4/IPv6 socket creation and io_uring_setup are 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 sendto may 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:

Terminal window
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:

Terminal window
$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.

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, and keyless-final-windows.json / .html: final portable offline acceptance.
  • evidence/keyless-conversion-dev.json, keyless-conversion-kids.json, and keyless-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, and keyless-http-windows.json: normal HTTP navigation and licensing-request traps.
  • evidence/keyless-regressions.json: captured local and native-host regression results.
  • README.md and TESTING-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 (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_token is not a session/end field. The only release_token string (Unknown |release_token|.) is in mojo_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 }.
  • issued_at is not corroborated as a lease field — overturned by the live capture (Phase 2b): issued_at IS a lease field, with expires_at = issued_at + lease_ttl_seconds. The earlier doubt was only that the lone issued_at string sits in federated_compute/.../payload_transparency.cc; the lease JSON is assembled at runtime, so a string search could not place it.
  • instance_id is not in the license string cluster — but the live capture (Phase 2b) confirms it is sent in the session/start request, the signed lease, and session/end. Like issued_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 at 0xe931fb7 passed to a verify primitive (0x6ee4d80, result checked == 1) on the session/start path, plus a K_wrapped unwrap 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_wrapped is 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.
  • 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 .1 source/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.