Host Handoff Flow
The loopback/cookie/handoff sequence is what a consumer host shell implements.
Both reference shells (examples/android-app and examples/ios-app) do it;
here is the exact flow.
Sequence
Section titled “Sequence”-
The host starts the worklet with three sandbox dirs:
[<webAssets>, <storage>, <cache>].webAssetsis your built web app;storageis durable (photos, p2p corestore);cacheis ephemeral. -
The worklet binds
127.0.0.1on an ephemeral port and writeshandoff.json({ origin, port, bootstrap }) intostorage. The session token is never written. -
The host polls for
handoff.json, deletes it as soon as it has read it (BareHandoff.consumeon both hosts), and injectsbootstrap(the one-time nonce) into the page before any page script runs, only on the loopback origin and only in the main frame:window.__ekrooh = { bootstrap: '<bootstrap>' };window.BareShell = true;The page never sees the session token: page JS (third-party scripts, a consumer plugin dispatch handler) could otherwise read it and mint authorized requests to every other gated resource.
-
The host loads
http://127.0.0.1:<port>/index.html. The worklet writes the file atomically with owner-only permissions; an unredeemed nonce expires after 120 s of running time from the page load (paused while the worklet is suspended; 600 s if the page never loads). -
The page’s WebSocket transport reads
window.__ekrooh.bootstrap, callsPOST /loginonce (the nonce is single-use), gets thebare_session_<port>cookie, and connects tows://127.0.0.1:<port>— same origin, so cookie-authenticated. The transport never places the credential in a URL. -
Media URLs returned by plugins are same-origin too —
<img>/fetchride the cookie, no token in the URL.
Android (reference)
Section titled “Android (reference)”examples/android-app/src/main/java/to/holepunch/bare/android/ shows the
full wiring. The worklet is owned by the Application (BareApplication,
ReferenceHost), so it survives rotation and a second launch of the
singleTop MainActivity; only the first WebView gets the nonce, later ones
log in with the cookie, and a handoff that arrives after the error page is
shown is still consumed and loaded. Key bits: start the worklet with
[webAssets, noBackupFilesDir/bare, cacheDir/bare-cache], read the handoff
with BareHandoff.consume(file), set BareWebViewClient.allowedOrigin, check
WebViewFeature.DOCUMENT_START_SCRIPT and inject
window.__ekrooh = { bootstrap } with
addDocumentStartJavaScript(script, setOf(handoff.origin)), then
loadUrl("http://127.0.0.1:<port>/index.html").
iOS (reference)
Section titled “iOS (reference)”examples/ios-app/app/BareRuntime.swift does the same with the library:
BareHandoff.consume(at:) reads and deletes the file, and
BareWebHost.load(_:in:delegate:) injects an origin-guarded, main-frame-only
WKUserScript at .atDocumentStart, loads the page, and removes the script
once the first page has loaded. If the handoff is slow, the host keeps polling
after showing a timeout message.
Security notes
Section titled “Security notes”handoff.jsonmust be written to the host-provided sandbox dir, never a shared location.- The page receives only a one-time bootstrap nonce. The session token
stays inside the worklet (
runtime.start()returns it) and is never accepted as a/loginbody. The cookie isHttpOnly; SameSite=Strict. - Non-browser clients use the
X-Bare-Tokenheader. There is no?token=URL fallback. - All loopback responses send
Referrer-Policy: no-referrer. - Both hosts confine the WebView to the handoff origin before the first load.