@ai-ecoverse/slicc-kernel 1.81.0 → 1.83.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.
package/dist/node.js CHANGED
@@ -28,7 +28,7 @@ function serveCdpPort(port, hook, request, onDone) {
28
28
  }
29
29
  });
30
30
  port.start?.();
31
- new Promise((resolve) => resolve(hook(request))).then(
31
+ new Promise((resolve2) => resolve2(hook(request))).then(
32
32
  (opened2) => {
33
33
  conn = opened2;
34
34
  if (done4) return opened2.close();
@@ -51,7 +51,7 @@ function serveCdpPort(port, hook, request, onDone) {
51
51
  };
52
52
  }
53
53
  function portCdpHook(ask, gone) {
54
- return (request) => new Promise((resolve, reject) => {
54
+ return (request) => new Promise((resolve2, reject) => {
55
55
  const { port1, port2 } = new MessageChannel();
56
56
  let closed = false;
57
57
  const conn = {
@@ -69,7 +69,7 @@ function portCdpHook(ask, gone) {
69
69
  };
70
70
  port1.addEventListener("message", ({ data }) => {
71
71
  if ("m" in data) conn.onmessage?.(data.m);
72
- else if ("open" in data) resolve(conn);
72
+ else if ("open" in data) resolve2(conn);
73
73
  else if ("error" in data) {
74
74
  closed = true;
75
75
  port1.close();
@@ -671,9 +671,9 @@ var KernelPipe = class {
671
671
  let filled = 0;
672
672
  for (const chunk2 of this.chunks) {
673
673
  if (filled === out.length) break;
674
- const count = Math.min(chunk2.length, out.length - filled);
675
- out.set(chunk2.subarray(0, count), filled);
676
- filled += count;
674
+ const count2 = Math.min(chunk2.length, out.length - filled);
675
+ out.set(chunk2.subarray(0, count2), filled);
676
+ filled += count2;
677
677
  }
678
678
  return out;
679
679
  }
@@ -704,25 +704,25 @@ var KernelPipe = class {
704
704
  let filled = 0;
705
705
  while (filled < n) {
706
706
  const head = this.chunks[0];
707
- const count = Math.min(head.length, n - filled);
708
- out.set(head.subarray(0, count), filled);
709
- filled += count;
710
- if (count === head.length) this.chunks.shift();
711
- else this.chunks[0] = head.subarray(count);
707
+ const count2 = Math.min(head.length, n - filled);
708
+ out.set(head.subarray(0, count2), filled);
709
+ filled += count2;
710
+ if (count2 === head.length) this.chunks.shift();
711
+ else this.chunks[0] = head.subarray(count2);
712
712
  }
713
713
  this.size -= n;
714
714
  return out;
715
715
  }
716
716
  changed(signal) {
717
717
  if (signal?.aborted) return Promise.reject(new PipeError("EINTR"));
718
- return new Promise((resolve, reject) => {
718
+ return new Promise((resolve2, reject) => {
719
719
  const onAbort = () => {
720
720
  this.waiters = this.waiters.filter((w) => w !== waiter);
721
721
  reject(new PipeError("EINTR"));
722
722
  };
723
723
  const waiter = () => {
724
724
  signal?.removeEventListener("abort", onAbort);
725
- resolve();
725
+ resolve2();
726
726
  };
727
727
  this.waiters.push(waiter);
728
728
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -731,7 +731,7 @@ var KernelPipe = class {
731
731
  wake() {
732
732
  const waiters = this.waiters;
733
733
  this.waiters = [];
734
- for (const resolve of waiters) resolve();
734
+ for (const resolve2 of waiters) resolve2();
735
735
  }
736
736
  };
737
737
 
@@ -1192,14 +1192,14 @@ var Changes = class {
1192
1192
  waiters = [];
1193
1193
  wait(signal) {
1194
1194
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
1195
- return new Promise((resolve, reject) => {
1195
+ return new Promise((resolve2, reject) => {
1196
1196
  const onAbort = () => {
1197
1197
  this.waiters = this.waiters.filter((w) => w !== waiter);
1198
1198
  reject(new KernelError("EINTR"));
1199
1199
  };
1200
1200
  const waiter = () => {
1201
1201
  signal?.removeEventListener("abort", onAbort);
1202
- resolve();
1202
+ resolve2();
1203
1203
  };
1204
1204
  this.waiters.push(waiter);
1205
1205
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -1208,7 +1208,7 @@ var Changes = class {
1208
1208
  wake() {
1209
1209
  const waiters = this.waiters;
1210
1210
  this.waiters = [];
1211
- for (const resolve of waiters) resolve();
1211
+ for (const resolve2 of waiters) resolve2();
1212
1212
  }
1213
1213
  };
1214
1214
  function link(a, b) {
@@ -1376,7 +1376,7 @@ var KernelSocket = class _KernelSocket {
1376
1376
  });
1377
1377
  const { host, port } = addr;
1378
1378
  const dialled = new Promise(
1379
- (resolve) => resolve(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
1379
+ (resolve2) => resolve2(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
1380
1380
  );
1381
1381
  dialled.then(
1382
1382
  (conn) => abort.signal.aborted && conn.close(),
@@ -1747,8 +1747,8 @@ var MessageQueue = class {
1747
1747
  this.overflow = overflow;
1748
1748
  this.limit = limit;
1749
1749
  this.taken = taken;
1750
- this.closed = new Promise((resolve) => {
1751
- this.settle = resolve;
1750
+ this.closed = new Promise((resolve2) => {
1751
+ this.settle = resolve2;
1752
1752
  });
1753
1753
  }
1754
1754
  push(message3) {
@@ -1787,8 +1787,8 @@ var MessageQueue = class {
1787
1787
  continue;
1788
1788
  }
1789
1789
  if (this.ended) return;
1790
- await new Promise((resolve) => {
1791
- this.waiter = resolve;
1790
+ await new Promise((resolve2) => {
1791
+ this.waiter = resolve2;
1792
1792
  });
1793
1793
  }
1794
1794
  }
@@ -1837,7 +1837,7 @@ function openWebSocket(Socket, request, withHeaders) {
1837
1837
  }
1838
1838
  socket.binaryType = "arraybuffer";
1839
1839
  const queue = new MessageQueue(() => socket.close(1e3, "too many waiting messages"));
1840
- return new Promise((resolve, reject) => {
1840
+ return new Promise((resolve2, reject) => {
1841
1841
  let open = false;
1842
1842
  let settled = false;
1843
1843
  const fail3 = (error) => {
@@ -1856,7 +1856,7 @@ function openWebSocket(Socket, request, withHeaders) {
1856
1856
  open = true;
1857
1857
  settled = true;
1858
1858
  signal.removeEventListener("abort", abort);
1859
- resolve({
1859
+ resolve2({
1860
1860
  protocol: socket.protocol,
1861
1861
  get buffered() {
1862
1862
  return socket.bufferedAmount;
@@ -2047,8 +2047,8 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2047
2047
  const relay = async (nid, socket) => {
2048
2048
  let ending;
2049
2049
  let late;
2050
- const unanswered = new Promise((resolve) => {
2051
- late = () => resolve("late");
2050
+ const unanswered = new Promise((resolve2) => {
2051
+ late = () => resolve2("late");
2052
2052
  });
2053
2053
  const flow = {
2054
2054
  inflight: 0,
@@ -2076,8 +2076,8 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2076
2076
  }
2077
2077
  flow.inflight += payloadSize(data);
2078
2078
  while (flow.inflight > INBOUND_LIMIT && !flow.closing) {
2079
- await new Promise((resolve) => {
2080
- flow.wake = resolve;
2079
+ await new Promise((resolve2) => {
2080
+ flow.wake = resolve2;
2081
2081
  });
2082
2082
  }
2083
2083
  }
@@ -2096,7 +2096,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
2096
2096
  if (!socket) return;
2097
2097
  socket.send(call.data);
2098
2098
  while (socket.buffered > SEND_BUFFER && sockets.has(call.nid)) {
2099
- await new Promise((resolve) => setTimeout(resolve, 10));
2099
+ await new Promise((resolve2) => setTimeout(resolve2, 10));
2100
2100
  }
2101
2101
  peer.postMessage({ net: "ws-acked", nid: call.nid, n: payloadSize(call.data) });
2102
2102
  };
@@ -3033,6 +3033,97 @@ var MemoryMeta = class {
3033
3033
  }
3034
3034
  };
3035
3035
 
3036
+ // src/fs/integrity.ts
3037
+ var KERNEL_CALLER = { pid: 0, uid: 0 };
3038
+ function integrityOptions(options) {
3039
+ if (options && typeof options.lookup !== "function") {
3040
+ throw new TypeError("protect.integrity.lookup must be a function");
3041
+ }
3042
+ integrityUnknown(options);
3043
+ }
3044
+ function integrityUnknown(options) {
3045
+ const unknown = options?.unknown ?? "allow";
3046
+ if (unknown !== "allow" && unknown !== "deny") {
3047
+ throw new TypeError("protect.integrity.unknown must be 'allow' or 'deny'");
3048
+ }
3049
+ return unknown;
3050
+ }
3051
+ function hex(digest) {
3052
+ return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
3053
+ }
3054
+ async function sha256(bytes) {
3055
+ return hex(await crypto.subtle.digest("SHA-256", bytes));
3056
+ }
3057
+ var Integrity = class {
3058
+ prefixes;
3059
+ root;
3060
+ lookup;
3061
+ unknown;
3062
+ report;
3063
+ unverified = /* @__PURE__ */ new Set();
3064
+ constructor(prefixes2, root, options, report) {
3065
+ this.prefixes = prefixes2;
3066
+ this.root = root;
3067
+ this.lookup = options.lookup;
3068
+ this.unknown = integrityUnknown(options);
3069
+ this.report = report;
3070
+ }
3071
+ async real(path) {
3072
+ const at = normalizePath(path);
3073
+ const real = await this.root.realpath?.(at, true).catch(() => void 0) ?? at;
3074
+ return isProtected(this.prefixes, real) ? real : void 0;
3075
+ }
3076
+ async key(path) {
3077
+ const real = await this.real(path);
3078
+ if (real === void 0 || !this.root.version) return void 0;
3079
+ return `${real}\0${await this.root.version(real)}`;
3080
+ }
3081
+ async read(path, caller, plain) {
3082
+ return (await this.readKeyed(path, caller, plain)).bytes;
3083
+ }
3084
+ async readKeyed(path, caller, plain) {
3085
+ const real = await this.real(path);
3086
+ if (real === void 0) return { bytes: await plain() };
3087
+ const read = this.root.readVersioned ? await this.root.readVersioned(real) : { bytes: await this.root.readFileBuffer(real), version: void 0 };
3088
+ const { bytes } = read;
3089
+ const keyed = read.version === void 0 ? { bytes } : { bytes, key: `${real}\0${read.version}` };
3090
+ const actual = await sha256(bytes);
3091
+ const denial = { pid: caller.pid, uid: caller.uid, path: real, actual };
3092
+ const tagged = caller.tag !== void 0 ? { tag: caller.tag } : {};
3093
+ let expected;
3094
+ try {
3095
+ expected = await this.expected(real);
3096
+ } catch {
3097
+ this.say({ ...denial, ...tagged, op: "exec-integrity", errno: "EACCES" });
3098
+ throw fsError("EACCES", `integrity lookup failed: ${real}`);
3099
+ }
3100
+ if (expected === void 0) {
3101
+ if (this.unknown === "allow") {
3102
+ if (!this.unverified.has(real)) {
3103
+ this.unverified.add(real);
3104
+ this.say({ ...denial, ...tagged, op: "exec-unverified", errno: "ENODATA" });
3105
+ }
3106
+ return keyed;
3107
+ }
3108
+ } else if (expected === actual) {
3109
+ return keyed;
3110
+ }
3111
+ const known = expected !== void 0 ? { expected } : {};
3112
+ this.say({ ...denial, ...tagged, ...known, op: "exec-integrity", errno: "EACCES" });
3113
+ throw fsError("EACCES", `integrity check failed: ${real}`);
3114
+ }
3115
+ async expected(path) {
3116
+ const answer = await this.lookup(path);
3117
+ return typeof answer === "string" ? answer.toLowerCase() : void 0;
3118
+ }
3119
+ say(event) {
3120
+ try {
3121
+ this.report(event);
3122
+ } catch {
3123
+ }
3124
+ }
3125
+ };
3126
+
3036
3127
  // src/fs/protect.ts
3037
3128
  function writerTags(protection) {
3038
3129
  const writers = protection?.writers ?? [];
@@ -3313,11 +3404,11 @@ var RemoteWatch = class {
3313
3404
  const seq2 = ++this.seq;
3314
3405
  this.channel.postMessage({ tab: this.tab, paths: this.mine, seq: seq2 });
3315
3406
  if (this.peers.size === 0) return Promise.resolve();
3316
- return new Promise((resolve) => {
3407
+ return new Promise((resolve2) => {
3317
3408
  const done4 = () => {
3318
3409
  clearTimeout(timer);
3319
3410
  this.waiting.delete(seq2);
3320
- resolve();
3411
+ resolve2();
3321
3412
  };
3322
3413
  const timer = setTimeout(() => {
3323
3414
  done4();
@@ -3413,7 +3504,7 @@ async function openSync(handle) {
3413
3504
  const delay = SYNC_BACKOFF_MS[attempt];
3414
3505
  if (!LOCKED.has(err?.name)) throw err;
3415
3506
  if (delay === void 0) return void 0;
3416
- await new Promise((resolve) => setTimeout(resolve, delay));
3507
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
3417
3508
  }
3418
3509
  }
3419
3510
  }
@@ -3866,7 +3957,7 @@ var OpfsFs = class {
3866
3957
  const again = name2 === "NotReadableError" || locks && LOCKED.has(name2);
3867
3958
  if (!again || attempt >= READ_ATTEMPTS)
3868
3959
  throw translate(err, path, locks ? "write" : "read");
3869
- await new Promise((resolve) => setTimeout(resolve, attempt * 10));
3960
+ await new Promise((resolve2) => setTimeout(resolve2, attempt * 10));
3870
3961
  }
3871
3962
  }
3872
3963
  }
@@ -3885,6 +3976,29 @@ var OpfsFs = class {
3885
3976
  false
3886
3977
  );
3887
3978
  }
3979
+ async readVersioned(path) {
3980
+ const handle = await this.existing(path);
3981
+ return this.retrying(
3982
+ path,
3983
+ async () => {
3984
+ const file = await handle.getFile();
3985
+ const bytes = new Uint8Array(await file.arrayBuffer());
3986
+ return { bytes, version: `${file.lastModified}:${file.size}` };
3987
+ },
3988
+ false
3989
+ );
3990
+ }
3991
+ async version(path) {
3992
+ const handle = await this.existing(path);
3993
+ return this.retrying(
3994
+ path,
3995
+ async () => {
3996
+ const file = await handle.getFile();
3997
+ return `${file.lastModified}:${file.size}`;
3998
+ },
3999
+ false
4000
+ );
4001
+ }
3888
4002
  async pread(path, offset, length2) {
3889
4003
  const handle = await this.existing(path);
3890
4004
  return this.retrying(
@@ -3900,8 +4014,8 @@ var OpfsFs = class {
3900
4014
  }
3901
4015
  async truncate(path, size) {
3902
4016
  const handle = await this.existing(path, true);
3903
- const resolve = this.ranged || this.remote?.watched();
3904
- const target = resolve ? (await this.locate(path, true)).path : path;
4017
+ const resolve2 = this.ranged || this.remote?.watched();
4018
+ const target = resolve2 ? (await this.locate(path, true)).path : path;
3905
4019
  await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
3906
4020
  this.written(target);
3907
4021
  }
@@ -4341,12 +4455,12 @@ var PathGate = class {
4341
4455
  tails = /* @__PURE__ */ new Map();
4342
4456
  async acquire(path) {
4343
4457
  const prev = this.tails.get(path) ?? Promise.resolve();
4344
- const { promise, resolve } = Promise.withResolvers();
4458
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
4345
4459
  const tail = prev.then(() => promise);
4346
4460
  this.tails.set(path, tail);
4347
4461
  await prev;
4348
4462
  return () => {
4349
- resolve();
4463
+ resolve2();
4350
4464
  if (this.tails.get(path) === tail) this.tails.delete(path);
4351
4465
  };
4352
4466
  }
@@ -5110,7 +5224,7 @@ var CdpFacade = class {
5110
5224
  const conn = await this.host(runtime);
5111
5225
  try {
5112
5226
  const reply = await new Promise(
5113
- (resolve, reject) => {
5227
+ (resolve2, reject) => {
5114
5228
  const timer = setTimeout(
5115
5229
  () => reject(new HttpError(504, "the CDP host did not answer Target.getTargets")),
5116
5230
  this.options.listTimeout ?? LIST_TIMEOUT
@@ -5123,7 +5237,7 @@ var CdpFacade = class {
5123
5237
  const parsed2 = JSON.parse(message3);
5124
5238
  if (parsed2.id !== 1) return;
5125
5239
  clearTimeout(timer);
5126
- resolve(parsed2);
5240
+ resolve2(parsed2);
5127
5241
  };
5128
5242
  conn.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
5129
5243
  }
@@ -5183,9 +5297,9 @@ var Relay = class {
5183
5297
  }
5184
5298
  async start(head) {
5185
5299
  await this.send(head);
5186
- const hostGone = new Promise((resolve) => {
5300
+ const hostGone = new Promise((resolve2) => {
5187
5301
  this.host.onclose = (reason) => {
5188
- void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve());
5302
+ void this.end(reason ? CLOSE.error : CLOSE.normal, reason ?? "").then(() => resolve2());
5189
5303
  };
5190
5304
  });
5191
5305
  this.host.onmessage = (message3) => {
@@ -5199,8 +5313,8 @@ var Relay = class {
5199
5313
  await Promise.race([pumping, hostGone]);
5200
5314
  await this.writes;
5201
5315
  let timer;
5202
- const waited = new Promise((resolve) => {
5203
- timer = setTimeout(resolve, this.closeWaitMs);
5316
+ const waited = new Promise((resolve2) => {
5317
+ timer = setTimeout(resolve2, this.closeWaitMs);
5204
5318
  });
5205
5319
  await Promise.race([pumping, waited]);
5206
5320
  clearTimeout(timer);
@@ -5539,7 +5653,7 @@ function owned(path, st) {
5539
5653
  function unchanged(a, b, bytes) {
5540
5654
  return a.ino === b.ino && a.mtime.getTime() === b.mtime.getTime() && a.size === b.size && bytes.length === b.size;
5541
5655
  }
5542
- async function sha256(bytes) {
5656
+ async function sha2562(bytes) {
5543
5657
  const digest = new Uint8Array(
5544
5658
  await crypto.subtle.digest("SHA-256", bytes)
5545
5659
  );
@@ -5595,7 +5709,7 @@ async function attest(fs, asked, o) {
5595
5709
  const bytes = (await fs.readFileBuffer(path)).slice();
5596
5710
  const after = await fs.lstat(path);
5597
5711
  if (unchanged(before, after, bytes)) {
5598
- return attested(path, after, bytes, await sha256(bytes), o, parents);
5712
+ return attested(path, after, bytes, await sha2562(bytes), o, parents);
5599
5713
  }
5600
5714
  if (tries >= 1) refuse("EAGAIN", path, "changed while it was read");
5601
5715
  }
@@ -5850,16 +5964,16 @@ var FreshModules = class {
5850
5964
  if (head) await decide(path, head);
5851
5965
  };
5852
5966
  }
5853
- const bound = /* @__PURE__ */ new WeakMap();
5967
+ const bound2 = /* @__PURE__ */ new WeakMap();
5854
5968
  return new Proxy(fs, {
5855
5969
  get(target, key, receiver) {
5856
5970
  if (Object.hasOwn(overrides, key)) return overrides[key];
5857
5971
  const value = Reflect.get(target, key, receiver);
5858
5972
  if (typeof value !== "function") return value;
5859
- let method = bound.get(value);
5973
+ let method = bound2.get(value);
5860
5974
  if (method === void 0) {
5861
5975
  method = value.bind(target);
5862
- bound.set(value, method);
5976
+ bound2.set(value, method);
5863
5977
  }
5864
5978
  return method;
5865
5979
  }
@@ -6188,7 +6302,7 @@ var ChildTable = class {
6188
6302
  }
6189
6303
  async nextChange(candidates, flags2, interrupt) {
6190
6304
  let wake;
6191
- const stateChange = new Promise((resolve) => wake = resolve);
6305
+ const stateChange = new Promise((resolve2) => wake = resolve2);
6192
6306
  const watching = flags2.untraced || flags2.continued;
6193
6307
  if (watching && wake) this.stateChanged.push(wake);
6194
6308
  try {
@@ -6402,6 +6516,65 @@ var ClipboardBroker = class {
6402
6516
  }
6403
6517
  };
6404
6518
 
6519
+ // src/kernel/exec-ticket.ts
6520
+ var TICKET_ENV = "SLICC_SEALED_TICKET";
6521
+ var TICKET_TTL_MS = 3e4;
6522
+ var MAX_TICKETS = 256;
6523
+ var TICKET = /^[0-9a-f]{32}$/;
6524
+ async function argvDigest(argv) {
6525
+ const bytes = new TextEncoder().encode(JSON.stringify(argv));
6526
+ const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
6527
+ return [...hash].map((b) => b.toString(16).padStart(2, "0")).join("");
6528
+ }
6529
+ function isStrings(value) {
6530
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
6531
+ }
6532
+ function stringRecord(value) {
6533
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
6534
+ const entries = Object.entries(value);
6535
+ return entries.every(([, v]) => typeof v === "string") ? Object.fromEntries(entries) : void 0;
6536
+ }
6537
+ var TicketBook = class {
6538
+ booked = /* @__PURE__ */ new Map();
6539
+ now;
6540
+ normalise = async (path) => path;
6541
+ constructor(now = Date.now) {
6542
+ this.now = now;
6543
+ }
6544
+ get size() {
6545
+ return this.booked.size;
6546
+ }
6547
+ async record(req, reply, pid) {
6548
+ const { ticket, error } = reply;
6549
+ if (error !== void 0 || typeof ticket !== "string" || !TICKET.test(ticket)) return;
6550
+ const { path, argv, cwd } = req;
6551
+ const entry = {
6552
+ pid,
6553
+ path: await this.normalise(path),
6554
+ requested: path,
6555
+ requestedCwd: cwd,
6556
+ argv: await argvDigest(argv),
6557
+ cwd: await this.normalise(cwd),
6558
+ at: this.now()
6559
+ };
6560
+ this.prune();
6561
+ while (this.booked.size >= MAX_TICKETS)
6562
+ this.booked.delete(this.booked.keys().next().value);
6563
+ this.booked.set(ticket, entry);
6564
+ }
6565
+ take(ticket) {
6566
+ const booked = this.booked.get(ticket);
6567
+ this.booked.delete(ticket);
6568
+ return booked && this.now() - booked.at < TICKET_TTL_MS ? booked : void 0;
6569
+ }
6570
+ prune() {
6571
+ const now = this.now();
6572
+ for (const [ticket, booked] of this.booked) {
6573
+ if (now - booked.at >= TICKET_TTL_MS) this.booked.delete(ticket);
6574
+ }
6575
+ }
6576
+ };
6577
+
6405
6578
  // src/mount/protocol.ts
6406
6579
  var DRIVER_PROTOCOL = [1, 2];
6407
6580
  function driverVersionError(theirs) {
@@ -6472,7 +6645,7 @@ var DriverConnection = class _DriverConnection {
6472
6645
  call(call, transfer = []) {
6473
6646
  if (this.failure) return Promise.reject(this.failure);
6474
6647
  const id2 = ++this.nextId;
6475
- return new Promise((resolve, reject) => {
6648
+ return new Promise((resolve2, reject) => {
6476
6649
  const timer = unref(
6477
6650
  setTimeout(() => {
6478
6651
  this.waiting.delete(id2);
@@ -6485,7 +6658,7 @@ var DriverConnection = class _DriverConnection {
6485
6658
  this.onFail?.(error);
6486
6659
  }, this.timeoutMs)
6487
6660
  );
6488
- this.waiting.set(id2, { resolve, reject, timer });
6661
+ this.waiting.set(id2, { resolve: resolve2, reject, timer });
6489
6662
  this.port.postMessage({ ...call, id: id2 }, transfer);
6490
6663
  });
6491
6664
  }
@@ -6628,8 +6801,8 @@ var HeldPaths = class extends Map {
6628
6801
  this.keptHandles.delete(handle);
6629
6802
  if (kept) this.letGo(kept);
6630
6803
  }
6631
- const count = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
6632
- if (count > 0) this.opens.set(key, count);
6804
+ const count2 = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
6805
+ if (count2 > 0) this.opens.set(key, count2);
6633
6806
  else {
6634
6807
  this.opens.delete(key);
6635
6808
  for (const id2 of this.handlesAt(key)) this.handles.delete(id2);
@@ -6643,8 +6816,8 @@ var HeldPaths = class extends Map {
6643
6816
  return this.lastHandle;
6644
6817
  }
6645
6818
  holdWrite(path, on) {
6646
- const count = (this.get(path) ?? 0) + (on ? 1 : -1);
6647
- if (count > 0) this.set(path, count);
6819
+ const count2 = (this.get(path) ?? 0) + (on ? 1 : -1);
6820
+ if (count2 > 0) this.set(path, count2);
6648
6821
  else {
6649
6822
  this.delete(path);
6650
6823
  this.revoked.delete(path);
@@ -6666,18 +6839,18 @@ var HeldPaths = class extends Map {
6666
6839
  for (const path of this.keys()) if (within4(path, prefix)) this.revoked.add(path);
6667
6840
  }
6668
6841
  renamed(from, to) {
6669
- for (const [path, count] of [...this]) {
6842
+ for (const [path, count2] of [...this]) {
6670
6843
  if (!within4(path, from)) continue;
6671
6844
  const moved2 = to + path.slice(from.length);
6672
6845
  this.delete(path);
6673
- this.set(moved2, (this.get(moved2) ?? 0) + count);
6846
+ this.set(moved2, (this.get(moved2) ?? 0) + count2);
6674
6847
  if (this.revoked.delete(path)) this.revoked.add(moved2);
6675
6848
  }
6676
- for (const [path, count] of [...this.opens]) {
6849
+ for (const [path, count2] of [...this.opens]) {
6677
6850
  if (!within4(path, from)) continue;
6678
6851
  const moved2 = to + path.slice(from.length);
6679
6852
  this.opens.delete(path);
6680
- this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
6853
+ this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
6681
6854
  }
6682
6855
  for (const [id2, key] of this.handles) {
6683
6856
  if (within4(key, from)) this.handles.set(id2, to + key.slice(from.length));
@@ -8998,7 +9171,7 @@ var AsyncOps = class {
8998
9171
  this.wake();
8999
9172
  }
9000
9173
  sleep(timeoutMs, signal) {
9001
- return new Promise((resolve, reject) => {
9174
+ return new Promise((resolve2, reject) => {
9002
9175
  let timer;
9003
9176
  const cleanup = () => {
9004
9177
  clearTimeout(timer);
@@ -9007,7 +9180,7 @@ var AsyncOps = class {
9007
9180
  };
9008
9181
  const wake = () => {
9009
9182
  cleanup();
9010
- resolve(true);
9183
+ resolve2(true);
9011
9184
  };
9012
9185
  const abort = () => {
9013
9186
  cleanup();
@@ -9018,7 +9191,7 @@ var AsyncOps = class {
9018
9191
  if (timeoutMs !== void 0) {
9019
9192
  timer = setTimeout(() => {
9020
9193
  cleanup();
9021
- resolve(false);
9194
+ resolve2(false);
9022
9195
  }, timeoutMs);
9023
9196
  }
9024
9197
  });
@@ -9102,10 +9275,10 @@ var Budget = class {
9102
9275
  async acquire(n, signal) {
9103
9276
  while (this.free < n) {
9104
9277
  signal.throwIfAborted();
9105
- await new Promise((resolve) => {
9278
+ await new Promise((resolve2) => {
9106
9279
  const done4 = () => {
9107
9280
  signal.removeEventListener("abort", done4);
9108
- resolve();
9281
+ resolve2();
9109
9282
  };
9110
9283
  this.waiters.push(done4);
9111
9284
  signal.addEventListener("abort", done4, { once: true });
@@ -9247,14 +9420,14 @@ async function watchHangup(conn, abort, until) {
9247
9420
  }
9248
9421
  }
9249
9422
  function untilAborted(response, signal) {
9250
- return new Promise((resolve, reject) => {
9423
+ return new Promise((resolve2, reject) => {
9251
9424
  const onAbort = () => {
9252
9425
  reject(signal.reason ?? new KernelError("EINTR"));
9253
9426
  response.then((late) => late.cancel()).catch(() => void 0);
9254
9427
  };
9255
9428
  if (signal.aborted) return onAbort();
9256
9429
  signal.addEventListener("abort", onAbort, { once: true });
9257
- response.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
9430
+ response.then(resolve2, reject).finally(() => signal.removeEventListener("abort", onAbort));
9258
9431
  });
9259
9432
  }
9260
9433
  function plainResponse(status, message3) {
@@ -9287,8 +9460,8 @@ function wireCode(code) {
9287
9460
  return sendableCode(code) ? code : 1011;
9288
9461
  }
9289
9462
  function quietly(ms) {
9290
- return new Promise((resolve) => {
9291
- AbortSignal.timeout(ms).addEventListener("abort", () => resolve(), { once: true });
9463
+ return new Promise((resolve2) => {
9464
+ AbortSignal.timeout(ms).addEventListener("abort", () => resolve2(), { once: true });
9292
9465
  });
9293
9466
  }
9294
9467
  var RealmProxy = class {
@@ -9386,7 +9559,7 @@ var RealmProxy = class {
9386
9559
  return false;
9387
9560
  }
9388
9561
  const url = ctx.origin ? tunnelRequestUrl(req, ctx.origin) : requestUrl(req);
9389
- const keep = keepsAlive(req);
9562
+ const keep2 = keepsAlive(req);
9390
9563
  const framing = requestFraming(req.headers);
9391
9564
  const reserve = framing.kind === "length" ? framing.length : framing.kind === "chunked" ? this.cap() : 0;
9392
9565
  if (reserve > this.cap()) throw new HttpError(413, `request body over ${this.cap()} bytes`);
@@ -9409,7 +9582,7 @@ var RealmProxy = class {
9409
9582
  } finally {
9410
9583
  this.bodies.release(reserve);
9411
9584
  }
9412
- return await this.relay(conn, req.method, req.minor, response, keep);
9585
+ return await this.relay(conn, req.method, req.minor, response, keep2);
9413
9586
  } finally {
9414
9587
  this.stop.signal.removeEventListener("abort", stopExchange);
9415
9588
  abort.abort();
@@ -9457,8 +9630,8 @@ var RealmProxy = class {
9457
9630
  bodiless: bodiless2
9458
9631
  });
9459
9632
  const chunked2 = !bodiless2 && minor >= 1;
9460
- const keep = keepAlive && (bodiless2 || chunked2);
9461
- headers.push(["Connection", keep ? "keep-alive" : "close"]);
9633
+ const keep2 = keepAlive && (bodiless2 || chunked2);
9634
+ headers.push(["Connection", keep2 ? "keep-alive" : "close"]);
9462
9635
  if (chunked2) headers.push(["Transfer-Encoding", "chunked"]);
9463
9636
  const signal = this.stop.signal;
9464
9637
  await conn.write(
@@ -9467,7 +9640,7 @@ var RealmProxy = class {
9467
9640
  );
9468
9641
  if (bodiless2) {
9469
9642
  await response.cancel();
9470
- return keep;
9643
+ return keep2;
9471
9644
  }
9472
9645
  try {
9473
9646
  for await (const piece of response.body) {
@@ -9478,7 +9651,7 @@ var RealmProxy = class {
9478
9651
  return false;
9479
9652
  }
9480
9653
  if (chunked2) await conn.write(LAST_CHUNK, signal);
9481
- return keep;
9654
+ return keep2;
9482
9655
  }
9483
9656
  async connect(conn, incoming, req, idle) {
9484
9657
  const target = tunnelTarget(req);
@@ -9701,10 +9874,10 @@ function header(der, at) {
9701
9874
  let len = der[at + 1];
9702
9875
  let start2 = at + 2;
9703
9876
  if (len & 128) {
9704
- const count = len & 127;
9877
+ const count2 = len & 127;
9705
9878
  len = 0;
9706
- for (let i = 0; i < count; i++) len = len * 256 + der[start2 + i];
9707
- start2 += count;
9879
+ for (let i = 0; i < count2; i++) len = len * 256 + der[start2 + i];
9880
+ start2 += count2;
9708
9881
  }
9709
9882
  if (start2 + len > der.length) throw new Error("truncated DER");
9710
9883
  return [tag, start2, start2 + len];
@@ -10761,7 +10934,7 @@ var Resolver = class {
10761
10934
  };
10762
10935
  function waitFor(lookup2, signal) {
10763
10936
  lookup2.waiters++;
10764
- return new Promise((resolve, reject) => {
10937
+ return new Promise((resolve2, reject) => {
10765
10938
  const abort = () => {
10766
10939
  if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
10767
10940
  reject(new KernelError("EINTR"));
@@ -10771,7 +10944,7 @@ function waitFor(lookup2, signal) {
10771
10944
  if (signal?.aborted) return;
10772
10945
  lookup2.waiters--;
10773
10946
  signal?.removeEventListener("abort", abort);
10774
- resolve(value);
10947
+ resolve2(value);
10775
10948
  });
10776
10949
  });
10777
10950
  }
@@ -10952,8 +11125,8 @@ var KernelTty = class {
10952
11125
  }
10953
11126
  rubOut(n) {
10954
11127
  const dropped2 = this.line.splice(this.line.length - n, n);
10955
- const utf82 = (this.termios.c_iflag & IUTF8) !== 0;
10956
- const cells = utf82 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
11128
+ const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
11129
+ const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
10957
11130
  if (cells > 0 && this.termios.c_lflag & ECHO) {
10958
11131
  this.output(encoder3.encode("\b \b".repeat(cells)), false);
10959
11132
  }
@@ -11023,14 +11196,14 @@ var KernelTty = class {
11023
11196
  }
11024
11197
  changed(signal) {
11025
11198
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
11026
- return new Promise((resolve, reject) => {
11199
+ return new Promise((resolve2, reject) => {
11027
11200
  const onAbort = () => {
11028
11201
  this.waiters = this.waiters.filter((w) => w !== waiter);
11029
11202
  reject(new KernelError("EINTR"));
11030
11203
  };
11031
11204
  const waiter = () => {
11032
11205
  signal?.removeEventListener("abort", onAbort);
11033
- resolve();
11206
+ resolve2();
11034
11207
  };
11035
11208
  this.waiters.push(waiter);
11036
11209
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -11039,7 +11212,7 @@ var KernelTty = class {
11039
11212
  wake() {
11040
11213
  const waiters = this.waiters;
11041
11214
  this.waiters = [];
11042
- for (const resolve of waiters) resolve();
11215
+ for (const resolve2 of waiters) resolve2();
11043
11216
  }
11044
11217
  };
11045
11218
 
@@ -11138,14 +11311,14 @@ var PtyPair = class {
11138
11311
  }
11139
11312
  changed(signal) {
11140
11313
  if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
11141
- return new Promise((resolve, reject) => {
11314
+ return new Promise((resolve2, reject) => {
11142
11315
  const onAbort = () => {
11143
11316
  this.waiters = this.waiters.filter((w) => w !== waiter);
11144
11317
  reject(new KernelError("EINTR"));
11145
11318
  };
11146
11319
  const waiter = () => {
11147
11320
  signal?.removeEventListener("abort", onAbort);
11148
- resolve();
11321
+ resolve2();
11149
11322
  };
11150
11323
  this.waiters.push(waiter);
11151
11324
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -11154,7 +11327,7 @@ var PtyPair = class {
11154
11327
  wake() {
11155
11328
  const waiters = this.waiters;
11156
11329
  this.waiters = [];
11157
- for (const resolve of waiters) resolve();
11330
+ for (const resolve2 of waiters) resolve2();
11158
11331
  }
11159
11332
  };
11160
11333
  var PtyTable = class {
@@ -11219,7 +11392,8 @@ function ptySyscall(req, ctx) {
11219
11392
  if (!ctx.ptys) throw new KernelError("ENOENT");
11220
11393
  const pair = ctx.ptys.get(req.n);
11221
11394
  ttyOpenable(ctx.guard, pair.slave);
11222
- const fd = ctx.fds.install(pair.openSlave(), 3);
11395
+ const slave = pair.openSlave();
11396
+ const fd = ctx.fds.install(ctx.ttyFilter?.(slave) ?? slave, 3);
11223
11397
  if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
11224
11398
  return json4(fd);
11225
11399
  }
@@ -11292,8 +11466,8 @@ async function runChild(req, deps) {
11292
11466
  };
11293
11467
  const controls = [];
11294
11468
  const capture = () => {
11295
- const { promise, resolve } = Promise.withResolvers();
11296
- const control = { shut: false, ended: resolve, eof: promise };
11469
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
11470
+ const control = { shut: false, ended: resolve2, eof: promise };
11297
11471
  controls.push(control);
11298
11472
  return { capture: true, max, overflow: () => stop("ENOBUFS"), control };
11299
11473
  };
@@ -11386,7 +11560,7 @@ async function waitReady(fds, read, write, deadline, interrupted2) {
11386
11560
  return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
11387
11561
  });
11388
11562
  let timer;
11389
- const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
11563
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
11390
11564
  try {
11391
11565
  await Promise.race([...changes, ...expiry, interrupted2]);
11392
11566
  } finally {
@@ -11470,10 +11644,10 @@ async function socketSyscall(req, proc) {
11470
11644
  }
11471
11645
  }
11472
11646
  function interruptible2(dialing, signal) {
11473
- return new Promise((resolve, reject) => {
11647
+ return new Promise((resolve2, reject) => {
11474
11648
  const abort = () => reject(new KernelError("EINTR"));
11475
11649
  signal.addEventListener("abort", abort, { once: true });
11476
- dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
11650
+ dialing.then(resolve2, reject).finally(() => signal.removeEventListener("abort", abort));
11477
11651
  });
11478
11652
  }
11479
11653
  function unnamed(domain) {
@@ -12320,7 +12494,7 @@ var WasmProcess = class {
12320
12494
  if (this.stopped) return "stop";
12321
12495
  this.stopped = sig;
12322
12496
  this.stops++;
12323
- this.resumed = new Promise((resolve) => this.wake = resolve);
12497
+ this.resumed = new Promise((resolve2) => this.wake = resolve2);
12324
12498
  const blocked = this.interrupt;
12325
12499
  this.interrupt = new AbortController();
12326
12500
  blocked.abort();
@@ -12385,7 +12559,9 @@ var WasmProcess = class {
12385
12559
  const { ptys, jobs } = this.options;
12386
12560
  const owner = this.options.owners ? this.ttyOwner() : void 0;
12387
12561
  const guard2 = this.guarded();
12388
- return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner });
12562
+ const { ttyFilter } = this.options;
12563
+ const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner, ttyFilter };
12564
+ return ptySyscall(req, ctx);
12389
12565
  }
12390
12566
  return await this.procSyscall(req, cancelled);
12391
12567
  } catch (e) {
@@ -12560,7 +12736,8 @@ var WasmProcess = class {
12560
12736
  const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
12561
12737
  if (!tty) throw new KernelError("ENXIO");
12562
12738
  if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
12563
- return this.fds.install(tty.file(), 3);
12739
+ const file = tty.file();
12740
+ return this.fds.install(this.options.ttyFilter?.(file) ?? file, 3);
12564
12741
  }
12565
12742
  openSealed() {
12566
12743
  const broker = this.options.sealed;
@@ -12600,15 +12777,8 @@ var WasmProcess = class {
12600
12777
  async sealedCaller() {
12601
12778
  const shown = await this.options.identity?.();
12602
12779
  const broker = this.options.sealed;
12603
- const tag = broker.tagOf(this.pid);
12604
- return {
12605
- pid: shown?.pid ?? this.pid,
12606
- ppid: shown?.ppid ?? 0,
12607
- uid: this.cred.euid,
12608
- gid: this.cred.egid,
12609
- ...tag !== void 0 ? { tag } : {},
12610
- users: broker.users
12611
- };
12780
+ const ids = { pid: shown?.pid ?? this.pid, ppid: shown?.ppid ?? 0 };
12781
+ return broker.stamp(ids, this.cred, broker.tagOf(this.pid));
12612
12782
  }
12613
12783
  guarded() {
12614
12784
  const { guard: guard2 } = this.options;
@@ -13116,13 +13286,13 @@ var WasmProcess = class {
13116
13286
  while (this.dlLock.depth > 0 && this.dlLock.owner !== owner) {
13117
13287
  if (this.dying) throw new KernelError("EINTR");
13118
13288
  const interrupt = this.interrupt.signal;
13119
- const gone = await new Promise((resolve) => {
13289
+ const gone = await new Promise((resolve2) => {
13120
13290
  const waiter = {
13121
13291
  owner,
13122
13292
  wake: (ended) => {
13123
13293
  this.dlLock.waiters.delete(waiter);
13124
13294
  interrupt.removeEventListener("abort", recheck);
13125
- resolve(ended);
13295
+ resolve2(ended);
13126
13296
  }
13127
13297
  };
13128
13298
  const recheck = () => waiter.wake(false);
@@ -13378,6 +13548,7 @@ function processOptions(opts, header4, hooks, created3) {
13378
13548
  ...inherited(opts),
13379
13549
  ...opts.owners ? { owners: true } : {},
13380
13550
  ...opts.sealed ? { sealed: opts.sealed } : {},
13551
+ ...opts.ttyFilter ? { ttyFilter: opts.ttyFilter } : {},
13381
13552
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
13382
13553
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
13383
13554
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
@@ -13872,6 +14043,523 @@ function replayingHops(inner, heldMs = HELD_MS) {
13872
14043
  };
13873
14044
  }
13874
14045
 
14046
+ // node_modules/@ai-ecoverse/slicc-scrub/dist/index.js
14047
+ var FRAGMENT = 16;
14048
+ var MIN_VALUE = 8;
14049
+ var MIN_BASE64 = 8;
14050
+ var encoder4 = new TextEncoder();
14051
+ var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
14052
+ function utf82(text) {
14053
+ return encoder4.encode(text);
14054
+ }
14055
+ function base642(bytes, padded = true) {
14056
+ let out = "";
14057
+ for (let i = 0; i < bytes.length; i += 3) {
14058
+ const a = bytes[i];
14059
+ const b = i + 1 < bytes.length ? bytes[i + 1] : 0;
14060
+ const c = i + 2 < bytes.length ? bytes[i + 2] : 0;
14061
+ const triple = a << 16 | b << 8 | c;
14062
+ const left = bytes.length - i;
14063
+ out += STD[triple >> 18 & 63] + STD[triple >> 12 & 63];
14064
+ out += left > 1 ? STD[triple >> 6 & 63] : padded ? "=" : "";
14065
+ out += left > 2 ? STD[triple & 63] : padded ? "=" : "";
14066
+ }
14067
+ return out;
14068
+ }
14069
+ function urlSafe(text) {
14070
+ return text.replace(/\+/g, "-").replace(/\//g, "_");
14071
+ }
14072
+ function hex2(bytes) {
14073
+ let out = "";
14074
+ for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
14075
+ return out;
14076
+ }
14077
+ function embedded(bytes, offset) {
14078
+ const shifted = new Uint8Array(offset + bytes.length);
14079
+ shifted.set(bytes, offset);
14080
+ const encoded = base642(shifted);
14081
+ const first = Math.ceil(8 * offset / 6);
14082
+ const last = Math.floor(8 * (offset + bytes.length) / 6);
14083
+ return encoded.slice(first, last);
14084
+ }
14085
+ function unicode(value, upper) {
14086
+ let out = "";
14087
+ for (let i = 0; i < value.length; i++) {
14088
+ const unit = value.charCodeAt(i);
14089
+ if (unit === 34) out += '\\"';
14090
+ else if (unit === 92) out += "\\\\";
14091
+ else if (unit < 32 || unit > 126) {
14092
+ const code = unit.toString(16).padStart(4, "0");
14093
+ out += `\\u${upper ? code.toUpperCase() : code}`;
14094
+ } else out += value[i];
14095
+ }
14096
+ return out;
14097
+ }
14098
+ function percent(value) {
14099
+ let component;
14100
+ try {
14101
+ component = encodeURIComponent(value);
14102
+ } catch {
14103
+ return [];
14104
+ }
14105
+ const lower = component.replace(/%[0-9A-F]{2}/g, (escape) => escape.toLowerCase());
14106
+ const plus = component.replace(/%20/g, "+");
14107
+ let form = "";
14108
+ for (const byte of utf82(value)) {
14109
+ const char = String.fromCharCode(byte);
14110
+ if (/[A-Za-z0-9*\-._]/.test(char)) form += char;
14111
+ else if (byte === 32) form += "+";
14112
+ else form += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
14113
+ }
14114
+ return [component, lower, plus, form];
14115
+ }
14116
+ function html(value, apostrophe) {
14117
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, apostrophe);
14118
+ }
14119
+ function encodings(bytes) {
14120
+ const standard = [base642(bytes), base642(bytes, false)];
14121
+ for (const offset of [0, 1, 2]) {
14122
+ const run2 = embedded(bytes, offset);
14123
+ if (run2.length >= MIN_BASE64) standard.push(run2);
14124
+ }
14125
+ return [...standard, ...standard.map(urlSafe)];
14126
+ }
14127
+ function shapes(value) {
14128
+ const raw = utf82(value);
14129
+ const seen = /* @__PURE__ */ new Set([value]);
14130
+ const out = [];
14131
+ if (raw.length < FRAGMENT) out.push({ bytes: raw, form: "raw" });
14132
+ for (let offset = 0; raw.length >= FRAGMENT && offset + FRAGMENT <= raw.length; offset++) {
14133
+ out.push({ bytes: raw.subarray(offset, offset + FRAGMENT), form: "fragment", offset });
14134
+ }
14135
+ const add = (text, form) => {
14136
+ if (seen.has(text)) return;
14137
+ seen.add(text);
14138
+ out.push({ bytes: utf82(text), form });
14139
+ };
14140
+ const json5 = JSON.stringify(value).slice(1, -1);
14141
+ for (const text of [
14142
+ json5,
14143
+ json5.replace(/\//g, "\\/"),
14144
+ unicode(value, false),
14145
+ unicode(value, true)
14146
+ ]) {
14147
+ add(text, "json");
14148
+ }
14149
+ for (const text of percent(value)) add(text, "percent");
14150
+ for (const text of [html(value, "&#39;"), html(value, "&#x27;")]) add(text, "html");
14151
+ for (const text of encodings(raw)) add(text, "base64");
14152
+ add(hex2(raw), "hex");
14153
+ add(hex2(raw).toUpperCase(), "hex");
14154
+ return out;
14155
+ }
14156
+ function wrappable(form) {
14157
+ return form === "base64" || form === "hex";
14158
+ }
14159
+ var Automaton = class {
14160
+ edges = /* @__PURE__ */ new Map();
14161
+ fail = [0];
14162
+ depth = [0];
14163
+ children = [0];
14164
+ out = [[]];
14165
+ dict = [-1];
14166
+ lengths = [];
14167
+ add(bytes) {
14168
+ let node = 0;
14169
+ for (const byte of bytes) {
14170
+ const key = node * 256 + byte;
14171
+ let next2 = this.edges.get(key);
14172
+ if (next2 === void 0) {
14173
+ next2 = this.depth.length;
14174
+ this.edges.set(key, next2);
14175
+ this.fail.push(0);
14176
+ this.depth.push(this.depth[node] + 1);
14177
+ this.children.push(0);
14178
+ this.children[node] += 1;
14179
+ this.out.push([]);
14180
+ this.dict.push(-1);
14181
+ }
14182
+ node = next2;
14183
+ }
14184
+ const id2 = this.lengths.length;
14185
+ this.lengths.push(bytes.length);
14186
+ this.out[node].push(id2);
14187
+ return id2;
14188
+ }
14189
+ build() {
14190
+ const queue = [];
14191
+ const byNode = /* @__PURE__ */ new Map();
14192
+ for (const [key, child] of this.edges) {
14193
+ const parent = Math.floor(key / 256);
14194
+ const list = byNode.get(parent) ?? [];
14195
+ list.push([key % 256, child]);
14196
+ byNode.set(parent, list);
14197
+ }
14198
+ for (const [, child] of byNode.get(0) ?? []) queue.push(child);
14199
+ for (let head = 0; head < queue.length; head++) {
14200
+ const node = queue[head];
14201
+ const fallback = this.fail[node];
14202
+ this.dict[node] = this.out[fallback].length > 0 ? fallback : this.dict[fallback];
14203
+ for (const [byte, child] of byNode.get(node) ?? []) {
14204
+ let state = fallback;
14205
+ let target = this.edges.get(state * 256 + byte);
14206
+ while (target === void 0 && state > 0) {
14207
+ state = this.fail[state];
14208
+ target = this.edges.get(state * 256 + byte);
14209
+ }
14210
+ this.fail[child] = target ?? 0;
14211
+ queue.push(child);
14212
+ }
14213
+ }
14214
+ }
14215
+ step(state, byte) {
14216
+ let current = state;
14217
+ for (; ; ) {
14218
+ const next2 = this.edges.get(current * 256 + byte);
14219
+ if (next2 !== void 0) return next2;
14220
+ if (current === 0) return 0;
14221
+ current = this.fail[current];
14222
+ }
14223
+ }
14224
+ matches(state, visit) {
14225
+ for (let node = state; node > 0; node = this.dict[node]) {
14226
+ for (const id2 of this.out[node]) visit(id2);
14227
+ }
14228
+ }
14229
+ partials(state, length2, visit) {
14230
+ for (let node = state; node > 0; node = this.fail[node]) {
14231
+ if (this.children[node] > 0) visit(length2 - this.depth[node]);
14232
+ }
14233
+ }
14234
+ };
14235
+ var RANK = {
14236
+ raw: 0,
14237
+ fragment: 1,
14238
+ json: 2,
14239
+ percent: 3,
14240
+ html: 4,
14241
+ base64: 5,
14242
+ hex: 6,
14243
+ wrapped: 7
14244
+ };
14245
+ var CR2 = 13;
14246
+ var LF2 = 10;
14247
+ var EMPTY2 = new Uint8Array(0);
14248
+ function register(automaton, sources, ids, bytes, source) {
14249
+ const key = Array.from(bytes).join(",");
14250
+ let id2 = ids.get(key);
14251
+ if (id2 === void 0) {
14252
+ id2 = automaton.add(bytes);
14253
+ ids.set(key, id2);
14254
+ sources[id2] = [];
14255
+ }
14256
+ sources[id2].push(source);
14257
+ }
14258
+ function prepare(secrets) {
14259
+ const main = new Automaton();
14260
+ const wrap = new Automaton();
14261
+ const sources = [];
14262
+ const wrapSources = [];
14263
+ const ids = /* @__PURE__ */ new Map();
14264
+ const wrapIds = /* @__PURE__ */ new Map();
14265
+ const values = secrets.map((secret) => utf82(secret.value));
14266
+ secrets.forEach((secret, index) => {
14267
+ if (typeof secret.value !== "string" || values[index].length < MIN_VALUE) {
14268
+ throw new RangeError(`the value of ${secret.name} is shorter than ${MIN_VALUE} bytes`);
14269
+ }
14270
+ for (const shape of shapes(secret.value)) {
14271
+ const source = { secret: index, form: shape.form, offset: shape.offset };
14272
+ register(main, sources, ids, shape.bytes, source);
14273
+ if (wrappable(shape.form)) register(wrap, wrapSources, wrapIds, shape.bytes, source);
14274
+ }
14275
+ });
14276
+ main.build();
14277
+ wrap.build();
14278
+ const replacements = secrets.map((secret) => [
14279
+ utf82(secret.placeholder),
14280
+ utf82(`[redacted:${secret.name}]`)
14281
+ ]);
14282
+ return { secrets, values, main, wrap, sources, wrapSources, replacements };
14283
+ }
14284
+ function better(a, b) {
14285
+ if (!b) return true;
14286
+ if (a.end !== b.end) return a.end > b.end;
14287
+ if (a.rank !== b.rank) return a.rank < b.rank;
14288
+ return a.secret < b.secret;
14289
+ }
14290
+ function resolve(c, input, start2, end, source) {
14291
+ if (source.form !== "fragment") {
14292
+ return {
14293
+ start: start2,
14294
+ end,
14295
+ secret: source.secret,
14296
+ form: source.form,
14297
+ rank: RANK[source.form],
14298
+ growing: false
14299
+ };
14300
+ }
14301
+ const value = c.values[source.secret];
14302
+ let stop = end;
14303
+ let at = source.offset + FRAGMENT;
14304
+ while (stop < input.length && at < value.length && input[stop] === value[at]) {
14305
+ stop++;
14306
+ at++;
14307
+ }
14308
+ const form = source.offset === 0 && at === value.length ? "raw" : "fragment";
14309
+ const growing = stop === input.length && at < value.length;
14310
+ return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
14311
+ }
14312
+ function keep(best, candidate) {
14313
+ if (better(candidate, best.get(candidate.start))) best.set(candidate.start, candidate);
14314
+ }
14315
+ function scanMain(c, input, best, partials) {
14316
+ let state = 0;
14317
+ for (let i = 0; i < input.length; i++) {
14318
+ state = c.main.step(state, input[i]);
14319
+ c.main.matches(state, (id2) => {
14320
+ const start2 = i + 1 - c.main.lengths[id2];
14321
+ for (const source of c.sources[id2]) {
14322
+ const candidate = resolve(c, input, start2, i + 1, source);
14323
+ if (candidate.growing) partials.push(candidate.start);
14324
+ keep(best, candidate);
14325
+ }
14326
+ });
14327
+ }
14328
+ c.main.partials(state, input.length, (start2) => partials.push(start2));
14329
+ }
14330
+ function scanWrapped(c, input, best, partials) {
14331
+ const map = [];
14332
+ for (let i = 0; i < input.length; i++) if (input[i] !== CR2 && input[i] !== LF2) map.push(i);
14333
+ if (map.length === input.length) return;
14334
+ let state = 0;
14335
+ for (let v = 0; v < map.length; v++) {
14336
+ state = c.wrap.step(state, input[map[v]]);
14337
+ c.wrap.matches(state, (id2) => {
14338
+ const first = v + 1 - c.wrap.lengths[id2];
14339
+ const start2 = map[first];
14340
+ const end = map[v] + 1;
14341
+ const wrapped = end - start2 !== c.wrap.lengths[id2];
14342
+ for (const source of c.wrapSources[id2]) {
14343
+ const form = wrapped ? "wrapped" : source.form;
14344
+ keep(best, { start: start2, end, secret: source.secret, form, rank: RANK[form], growing: false });
14345
+ }
14346
+ });
14347
+ }
14348
+ c.wrap.partials(state, map.length, (start2) => partials.push(map[start2]));
14349
+ }
14350
+ function count(c, hits, candidate) {
14351
+ const name2 = c.secrets[candidate.secret].name;
14352
+ const key = `${name2}\0${candidate.form}`;
14353
+ const hit = hits.get(key) ?? { name: name2, form: candidate.form, count: 0 };
14354
+ hit.count += 1;
14355
+ hits.set(key, hit);
14356
+ }
14357
+ function concat5(parts2) {
14358
+ let length2 = 0;
14359
+ for (const part of parts2) length2 += part.length;
14360
+ const out = new Uint8Array(length2);
14361
+ let at = 0;
14362
+ for (const part of parts2) {
14363
+ out.set(part, at);
14364
+ at += part.length;
14365
+ }
14366
+ return out;
14367
+ }
14368
+ function pass(c, input, final) {
14369
+ const best = /* @__PURE__ */ new Map();
14370
+ const partials = [];
14371
+ scanMain(c, input, best, partials);
14372
+ scanWrapped(c, input, best, partials);
14373
+ const holds = final ? [] : [...new Set(partials)].sort((a, b) => a - b);
14374
+ const starts = [...best.keys()].sort((a, b) => a - b);
14375
+ const parts2 = [];
14376
+ const hits = /* @__PURE__ */ new Map();
14377
+ let pos = 0;
14378
+ let cut = input.length;
14379
+ let s = 0;
14380
+ let h = 0;
14381
+ for (; ; ) {
14382
+ while (s < starts.length && starts[s] < pos) s++;
14383
+ while (h < holds.length && holds[h] < pos) h++;
14384
+ const hold = h < holds.length ? holds[h] : Infinity;
14385
+ const next2 = s < starts.length ? starts[s] : Infinity;
14386
+ if (hold <= next2) {
14387
+ cut = Math.min(hold, input.length);
14388
+ break;
14389
+ }
14390
+ const candidate = best.get(next2);
14391
+ parts2.push(
14392
+ input.subarray(pos, next2),
14393
+ c.replacements[candidate.secret][candidate.form === "raw" ? 0 : 1]
14394
+ );
14395
+ count(c, hits, candidate);
14396
+ pos = candidate.end;
14397
+ }
14398
+ parts2.push(input.subarray(pos, cut));
14399
+ return { output: concat5(parts2), cut, hits };
14400
+ }
14401
+ function merge(into, from) {
14402
+ for (const [key, hit] of from) {
14403
+ const known = into.get(key);
14404
+ if (known) known.count += hit.count;
14405
+ else into.set(key, { ...hit });
14406
+ }
14407
+ }
14408
+ function compile(secrets, options = {}) {
14409
+ const c = prepare(secrets.map((secret) => ({ ...secret })));
14410
+ const decoder4 = new TextDecoder();
14411
+ const idleMs = options.idleMs ?? 250;
14412
+ const scrubBytes = (input) => {
14413
+ if (c.secrets.length === 0) return { bytes: input.slice(), hits: [] };
14414
+ const result = pass(c, input, true);
14415
+ return { bytes: result.output, hits: [...result.hits.values()] };
14416
+ };
14417
+ return {
14418
+ version: options.version ?? 0,
14419
+ idleMs,
14420
+ size: c.main.lengths.length,
14421
+ bytes: scrubBytes,
14422
+ text(input) {
14423
+ const { bytes, hits } = scrubBytes(utf82(input));
14424
+ return { text: decoder4.decode(bytes), hits };
14425
+ },
14426
+ stream() {
14427
+ let pending = EMPTY2;
14428
+ const hits = /* @__PURE__ */ new Map();
14429
+ const run2 = (final) => {
14430
+ if (c.secrets.length === 0 || pending.length === 0) {
14431
+ const out = pending;
14432
+ pending = EMPTY2;
14433
+ return out;
14434
+ }
14435
+ const result = pass(c, pending, final);
14436
+ merge(hits, result.hits);
14437
+ pending = pending.slice(result.cut);
14438
+ return result.output;
14439
+ };
14440
+ return {
14441
+ push(chunk2) {
14442
+ pending = concat5([pending, chunk2]);
14443
+ return run2(false);
14444
+ },
14445
+ idle() {
14446
+ return pending.length < FRAGMENT ? run2(true) : EMPTY2;
14447
+ },
14448
+ end() {
14449
+ return run2(true);
14450
+ },
14451
+ get hits() {
14452
+ return [...hits.values()];
14453
+ }
14454
+ };
14455
+ }
14456
+ };
14457
+ }
14458
+
14459
+ // src/kernel/scrub-file.ts
14460
+ var SCRUB_IDLE_MS = 50;
14461
+ var encoder5 = new TextEncoder();
14462
+ function scrubberOf(secrets) {
14463
+ const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
14464
+ return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
14465
+ }
14466
+ async function writeAll2(file, bytes, signal) {
14467
+ const write = file.write;
14468
+ for (let at = 0; at < bytes.length; ) {
14469
+ at += await write.call(file, bytes.subarray(at), at === 0 ? signal : void 0);
14470
+ }
14471
+ }
14472
+ function bound(file, key) {
14473
+ const method = file[key];
14474
+ return typeof method === "function" ? { [key]: method.bind(file) } : {};
14475
+ }
14476
+ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
14477
+ const file = inner.file;
14478
+ const stream2 = scrubber.stream();
14479
+ let queue = Promise.resolve();
14480
+ let timer;
14481
+ let failed3;
14482
+ const raise = () => {
14483
+ if (failed3 === void 0) return;
14484
+ const err = failed3;
14485
+ failed3 = void 0;
14486
+ throw err;
14487
+ };
14488
+ const enqueue = (bytes, signal) => {
14489
+ const next2 = queue.then(() => bytes.length > 0 ? writeAll2(file, bytes, signal) : void 0);
14490
+ queue = next2.catch(() => void 0);
14491
+ return next2;
14492
+ };
14493
+ const release = () => {
14494
+ timer = void 0;
14495
+ enqueue(stream2.idle()).catch((err) => {
14496
+ failed3 = err;
14497
+ });
14498
+ };
14499
+ return new OpenFile({
14500
+ ...bound(file, "read"),
14501
+ ...bound(file, "peek"),
14502
+ ...bound(file, "poll"),
14503
+ ...bound(file, "changed"),
14504
+ ...bound(file, "stat"),
14505
+ ...file.tty ? { tty: file.tty } : {},
14506
+ ...file.clipboard ? { clipboard: file.clipboard } : {},
14507
+ ...file.write ? {
14508
+ write: async (bytes, signal) => {
14509
+ if (timer !== void 0) clearTimeout(timer);
14510
+ raise();
14511
+ await enqueue(stream2.push(bytes), signal);
14512
+ timer = setTimeout(release, idleMs);
14513
+ return bytes.length;
14514
+ }
14515
+ } : {},
14516
+ flush: async () => {
14517
+ await queue;
14518
+ raise();
14519
+ await file.flush?.();
14520
+ },
14521
+ close: async () => {
14522
+ if (timer !== void 0) clearTimeout(timer);
14523
+ let failure;
14524
+ try {
14525
+ await enqueue(stream2.end());
14526
+ raise();
14527
+ } catch (err) {
14528
+ failure = { err };
14529
+ }
14530
+ await inner.release();
14531
+ if (failure) throw failure.err;
14532
+ }
14533
+ });
14534
+ }
14535
+ function scrubbedAll(file, scrubbers) {
14536
+ return scrubbers.reduce((inner, scrubber) => scrubbed(inner, scrubber), file);
14537
+ }
14538
+ function stdioFiles(fds) {
14539
+ const files = /* @__PURE__ */ new Set();
14540
+ for (const fd of [1, 2]) {
14541
+ try {
14542
+ files.add(fds.get(fd));
14543
+ } catch {
14544
+ }
14545
+ }
14546
+ return files;
14547
+ }
14548
+ function scrubStdio(fds, scrubber) {
14549
+ const wrapped = /* @__PURE__ */ new Map();
14550
+ const stdio = stdioFiles(fds);
14551
+ for (const fd of fds.numbers()) {
14552
+ const original = fds.get(fd);
14553
+ if (!stdio.has(original) && !original.file.tty) continue;
14554
+ const known = wrapped.get(original);
14555
+ const flags2 = fds.statusFlags(fd);
14556
+ const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
14557
+ wrapped.set(original, wrapper);
14558
+ fds.installAt(fd, wrapper);
14559
+ if (flags2 !== void 0) fds.setStatusFlags(fd, flags2);
14560
+ }
14561
+ }
14562
+
13875
14563
  // src/kernel/sealed.ts
13876
14564
  var SEALED_MAX_BYTES = 64 * 1024;
13877
14565
  var SEALED_TIMEOUT_MS = 1e4;
@@ -13885,7 +14573,7 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
13885
14573
  "cancel",
13886
14574
  "materialize"
13887
14575
  ]);
13888
- var encoder4 = new TextEncoder();
14576
+ var encoder6 = new TextEncoder();
13889
14577
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
13890
14578
  function parseSealed(bytes) {
13891
14579
  try {
@@ -13901,11 +14589,16 @@ function parseSealed(bytes) {
13901
14589
  const req = parsed2;
13902
14590
  if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
13903
14591
  throw new KernelError("EINVAL");
14592
+ if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
13904
14593
  return req;
13905
14594
  } finally {
13906
14595
  bytes.fill(0);
13907
14596
  }
13908
14597
  }
14598
+ function materializable(req) {
14599
+ if (req.shellGrants !== true && !isStrings(req.names)) return false;
14600
+ return isStrings(req.argv) && typeof req.path === "string" && typeof req.cwd === "string";
14601
+ }
13909
14602
  function errorReply(code, message3) {
13910
14603
  return { v: 1, error: { code, message: message3 } };
13911
14604
  }
@@ -13949,12 +14642,14 @@ var SealedChannel = class {
13949
14642
  const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
13950
14643
  const req = { ...fields, v: 1, op: fields.op, caller };
13951
14644
  const flight = { control, done: Promise.resolve() };
13952
- flight.done = this.answer(req, control.signal).then((reply) => {
14645
+ flight.done = this.answer(req, control.signal).then(async (reply) => {
13953
14646
  clearTimeout(timer);
13954
14647
  gone.removeEventListener("abort", withdraw);
14648
+ if (reply && req.op === "materialize")
14649
+ await this.broker.tickets.record(req, reply, caller.pid);
13955
14650
  if (this.inflight !== flight) return;
13956
14651
  this.inflight = void 0;
13957
- if (reply) this.reply = encoder4.encode(serialized(reply));
14652
+ if (reply) this.reply = encoder6.encode(serialized(reply));
13958
14653
  });
13959
14654
  this.inflight = flight;
13960
14655
  return length2;
@@ -13962,8 +14657,8 @@ var SealedChannel = class {
13962
14657
  async answer(req, signal) {
13963
14658
  const hook = this.broker.hook;
13964
14659
  if (!hook) return errorReply("ENOTCONN", "no vault is connected");
13965
- const stopped = new Promise((resolve) => {
13966
- signal.addEventListener("abort", () => resolve(void 0), { once: true });
14660
+ const stopped = new Promise((resolve2) => {
14661
+ signal.addEventListener("abort", () => resolve2(void 0), { once: true });
13967
14662
  });
13968
14663
  if (signal.aborted) return void 0;
13969
14664
  const asked = Promise.resolve().then(() => hook(req, { signal })).then(
@@ -14018,17 +14713,60 @@ var SealedChannel = class {
14018
14713
  this.reply = void 0;
14019
14714
  }
14020
14715
  };
14716
+ function swapOf(reply) {
14717
+ if (!isRecord(reply) || reply.error !== void 0) return void 0;
14718
+ const values = stringRecord(reply.values);
14719
+ const placeholders = stringRecord(reply.placeholders);
14720
+ return values && placeholders ? { values, placeholders } : void 0;
14721
+ }
14021
14722
  var SealedBroker = class {
14022
14723
  hook;
14023
14724
  users;
14024
14725
  timeoutMs;
14025
14726
  tagOf;
14727
+ tickets = new TicketBook();
14728
+ userOf = async () => void 0;
14026
14729
  constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
14027
14730
  this.hook = hook;
14028
14731
  this.users = users;
14029
14732
  this.timeoutMs = timeoutMs;
14030
14733
  this.tagOf = tagOf;
14031
14734
  }
14735
+ async stamp(ids, cred, tag) {
14736
+ const user = await this.userOf(cred.euid).catch(() => void 0);
14737
+ return {
14738
+ pid: ids.pid,
14739
+ ppid: ids.ppid,
14740
+ uid: cred.euid,
14741
+ gid: cred.egid,
14742
+ ...user !== void 0 ? { user } : {},
14743
+ groups: [...cred.groups],
14744
+ users: this.users,
14745
+ ...tag !== void 0 ? { tag } : {}
14746
+ };
14747
+ }
14748
+ async consume(fields) {
14749
+ const { hook } = this;
14750
+ if (!hook) return void 0;
14751
+ const control = new AbortController();
14752
+ const timer = setTimeout(() => control.abort("timeout"), this.timeoutMs);
14753
+ const stopped = new Promise((resolve2) => {
14754
+ control.signal.addEventListener("abort", () => resolve2(void 0), { once: true });
14755
+ });
14756
+ const req = {
14757
+ ...fields,
14758
+ v: 1,
14759
+ op: "materializeConsume",
14760
+ caller: fields.stamp
14761
+ };
14762
+ try {
14763
+ const asked = Promise.resolve().then(() => hook(req, { signal: control.signal })).then(swapOf, () => void 0);
14764
+ return await Promise.race([asked, stopped]);
14765
+ } finally {
14766
+ clearTimeout(timer);
14767
+ control.abort("done");
14768
+ }
14769
+ }
14032
14770
  open() {
14033
14771
  const channel = new SealedChannel(this);
14034
14772
  return new OpenFile({
@@ -14527,11 +15265,11 @@ function codeOf3(err) {
14527
15265
  }
14528
15266
  function wait(ms, signal) {
14529
15267
  if (signal.aborted) return Promise.resolve();
14530
- return new Promise((resolve) => {
15268
+ return new Promise((resolve2) => {
14531
15269
  const done4 = () => {
14532
15270
  clearTimeout(timer);
14533
15271
  signal.removeEventListener("abort", done4);
14534
- resolve();
15272
+ resolve2();
14535
15273
  };
14536
15274
  const timer = setTimeout(done4, ms);
14537
15275
  signal.addEventListener("abort", done4, { once: true });
@@ -14858,11 +15596,11 @@ async function resize(h, path, size) {
14858
15596
  }
14859
15597
  }
14860
15598
  function sleep(ms, signal) {
14861
- return new Promise((resolve) => {
15599
+ return new Promise((resolve2) => {
14862
15600
  const done4 = () => {
14863
15601
  clearTimeout(timer);
14864
15602
  signal.removeEventListener("abort", done4);
14865
- resolve();
15603
+ resolve2();
14866
15604
  };
14867
15605
  const timer = setTimeout(done4, ms);
14868
15606
  signal.addEventListener("abort", done4);
@@ -14945,7 +15683,7 @@ var PORT_DEAD_MS = 1e4;
14945
15683
  var PORT_LINGER_MS = 1e3;
14946
15684
  function timed(transport, ms) {
14947
15685
  const inflight = /* @__PURE__ */ new Set();
14948
- const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
15686
+ const guard2 = (name2, run2, late, expire) => new Promise((resolve2, reject) => {
14949
15687
  let expired = false;
14950
15688
  const running = run2();
14951
15689
  inflight.add(running);
@@ -14962,7 +15700,7 @@ function timed(transport, ms) {
14962
15700
  (value) => {
14963
15701
  clearTimeout(timer);
14964
15702
  if (expired) late?.(value);
14965
- resolve(value);
15703
+ resolve2(value);
14966
15704
  },
14967
15705
  (err) => {
14968
15706
  clearTimeout(timer);
@@ -15097,7 +15835,7 @@ var PortTransport = class {
15097
15835
  ask(message3, bytes = 0, transfer = []) {
15098
15836
  const id2 = ++this.next;
15099
15837
  if (this.dead) return Promise.reject(fsError2("ENOTCONN", this.dead));
15100
- return new Promise((resolve, reject) => {
15838
+ return new Promise((resolve2, reject) => {
15101
15839
  const timer = setTimeout(
15102
15840
  () => {
15103
15841
  this.waiting.delete(id2);
@@ -15111,7 +15849,7 @@ var PortTransport = class {
15111
15849
  clearTimeout(timer);
15112
15850
  this.waiting.delete(id2);
15113
15851
  if (reply.error) reject(fsError2(reply.error.code ?? "EIO", reply.error.message ?? ""));
15114
- else resolve(reply);
15852
+ else resolve2(reply);
15115
15853
  });
15116
15854
  try {
15117
15855
  this.port.postMessage({ id: id2, ...message3 }, transfer);
@@ -15140,7 +15878,7 @@ var PortTransport = class {
15140
15878
  this.fail("the folder's watch ended");
15141
15879
  return Promise.resolve();
15142
15880
  }
15143
- return new Promise((resolve, reject) => {
15881
+ return new Promise((resolve2, reject) => {
15144
15882
  const finish = () => {
15145
15883
  this.waiting.delete(id2);
15146
15884
  signal.removeEventListener("abort", stop);
@@ -15151,7 +15889,7 @@ var PortTransport = class {
15151
15889
  this.port.postMessage({ id: id2, op: "unwatch" });
15152
15890
  } catch {
15153
15891
  }
15154
- resolve();
15892
+ resolve2();
15155
15893
  };
15156
15894
  this.watchBeat();
15157
15895
  this.waiting.set(id2, (reply) => {
@@ -15159,7 +15897,7 @@ var PortTransport = class {
15159
15897
  if (reply.event) return onEvent(reply.event);
15160
15898
  finish();
15161
15899
  if (reply.error) reject(fsError2(reply.error.code ?? "ENOTCONN", reply.error.message ?? ""));
15162
- else resolve();
15900
+ else resolve2();
15163
15901
  });
15164
15902
  signal.addEventListener("abort", stop, { once: true });
15165
15903
  try {
@@ -15375,10 +16113,10 @@ var ChunkedBytes = class {
15375
16113
  }
15376
16114
  truncate(size) {
15377
16115
  if (size < this.size) {
15378
- const keep = Math.ceil(size / CHUNK3);
15379
- this.chunks.length = Math.min(this.chunks.length, keep);
15380
- const tail = this.chunks[keep - 1];
15381
- const from = size - (keep - 1) * CHUNK3;
16116
+ const keep2 = Math.ceil(size / CHUNK3);
16117
+ this.chunks.length = Math.min(this.chunks.length, keep2);
16118
+ const tail = this.chunks[keep2 - 1];
16119
+ const from = size - (keep2 - 1) * CHUNK3;
15382
16120
  if (tail && from < tail.length) tail.fill(0, from);
15383
16121
  }
15384
16122
  this.size = size;
@@ -15623,8 +16361,8 @@ function importedMemory(bytes) {
15623
16361
  at = end;
15624
16362
  continue;
15625
16363
  }
15626
- const count = u32();
15627
- for (let i = 0; i < count; i++) {
16364
+ const count2 = u32();
16365
+ for (let i = 0; i < count2; i++) {
15628
16366
  const module = name2();
15629
16367
  const field3 = name2();
15630
16368
  const kind = bytes[at++];
@@ -16053,7 +16791,7 @@ ${lines2}
16053
16791
  const withdraw = () => control.abort("withdrawn");
16054
16792
  requester.addEventListener("abort", withdraw, { once: true });
16055
16793
  const aborted2 = new Promise(
16056
- (resolve) => control.signal.addEventListener("abort", () => resolve("aborted"), { once: true })
16794
+ (resolve2) => control.signal.addEventListener("abort", () => resolve2("aborted"), { once: true })
16057
16795
  );
16058
16796
  try {
16059
16797
  const answered = Promise.resolve().then(() => approve(req, { signal: control.signal })).catch(() => "deny");
@@ -16179,7 +16917,7 @@ var LAYOUT = [
16179
16917
  ];
16180
16918
  var LOAD_SAMPLE_MS = 5e3;
16181
16919
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
16182
- var encoder5 = new TextEncoder();
16920
+ var encoder7 = new TextEncoder();
16183
16921
  var USERS_MODES = ["off", "report", "enforce"];
16184
16922
  function usersMode(mode = "off") {
16185
16923
  if (USERS_MODES.includes(mode)) return mode;
@@ -16210,6 +16948,30 @@ function shebang(head) {
16210
16948
  if (head[0] !== 35 || head[1] !== 33) return void 0;
16211
16949
  return new TextDecoder().decode(head.subarray(0, SHEBANG_MAX)).slice(2).split("\n")[0].trim().split(/[ \t]+/);
16212
16950
  }
16951
+ async function rootOnly(fs, path) {
16952
+ const names = chain(path);
16953
+ for (const [i, at] of names.entries()) {
16954
+ const st = await fs.lstat(at).catch(() => void 0);
16955
+ const kind = i === names.length - 1 ? st?.isFile : st?.isDirectory;
16956
+ if (!kind || st?.uid !== 0 || (st.mode & 18) !== 0) return false;
16957
+ }
16958
+ return true;
16959
+ }
16960
+ function plainEnv(words2) {
16961
+ const next2 = words2[1];
16962
+ if (baseName(words2[0] ?? "") !== "env" || next2 === void 0) return false;
16963
+ return next2 === "-S" || !next2.startsWith("-") && !next2.includes("=");
16964
+ }
16965
+ function shebangCommand(words2) {
16966
+ if (!plainEnv(words2)) {
16967
+ const arg2 = words2.slice(1).join(" ");
16968
+ return { interp: words2[0], args: arg2 ? [arg2] : [] };
16969
+ }
16970
+ const split2 = words2[1] === "-S";
16971
+ const [interp, ...rest2] = words2.slice(split2 ? 2 : 1);
16972
+ const arg = rest2.join(" ");
16973
+ return { interp, args: split2 ? rest2 : arg ? [arg] : [] };
16974
+ }
16213
16975
  function interpreter(head) {
16214
16976
  const words2 = shebang(head);
16215
16977
  if (!words2) return "node";
@@ -16263,7 +17025,7 @@ function childHandle(handle) {
16263
17025
  onState: (listener) => handle.onState(listener)
16264
17026
  };
16265
17027
  }
16266
- function concat5(chunks2) {
17028
+ function concat6(chunks2) {
16267
17029
  const out = new Uint8Array(chunks2.reduce((n, chunk2) => n + chunk2.length, 0));
16268
17030
  let offset = 0;
16269
17031
  for (const chunk2 of chunks2) {
@@ -16295,6 +17057,13 @@ var Launcher = class {
16295
17057
  unwatchScopes = () => void 0;
16296
17058
  warned = /* @__PURE__ */ new Set();
16297
17059
  compiled = /* @__PURE__ */ new Map();
17060
+ texts = /* @__PURE__ */ new Map();
17061
+ integrity;
17062
+ scannedFs;
17063
+ get scanned() {
17064
+ this.scannedFs ??= this.verifiedFs(this.base);
17065
+ return this.scannedFs;
17066
+ }
16298
17067
  processes = /* @__PURE__ */ new Map();
16299
17068
  zombies = /* @__PURE__ */ new Set();
16300
17069
  orphans = /* @__PURE__ */ new Set();
@@ -16342,6 +17111,7 @@ var Launcher = class {
16342
17111
  usersMode;
16343
17112
  dac;
16344
17113
  sealed;
17114
+ scrubs = /* @__PURE__ */ new Map();
16345
17115
  clipboard;
16346
17116
  rootFs;
16347
17117
  fileWatches;
@@ -16365,6 +17135,8 @@ var Launcher = class {
16365
17135
  void 0,
16366
17136
  (pid) => this.tagOf(pid)
16367
17137
  );
17138
+ this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
17139
+ this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
16368
17140
  if (this.owners) options.fs.trackOwners?.();
16369
17141
  this.mounts = new MountTable({
16370
17142
  open: (type, spec) => this.driver(type, spec),
@@ -16398,7 +17170,7 @@ var Launcher = class {
16398
17170
  if (options.uplink) this.net.useUplink({ uplink: options.uplink, routes: this.routes });
16399
17171
  enableNetwork(this.net, {
16400
17172
  transport: this.transport,
16401
- engine: kernelTlsEngine(packageTlsEngine(options.fs, this.modulesDir)),
17173
+ engine: kernelTlsEngine(packageTlsEngine(this.verifiedFs(options.fs), this.modulesDir)),
16402
17174
  ca: this.ca
16403
17175
  });
16404
17176
  enableCdp(this.net, this.cdp);
@@ -16433,6 +17205,16 @@ var Launcher = class {
16433
17205
  this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
16434
17206
  this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
16435
17207
  options.fs.protect?.(this.prefixes);
17208
+ const integrity = options.protect?.integrity;
17209
+ integrityOptions(integrity);
17210
+ if (integrity && this.prefixes.length > 0) {
17211
+ this.integrity = new Integrity(
17212
+ this.prefixes,
17213
+ options.fs,
17214
+ integrity,
17215
+ (event) => options.onDenial?.(event)
17216
+ );
17217
+ }
16436
17218
  this.users = new UserDb(this.fs, this.owners);
16437
17219
  this.sudoer = new Sudo({
16438
17220
  fs: this.fs,
@@ -16478,7 +17260,7 @@ var Launcher = class {
16478
17260
  commands(scope = "") {
16479
17261
  let found = this.catalogs.get(scope);
16480
17262
  if (!found) {
16481
- found = this.roots(scope).then((roots) => scanCommands(this.base, roots)).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
17263
+ found = this.roots(scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => commands.set(SUDO_COMMAND.name, SUDO_COMMAND));
16482
17264
  this.catalogs.set(scope, found);
16483
17265
  }
16484
17266
  return found;
@@ -16486,7 +17268,7 @@ var Launcher = class {
16486
17268
  ownCommands(scope) {
16487
17269
  let found = this.ownCatalogs.get(scope);
16488
17270
  if (!found) {
16489
- found = pnpmGlobalRoots(this.base, scope).then((roots) => scanCommands(this.base, roots)).then((commands) => new Set(commands.keys()));
17271
+ found = pnpmGlobalRoots(this.base, scope).then((roots) => scanCommands(this.scanned, roots)).then((commands) => new Set(commands.keys()));
16490
17272
  this.ownCatalogs.set(scope, found);
16491
17273
  }
16492
17274
  return found;
@@ -16495,7 +17277,7 @@ var Launcher = class {
16495
17277
  let found = this.binfmts.get(scope);
16496
17278
  if (!found) {
16497
17279
  found = this.roots(scope).then(
16498
- (roots) => scanBinfmts(this.base, roots, (message3) => {
17280
+ (roots) => scanBinfmts(this.scanned, roots, (message3) => {
16499
17281
  if (this.warned.has(message3)) return;
16500
17282
  this.warned.add(message3);
16501
17283
  console.warn(message3);
@@ -16603,21 +17385,26 @@ var Launcher = class {
16603
17385
  const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
16604
17386
  const words2 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
16605
17387
  if (!words2) return void 0;
16606
- if (baseName(words2[0]) === "env") words2.shift();
16607
- const [interp, ...rest2] = words2;
16608
- const arg = rest2.join(" ");
16609
- const passed = [...arg ? [arg] : [], command ? script : file, ...argv.slice(1)];
16610
- const found = interp ? await this.resolve(interp, interp, cwd, scope) : void 0;
17388
+ const { interp, args } = shebangCommand(words2);
17389
+ const passed = [...args, command ? script : file, ...argv.slice(1)];
17390
+ if (!interp) return void 0;
17391
+ const found = await this.resolve(interp, interp, cwd, scope);
16611
17392
  if (!found) {
16612
- if (!interp || depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
17393
+ if (depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
16613
17394
  return void 0;
16614
17395
  }
16615
17396
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
16616
17397
  const via = [interp, ...inner?.via ?? []];
16617
- return inner && { ...inner, target: withScriptEnv(inner.target, command), via };
17398
+ const sources = [script, ...inner?.sources ?? []];
17399
+ return inner && { ...inner, target: withScriptEnv(inner.target, command), via, sources };
16618
17400
  }
16619
17401
  const target = withScriptEnv(found, command);
16620
- return { target, args: [...target.prefix ?? [], ...passed], via: [interp] };
17402
+ return {
17403
+ target,
17404
+ args: [...target.prefix ?? [], ...passed],
17405
+ via: [interp],
17406
+ sources: [script]
17407
+ };
16621
17408
  }
16622
17409
  async plan(file, argv, cwd, scope, argv0) {
16623
17410
  const found = await this.resolve(file, argv[0] ?? file, cwd, scope);
@@ -16643,13 +17430,106 @@ var Launcher = class {
16643
17430
  await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
16644
17431
  }
16645
17432
  }
17433
+ async checkedPlan(req, { ppid, cred, scope }, fds) {
17434
+ const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
17435
+ try {
17436
+ await this.mayExec(cred, { ...req, pid: ppid }, planned);
17437
+ } catch (err) {
17438
+ await fds.closeAll();
17439
+ throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
17440
+ }
17441
+ if (planned) return planned;
17442
+ await fds.closeAll();
17443
+ throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
17444
+ }
17445
+ async ticketFile(req, fds) {
17446
+ if (req.file.startsWith("/")) return physicalPathAsync(this.base, req.file);
17447
+ await fds.closeAll();
17448
+ throw new SpawnError("EACCES");
17449
+ }
17450
+ takeTicket(req) {
17451
+ if (!(TICKET_ENV in req.env)) return void 0;
17452
+ return this.sealed.tickets.take(req.env[TICKET_ENV] ?? "") ?? null;
17453
+ }
17454
+ async redeemed(req, who, planned, fds) {
17455
+ if (who.booked === void 0) return { env: req.env };
17456
+ try {
17457
+ const redeemed = await this.redeem(req, who, planned);
17458
+ if (redeemed.scrub) scrubStdio(fds, redeemed.scrub);
17459
+ return redeemed;
17460
+ } catch {
17461
+ await fds.closeAll();
17462
+ throw new SpawnError("EACCES");
17463
+ }
17464
+ }
17465
+ async redeem(req, { ppid, cred, scope, booked }, planned) {
17466
+ const { [TICKET_ENV]: ticket = "", ...env } = req.env;
17467
+ const caller = this.jobs.shown(ppid);
17468
+ const named = typeof req.argv0 === "string" && !planned.script && !planned.target.fixed;
17469
+ const argv = named ? [req.argv0, ...req.argv.slice(1)] : req.argv;
17470
+ const exact = booked?.pid === caller && req.file === booked.path && await physicalPathAsync(this.base, req.cwd) === booked.cwd && await argvDigest(argv) === booked.argv;
17471
+ if (!exact) throw new SpawnError("EACCES");
17472
+ const { requested: path, requestedCwd: cwd, path: resolved2 } = booked;
17473
+ const pid = this.nextPid++;
17474
+ const ids = { pid: req.exec ? caller : pid, ppid: caller };
17475
+ const stamp = await this.sealed.stamp(ids, cred, this.tagOf(ppid));
17476
+ const interpreters = await this.interpreterChain(planned, booked.cwd, scope);
17477
+ const trusted = this.usersMode === "enforce" && await this.trustedFiles(planned, req.file, booked.cwd, scope);
17478
+ const swap = await this.sealed.consume({
17479
+ ticket,
17480
+ stamp,
17481
+ path,
17482
+ resolved: resolved2,
17483
+ argv,
17484
+ cwd,
17485
+ interpreters,
17486
+ trusted
17487
+ });
17488
+ if (!swap) throw new SpawnError("EACCES");
17489
+ const secrets = [];
17490
+ for (const [name2, placeholder] of Object.entries(swap.placeholders)) {
17491
+ const value = swap.values[name2];
17492
+ if (value === void 0 || env[name2] !== placeholder) continue;
17493
+ env[name2] = value;
17494
+ secrets.push({ name: name2, value, placeholder });
17495
+ }
17496
+ const scrub = scrubberOf(secrets);
17497
+ return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
17498
+ }
17499
+ async interpreterChain(planned, cwd, scope) {
17500
+ const out = [];
17501
+ for (const entry of planned.via ?? []) {
17502
+ if (entry.includes("/")) {
17503
+ out.push(await physicalPathAsync(this.base, this.fs.resolvePath(cwd, entry)));
17504
+ continue;
17505
+ }
17506
+ const script = (await this.scriptCommand(entry, cwd, scope))?.script;
17507
+ out.push(
17508
+ await physicalPathAsync(this.base, script ?? await this.commandFile(entry, cwd, scope))
17509
+ );
17510
+ }
17511
+ return out;
17512
+ }
17513
+ async commandFile(name2, cwd, scope) {
17514
+ const scoped = await this.resolve(name2, name2, cwd, scope);
17515
+ const system = scope === "" ? scoped : await this.resolve(name2, name2, cwd, "");
17516
+ if (!scoped || system?.glue === scoped.glue) return `/usr/bin/${name2}`;
17517
+ return scoped.glue;
17518
+ }
17519
+ async trustedFiles(planned, file, cwd, scope) {
17520
+ const files = await this.backingOf(planned, file, cwd, scope);
17521
+ for (const at of files) {
17522
+ if (!await rootOnly(this.base, await physicalPathAsync(this.base, at))) return false;
17523
+ }
17524
+ return true;
17525
+ }
16646
17526
  async grantedCwd(caller, cwd) {
16647
17527
  const real = await physicalPathAsync(this.base, cwd).catch(() => void 0);
16648
17528
  return real === cwd && await this.sudoer.reachable(caller, cwd);
16649
17529
  }
16650
17530
  async binfmtManifests(scripts) {
16651
17531
  const sources = /* @__PURE__ */ new Map();
16652
- const binfmts = await scanBinfmts(this.base, await this.roots(), void 0, sources);
17532
+ const binfmts = await scanBinfmts(this.scanned, await this.roots(), void 0, sources);
16653
17533
  const manifests = [];
16654
17534
  for (const script of scripts) {
16655
17535
  const head = await this.head(script);
@@ -16661,13 +17541,15 @@ var Launcher = class {
16661
17541
  }
16662
17542
  async backing(file, cwd) {
16663
17543
  const planned = await this.plan(file, [file], cwd, "");
16664
- if (!planned) return void 0;
17544
+ return planned && this.backingOf(planned, file, cwd, "");
17545
+ }
17546
+ async backingOf(planned, file, cwd, scope) {
16665
17547
  const { glue, wasm, imports } = planned.target;
16666
17548
  const files = [glue, wasm, ...imports ? [imports] : []];
16667
17549
  const scripts = [];
16668
17550
  for (const name2 of [file, ...planned.via ?? []]) {
16669
17551
  if (name2.includes("/") && !COMMAND2.test(name2)) scripts.push(this.fs.resolvePath(cwd, name2));
16670
- const script = (await this.scriptCommand(name2, cwd, ""))?.script;
17552
+ const script = (await this.scriptCommand(name2, cwd, scope))?.script;
16671
17553
  if (script) scripts.push(script);
16672
17554
  }
16673
17555
  files.push(...scripts, ...await this.binfmtManifests(scripts));
@@ -16684,25 +17566,72 @@ var Launcher = class {
16684
17566
  if (!head || head[0] === 35 && head[1] === 33) return "ENOENT";
16685
17567
  return binfmtOf(await this.interpreters(scope), path) ? "ENOENT" : "ENOEXEC";
16686
17568
  }
16687
- module(path) {
16688
- return this.base.stat(path).then((st) => {
16689
- const key = `${path}:${st.size}:${st.mtime.getTime()}`;
16690
- let module = this.compiled.get(key);
16691
- if (!module) {
16692
- module = this.base.readFileBuffer(path).then(async (bytes) => {
16693
- const memory = importedMemory(bytes);
16694
- const foreign = foreignImports(bytes);
16695
- const compiled = await WebAssembly.compile(bytes);
16696
- return {
16697
- module: compiled,
16698
- ...memory ? { memory } : {},
16699
- foreign
16700
- };
16701
- });
16702
- this.compiled.set(key, module);
16703
- module.catch(() => this.compiled.delete(key));
16704
- }
16705
- return module;
17569
+ verifiedFs(fs) {
17570
+ const read = (path) => this.verified(path, KERNEL_CALLER, fs);
17571
+ return Object.assign(Object.create(fs), {
17572
+ readFileBuffer: read,
17573
+ readFile: (path) => this.integrity ? this.text(path, KERNEL_CALLER) : fs.readFile(path)
17574
+ });
17575
+ }
17576
+ async cacheKey(path) {
17577
+ const verified = await this.integrity?.key(path);
17578
+ if (verified !== void 0) return verified;
17579
+ const st = await this.base.stat(path);
17580
+ return `${path}:${st.size}:${st.mtime.getTime()}`;
17581
+ }
17582
+ verified(path, caller, fs = this.base) {
17583
+ const plain = () => fs.readFileBuffer(path);
17584
+ return this.integrity ? this.integrity.read(path, caller, plain) : plain();
17585
+ }
17586
+ keyed(path, caller) {
17587
+ const plain = async () => this.base.readFileBuffer(path);
17588
+ return this.integrity ? this.integrity.readKeyed(path, caller, plain) : plain().then((bytes) => ({ bytes }));
17589
+ }
17590
+ cached(cache, key, load) {
17591
+ const hit = cache.get(key);
17592
+ if (hit !== void 0) return hit;
17593
+ const loading2 = load();
17594
+ const found = loading2.then(({ value }) => value);
17595
+ cache.set(key, found);
17596
+ loading2.then(
17597
+ (done4) => {
17598
+ if (done4.key === key || done4.key === void 0 && !key.includes("\0")) return;
17599
+ if (cache.get(key) === found) cache.delete(key);
17600
+ if (done4.key !== void 0) cache.set(done4.key, found);
17601
+ },
17602
+ () => cache.delete(key)
17603
+ );
17604
+ return found;
17605
+ }
17606
+ async module(path, caller) {
17607
+ const key = await this.cacheKey(path);
17608
+ return this.cached(this.compiled, key, async () => {
17609
+ const { bytes, key: read } = await this.keyed(path, caller);
17610
+ const memory = importedMemory(bytes);
17611
+ const foreign = foreignImports(bytes);
17612
+ const compiled = await WebAssembly.compile(bytes);
17613
+ return { value: { module: compiled, ...memory ? { memory } : {}, foreign }, key: read };
17614
+ });
17615
+ }
17616
+ async checkedSources(planned, caller) {
17617
+ const { target } = planned;
17618
+ const files = [target.wasm, target.glue, target.imports, ...planned.sources ?? []];
17619
+ const manifests = /* @__PURE__ */ new Set();
17620
+ for (const file of files) {
17621
+ const root = file === void 0 ? void 0 : PACKAGE_ROOT.exec(file)?.[1];
17622
+ if (root !== void 0) manifests.add(`${root}/package.json`);
17623
+ }
17624
+ for (const source of planned.sources ?? []) await this.text(source, caller);
17625
+ for (const manifest of manifests) {
17626
+ if (await this.base.exists(manifest)) await this.text(manifest, caller);
17627
+ }
17628
+ }
17629
+ async text(path, caller) {
17630
+ if (!this.integrity) return this.base.readFile(path);
17631
+ const key = await this.cacheKey(path);
17632
+ return this.cached(this.texts, key, async () => {
17633
+ const { bytes, key: read } = await this.keyed(path, caller);
17634
+ return { value: new TextDecoder().decode(bytes), key: read };
16706
17635
  });
16707
17636
  }
16708
17637
  async names(path, module) {
@@ -16714,23 +17643,23 @@ var Launcher = class {
16714
17643
  const optional = `${root}-names${beside.slice(root.length)}`;
16715
17644
  return await this.base.exists(optional) ? optional : void 0;
16716
17645
  }
16717
- async program(target, env) {
17646
+ async program(target, env, caller) {
16718
17647
  if (target.abi === "js" && target.wasm === SUDO_PATH) {
16719
17648
  return { abi: "js", glue: SUDO_SOURCE, path: SUDO_PATH };
16720
17649
  }
16721
17650
  if (target.abi === "js") {
16722
- return { abi: "js", glue: await this.base.readFile(target.wasm), path: target.wasm };
17651
+ return { abi: "js", glue: await this.text(target.wasm, caller), path: target.wasm };
16723
17652
  }
16724
17653
  if (target.abi !== "wasi") {
16725
17654
  const [glue, { module: module2 }] = await Promise.all([
16726
- this.base.readFile(target.glue),
16727
- this.module(target.wasm)
17655
+ this.text(target.glue, caller),
17656
+ this.module(target.wasm, caller)
16728
17657
  ]);
16729
17658
  return { glue, module: module2 };
16730
17659
  }
16731
- const { module, memory, foreign } = await this.module(target.wasm);
17660
+ const { module, memory, foreign } = await this.module(target.wasm, caller);
16732
17661
  const names = env.SLICC_WASM_BACKTRACE === "1" ? await this.names(target.wasm, module) : void 0;
16733
- const imports = target.imports ? await this.base.readFile(target.imports) : void 0;
17662
+ const imports = target.imports ? await this.text(target.imports, caller) : void 0;
16734
17663
  return {
16735
17664
  abi: "wasi",
16736
17665
  glue: "",
@@ -16749,10 +17678,13 @@ var Launcher = class {
16749
17678
  const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
16750
17679
  const env = { ...expandDefaults(target.env, seen), ...req.env };
16751
17680
  for (const key of target.unset ?? []) delete env[key];
17681
+ delete env[TICKET_ENV];
16752
17682
  env.HOSTNAME = this.hostname;
16753
17683
  let program;
16754
17684
  try {
16755
- program = await this.program(target, env);
17685
+ const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tag };
17686
+ if (this.integrity) await this.checkedSources(planned, caller);
17687
+ program = await this.program(target, env, caller);
16756
17688
  } catch (err) {
16757
17689
  await req.fds.closeAll();
16758
17690
  throw err;
@@ -16762,16 +17694,23 @@ var Launcher = class {
16762
17694
  groupSession(pgid) {
16763
17695
  return this.jobs.sessionOf(pgid);
16764
17696
  }
17697
+ treeScrubs(pid, req) {
17698
+ const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
17699
+ const scrubs = req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
17700
+ if (scrubs) this.scrubs.set(pid, scrubs);
17701
+ return scrubs;
17702
+ }
16765
17703
  start(req) {
16766
17704
  this.sampling();
16767
17705
  if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
16768
17706
  throw new SpawnError("ESRCH");
16769
17707
  }
16770
- const pid = this.nextPid++;
17708
+ const pid = req.pid ?? this.nextPid++;
17709
+ const scrubs = this.treeScrubs(pid, req);
16771
17710
  const stack = this.stackFor(req.tag);
16772
17711
  const terminal = req.fds.stdioTerminal();
16773
17712
  const decided = req.decided ?? Promise.resolve(true);
16774
- const count = (byParent) => {
17713
+ const count2 = (byParent) => {
16775
17714
  this.identities.asked++;
16776
17715
  if (!byParent) this.identities.timedOut++;
16777
17716
  };
@@ -16779,7 +17718,7 @@ var Launcher = class {
16779
17718
  const handle = spawnWasmProcess({
16780
17719
  pid,
16781
17720
  identity: async () => {
16782
- await (asked ??= decided.then(count));
17721
+ await (asked ??= decided.then(count2));
16783
17722
  return this.identityOf(pid);
16784
17723
  },
16785
17724
  onSyscall: (call) => {
@@ -16793,6 +17732,7 @@ var Launcher = class {
16793
17732
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
16794
17733
  ...this.owners ? { owners: true } : {},
16795
17734
  sealed: this.sealed,
17735
+ ...scrubs ? { ttyFilter: (file) => scrubbedAll(file, scrubs) } : {},
16796
17736
  clipboard: this.clipboard,
16797
17737
  ...this.dac ? { dac: this.dac } : {},
16798
17738
  program: req.program,
@@ -16859,6 +17799,7 @@ var Launcher = class {
16859
17799
  if (spent) addTimes(this.imageTimes, this.jobs.shown(pid), spent);
16860
17800
  this.stopped.delete(pid);
16861
17801
  this.processes.delete(pid);
17802
+ this.scrubs.delete(pid);
16862
17803
  if (this.processes.size === 0) {
16863
17804
  clearInterval(this.sampler);
16864
17805
  this.sampler = void 0;
@@ -17030,6 +17971,7 @@ var Launcher = class {
17030
17971
  }
17031
17972
  spawner(ppid, report) {
17032
17973
  return async (req, fds, grant) => {
17974
+ const booked = this.takeTicket(req);
17033
17975
  if (grant && !await this.grantedCwd(grant.caller, req.cwd)) {
17034
17976
  await fds.closeAll();
17035
17977
  throw new SpawnError("EACCES");
@@ -17041,23 +17983,20 @@ var Launcher = class {
17041
17983
  }
17042
17984
  const cred = grant ? grant.cred : this.credOf(ppid);
17043
17985
  const scope = grant ? "" : await this.scopeOf(cred.euid);
17044
- const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
17045
- try {
17046
- await this.mayExec(cred, { ...req, pid: ppid }, planned);
17047
- } catch (err) {
17048
- await fds.closeAll();
17049
- throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
17050
- }
17051
- if (!planned) {
17052
- await fds.closeAll();
17053
- throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
17054
- }
17986
+ const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
17987
+ const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
17988
+ const redeemed = await this.redeemed(
17989
+ { ...req, file },
17990
+ { ppid, cred, scope, booked },
17991
+ planned,
17992
+ fds
17993
+ );
17055
17994
  const exec = req.exec ? { exec: true } : {};
17056
17995
  const deciding = req.exec ? void 0 : this.undecided(ppid);
17057
17996
  const handle = await this.launch(planned, {
17058
17997
  ...deciding ? { decided: deciding.promise } : {},
17059
- env: req.env,
17060
17998
  cwd: req.cwd,
17999
+ ...redeemed,
17061
18000
  fds,
17062
18001
  report,
17063
18002
  ppid,
@@ -17110,11 +18049,11 @@ var Launcher = class {
17110
18049
  if (!waiting) return;
17111
18050
  this.deciding.delete(ppid);
17112
18051
  setTimeout(() => {
17113
- for (const resolve of waiting) resolve(true);
18052
+ for (const resolve2 of waiting) resolve2(true);
17114
18053
  }, 0);
17115
18054
  }
17116
18055
  forker(ppid, parent) {
17117
- const { exec: _exec, pgid: _pgid, group: _group, ...image } = parent;
18056
+ const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
17118
18057
  return async (state, fds) => childHandle(
17119
18058
  this.start({
17120
18059
  ...image,
@@ -17244,11 +18183,14 @@ var Launcher = class {
17244
18183
  }
17245
18184
  };
17246
18185
  }
17247
- const module = (await scanFilesystems(this.base, await this.roots())).get(type);
18186
+ const module = (await scanFilesystems(this.scanned, await this.roots())).get(type);
17248
18187
  if (!module) throw fsError("ENODEV", `unknown file system type ${type}`);
17249
18188
  if (!this.createDriverWorker)
17250
18189
  throw fsError("ENODEV", `this kernel cannot start ${type} drivers`);
17251
- const code = await this.base.readFile(module);
18190
+ const root = PACKAGE_ROOT.exec(module)?.[1];
18191
+ if (this.integrity && root !== void 0)
18192
+ await this.text(`${root}/package.json`, KERNEL_CALLER);
18193
+ const code = new TextDecoder().decode(await this.verified(module, KERNEL_CALLER));
17252
18194
  const worker = this.createDriverWorker();
17253
18195
  const driver = new MessageChannel();
17254
18196
  const client = new MessageChannel();
@@ -17488,7 +18430,14 @@ var Launcher = class {
17488
18430
  );
17489
18431
  }
17490
18432
  environment(cwd, extra, account) {
17491
- return { ...this.env, ...userEnv(account, this.pnpmHome), PWD: cwd, ...extra };
18433
+ const env = {
18434
+ ...this.env,
18435
+ ...userEnv(account, this.pnpmHome),
18436
+ PWD: cwd,
18437
+ ...extra
18438
+ };
18439
+ delete env[TICKET_ENV];
18440
+ return env;
17492
18441
  }
17493
18442
  async account(user) {
17494
18443
  if (typeof user === "object") return user;
@@ -17584,7 +18533,7 @@ var Launcher = class {
17584
18533
  },
17585
18534
  account
17586
18535
  );
17587
- const report = (message3) => options.onData(encoder5.encode(`${message3}\r
18536
+ const report = (message3) => options.onData(encoder7.encode(`${message3}\r
17588
18537
  `));
17589
18538
  const handle = await this.launch(planned, {
17590
18539
  env,
@@ -17615,18 +18564,18 @@ var Launcher = class {
17615
18564
  const env = this.environment(cwd, options.env, account);
17616
18565
  const out = [];
17617
18566
  const err = [];
17618
- const keep = options.collect !== false;
18567
+ const keep2 = options.collect !== false;
17619
18568
  const collect = (chunks2, tee) => (bytes) => {
17620
- if (keep) chunks2.push(bytes);
18569
+ if (keep2) chunks2.push(bytes);
17621
18570
  tee?.(bytes);
17622
18571
  };
17623
18572
  const stdout = collect(out, options.onStdout);
17624
18573
  const stderr = collect(err, options.onStderr);
17625
- const report = (message3) => stderr(encoder5.encode(`${message3}
18574
+ const report = (message3) => stderr(encoder7.encode(`${message3}
17626
18575
  `));
17627
18576
  if (!planned) {
17628
18577
  report(`${file}: command not found`);
17629
- return { status: NOT_FOUND, stdout: concat5(out), stderr: concat5(err) };
18578
+ return { status: NOT_FOUND, stdout: concat6(out), stderr: concat6(err) };
17630
18579
  }
17631
18580
  const fds = new FdTable();
17632
18581
  fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
@@ -17644,7 +18593,7 @@ var Launcher = class {
17644
18593
  options.onStarted?.(handle.pid);
17645
18594
  const status = await handle.exited;
17646
18595
  const signal = handle.termsig();
17647
- return { status, ...signal ? { signal } : {}, stdout: concat5(out), stderr: concat5(err) };
18596
+ return { status, ...signal ? { signal } : {}, stdout: concat6(out), stderr: concat6(err) };
17648
18597
  }
17649
18598
  };
17650
18599
 
@@ -17713,10 +18662,10 @@ var Chunks = class {
17713
18662
  this.grow(size);
17714
18663
  return;
17715
18664
  }
17716
- const keep = Math.ceil(size / CHUNK4);
17717
- this.blobs.length = keep;
17718
- if (keep > 0)
17719
- this.blobs[keep - 1] = this.blobs[keep - 1].slice(0, size - (keep - 1) * CHUNK4);
18665
+ const keep2 = Math.ceil(size / CHUNK4);
18666
+ this.blobs.length = keep2;
18667
+ if (keep2 > 0)
18668
+ this.blobs[keep2 - 1] = this.blobs[keep2 - 1].slice(0, size - (keep2 - 1) * CHUNK4);
17720
18669
  this.size = size;
17721
18670
  }
17722
18671
  };
@@ -17865,12 +18814,12 @@ function codeOf5(e) {
17865
18814
  return typeof code === "string" ? code : void 0;
17866
18815
  }
17867
18816
  function probe(secure, host, port, ms) {
17868
- return new Promise((resolve) => {
18817
+ return new Promise((resolve2) => {
17869
18818
  const socket = secure ? tls({ host, port, ...isIP(host) ? {} : { servername: host } }) : tcp({ host, port });
17870
18819
  const done4 = (code) => {
17871
18820
  clearTimeout(timer);
17872
18821
  socket.destroy();
17873
- resolve(code);
18822
+ resolve2(code);
17874
18823
  };
17875
18824
  const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
17876
18825
  socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
@@ -17962,8 +18911,8 @@ var RemoteTransport = class {
17962
18911
  }
17963
18912
  ask(call) {
17964
18913
  if (this.failure) return Promise.reject(this.failure);
17965
- return new Promise((resolve, reject) => {
17966
- this.waiting.set(call.nid, { resolve, reject });
18914
+ return new Promise((resolve2, reject) => {
18915
+ this.waiting.set(call.nid, { resolve: resolve2, reject });
17967
18916
  this.port.postMessage(call);
17968
18917
  });
17969
18918
  }
@@ -18143,7 +19092,7 @@ function dialStream(port, onClose) {
18143
19092
  write(chunk2) {
18144
19093
  if (closed) throw errorWithCode("EPIPE: the connection is closed");
18145
19094
  const copy = fresh(chunk2);
18146
- const acked = new Promise((resolve, reject) => acks.push({ resolve, reject }));
19095
+ const acked = new Promise((resolve2, reject) => acks.push({ resolve: resolve2, reject }));
18147
19096
  port.postMessage(copy, [copy.buffer]);
18148
19097
  return acked;
18149
19098
  },
@@ -18163,7 +19112,7 @@ function dialStream(port, onClose) {
18163
19112
 
18164
19113
  // src/client/loopback-fetch.ts
18165
19114
  var OWN_HEADERS = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
18166
- var encoder6 = new TextEncoder();
19115
+ var encoder8 = new TextEncoder();
18167
19116
  var latin12 = new TextDecoder("latin1");
18168
19117
  var ByteSource = class {
18169
19118
  buffered = new Uint8Array(0);
@@ -18181,7 +19130,7 @@ var ByteSource = class {
18181
19130
  return true;
18182
19131
  }
18183
19132
  async until(marker) {
18184
- const needle = encoder6.encode(marker);
19133
+ const needle = encoder8.encode(marker);
18185
19134
  for (; ; ) {
18186
19135
  const at = indexOf(this.buffered, needle);
18187
19136
  if (at >= 0) return this.take(at + needle.length);
@@ -18192,13 +19141,13 @@ var ByteSource = class {
18192
19141
  if (this.buffered.length === 0 && !await this.more()) return void 0;
18193
19142
  return this.take(Math.min(max, this.buffered.length));
18194
19143
  }
18195
- async exactly(count) {
18196
- while (this.buffered.length < count) if (!await this.more()) return void 0;
18197
- return this.take(count);
19144
+ async exactly(count2) {
19145
+ while (this.buffered.length < count2) if (!await this.more()) return void 0;
19146
+ return this.take(count2);
18198
19147
  }
18199
- take(count) {
18200
- const out = this.buffered.slice(0, count);
18201
- this.buffered = this.buffered.slice(count);
19148
+ take(count2) {
19149
+ const out = this.buffered.slice(0, count2);
19150
+ this.buffered = this.buffered.slice(count2);
18202
19151
  return out;
18203
19152
  }
18204
19153
  };
@@ -18289,7 +19238,7 @@ async function requestBytes(request) {
18289
19238
  });
18290
19239
  if (body) lines2.push(`Content-Length: ${body.length}`);
18291
19240
  lines2.push("Connection: close", "", "");
18292
- const head = encoder6.encode(lines2.join("\r\n"));
19241
+ const head = encoder8.encode(lines2.join("\r\n"));
18293
19242
  const out = new Uint8Array(head.length + (body?.length ?? 0));
18294
19243
  out.set(head);
18295
19244
  if (body) out.set(body, head.length);
@@ -18343,8 +19292,8 @@ var KernelCallError = class extends Error {
18343
19292
  this.code = code;
18344
19293
  }
18345
19294
  };
18346
- var encoder7 = new TextEncoder();
18347
- var bytesOf2 = (data) => typeof data === "string" ? encoder7.encode(data) : data;
19295
+ var encoder9 = new TextEncoder();
19296
+ var bytesOf2 = (data) => typeof data === "string" ? encoder9.encode(data) : data;
18348
19297
  var UNAVAILABLE = {
18349
19298
  manualRedirects: false,
18350
19299
  encodedBodies: false,
@@ -18364,7 +19313,7 @@ async function holdLock(locks) {
18364
19313
  await granted2.promise;
18365
19314
  return { name: name2, release: () => held.resolve() };
18366
19315
  }
18367
- function concat6(chunks2) {
19316
+ function concat7(chunks2) {
18368
19317
  const out = new Uint8Array(chunks2.reduce((n, c) => n + c.length, 0));
18369
19318
  let at = 0;
18370
19319
  for (const chunk2 of chunks2) {
@@ -18415,8 +19364,8 @@ var Channel = class {
18415
19364
  const id2 = ++this.nextId;
18416
19365
  const { failure } = this;
18417
19366
  if (failure) return { id: id2, done: Promise.reject(failure) };
18418
- const done4 = new Promise((resolve, reject) => {
18419
- this.pending.set(id2, { resolve, reject, ...handlers });
19367
+ const done4 = new Promise((resolve2, reject) => {
19368
+ this.pending.set(id2, { resolve: resolve2, reject, ...handlers });
18420
19369
  this.port.postMessage({ ...call, id: id2 });
18421
19370
  });
18422
19371
  return { id: id2, done: done4 };
@@ -18554,7 +19503,7 @@ async function runOn(channel, argv, opts) {
18554
19503
  onStderr: tee(2, onStderr)
18555
19504
  });
18556
19505
  const { status, signal } = await child.ended;
18557
- const text = (fd) => new TextDecoder().decode(concat6(chunks2[fd]));
19506
+ const text = (fd) => new TextDecoder().decode(concat7(chunks2[fd]));
18558
19507
  return {
18559
19508
  pid: child.pid,
18560
19509
  status,
@@ -18833,10 +19782,10 @@ async function createNodeKernel(options = {}) {
18833
19782
  ...options.approve ? { approve: options.approve } : {}
18834
19783
  });
18835
19784
  await launcher.prepare();
18836
- const guard2 = (work) => new Promise((resolve, reject) => {
19785
+ const guard2 = (work) => new Promise((resolve2, reject) => {
18837
19786
  if (terminated) return reject(new Error(TERMINATED));
18838
19787
  pending.add(reject);
18839
- work().then(resolve, reject).finally(() => pending.delete(reject));
19788
+ work().then(resolve2, reject).finally(() => pending.delete(reject));
18840
19789
  });
18841
19790
  const decoder4 = new TextDecoder();
18842
19791
  return {