pub fn render(node_id: &str, browser_origin: &str, owner_token: &str, device_name: &str) -> String {
let node_id_js = js_string_literal(node_id);
let browser_origin_js = js_string_literal(browser_origin);
let owner_token_js = js_string_literal(owner_token);
let device_name_js = js_string_literal(device_name);
format!(
r#"(async () => {{
// Generated by `node-app harness pair-browser` — implements spec D2
// (docs/superpowers/specs/2026-08-10-harness-browser-pairing-design.md).
// This snippet embeds a LIVE owner bearer token. Do not save it, paste it
// into a shared channel, or commit it anywhere.
const OWNER_TOKEN = {owner_token_js};
const DEVICE_NAME = {device_name_js};
// 0. Origin guard. Every fetch below is a same-origin RELATIVE url — there
// is no absolute BASE_URL in this snippet at all — so if this page is
// not actually this node's own origin, the very first fetch would
// silently hit whatever else is listening on THIS origin instead of
// failing loudly. Rather than let that happen, prove same-origin
// identity first via a same-origin GET of the node's own well-known
// origin-proof endpoint (AC-0,
// docs/superpowers/plans/2026-08-10-harness-browser-pairing.md): what
// was attempted, what was observed, and the remedy.
const EXPECTED_NODE_ID = {node_id_js};
const EXPECTED_ORIGIN_HINT = {browser_origin_js};
let originProof;
try {{
const originResponse = await fetch("/.well-known/client-node-origin");
if (!originResponse.ok) {{
throw new Error(`HTTP ${{originResponse.status}}`);
}}
originProof = (await originResponse.json()).data;
}} catch (err) {{
throw new Error(
`harness pair-browser: attempted a same-origin GET /.well-known/client-node-origin ` +
`to confirm this page (${{location.origin}}) belongs to the target node before pairing; ` +
`observed: ${{err}}. Remedy: open the node's own origin (${{EXPECTED_ORIGIN_HINT}}) in ` +
`this browser tab, then paste the snippet again.`,
);
}}
if (!originProof || originProof.node_id !== EXPECTED_NODE_ID) {{
throw new Error(
`harness pair-browser: attempted a same-origin GET /.well-known/client-node-origin ` +
`to confirm this page (${{location.origin}}) belongs to the target node before pairing; ` +
`observed node_id "${{originProof && originProof.node_id}}", expected ` +
`"${{EXPECTED_NODE_ID}}" — this page belongs to a DIFFERENT node. Remedy: open the ` +
`node's own origin (${{EXPECTED_ORIGIN_HINT}}) in this browser tab, then paste the ` +
`snippet again.`,
);
}}
const BASE58_ALPHABET =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
// Matches client/adapters-web/src/identity/did.js's base58Btc byte for byte.
function base58btc(bytes) {{
if (bytes.byteLength === 0) return "";
let leadingZeros = 0;
while (leadingZeros < bytes.byteLength && bytes[leadingZeros] === 0) {{
leadingZeros += 1;
}}
if (leadingZeros === bytes.byteLength) return "1".repeat(leadingZeros);
const digits = [0];
for (const byte of bytes.slice(leadingZeros)) {{
let carry = byte;
for (let index = 0; index < digits.length; index += 1) {{
const value = (digits[index] ?? 0) * 256 + carry;
digits[index] = value % 58;
carry = Math.floor(value / 58);
}}
while (carry > 0) {{
digits.push(carry % 58);
carry = Math.floor(carry / 58);
}}
}}
let encoded = "1".repeat(leadingZeros);
for (let index = digits.length - 1; index >= 0; index -= 1) {{
encoded += BASE58_ALPHABET[digits[index] ?? 0];
}}
return encoded;
}}
function hex(bytes) {{
return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
}}
function base64UrlNoPad(bytes) {{
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}}
// Every fetch is routed through here so every response is status-checked —
// a silent partial write would leave the PWA worse off than unpaired.
async function checkedFetch(url, init) {{
const response = await fetch(url, init);
if (!response.ok) {{
const body = await response.text().catch(() => "<unreadable body>");
throw new Error(`harness pair-browser: ${{init.method}} ${{url}} failed: ${{response.status}} ${{body}}`);
}}
return response.json();
}}
// 1. Generate the canonical non-extractable Ed25519 device key. `false` is
// required: it is what makes the private key non-extractable, matching
// client/adapters-web/src/identity/key-custody.js's
// generateNonExtractableKeyMaterial exactly.
const keyPair = await crypto.subtle.generateKey({{ name: "Ed25519" }}, false, ["sign", "verify"]);
const publicKeyBytes = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
if (publicKeyBytes.byteLength !== 32) {{
throw new Error(`harness pair-browser: unexpected Ed25519 public key length ${{publicKeyBytes.byteLength}}`);
}}
// 2. Derive did / publicKeyMultibase / nodeId — matches
// client/adapters-web/src/identity/did.js:8-21 byte for byte.
const publicKeyMultibase = "z" + base58btc(publicKeyBytes);
const didKeyMaterial = new Uint8Array(2 + publicKeyBytes.byteLength);
didKeyMaterial.set([0xed, 0x01]);
didKeyMaterial.set(publicKeyBytes, 2);
const did = "did:key:z" + base58btc(didKeyMaterial);
const nodeId = hex(publicKeyBytes);
// keyId = hex of the first 16 bytes of SHA-256(publicKey), matching
// key-custody.js:197-200's deriveKeyId.
const publicKeyDigest = new Uint8Array(await crypto.subtle.digest("SHA-256", publicKeyBytes));
const keyId = hex(publicKeyDigest.slice(0, 16));
const identity = {{
device_did: did,
public_key_multibase: publicKeyMultibase,
iroh_node_id: nodeId,
}};
async function signChallenge(nonce) {{
const signatureBytes = new Uint8Array(
await crypto.subtle.sign({{ name: "Ed25519" }}, keyPair.privateKey, new TextEncoder().encode(nonce)),
);
return base64UrlNoPad(signatureBytes);
}}
async function requestChallenge(purpose) {{
const response = await checkedFetch(`/api/v2/client-devices/challenge`, {{
method: "POST",
headers: {{ "content-type": "application/json" }},
body: JSON.stringify({{ identity, purpose }}),
}});
return response.data;
}}
// 3-4. Pairing challenge, sign, pair WITH the owner bearer header. The
// transport re-verifies that token and stamps `authorized_owner_id`,
// which triggers `activate_for_token_owner` server-side and
// self-approves the device immediately — no coordinator, no six-digit
// code, no second browser.
const pairingChallenge = await requestChallenge("pairing");
const pairingSignature = await signChallenge(pairingChallenge.nonce);
await checkedFetch(`/api/v2/client-devices/pair`, {{
method: "POST",
headers: {{ "content-type": "application/json", authorization: `Bearer ${{OWNER_TOKEN}}` }},
body: JSON.stringify({{
challenge_id: pairingChallenge.challenge_id,
signature: pairingSignature,
identity,
device_name: DEVICE_NAME,
device_type: "pwa",
}}),
}});
// 5. Repeat the challenge with purpose "session"; sign; establish session.
const sessionChallenge = await requestChallenge("session");
const sessionSignature = await signChallenge(sessionChallenge.nonce);
const session = await checkedFetch(`/api/v2/client-devices/session`, {{
method: "POST",
headers: {{ "content-type": "application/json" }},
body: JSON.stringify({{
challenge_id: sessionChallenge.challenge_id,
signature: sessionSignature,
identity,
device_name: DEVICE_NAME,
device_type: "pwa",
}}),
}});
const established = session.data;
// 6. Write IndexedDB `node-client-identity` / store `identity` — the three
// explicit keys the boot gate reads
// (client/domain/src/services/device-boot-state-service.js:5-45), in the
// exact shape client/adapters-web/src/storage/identity-store.js writes.
const db = await new Promise((resolve, reject) => {{
const request = indexedDB.open("node-client-identity", 1);
request.addEventListener("upgradeneeded", () => {{
if (!request.result.objectStoreNames.contains("identity")) {{
request.result.createObjectStore("identity");
}}
}});
request.addEventListener("success", () => resolve(request.result));
request.addEventListener("error", () => reject(new Error("harness pair-browser: failed to open node-client-identity")));
}});
try {{
await new Promise((resolve, reject) => {{
const tx = db.transaction("identity", "readwrite");
const store = tx.objectStore("identity");
store.put(
{{
createdAt: new Date().toISOString(),
custodyMode: "non-extractable",
keyId,
privateKey: keyPair.privateKey,
publicKey: publicKeyBytes,
version: 1,
}},
"key-material",
);
store.put(
{{
credential: established.delegation_credential,
expiresAt: established.delegation_expires_at,
issuedAt: established.delegation_issued_at,
revocationId: established.revocation_id,
scopes: established.scopes,
}},
"delegation",
);
store.put({{ generation: 1, state: "active" }}, "lifecycle");
tx.addEventListener("complete", () => resolve());
tx.addEventListener("abort", () => reject(new Error("harness pair-browser: failed to write node-client-identity")));
tx.addEventListener("error", () => reject(new Error("harness pair-browser: failed to write node-client-identity")));
}});
}} finally {{
db.close();
}}
// 7. Trust snapshot — the pairing gate probes https://<host> even when the
// page itself is served over HTTP.
const trustOrigin = location.origin.replace(/^http:/, "https:");
localStorage.setItem(`node.trust.verified.${{trustOrigin}}`, new Date().toISOString());
console.log("harness pair-browser: paired", {{ did, deviceId: established.device_id }});
// 8. Reload so the shell re-reads the boot gate over the real delegation.
location.reload();
}})();
"#
)
}
fn js_string_literal(value: &str) -> String {
let mut out = String::with_capacity(value.len() + 2);
out.push('"');
for ch in value.chars() {
match ch {
'\\' => out.push_str("\\\\"),
'"' => out.push_str("\\\""),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
'\u{2028}' => out.push_str("\\u2028"),
'\u{2029}' => out.push_str("\\u2029"),
c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
c => out.push(c),
}
}
out.push('"');
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn render_interpolates_node_id_origin_and_token() {
let out = render("03aabb", "https://127.0.0.1:4431", "tok-123", "harness-browser");
assert!(out.contains("03aabb"));
assert!(out.contains("https://127.0.0.1:4431"));
assert!(out.contains("tok-123"));
assert!(out.contains("harness-browser"));
}
#[test]
fn render_escapes_values_for_a_js_string_literal() {
let out = render("node", "https://origin", "a'b\\c", "n");
assert!(
!out.contains("a'b\\c"),
"raw value must not land unescaped in JS source"
);
assert!(out.contains("a'b\\\\c"));
}
#[test]
fn escaping_neutralizes_a_string_breakout_attempt() {
let out = render("node", "https://origin", "\"; fetch('https://evil'); //", "n");
assert!(
out.contains("\\\"; fetch('https://evil'); //"),
"the escaped form (backslash-quote) must be present"
);
let bare = "\"; fetch";
let mut search_start = 0;
while let Some(found) = out[search_start..].find(bare) {
let absolute = search_start + found;
assert!(
absolute > 0 && out.as_bytes()[absolute - 1] == b'\\',
"every occurrence of `\"; fetch` must be immediately preceded by an \
escaping backslash, found a bare one at byte {absolute}"
);
search_start = absolute + bare.len();
}
}
#[test]
fn snippet_checks_every_fetch_response() {
let out = render("node", "https://origin", "t", "n");
let fetches = out.matches("await fetch(").count();
let checks = out.matches("response.ok").count()
+ out.matches("Response.ok").count()
+ out.matches(".ok)").count();
assert!(
checks >= fetches,
"every fetch must be status-checked: {fetches} fetches, {checks} checks"
);
}
#[test]
fn snippet_uses_non_extractable_key_generation() {
let out = render("node", "https://origin", "t", "n");
assert!(out.contains(r#"generateKey({ name: "Ed25519" }, false, ["sign", "verify"])"#));
}
#[test]
fn snippet_pairs_with_the_owner_bearer_header() {
let out = render("node", "https://origin", "t", "n");
assert!(out.contains("authorization: `Bearer ${OWNER_TOKEN}`"));
}
#[test]
fn render_uses_only_relative_urls_in_every_fetch_call() {
let out = render(
"03aabb",
"https://127.0.0.1:4431",
"tok-123",
"harness-browser",
);
for literal in [
"fetch(\"/.well-known/client-node-origin\")",
"checkedFetch(`/api/v2/client-devices/challenge`",
"checkedFetch(`/api/v2/client-devices/pair`",
"checkedFetch(`/api/v2/client-devices/session`",
] {
assert!(
out.contains(literal),
"expected a relative fetch call {literal:?} in the rendered snippet"
);
}
assert!(
!out.contains("const BASE_URL"),
"no absolute BASE_URL constant declaration should remain in the snippet"
);
assert!(
!out.contains("${BASE_URL}"),
"no fetch call should interpolate a BASE_URL variable any more"
);
for absolute_fetch in [
"fetch(`${",
"fetch(`http",
"fetch(\"http",
"checkedFetch(`https://",
"checkedFetch(`http://",
] {
assert!(
!out.contains(absolute_fetch),
"found an absolute-URL fetch pattern {absolute_fetch:?} in the snippet"
);
}
}
#[test]
fn render_includes_an_origin_guard_before_any_pairing_fetch() {
let out = render(
"03aabb",
"https://127.0.0.1:4431",
"tok-123",
"harness-browser",
);
assert!(out.contains("/.well-known/client-node-origin"));
assert!(out.contains("EXPECTED_NODE_ID"));
assert!(
out.contains("03aabb"),
"the expected node_id must be embedded for comparison"
);
assert!(
out.contains("https://127.0.0.1:4431"),
"the browser_origin hint must be embedded in the guard's error text"
);
assert!(out.contains("originProof.node_id !== EXPECTED_NODE_ID"));
let guard_pos = out
.find("/.well-known/client-node-origin")
.expect("origin guard fetch present");
let challenge_pos = out
.find("client-devices/challenge")
.expect("challenge fetch present");
assert!(
guard_pos < challenge_pos,
"origin guard must run before the pairing challenge fetch"
);
}
#[test]
fn snippet_derivation_matches_the_pwas_multicodec_prefix_and_alphabet() {
let out = render("node", "https://origin", "t", "n");
assert!(
out.contains("didKeyMaterial.set([0xed, 0x01]);"),
"did:key derivation must use the Ed25519 multicodec prefix (0xed, 0x01)"
);
assert!(out.contains("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"));
assert!(out.contains("publicKeyDigest.slice(0, 16)"));
}
}