sessionpipe-core 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,352 @@
1
+ // sessionpipe-core 0.2.0 · Apache-2.0 · https://sessionpipe.org
2
+
3
+ // src/control/verify.ts
4
+ var GRANT_MAX_MS = 24 * 36e5;
5
+ var SKEW_MS = 5 * 6e4;
6
+ var MACHINE_MAX_AGE_MS = 24 * 36e5;
7
+ var RECEIVER_MAX_AGE_MS = 10 * 6e4;
8
+ var NONCE_TTL_MS = 25 * 36e5;
9
+ var CMD_MAX = 12e3;
10
+ var TEXT_MAX = 4e3;
11
+ var NOTE_MAX = 2e3;
12
+ var GRANT_STR_MAX = 2e3;
13
+ var COMMAND_TYPE = "sessionpipe.control";
14
+ var GRANT_TYPE = "sessionpipe.grant";
15
+ var CONTROL_KINDS = ["prompt", "permission.answer", "cancel", "start"];
16
+ var ES256 = -7;
17
+ var RS256 = -257;
18
+ var NONCE_RE = /^[A-Za-z0-9_-]{22,64}$/;
19
+ var MACHINE_RE = /^m_[A-Za-z0-9_-]{16,40}$/;
20
+ var HARNESS_RE = /^[a-z][a-z0-9-]{0,39}$/;
21
+ var SESSION_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;
22
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
23
+ var B64URL_RE = /^[A-Za-z0-9_-]*$/;
24
+ var UP = 1;
25
+ var UV = 4;
26
+ var enc = new TextEncoder();
27
+ var dec = new TextDecoder();
28
+ function b64url(bytes) {
29
+ const u = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
30
+ let s = "";
31
+ for (const b of u) s += String.fromCharCode(b);
32
+ return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
33
+ }
34
+ function fromB64url(s) {
35
+ if (typeof s !== "string" || !B64URL_RE.test(s) || s.length % 4 === 1) return null;
36
+ try {
37
+ const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((s.length + 3) % 4));
38
+ const out = new Uint8Array(bin.length);
39
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
40
+ return out;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ async function sha256(data) {
46
+ const bytes = typeof data === "string" ? enc.encode(data) : data;
47
+ return new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
48
+ }
49
+ function concat(a, b) {
50
+ const out = new Uint8Array(a.length + b.length);
51
+ out.set(a, 0);
52
+ out.set(b, a.length);
53
+ return out;
54
+ }
55
+ function sameBytes(a, b) {
56
+ if (a.length !== b.length) return false;
57
+ let d = 0;
58
+ for (let i = 0; i < a.length; i++) d |= a[i] ^ b[i];
59
+ return d === 0;
60
+ }
61
+ function derToRaw(der) {
62
+ if (der[0] !== 48 || der.length < 8 || der[1] !== der.length - 2) return null;
63
+ let i = 2;
64
+ const part = () => {
65
+ if (der[i] !== 2) return null;
66
+ const len = der[i + 1];
67
+ if (len < 1 || len > 33) return null;
68
+ let v = der.slice(i + 2, i + 2 + len);
69
+ if (v.length !== len) return null;
70
+ if (v[0] & 128) return null;
71
+ if (len > 1 && v[0] === 0 && !(v[1] & 128)) return null;
72
+ i += 2 + len;
73
+ while (v.length > 32 && v[0] === 0) v = v.slice(1);
74
+ if (v.length > 32) return null;
75
+ const out = new Uint8Array(32);
76
+ out.set(v, 32 - v.length);
77
+ return out;
78
+ };
79
+ const r = part();
80
+ const s = part();
81
+ return r && s && i === der.length ? concat(r, s) : null;
82
+ }
83
+ var u8 = (b) => b;
84
+ async function importKey(key) {
85
+ const spki = fromB64url(key.spki);
86
+ if (!spki?.length) return null;
87
+ try {
88
+ if (key.alg === ES256)
89
+ return await crypto.subtle.importKey("spki", u8(spki), { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
90
+ if (key.alg === RS256) {
91
+ const k = await crypto.subtle.importKey("spki", u8(spki), { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, [
92
+ "verify"
93
+ ]);
94
+ const bits = k.algorithm.modulusLength ?? 0;
95
+ return bits >= 2048 ? k : null;
96
+ }
97
+ } catch {
98
+ }
99
+ return null;
100
+ }
101
+ async function keyUsable(key) {
102
+ return !!key && typeof key.id === "string" && /^[A-Za-z0-9_-]{16,1400}$/.test(key.id) && (key.alg === ES256 || key.alg === RS256) && !!await importKey(key);
103
+ }
104
+ function originAllowed(origin, rpId) {
105
+ if (typeof origin !== "string" || !rpId) return false;
106
+ let u;
107
+ try {
108
+ u = new URL(origin);
109
+ } catch {
110
+ return false;
111
+ }
112
+ if (u.origin !== origin) return false;
113
+ if (u.hostname !== rpId && !u.hostname.endsWith(`.${rpId}`)) return false;
114
+ if (u.protocol === "https:") return true;
115
+ return u.protocol === "http:" && rpId === "localhost";
116
+ }
117
+ var no = (why) => ({ ok: false, why });
118
+ async function verifyAssertion(challenge, a, key, rpId) {
119
+ if (!a || typeof a !== "object") return no("no passkey signature");
120
+ if (a.cred !== key.id) return no("signed with a key this machine doesn't trust");
121
+ const cdBytes = fromB64url(a.cd);
122
+ const ad = fromB64url(a.ad);
123
+ const sig = fromB64url(a.sig);
124
+ if (!cdBytes || !ad || !sig?.length) return no("the passkey signature is malformed");
125
+ let cd;
126
+ try {
127
+ cd = JSON.parse(dec.decode(cdBytes));
128
+ } catch {
129
+ return no("the passkey signature is malformed");
130
+ }
131
+ if (!cd || typeof cd !== "object") return no("the passkey signature is malformed");
132
+ if (cd.type !== "webauthn.get") return no("the passkey answered a different question");
133
+ if (cd.challenge !== b64url(challenge)) return no("the passkey signed something else");
134
+ if (!originAllowed(cd.origin, rpId)) return no(`the passkey was used on another site, not ${rpId}`);
135
+ if (cd.crossOrigin === true) return no("the passkey was used inside another site's frame");
136
+ if (ad.length < 37) return no("the passkey signature is malformed");
137
+ if (!sameBytes(ad.slice(0, 32), await sha256(rpId))) return no(`the passkey belongs to another site, not ${rpId}`);
138
+ const flags = ad[32];
139
+ if (!(flags & UP) || !(flags & UV)) return no("the passkey signed without verifying the person");
140
+ const pub = await importKey(key);
141
+ if (!pub) return no("a trusted key is unusable");
142
+ const signed = concat(ad, await sha256(cdBytes));
143
+ let good = false;
144
+ try {
145
+ if (key.alg === ES256) {
146
+ const raw = derToRaw(sig);
147
+ good = !!raw && await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, pub, u8(raw), u8(signed));
148
+ } else {
149
+ good = await crypto.subtle.verify({ name: "RSASSA-PKCS1-v1_5" }, pub, u8(sig), u8(signed));
150
+ }
151
+ } catch {
152
+ good = false;
153
+ }
154
+ return good ? { ok: true } : no("the passkey signature doesn't verify");
155
+ }
156
+ function pairChallenge(machine, code) {
157
+ return sha256(`sessionpipe.pair:${machine}:${code}`);
158
+ }
159
+ async function verifyEnrollment(o) {
160
+ if (!await keyUsable(o.key)) return no("the key is in a shape this machine can't use");
161
+ return verifyAssertion(await pairChallenge(o.machine, o.code), o.proof, o.key, o.rpId);
162
+ }
163
+ var bad = (code, why) => ({
164
+ ok: false,
165
+ code,
166
+ why
167
+ });
168
+ var isNum = (n) => typeof n === "number" && Number.isFinite(n);
169
+ function sessionIdOk(harness, id) {
170
+ if (!SESSION_ID_RE.test(id)) return false;
171
+ if (harness === "claude-code") return UUID_RE.test(id);
172
+ return true;
173
+ }
174
+ function parseSessionRef(ref) {
175
+ if (typeof ref !== "string") return null;
176
+ const i = ref.indexOf(":");
177
+ if (i <= 0) return null;
178
+ const harness = ref.slice(0, i);
179
+ const id = ref.slice(i + 1);
180
+ return HARNESS_RE.test(harness) && sessionIdOk(harness, id) ? { harness, id } : null;
181
+ }
182
+ function parseCommand(str) {
183
+ if (typeof str !== "string" || !str || str.length > CMD_MAX)
184
+ return bad("malformed", "the command is missing or too long");
185
+ let c;
186
+ try {
187
+ c = JSON.parse(str);
188
+ } catch {
189
+ return bad("malformed", "the command is not JSON");
190
+ }
191
+ if (!c || typeof c !== "object" || Array.isArray(c)) return bad("malformed", "the command is not an object");
192
+ if (c.v !== 1 || c.t !== COMMAND_TYPE) return bad("not_a_command", "not a sessionpipe control command");
193
+ if (typeof c.machine !== "string" || !MACHINE_RE.test(c.machine)) return bad("malformed", "which machine?");
194
+ if (!CONTROL_KINDS.includes(c.kind))
195
+ return bad("unknown_kind", "the command asks for something unknown");
196
+ const ref = parseSessionRef(c.session);
197
+ if (!ref) return bad("bad_session", "which session?");
198
+ if (typeof c.nonce !== "string" || !NONCE_RE.test(c.nonce))
199
+ return bad("bad_nonce", "the nonce is missing or malformed");
200
+ if (!isNum(c.iat) || !Number.isInteger(c.iat) || c.iat <= 0)
201
+ return bad("malformed", "the command has no signing time");
202
+ const has = (k) => c[k] !== void 0;
203
+ const text = () => typeof c.text === "string" && c.text.trim().length > 0 && c.text.length <= TEXT_MAX;
204
+ switch (c.kind) {
205
+ case "prompt":
206
+ if (!text()) return bad("bad_fields", `a prompt needs text, at most ${TEXT_MAX} characters`);
207
+ if (has("for") || has("decision") || has("cwd")) return bad("bad_fields", "a prompt carries only text");
208
+ break;
209
+ case "start":
210
+ if (!text()) return bad("bad_fields", `a new session needs text, at most ${TEXT_MAX} characters`);
211
+ if (ref.harness !== "claude-code") return bad("bad_fields", "only Claude Code sessions can be started");
212
+ if (typeof c.cwd !== "string" || !isAbsolute(c.cwd) || c.cwd.length > 1024)
213
+ return bad("bad_fields", "a new session needs an absolute folder");
214
+ if (has("for") || has("decision")) return bad("bad_fields", "a start carries text and cwd");
215
+ break;
216
+ case "permission.answer":
217
+ if (typeof c.for !== "string" || !c.for || c.for.length > 128) return bad("bad_fields", "which attention?");
218
+ if (c.decision !== "allow" && c.decision !== "deny") return bad("bad_fields", "allow or deny?");
219
+ if (has("text") || has("cwd")) return bad("bad_fields", "a permission answer carries for, decision and note");
220
+ break;
221
+ case "cancel":
222
+ if (has("text") || has("for") || has("decision") || has("cwd"))
223
+ return bad("bad_fields", "a cancel carries nothing");
224
+ break;
225
+ }
226
+ if (has("note") && (typeof c.note !== "string" || c.note.length > NOTE_MAX))
227
+ return bad("bad_fields", `a note is text, at most ${NOTE_MAX} characters`);
228
+ return { ok: true, cmd: c };
229
+ }
230
+ function isAbsolute(p) {
231
+ return p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p);
232
+ }
233
+ function parseGrant(str, rpId) {
234
+ if (typeof str !== "string" || !str || str.length > GRANT_STR_MAX)
235
+ return { ok: false, why: "the grant is missing or too long" };
236
+ let g;
237
+ try {
238
+ g = JSON.parse(str);
239
+ } catch {
240
+ return { ok: false, why: "the grant is not JSON" };
241
+ }
242
+ if (!g || typeof g !== "object" || Array.isArray(g)) return { ok: false, why: "the grant is not an object" };
243
+ if (g.v !== 1 || g.t !== GRANT_TYPE) return { ok: false, why: "not a sessionpipe grant" };
244
+ if (g.rp !== rpId) return { ok: false, why: `the grant is for another site, not ${rpId}` };
245
+ if (typeof g.pub !== "string" || !fromB64url(g.pub)?.length || !isNum(g.iat) || !isNum(g.exp))
246
+ return { ok: false, why: "the grant is missing parts" };
247
+ if (g.exp <= g.iat) return { ok: false, why: "the grant ends before it starts" };
248
+ if (g.exp - g.iat > GRANT_MAX_MS + SKEW_MS) return { ok: false, why: "the grant lasts longer than a day" };
249
+ return { ok: true, grant: g };
250
+ }
251
+ async function verifyCsig(cmd, csig, pub) {
252
+ const sig = fromB64url(csig);
253
+ const spki = fromB64url(pub);
254
+ if (sig?.length !== 64 || !spki) return false;
255
+ try {
256
+ const key = await crypto.subtle.importKey("spki", u8(spki), { name: "ECDSA", namedCurve: "P-256" }, false, [
257
+ "verify"
258
+ ]);
259
+ return await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key, u8(sig), u8(enc.encode(cmd)));
260
+ } catch {
261
+ return false;
262
+ }
263
+ }
264
+ async function verifyCommand(s, o) {
265
+ if (!s || typeof s !== "object") return bad("malformed", "nothing to verify");
266
+ const p = parseCommand(s.cmd);
267
+ if (!p.ok) return p;
268
+ const c = p.cmd;
269
+ if (c.machine !== o.machine) return bad("wrong_machine", "the command is for another machine");
270
+ let via = null;
271
+ let keyId = "";
272
+ let refusal = null;
273
+ if (s.grant && s.csig) {
274
+ const r = await checkGrantPath(s, c, o);
275
+ if (r.ok) {
276
+ via = "grant";
277
+ keyId = r.key;
278
+ } else refusal = r;
279
+ }
280
+ if (!via && s.confirm) {
281
+ const key = o.trusted.find((k) => k && k.id === s.confirm?.cred);
282
+ if (!key) refusal = { code: "untrusted_key", why: "confirmed with a key this machine doesn't trust" };
283
+ else {
284
+ const a = await verifyAssertion(await sha256(s.cmd), s.confirm, key, o.rpId);
285
+ if (a.ok) {
286
+ via = "confirm";
287
+ keyId = key.id;
288
+ } else refusal = { code: "bad_assertion", why: a.why };
289
+ }
290
+ }
291
+ if (!via) {
292
+ if (refusal) return bad(refusal.code, refusal.why);
293
+ return bad("unsigned", "the command isn't signed");
294
+ }
295
+ if (c.iat > o.now + SKEW_MS) return bad("future", "the command is dated in the future (is this clock right?)");
296
+ if (o.now - c.iat > (o.maxAgeMs ?? MACHINE_MAX_AGE_MS)) return bad("too_old", "the command was signed too long ago");
297
+ if (o.seenNonce) {
298
+ let seen;
299
+ try {
300
+ seen = await o.seenNonce(c.nonce);
301
+ } catch {
302
+ return bad("nonce_store", "the nonce couldn't be recorded, so the command didn't run");
303
+ }
304
+ if (seen) return bad("replay", "this command was already used once");
305
+ }
306
+ return { ok: true, cmd: c, via, key: keyId };
307
+ }
308
+ async function checkGrantPath(s, c, o) {
309
+ const bundle = s.grant;
310
+ const g = parseGrant(bundle.str, o.rpId);
311
+ if (!g.ok) return bad("bad_grant", g.why);
312
+ const key = o.trusted.find((k) => k && k.id === bundle.cred);
313
+ if (!key) return bad("untrusted_key", "the grant was signed with a key this machine doesn't trust");
314
+ const a = await verifyAssertion(await sha256(bundle.str), bundle, key, o.rpId);
315
+ if (!a.ok) return bad("bad_assertion", a.why);
316
+ if (!await verifyCsig(s.cmd, s.csig, g.grant.pub))
317
+ return bad("bad_csig", "the command's signature doesn't verify \u2014 it was changed after it was signed");
318
+ if (c.iat < g.grant.iat - SKEW_MS || c.iat > g.grant.exp)
319
+ return bad("outside_grant", "the command was signed outside its grant's window");
320
+ return { ok: true, key: key.id };
321
+ }
322
+ export {
323
+ CMD_MAX,
324
+ COMMAND_TYPE,
325
+ CONTROL_KINDS,
326
+ ES256,
327
+ GRANT_MAX_MS,
328
+ GRANT_STR_MAX,
329
+ GRANT_TYPE,
330
+ MACHINE_MAX_AGE_MS,
331
+ NONCE_TTL_MS,
332
+ NOTE_MAX,
333
+ RECEIVER_MAX_AGE_MS,
334
+ RS256,
335
+ SKEW_MS,
336
+ TEXT_MAX,
337
+ b64url,
338
+ derToRaw,
339
+ fromB64url,
340
+ keyUsable,
341
+ originAllowed,
342
+ pairChallenge,
343
+ parseCommand,
344
+ parseGrant,
345
+ parseSessionRef,
346
+ sessionIdOk,
347
+ sha256,
348
+ verifyAssertion,
349
+ verifyCommand,
350
+ verifyEnrollment
351
+ };
352
+ //# sourceMappingURL=control.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/control/verify.ts"],
4
+ "sourcesContent": ["// SPDX-License-Identifier: Apache-2.0\n// The one verifier for signed control commands (spec/CONTROL.md \u00A74\u2013\u00A75). A machine\n// runs a command only when this says yes; a receiver MAY run it too, as an early\n// word, so a bad command is refused where the person can see why.\n//\n// WebCrypto only (globalThis.crypto.subtle), no node: imports, no clock of its own\n// (`now` is passed in): the same file runs in a Cloudflare Worker, in a browser and\n// in Node \u2265 20, and conformance/control-vectors/ pins its answers.\n//\n// Every signature covers the EXACT bytes as transmitted (`cmd`, `grant.str`): the\n// strings are parsed to read their fields and never re-serialized to verify.\n\n/** One passkey tap vouches for a day key for at most this long. */\nexport const GRANT_MAX_MS = 24 * 3600_000;\n/** Clock slack between the signing device, the receiver and the machine. */\nexport const SKEW_MS = 5 * 60_000;\n/** The machine judges a command by when it was SIGNED: a message sent while the lid\n * was shut still runs when the machine wakes, up to a day later. */\nexport const MACHINE_MAX_AGE_MS = 24 * 3600_000;\n/** A receiver's early check is stricter: a command is carried within ten minutes of\n * being signed, or not at all. */\nexport const RECEIVER_MAX_AGE_MS = 10 * 60_000;\n/** How long a machine remembers a nonce: longer than MACHINE_MAX_AGE_MS + SKEW_MS. */\nexport const NONCE_TTL_MS = 25 * 3600_000;\n/** The whole command string, in UTF-16 code units (what JSON.parse sees). */\nexport const CMD_MAX = 12_000;\n/** A prompt's text, in characters. */\nexport const TEXT_MAX = 4_000;\n/** A permission answer's note. */\nexport const NOTE_MAX = 2_000;\n/** A grant string. */\nexport const GRANT_STR_MAX = 2_000;\n\nexport const COMMAND_TYPE = \"sessionpipe.control\";\nexport const GRANT_TYPE = \"sessionpipe.grant\";\nexport const CONTROL_KINDS = [\"prompt\", \"permission.answer\", \"cancel\", \"start\"] as const;\nexport type ControlKind = (typeof CONTROL_KINDS)[number];\n\n/** COSE algorithm ids a trusted key may use. */\nexport const ES256 = -7;\nexport const RS256 = -257;\n\n/** A key enrolled on the machine: a passkey's credential id, COSE alg and SPKI (base64url). */\nexport interface TrustedKey {\n id: string;\n alg: number;\n spki: string;\n}\n/** A WebAuthn assertion: credential id, authenticatorData, clientDataJSON, signature (base64url). */\nexport interface Assertion {\n cred: string;\n ad: string;\n cd: string;\n sig: string;\n}\n/** The day's grant: a passkey's assertion over sha256(str), where str names the day key. */\nexport interface GrantBundle extends Assertion {\n str: string;\n}\n/** What a receiver carries for one command (control.json minus the receiver's envelope). */\nexport interface SignedCommand {\n cmd: string;\n csig?: string | null;\n grant?: GrantBundle | null;\n confirm?: Assertion | null;\n}\nexport interface Grant {\n v: 1;\n t: typeof GRANT_TYPE;\n pub: string;\n iat: number;\n exp: number;\n rp: string;\n}\nexport interface ControlCommand {\n v: 1;\n t: typeof COMMAND_TYPE;\n machine: string;\n kind: ControlKind;\n /** `<harness>:<id>`, e.g. `claude-code:8c132906-\u2026`. */\n session: string;\n text?: string;\n for?: string;\n decision?: \"allow\" | \"deny\";\n note?: string;\n cwd?: string;\n nonce: string;\n iat: number;\n}\n\n/** Why a command was refused. Stable: conformance vectors and acks name these. */\nexport type RefusalCode =\n | \"malformed\"\n | \"not_a_command\"\n | \"wrong_machine\"\n | \"unknown_kind\"\n | \"bad_session\"\n | \"bad_fields\"\n | \"bad_nonce\"\n | \"too_old\"\n | \"future\"\n | \"unsigned\"\n | \"untrusted_key\"\n | \"bad_assertion\"\n | \"bad_grant\"\n | \"outside_grant\"\n | \"bad_csig\"\n | \"replay\"\n | \"nonce_store\";\n\nexport type Verdict =\n | { ok: true; cmd: ControlCommand; via: \"grant\" | \"confirm\"; key: string }\n | { ok: false; code: RefusalCode; why: string };\n\nexport interface VerifyOptions {\n /** This machine's id; a command for another machine is refused. */\n machine: string;\n /** The receiver's WebAuthn rp id the keys were enrolled for (well-known control.signing.rp_id). */\n rpId: string;\n /** The keys enrolled on this machine (or, for a receiver's early check, the account's). */\n trusted: TrustedKey[];\n /** Epoch ms. */\n now: number;\n /** MACHINE_MAX_AGE_MS on a machine (default), RECEIVER_MAX_AGE_MS on a receiver. */\n maxAgeMs?: number;\n /** Called once, last, only for a command whose signatures all verified: answer\n * true when the nonce was seen before, otherwise record it (durably) and answer\n * false. A throw refuses the command (`nonce_store`): never run what could not\n * be remembered. Omit it on a receiver's early check. */\n seenNonce?: (nonce: string) => boolean | Promise<boolean>;\n}\n\nconst NONCE_RE = /^[A-Za-z0-9_-]{22,64}$/;\nconst MACHINE_RE = /^m_[A-Za-z0-9_-]{16,40}$/;\nconst HARNESS_RE = /^[a-z][a-z0-9-]{0,39}$/;\nconst SESSION_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst B64URL_RE = /^[A-Za-z0-9_-]*$/;\nconst UP = 0x01;\nconst UV = 0x04;\n\n// --- encodings ------------------------------------------------------------------\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nexport function b64url(bytes: Uint8Array | ArrayBuffer): string {\n const u = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let s = \"\";\n for (const b of u) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** base64url without padding, strictly; null for anything else. */\nexport function fromB64url(s: unknown): Uint8Array | null {\n if (typeof s !== \"string\" || !B64URL_RE.test(s) || s.length % 4 === 1) return null;\n try {\n const bin = atob(s.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"===\".slice((s.length + 3) % 4));\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n } catch {\n return null;\n }\n}\n\nexport async function sha256(data: string | Uint8Array): Promise<Uint8Array> {\n const bytes = typeof data === \"string\" ? enc.encode(data) : data;\n return new Uint8Array(await crypto.subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>));\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.length + b.length);\n out.set(a, 0);\n out.set(b, a.length);\n return out;\n}\n\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let d = 0;\n for (let i = 0; i < a.length; i++) d |= (a[i] as number) ^ (b[i] as number);\n return d === 0;\n}\n\n/** A DER ECDSA signature (what WebAuthn returns) as the 64-byte r\u2016s WebCrypto verifies.\n * Strict: every length must match the bytes, so one signature has one encoding (a\n * P-256 signature is at most 72 bytes, so the short length form is the only one). */\nexport function derToRaw(der: Uint8Array): Uint8Array | null {\n if (der[0] !== 0x30 || der.length < 8 || der[1] !== der.length - 2) return null;\n let i = 2;\n const part = (): Uint8Array | null => {\n if (der[i] !== 0x02) return null;\n const len = der[i + 1] as number;\n if (len < 1 || len > 33) return null;\n let v = der.slice(i + 2, i + 2 + len);\n if (v.length !== len) return null;\n // Minimal integers only: no needless leading zero, and never negative.\n if ((v[0] as number) & 0x80) return null;\n if (len > 1 && v[0] === 0 && !((v[1] as number) & 0x80)) return null;\n i += 2 + len;\n while (v.length > 32 && v[0] === 0) v = v.slice(1);\n if (v.length > 32) return null;\n const out = new Uint8Array(32);\n out.set(v, 32 - v.length);\n return out;\n };\n const r = part();\n const s = part();\n return r && s && i === der.length ? concat(r, s) : null;\n}\n\nconst u8 = (b: Uint8Array) => b as Uint8Array<ArrayBuffer>;\n/** WebCrypto's key type, named without the DOM lib (Workers, Node and browsers all have it). */\ntype CryptoKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\nasync function importKey(key: TrustedKey): Promise<CryptoKey | null> {\n const spki = fromB64url(key.spki);\n if (!spki?.length) return null;\n try {\n if (key.alg === ES256)\n return await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\"verify\"]);\n if (key.alg === RS256) {\n const k = await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" }, false, [\n \"verify\",\n ]);\n const bits = (k.algorithm as { modulusLength?: number }).modulusLength ?? 0;\n return bits >= 2048 ? k : null;\n }\n } catch {}\n return null;\n}\n\n/** Can this key be enrolled at all? A machine refuses to trust a key it could never verify with. */\nexport async function keyUsable(key: TrustedKey): Promise<boolean> {\n return (\n !!key &&\n typeof key.id === \"string\" &&\n /^[A-Za-z0-9_-]{16,1400}$/.test(key.id) &&\n (key.alg === ES256 || key.alg === RS256) &&\n !!(await importKey(key))\n );\n}\n\n// --- WebAuthn -------------------------------------------------------------------\n\n/** A WebAuthn origin a key may have been used from: https on the rp id or a subdomain;\n * for rp id `localhost`, plain http on any port too. A bare origin, as a browser writes it. */\nexport function originAllowed(origin: unknown, rpId: string): boolean {\n if (typeof origin !== \"string\" || !rpId) return false;\n let u: URL;\n try {\n u = new URL(origin);\n } catch {\n return false;\n }\n if (u.origin !== origin) return false;\n if (u.hostname !== rpId && !u.hostname.endsWith(`.${rpId}`)) return false;\n if (u.protocol === \"https:\") return true;\n return u.protocol === \"http:\" && rpId === \"localhost\";\n}\n\ntype Check = { ok: true } | { ok: false; why: string };\nconst no = (why: string): Check => ({ ok: false, why });\n\n/** One passkey assertion over `challenge`, made with `key`, by a verified person. */\nexport async function verifyAssertion(\n challenge: Uint8Array,\n a: Assertion | null | undefined,\n key: TrustedKey,\n rpId: string,\n): Promise<Check> {\n if (!a || typeof a !== \"object\") return no(\"no passkey signature\");\n if (a.cred !== key.id) return no(\"signed with a key this machine doesn't trust\");\n const cdBytes = fromB64url(a.cd);\n const ad = fromB64url(a.ad);\n const sig = fromB64url(a.sig);\n if (!cdBytes || !ad || !sig?.length) return no(\"the passkey signature is malformed\");\n let cd: { type?: unknown; challenge?: unknown; origin?: unknown; crossOrigin?: unknown };\n try {\n cd = JSON.parse(dec.decode(cdBytes));\n } catch {\n return no(\"the passkey signature is malformed\");\n }\n if (!cd || typeof cd !== \"object\") return no(\"the passkey signature is malformed\");\n if (cd.type !== \"webauthn.get\") return no(\"the passkey answered a different question\");\n if (cd.challenge !== b64url(challenge)) return no(\"the passkey signed something else\");\n if (!originAllowed(cd.origin, rpId)) return no(`the passkey was used on another site, not ${rpId}`);\n if (cd.crossOrigin === true) return no(\"the passkey was used inside another site's frame\");\n if (ad.length < 37) return no(\"the passkey signature is malformed\");\n if (!sameBytes(ad.slice(0, 32), await sha256(rpId))) return no(`the passkey belongs to another site, not ${rpId}`);\n const flags = ad[32] as number;\n if (!(flags & UP) || !(flags & UV)) return no(\"the passkey signed without verifying the person\");\n const pub = await importKey(key);\n if (!pub) return no(\"a trusted key is unusable\");\n const signed = concat(ad, await sha256(cdBytes));\n let good = false;\n try {\n if (key.alg === ES256) {\n const raw = derToRaw(sig);\n good = !!raw && (await crypto.subtle.verify({ name: \"ECDSA\", hash: \"SHA-256\" }, pub, u8(raw), u8(signed)));\n } else {\n good = await crypto.subtle.verify({ name: \"RSASSA-PKCS1-v1_5\" }, pub, u8(sig), u8(signed));\n }\n } catch {\n good = false;\n }\n return good ? { ok: true } : no(\"the passkey signature doesn't verify\");\n}\n\n/** What a passkey signs to enroll on a machine: bound to that machine and that pairing code. */\nexport function pairChallenge(machine: string, code: string): Promise<Uint8Array> {\n return sha256(`sessionpipe.pair:${machine}:${code}`);\n}\n\n/** The enrollment proof: the new key's assertion over pairChallenge(machine, code).\n * Only after this verifies does the key go into the machine's trust store. */\nexport async function verifyEnrollment(o: {\n machine: string;\n code: string;\n key: TrustedKey;\n proof: Assertion;\n rpId: string;\n}): Promise<Check> {\n if (!(await keyUsable(o.key))) return no(\"the key is in a shape this machine can't use\");\n return verifyAssertion(await pairChallenge(o.machine, o.code), o.proof, o.key, o.rpId);\n}\n\n// --- the command ------------------------------------------------------------------\n\ntype Parsed = { ok: true; cmd: ControlCommand } | { ok: false; code: RefusalCode; why: string };\nconst bad = (code: RefusalCode, why: string): { ok: false; code: RefusalCode; why: string } => ({\n ok: false,\n code,\n why,\n});\nconst isNum = (n: unknown): n is number => typeof n === \"number\" && Number.isFinite(n);\n\n/** The session id rule per harness (ADAPTERS.md): Claude Code ids are UUIDs. */\nexport function sessionIdOk(harness: string, id: string): boolean {\n if (!SESSION_ID_RE.test(id)) return false;\n if (harness === \"claude-code\") return UUID_RE.test(id);\n return true;\n}\n\n/** Split `<harness>:<id>`; null when either half breaks its rule. */\nexport function parseSessionRef(ref: unknown): { harness: string; id: string } | null {\n if (typeof ref !== \"string\") return null;\n const i = ref.indexOf(\":\");\n if (i <= 0) return null;\n const harness = ref.slice(0, i);\n const id = ref.slice(i + 1);\n return HARNESS_RE.test(harness) && sessionIdOk(harness, id) ? { harness, id } : null;\n}\n\n/** Read the command's fields from the exact string. Structure only; no clock, no keys. */\nexport function parseCommand(str: unknown): Parsed {\n if (typeof str !== \"string\" || !str || str.length > CMD_MAX)\n return bad(\"malformed\", \"the command is missing or too long\");\n let c: Record<string, unknown>;\n try {\n c = JSON.parse(str);\n } catch {\n return bad(\"malformed\", \"the command is not JSON\");\n }\n if (!c || typeof c !== \"object\" || Array.isArray(c)) return bad(\"malformed\", \"the command is not an object\");\n if (c.v !== 1 || c.t !== COMMAND_TYPE) return bad(\"not_a_command\", \"not a sessionpipe control command\");\n if (typeof c.machine !== \"string\" || !MACHINE_RE.test(c.machine)) return bad(\"malformed\", \"which machine?\");\n if (!CONTROL_KINDS.includes(c.kind as ControlKind))\n return bad(\"unknown_kind\", \"the command asks for something unknown\");\n const ref = parseSessionRef(c.session);\n if (!ref) return bad(\"bad_session\", \"which session?\");\n if (typeof c.nonce !== \"string\" || !NONCE_RE.test(c.nonce))\n return bad(\"bad_nonce\", \"the nonce is missing or malformed\");\n if (!isNum(c.iat) || !Number.isInteger(c.iat) || c.iat <= 0)\n return bad(\"malformed\", \"the command has no signing time\");\n const has = (k: string) => c[k] !== undefined;\n const text = () => typeof c.text === \"string\" && c.text.trim().length > 0 && c.text.length <= TEXT_MAX;\n switch (c.kind as ControlKind) {\n case \"prompt\":\n if (!text()) return bad(\"bad_fields\", `a prompt needs text, at most ${TEXT_MAX} characters`);\n if (has(\"for\") || has(\"decision\") || has(\"cwd\")) return bad(\"bad_fields\", \"a prompt carries only text\");\n break;\n case \"start\":\n if (!text()) return bad(\"bad_fields\", `a new session needs text, at most ${TEXT_MAX} characters`);\n if (ref.harness !== \"claude-code\") return bad(\"bad_fields\", \"only Claude Code sessions can be started\");\n if (typeof c.cwd !== \"string\" || !isAbsolute(c.cwd) || c.cwd.length > 1024)\n return bad(\"bad_fields\", \"a new session needs an absolute folder\");\n if (has(\"for\") || has(\"decision\")) return bad(\"bad_fields\", \"a start carries text and cwd\");\n break;\n case \"permission.answer\":\n if (typeof c.for !== \"string\" || !c.for || c.for.length > 128) return bad(\"bad_fields\", \"which attention?\");\n if (c.decision !== \"allow\" && c.decision !== \"deny\") return bad(\"bad_fields\", \"allow or deny?\");\n if (has(\"text\") || has(\"cwd\")) return bad(\"bad_fields\", \"a permission answer carries for, decision and note\");\n break;\n case \"cancel\":\n if (has(\"text\") || has(\"for\") || has(\"decision\") || has(\"cwd\"))\n return bad(\"bad_fields\", \"a cancel carries nothing\");\n break;\n }\n if (has(\"note\") && (typeof c.note !== \"string\" || c.note.length > NOTE_MAX))\n return bad(\"bad_fields\", `a note is text, at most ${NOTE_MAX} characters`);\n return { ok: true, cmd: c as unknown as ControlCommand };\n}\n\nfunction isAbsolute(p: string): boolean {\n return p.startsWith(\"/\") || /^[A-Za-z]:[\\\\/]/.test(p);\n}\n\n/** The grant's fields, read from its exact string. */\nexport function parseGrant(str: unknown, rpId: string): { ok: true; grant: Grant } | { ok: false; why: string } {\n if (typeof str !== \"string\" || !str || str.length > GRANT_STR_MAX)\n return { ok: false, why: \"the grant is missing or too long\" };\n let g: Record<string, unknown>;\n try {\n g = JSON.parse(str);\n } catch {\n return { ok: false, why: \"the grant is not JSON\" };\n }\n if (!g || typeof g !== \"object\" || Array.isArray(g)) return { ok: false, why: \"the grant is not an object\" };\n if (g.v !== 1 || g.t !== GRANT_TYPE) return { ok: false, why: \"not a sessionpipe grant\" };\n if (g.rp !== rpId) return { ok: false, why: `the grant is for another site, not ${rpId}` };\n if (typeof g.pub !== \"string\" || !fromB64url(g.pub)?.length || !isNum(g.iat) || !isNum(g.exp))\n return { ok: false, why: \"the grant is missing parts\" };\n if (g.exp <= g.iat) return { ok: false, why: \"the grant ends before it starts\" };\n if (g.exp - g.iat > GRANT_MAX_MS + SKEW_MS) return { ok: false, why: \"the grant lasts longer than a day\" };\n return { ok: true, grant: g as unknown as Grant };\n}\n\nasync function verifyCsig(cmd: string, csig: unknown, pub: string): Promise<boolean> {\n const sig = fromB64url(csig);\n const spki = fromB64url(pub);\n if (sig?.length !== 64 || !spki) return false;\n try {\n const key = await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\n \"verify\",\n ]);\n return await crypto.subtle.verify({ name: \"ECDSA\", hash: \"SHA-256\" }, key, u8(sig), u8(enc.encode(cmd)));\n } catch {\n return false;\n }\n}\n\n/**\n * Is this a command this machine may run? The checks, in the spec's order\n * (CONTROL.md \u00A75): the exact bytes parse; the command is for this machine; it is\n * signed \u2014 a grant from a trusted key plus the day key's signature, or a confirm\n * over sha256(cmd); it was signed within the age window; its nonce is new.\n * The folder and open-attention checks that follow are the daemon's (they read\n * the machine, not the command).\n */\nexport async function verifyCommand(s: SignedCommand | null | undefined, o: VerifyOptions): Promise<Verdict> {\n if (!s || typeof s !== \"object\") return bad(\"malformed\", \"nothing to verify\");\n const p = parseCommand(s.cmd);\n if (!p.ok) return p;\n const c = p.cmd;\n if (c.machine !== o.machine) return bad(\"wrong_machine\", \"the command is for another machine\");\n\n let via: \"grant\" | \"confirm\" | null = null;\n let keyId = \"\";\n let refusal: { code: RefusalCode; why: string } | null = null;\n if (s.grant && s.csig) {\n const r = await checkGrantPath(s, c, o);\n if (r.ok) {\n via = \"grant\";\n keyId = r.key;\n } else refusal = r;\n }\n if (!via && s.confirm) {\n const key = o.trusted.find((k) => k && k.id === s.confirm?.cred);\n if (!key) refusal = { code: \"untrusted_key\", why: \"confirmed with a key this machine doesn't trust\" };\n else {\n const a = await verifyAssertion(await sha256(s.cmd), s.confirm, key, o.rpId);\n if (a.ok) {\n via = \"confirm\";\n keyId = key.id;\n } else refusal = { code: \"bad_assertion\", why: a.why };\n }\n }\n if (!via) {\n if (refusal) return bad(refusal.code, refusal.why);\n return bad(\"unsigned\", \"the command isn't signed\");\n }\n if (c.iat > o.now + SKEW_MS) return bad(\"future\", \"the command is dated in the future (is this clock right?)\");\n if (o.now - c.iat > (o.maxAgeMs ?? MACHINE_MAX_AGE_MS)) return bad(\"too_old\", \"the command was signed too long ago\");\n if (o.seenNonce) {\n let seen: boolean;\n try {\n seen = await o.seenNonce(c.nonce);\n } catch {\n return bad(\"nonce_store\", \"the nonce couldn't be recorded, so the command didn't run\");\n }\n if (seen) return bad(\"replay\", \"this command was already used once\");\n }\n return { ok: true, cmd: c, via, key: keyId };\n}\n\nasync function checkGrantPath(\n s: SignedCommand,\n c: ControlCommand,\n o: VerifyOptions,\n): Promise<{ ok: true; key: string } | { ok: false; code: RefusalCode; why: string }> {\n const bundle = s.grant as GrantBundle;\n const g = parseGrant(bundle.str, o.rpId);\n if (!g.ok) return bad(\"bad_grant\", g.why);\n const key = o.trusted.find((k) => k && k.id === bundle.cred);\n if (!key) return bad(\"untrusted_key\", \"the grant was signed with a key this machine doesn't trust\");\n const a = await verifyAssertion(await sha256(bundle.str), bundle, key, o.rpId);\n if (!a.ok) return bad(\"bad_assertion\", a.why);\n if (!(await verifyCsig(s.cmd, s.csig, g.grant.pub)))\n return bad(\"bad_csig\", \"the command's signature doesn't verify \u2014 it was changed after it was signed\");\n if (c.iat < g.grant.iat - SKEW_MS || c.iat > g.grant.exp)\n return bad(\"outside_grant\", \"the command was signed outside its grant's window\");\n return { ok: true, key: key.id };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }