@ai-ecoverse/slicc-kernel 1.151.1 → 1.152.1

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.
@@ -1569,9 +1569,10 @@ var OpfsFs = class {
1569
1569
  }
1570
1570
  async fileId(path) {
1571
1571
  const found = await this.locate(path, true);
1572
- const [entry] = await this.meta.get([found.path]);
1572
+ const [entry] = await (this.meta.fresh ?? this.meta.get).call(this.meta, [found.path]);
1573
1573
  if (entry?.fid !== void 0) return entry.fid;
1574
1574
  const fid = crypto.randomUUID();
1575
+ this.idWrites++;
1575
1576
  await this.meta.update(found.path, (now) => ({
1576
1577
  ...occupied(now),
1577
1578
  path: found.path,
@@ -1842,6 +1843,10 @@ var OpfsFs = class {
1842
1843
  opfsCalls() {
1843
1844
  return this.calls;
1844
1845
  }
1846
+ idWrites = 0;
1847
+ fileIdWrites() {
1848
+ return this.idWrites;
1849
+ }
1845
1850
  opfsOps() {
1846
1851
  return this.calls + this.others;
1847
1852
  }
@@ -2301,7 +2306,7 @@ var OpfsFs = class {
2301
2306
  return new Uint8Array(await file.slice(offset, offset + length2).arrayBuffer());
2302
2307
  });
2303
2308
  }
2304
- async pwrite(path, offset, bytes) {
2309
+ async pwrite(path, offset, bytes, _transfer, options) {
2305
2310
  const at = normalizePath(path);
2306
2311
  const movedAway = this.renamesInFlight() ? await this.writeWaits(at) : false;
2307
2312
  this.fileWrites.set(at, (this.fileWrites.get(at) ?? 0) + 1);
@@ -2316,7 +2321,10 @@ var OpfsFs = class {
2316
2321
  this.files.delete(target);
2317
2322
  }
2318
2323
  }
2319
- const [handle] = await this.fileFor(path, !existed && !movedAway);
2324
+ const [handle] = await this.fileFor(
2325
+ path,
2326
+ !existed && !movedAway && options?.create !== false
2327
+ );
2320
2328
  await this.writeAt(handle, target, path, offset, bytes);
2321
2329
  } finally {
2322
2330
  this.writeEnded(at);
@@ -4155,14 +4163,14 @@ var WsReader = class {
4155
4163
  }
4156
4164
  };
4157
4165
  function switchingHead(accept2, protocol = "") {
4158
- const chosen2 = protocol ? `Sec-WebSocket-Protocol: ${protocol}\r
4166
+ const chosen3 = protocol ? `Sec-WebSocket-Protocol: ${protocol}\r
4159
4167
  ` : "";
4160
4168
  return latin1Bytes(
4161
4169
  `HTTP/1.1 101 Switching Protocols\r
4162
4170
  Upgrade: websocket\r
4163
4171
  Connection: Upgrade\r
4164
4172
  Sec-WebSocket-Accept: ${accept2}\r
4165
- ${chosen2}\r
4173
+ ${chosen3}\r
4166
4174
  `
4167
4175
  );
4168
4176
  }
@@ -6155,6 +6163,7 @@ var KernelSocket = class _KernelSocket {
6155
6163
  return this.dial(addr, route2, secure2);
6156
6164
  }
6157
6165
  serving;
6166
+ pending;
6158
6167
  served = false;
6159
6168
  get loopbackLinked() {
6160
6169
  return this.state === "connected" && !this.switch;
@@ -6339,6 +6348,8 @@ var KernelSocket = class _KernelSocket {
6339
6348
  this.boundAt = void 0;
6340
6349
  this.serving?.release();
6341
6350
  this.serving = void 0;
6351
+ this.pending?.();
6352
+ this.pending = void 0;
6342
6353
  this.changes.wake();
6343
6354
  }
6344
6355
  static pair(net, domain, cred) {
@@ -7915,7 +7926,7 @@ function satisfies(version, range2) {
7915
7926
  }
7916
7927
 
7917
7928
  // src/version.ts
7918
- var KERNEL_VERSION = true ? "1.151.1" : "0.0.0-dev";
7929
+ var KERNEL_VERSION = true ? "1.152.1" : "0.0.0-dev";
7919
7930
  var OS_RELEASE = "6.0.0-slicc";
7920
7931
  function procVersion() {
7921
7932
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -10696,9 +10707,9 @@ function withEtcFiles(fs, files) {
10696
10707
  readFileBuffer: (path) => fs.readFileBuffer(path).catch(fallback(path, (b) => b)),
10697
10708
  ...ranged2 ? {
10698
10709
  pread: (path, offset, length2, version) => ranged2.pread(path, offset, length2, version).catch(fallback(path, (b) => b.slice(offset, offset + length2))),
10699
- pwrite: async (path, offset, bytes, transfer) => {
10710
+ pwrite: async (path, offset, bytes, transfer, options) => {
10700
10711
  if (files.has(path)) await materialize(path);
10701
- return ranged2.pwrite(path, offset, bytes, transfer);
10712
+ return ranged2.pwrite(path, offset, bytes, transfer, options);
10702
10713
  },
10703
10714
  truncate: async (path, size) => {
10704
10715
  if (files.has(path)) await materialize(path);
@@ -10807,10 +10818,10 @@ var FreshModules = class {
10807
10818
  };
10808
10819
  const ranged2 = rangedOps(fs);
10809
10820
  if (ranged2) {
10810
- overrides.pwrite = async (path, at, bytes, transfer) => {
10821
+ overrides.pwrite = async (path, at, bytes, transfer, options) => {
10811
10822
  const touches3 = at < MAGIC.length && this.fresh.has(normalizePath(path));
10812
10823
  const first = at === 0 && bytes.length >= MAGIC.length ? bytes.slice(0, MAGIC.length) : void 0;
10813
- await ranged2.pwrite(path, at, bytes, transfer);
10824
+ await ranged2.pwrite(path, at, bytes, transfer, options);
10814
10825
  if (touches3) await decide(path, first);
10815
10826
  };
10816
10827
  overrides.truncate = async (path, size) => {
@@ -12088,6 +12099,9 @@ var HeldSet = class extends Set {
12088
12099
  if (over !== void 0) holds2.doubt(over);
12089
12100
  }
12090
12101
  }
12102
+ async bindIds(id2) {
12103
+ for (const holds2 of [...this]) await holds2.bindIds(id2);
12104
+ }
12091
12105
  delete(holds2) {
12092
12106
  const removed = super.delete(holds2);
12093
12107
  for (const path of /* @__PURE__ */ new Set([...holds2.keys(), ...holds2.opens.keys()])) this.idle(path);
@@ -12141,8 +12155,17 @@ var HeldPaths = class extends Map {
12141
12155
  }
12142
12156
  boundIno(handle) {
12143
12157
  const doubt = this.doubted.get(handle);
12144
- const ino = this.bound.get(handle);
12145
- return doubt === void 0 || ino === void 0 ? void 0 : { ino, doubt };
12158
+ const bound3 = this.bound.get(handle);
12159
+ return doubt === void 0 || bound3 === void 0 ? void 0 : { ...bound3, doubt };
12160
+ }
12161
+ async bindIds(id2) {
12162
+ for (const [handle, bound3] of [...this.bound]) {
12163
+ const key = this.handles.get(handle);
12164
+ if (bound3.fid !== void 0 || key === void 0) continue;
12165
+ const fid = await id2(key)?.catch(() => void 0);
12166
+ if (fid !== void 0 && this.bound.get(handle) === bound3)
12167
+ this.bound.set(handle, { ...bound3, fid });
12168
+ }
12146
12169
  }
12147
12170
  trust(handle, doubt) {
12148
12171
  if (this.doubted.get(handle) === doubt) this.doubted.delete(handle);
@@ -12183,14 +12206,15 @@ var HeldPaths = class extends Map {
12183
12206
  for (const file of [...this.uses.keys()]) this.letGo(file, true);
12184
12207
  this.keptHandles.clear();
12185
12208
  }
12186
- hold(path, on, open2 = false, real, handle, ino) {
12209
+ hold(path, on, open2 = false, real, handle, ino, fid) {
12187
12210
  if (open2) {
12188
12211
  const made2 = this.holdOpen(path, on, real, handle);
12189
12212
  if (handle !== void 0) {
12190
12213
  this.bound.delete(handle);
12191
12214
  this.doubted.delete(handle);
12192
12215
  }
12193
- if (made2 !== void 0 && ino !== void 0) this.bound.set(made2, ino);
12216
+ if (made2 !== void 0 && ino !== void 0)
12217
+ this.bound.set(made2, fid === void 0 ? { ino } : { ino, fid });
12194
12218
  return made2;
12195
12219
  }
12196
12220
  this.holdWrite(path, on);
@@ -12926,7 +12950,7 @@ var MountFs = class _MountFs {
12926
12950
  );
12927
12951
  }
12928
12952
  }
12929
- async pwrite(path, offset, bytes, transfer = false, redirects = 0) {
12953
+ async pwrite(path, offset, bytes, transfer = false, options, redirects = 0) {
12930
12954
  const real = await this.follow(path, 0, true);
12931
12955
  const found = this.at(real);
12932
12956
  const { pwrite } = this.base;
@@ -12934,8 +12958,8 @@ var MountFs = class _MountFs {
12934
12958
  return this.based(
12935
12959
  [real],
12936
12960
  redirects,
12937
- () => pwrite.call(this.base, real, offset, bytes),
12938
- ([to], n) => this.pwrite(to, offset, bytes, transfer, n)
12961
+ () => pwrite.call(this.base, real, offset, bytes, false, options),
12962
+ ([to], n) => this.pwrite(to, offset, bytes, transfer, options, n)
12939
12963
  );
12940
12964
  }
12941
12965
  if (!found)
@@ -12959,7 +12983,7 @@ var MountFs = class _MountFs {
12959
12983
  op: "open",
12960
12984
  path: rel2,
12961
12985
  write: true,
12962
- create: true,
12986
+ create: options?.create !== false,
12963
12987
  truncate: false,
12964
12988
  exclusive: false
12965
12989
  });
@@ -13727,7 +13751,7 @@ async function run(entry, path, req) {
13727
13751
  case "truncate":
13728
13752
  return ranged(fs, path, req);
13729
13753
  case "append":
13730
- return json2(await appended(fs, path, req.body ?? new Uint8Array(0)));
13754
+ return json2(await appended(fs, path, req.body ?? new Uint8Array(0), req.handle));
13731
13755
  default:
13732
13756
  return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
13733
13757
  }
@@ -13871,13 +13895,17 @@ async function holdOp(entry, path, req) {
13871
13895
  await entry.source?.open(path, real);
13872
13896
  await stillThere(entry, path, req);
13873
13897
  if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
13898
+ const fid = req.ino === void 0 ? void 0 : await idOf(entry, real ?? path);
13874
13899
  if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
13875
- return json2(entry.hold?.(path, on, open2, real, void 0, req.ino) ?? null);
13900
+ return json2(entry.hold?.(path, on, open2, real, void 0, req.ino, fid) ?? null);
13876
13901
  }
13877
13902
  }
13878
13903
  entry.hold?.(path, on, open2, void 0, req.handle);
13879
13904
  return done2;
13880
13905
  }
13906
+ async function idOf(entry, path) {
13907
+ return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
13908
+ }
13881
13909
  async function ownEntry(entry, path, real) {
13882
13910
  const own = await entry.fs.realpath?.(path, false).catch((err) => {
13883
13911
  if (err.code !== "ENOENT") throw err;
@@ -13972,14 +14000,14 @@ async function createExclusive(fs, path, owner) {
13972
14000
  await fs.writeFile(path, new Uint8Array(0), owner);
13973
14001
  }
13974
14002
  var appending = /* @__PURE__ */ new Map();
13975
- async function appended(fs, path, body) {
14003
+ async function appended(fs, path, body, handle) {
13976
14004
  const key = await fs.realpath?.(path, true).catch(() => void 0) ?? path;
13977
14005
  const before = appending.get(key) ?? Promise.resolve();
13978
14006
  const run2 = before.then(async () => {
13979
14007
  if (fs.append) return fs.append(path, body);
13980
14008
  const size = (await fs.stat(path)).size;
13981
14009
  const ops = rangedOps(fs);
13982
- if (ops) await ops.pwrite(path, size, body, true);
14010
+ if (ops) await ops.pwrite(path, size, body, true, noCreate(handle));
13983
14011
  else {
13984
14012
  const whole = new Uint8Array(size + body.length);
13985
14013
  whole.set(await fs.readFileBuffer(path));
@@ -13995,6 +14023,9 @@ async function appended(fs, path, body) {
13995
14023
  });
13996
14024
  return run2;
13997
14025
  }
14026
+ function noCreate(handle) {
14027
+ return handle === void 0 ? void 0 : { create: false };
14028
+ }
13998
14029
  async function ranged(fs, path, req) {
13999
14030
  const ops = rangedOps(fs);
14000
14031
  if (!ops) throw syncError("ENOSYS", path);
@@ -14005,7 +14036,8 @@ async function ranged(fs, path, req) {
14005
14036
  kind: "bytes",
14006
14037
  bytes: await ops.pread(path, offset, req.length ?? 0, req.version)
14007
14038
  };
14008
- if (req.op === "pwrite") await ops.pwrite(path, offset, req.body ?? new Uint8Array(0), true);
14039
+ if (req.op === "pwrite")
14040
+ await ops.pwrite(path, offset, req.body ?? new Uint8Array(0), true, noCreate(req.handle));
14009
14041
  else await ops.truncate(path, req.length ?? 0);
14010
14042
  return done2;
14011
14043
  }
@@ -14035,7 +14067,10 @@ async function bound(entry, path, handle) {
14035
14067
  const bound3 = entry.boundIno?.(handle);
14036
14068
  if (bound3 === void 0) return;
14037
14069
  const now = await entry.fs.stat(path).catch(() => void 0);
14038
- if (now?.ino === bound3.ino) {
14070
+ if (now === void 0 && bound3.fid !== void 0 && !await entry.fs.lstat(path).catch(() => void 0))
14071
+ throw syncError("ENOENT", path);
14072
+ const same3 = bound3.fid === void 0 ? now?.ino === bound3.ino : now?.fid === bound3.fid || await idOf(entry, path) === bound3.fid;
14073
+ if (same3) {
14039
14074
  entry.trust?.(handle, bound3.doubt);
14040
14075
  return;
14041
14076
  }
@@ -14849,6 +14884,8 @@ var SYS_TREE = {
14849
14884
  slicc_rename_ack_busy: (listing) => `${listing.renameAcks?.busy ?? 0}
14850
14885
  `,
14851
14886
  slicc_stale_fds: (listing) => `${listing.staleFds ?? 0}
14887
+ `,
14888
+ slicc_file_ids: (listing) => `${listing.fileIds ?? 0}
14852
14889
  `,
14853
14890
  slicc_rename_acks: (listing) => {
14854
14891
  const samples = [...listing.renameAcks?.ackMs ?? []].sort((a, b) => a - b);
@@ -16779,6 +16816,8 @@ var TLS_CLIENT_NO_SNI = 2;
16779
16816
  var TLS_CLIENT_VERIFY_CHAIN_ONLY = 4;
16780
16817
  var TLS_SERVER_REQUEST_CERT = 1;
16781
16818
  var TLS_SERVER_REQUIRE_CERT = 2;
16819
+ var TLS_SERVER_SELECT = 4;
16820
+ var TLS_HANDSHAKE_SELECT = 2;
16782
16821
  var SCRATCH = 64 * 1024;
16783
16822
  var EOF = -1;
16784
16823
  var TlsError = class extends Error {
@@ -16846,12 +16885,15 @@ var TlsEngine = class {
16846
16885
  if (!ptr) throw this.error(this.m._tls_last_error(), "server certificate");
16847
16886
  return new TlsLeaf(this, this.m, ptr);
16848
16887
  }
16888
+ get serverSelect() {
16889
+ return typeof this.m._tls_server_select === "function";
16890
+ }
16849
16891
  serverSession(leaf, alpn, clientCa, flags2) {
16850
16892
  const open2 = this.m._tls_server_new;
16851
16893
  const wire = alpnWire(alpn);
16852
16894
  const ptr = this.withBytes(
16853
16895
  wire,
16854
- (p) => open2(leaf.ptr, wire.length > 0 ? p : 0, wire.length, clientCa?.ptr ?? 0, flags2)
16896
+ (p) => open2(leaf?.ptr ?? 0, wire.length > 0 ? p : 0, wire.length, clientCa?.ptr ?? 0, flags2)
16855
16897
  );
16856
16898
  if (!ptr) throw this.error(this.m._tls_last_error(), "tls server");
16857
16899
  return new TlsSession(this, this.m, ptr);
@@ -17050,9 +17092,35 @@ var TlsSession = class {
17050
17092
  return this.m;
17051
17093
  }
17052
17094
  handshake() {
17095
+ return this.step() === 1;
17096
+ }
17097
+ step() {
17053
17098
  const ret = this.c._tls_handshake(this.ptr);
17054
17099
  if (ret < 0) throw this.engine.error(ret, "tls handshake");
17055
- return ret === 1;
17100
+ return ret;
17101
+ }
17102
+ clientAlpn() {
17103
+ const list = this.m._tls_client_alpn;
17104
+ const out = this.engine.intoScratch((p, max) => list(this.ptr, p, max));
17105
+ if (!(out instanceof Uint8Array)) return [];
17106
+ const names2 = [];
17107
+ for (let at = 0; at < out.length; at += 1 + out[at]) {
17108
+ names2.push(new TextDecoder().decode(out.subarray(at + 1, at + 1 + out[at])));
17109
+ }
17110
+ return names2;
17111
+ }
17112
+ select(leaf, alpn) {
17113
+ const pick = this.m._tls_server_select;
17114
+ const name2 = new TextEncoder().encode(alpn ?? "");
17115
+ const ret = this.engine.withBytes(
17116
+ name2,
17117
+ (p) => pick(this.ptr, leaf?.ptr ?? 0, name2.length > 0 ? p : 0, name2.length)
17118
+ );
17119
+ if (ret !== 0) throw this.engine.error(ret, "tls select");
17120
+ }
17121
+ refuse(alert) {
17122
+ const ret = this.m._tls_server_refuse(this.ptr, alert);
17123
+ if (ret !== 0) throw this.engine.error(ret, "tls refuse");
17056
17124
  }
17057
17125
  get verifyResult() {
17058
17126
  return this.c._tls_verify_result(this.ptr);
@@ -18890,17 +18958,39 @@ function serverRefusal(err, session, first) {
18890
18958
  const text = err instanceof TlsError ? err.message : String(err);
18891
18959
  return new KernelError("EPROTO", `ERR_SSL_HANDSHAKE_FAILURE: ${text}${alertOf(session)}`);
18892
18960
  }
18893
- async function serverHandshake(conn, session, inbox, first) {
18961
+ async function chosen(conn, session, select) {
18962
+ if (!select) throw new KernelError("EPROTO", "ERR_SSL_HANDSHAKE_FAILURE: no selection");
18963
+ const choice = await select({ servername: session.sni, protocols: session.clientAlpn() });
18964
+ if (choice.refuse === void 0) {
18965
+ try {
18966
+ session.select(choice.leaf, choice.alpn);
18967
+ } finally {
18968
+ choice.done?.();
18969
+ }
18970
+ return;
18971
+ }
18972
+ session.refuse(choice.refuse);
18973
+ await send(conn, session).catch(() => void 0);
18974
+ throw new KernelError(
18975
+ "EPROTO",
18976
+ `ERR_SSL_HANDSHAKE_FAILURE: the program refused the handshake (alert ${choice.refuse})`
18977
+ );
18978
+ }
18979
+ async function serverHandshake(conn, session, inbox, hooks) {
18894
18980
  for (; ; ) {
18895
- let done5;
18981
+ let step2;
18896
18982
  try {
18897
- done5 = session.handshake();
18983
+ step2 = session.step();
18898
18984
  } catch (err) {
18899
18985
  await send(conn, session).catch(() => void 0);
18900
- throw serverRefusal(err, session, first());
18986
+ throw serverRefusal(err, session, hooks.first());
18987
+ }
18988
+ if (step2 === TLS_HANDSHAKE_SELECT) {
18989
+ await chosen(conn, session, hooks.select);
18990
+ continue;
18901
18991
  }
18902
18992
  await send(conn, session);
18903
- if (done5) return;
18993
+ if (step2 === 1) return;
18904
18994
  if (!await inbox.more()) {
18905
18995
  throw new KernelError(
18906
18996
  "ECONNRESET",
@@ -18909,7 +18999,7 @@ async function serverHandshake(conn, session, inbox, first) {
18909
18999
  }
18910
19000
  }
18911
19001
  }
18912
- async function tlsServer(conn, session, clientCerts) {
19002
+ async function tlsServer(conn, session, clientCerts, select) {
18913
19003
  let first;
18914
19004
  const watched = {
18915
19005
  ...conn,
@@ -18921,7 +19011,7 @@ async function tlsServer(conn, session, clientCerts) {
18921
19011
  };
18922
19012
  const inbox = new Ciphertext(watched, session);
18923
19013
  try {
18924
- await serverHandshake(conn, session, inbox, () => first);
19014
+ await serverHandshake(conn, session, inbox, { first: () => first, select });
18925
19015
  const chain2 = session.peerChain();
18926
19016
  const code = clientCerts ? verifyCode(session.verifyResult) : void 0;
18927
19017
  const protocol = session.version === 772 ? "TLSv1.3" : "TLSv1.2";
@@ -22126,10 +22216,35 @@ function storeOf(engine, ca) {
22126
22216
  throw new KernelError("EINVAL", `ERR_INVALID_ARG_VALUE: ca: ${err.message}`);
22127
22217
  }
22128
22218
  }
22129
- function flagsOf(req) {
22219
+ function certFlags(req) {
22130
22220
  if (!req.requestCert) return 0;
22131
22221
  return req.rejectUnauthorized === false ? TLS_SERVER_REQUEST_CERT : TLS_SERVER_REQUIRE_CERT;
22132
22222
  }
22223
+ function flagsOf(req) {
22224
+ if (!req.select) return certFlags(req);
22225
+ if (req.alpn && req.alpn.length > 0)
22226
+ throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS: alpn with select");
22227
+ return certFlags(req) | TLS_SERVER_SELECT;
22228
+ }
22229
+ async function engineOf(proc, select) {
22230
+ const engine = await supportOf(proc.net).engine();
22231
+ if (!engine.serverSessions)
22232
+ throw new KernelError("ENOTSUP", "the installed tls engine has no server sessions");
22233
+ if (select && !engine.serverSelect)
22234
+ throw new KernelError("ENOTSUP", "the installed tls engine has no server selection");
22235
+ return engine;
22236
+ }
22237
+ async function importedLeaf(proc, own, select) {
22238
+ const cert = certBytes(own);
22239
+ let engine;
22240
+ try {
22241
+ engine = await engineOf(proc, select);
22242
+ } catch (err) {
22243
+ wipe(cert);
22244
+ throw err;
22245
+ }
22246
+ return { engine, leaf: leafOf(engine, cert) };
22247
+ }
22133
22248
  function timeoutOf(req) {
22134
22249
  const ms = req.handshakeTimeout ?? HANDSHAKE_TIMEOUT_MS;
22135
22250
  if (!Number.isInteger(ms) || ms <= 0)
@@ -22151,23 +22266,14 @@ function handshakeSignal(proc, ms) {
22151
22266
  async function serverOf(proc, req) {
22152
22267
  const own = req.cert;
22153
22268
  delete req.cert;
22154
- if (!own) throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: a server needs cert and key");
22155
- const cert = certBytes(own);
22156
- let engine;
22157
- try {
22158
- engine = await supportOf(proc.net).engine();
22159
- if (!engine.serverSessions)
22160
- throw new KernelError("ENOTSUP", "the installed tls engine has no server sessions");
22161
- } catch (err) {
22162
- wipe(cert);
22163
- throw err;
22164
- }
22165
- const leaf = leafOf(engine, cert);
22269
+ if (!own && !req.select)
22270
+ throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: a server needs cert and key");
22271
+ const { engine, leaf } = own ? await importedLeaf(proc, own, req.select) : { engine: await engineOf(proc, true), leaf: void 0 };
22166
22272
  let mine;
22167
22273
  try {
22168
22274
  mine = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
22169
22275
  const flags2 = flagsOf(req);
22170
- const store = mine ?? (flags2 ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
22276
+ const store = mine ?? (flags2 & ~TLS_SERVER_SELECT ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
22171
22277
  return {
22172
22278
  engine,
22173
22279
  leaf,
@@ -22176,12 +22282,12 @@ async function serverOf(proc, req) {
22176
22282
  flags: flags2,
22177
22283
  timeoutMs: timeoutOf(req),
22178
22284
  release() {
22179
- leaf.release();
22285
+ leaf?.release();
22180
22286
  mine?.release();
22181
22287
  }
22182
22288
  };
22183
22289
  } catch (err) {
22184
- leaf.release();
22290
+ leaf?.release();
22185
22291
  mine?.release();
22186
22292
  throw err;
22187
22293
  }
@@ -22192,30 +22298,133 @@ async function listenSecure(proc, socket, req) {
22192
22298
  socket.serving?.release();
22193
22299
  socket.serving = server;
22194
22300
  }
22301
+ function servedOf(info) {
22302
+ return {
22303
+ path: "kernel",
22304
+ protocol: info.protocol,
22305
+ alpnProtocol: info.alpnProtocol,
22306
+ servername: info.servername,
22307
+ authorized: info.authorized,
22308
+ ...info.authorizationError ? { authorizationError: info.authorizationError } : {},
22309
+ peerCertificates: info.peerCertificates.map(encode),
22310
+ cipher: info.cipher
22311
+ };
22312
+ }
22313
+ var selections = /* @__PURE__ */ new WeakMap();
22314
+ function pausing(signal) {
22315
+ let paused;
22316
+ const pausedAt = new Promise((resolve2) => {
22317
+ paused = resolve2;
22318
+ });
22319
+ const select = (offer) => new Promise((choose, fail2) => {
22320
+ if (signal.aborted) fail2(signal.reason);
22321
+ signal.addEventListener("abort", () => fail2(signal.reason), { once: true });
22322
+ paused({ offer, choose, fail: fail2 });
22323
+ });
22324
+ return { pausedAt, select };
22325
+ }
22195
22326
  async function serveSecure(proc, socket, req) {
22196
22327
  const shared = req.listener?.serving;
22197
22328
  if (req.listener && !shared) throw new KernelError("EINVAL", "the listener has no tls");
22198
22329
  const server = shared ?? await serverOf(proc, req);
22330
+ const { engine, leaf, alpn, store, flags: flags2 } = server;
22331
+ const limit = handshakeSignal(proc, server.timeoutMs);
22332
+ const { pausedAt, select } = pausing(limit.signal);
22333
+ const certs = (flags2 & ~TLS_SERVER_SELECT) !== 0;
22334
+ const run2 = socket.secureLink(
22335
+ (raw2) => tlsServer(
22336
+ raw2,
22337
+ engine.serverSession(leaf, alpn, store, flags2),
22338
+ certs,
22339
+ flags2 & TLS_SERVER_SELECT ? select : void 0
22340
+ ),
22341
+ limit.signal,
22342
+ (reason) => !shared && reason === timedOut
22343
+ ).finally(() => {
22344
+ limit.done();
22345
+ selections.delete(socket);
22346
+ socket.pending = void 0;
22347
+ if (!shared) server.release();
22348
+ });
22349
+ run2.catch(() => void 0);
22350
+ const first = await Promise.race([run2, pausedAt.then((pause2) => ({ pause: pause2 }))]);
22351
+ if (!("pause" in first)) return servedOf(first);
22352
+ selections.set(socket, { pause: first.pause, run: run2 });
22353
+ socket.pending = () => first.pause.fail(new KernelError("ECONNRESET"));
22354
+ return { select: first.pause.offer };
22355
+ }
22356
+ var REFUSALS = /* @__PURE__ */ new Set([112, 120, 40, 49, 80]);
22357
+ var TlsContext = class {
22358
+ leaf;
22359
+ constructor(leaf) {
22360
+ this.leaf = leaf;
22361
+ }
22362
+ close() {
22363
+ this.leaf.release();
22364
+ }
22365
+ };
22366
+ var TLS_CONTEXTS_MAX = 64;
22367
+ async function contextSecure(proc, cert) {
22368
+ if (!cert) throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: a context needs cert and key");
22369
+ const held = proc.fds.numbers().filter((fd) => proc.fds.get(fd).file instanceof TlsContext);
22370
+ if (held.length >= TLS_CONTEXTS_MAX) {
22371
+ wipe(certBytes(cert));
22372
+ throw new KernelError("EMFILE", `at most ${TLS_CONTEXTS_MAX} tls contexts per process`);
22373
+ }
22374
+ const { leaf } = await importedLeaf(proc, cert);
22375
+ const file = new OpenFile(new TlsContext(leaf));
22199
22376
  try {
22200
- const { engine, leaf, alpn, store, flags: flags2 } = server;
22201
- const limit = handshakeSignal(proc, server.timeoutMs);
22202
- const info = await socket.secureLink(
22203
- (raw2) => tlsServer(raw2, engine.serverSession(leaf, alpn, store, flags2), flags2 !== 0),
22204
- limit.signal,
22205
- (reason) => !shared && reason === timedOut
22206
- ).finally(limit.done);
22207
- return {
22208
- path: "kernel",
22209
- protocol: info.protocol,
22210
- alpnProtocol: info.alpnProtocol,
22211
- servername: info.servername,
22212
- authorized: info.authorized,
22213
- ...info.authorizationError ? { authorizationError: info.authorizationError } : {},
22214
- peerCertificates: info.peerCertificates.map(encode),
22215
- cipher: info.cipher
22216
- };
22377
+ const fd = proc.fds.install(file, 3);
22378
+ proc.fds.setCloseOnExec(fd);
22379
+ return fd;
22380
+ } catch (err) {
22381
+ leaf.release();
22382
+ throw err;
22383
+ }
22384
+ }
22385
+ function contextAt(fds, fd) {
22386
+ const open2 = fds.get(fd);
22387
+ const file = open2.file;
22388
+ if (!(file instanceof TlsContext)) throw new KernelError("EBADF", "not a tls context");
22389
+ const held = open2.retain();
22390
+ let released2 = false;
22391
+ return {
22392
+ leaf: file.leaf,
22393
+ done: () => {
22394
+ if (released2) return;
22395
+ released2 = true;
22396
+ void held.release();
22397
+ }
22398
+ };
22399
+ }
22400
+ function choiceOf(fds, pause2, req) {
22401
+ if (req.refuse !== void 0) {
22402
+ if (!REFUSALS.has(req.refuse)) throw new KernelError("EINVAL", "not a refusal alert");
22403
+ return { refuse: req.refuse };
22404
+ }
22405
+ if (req.alpn !== void 0 && !pause2.offer.protocols.includes(req.alpn))
22406
+ throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_INVALID_RESULT: not offered");
22407
+ return {
22408
+ ...req.context === void 0 ? {} : contextAt(fds, req.context),
22409
+ ...req.alpn === void 0 ? {} : { alpn: req.alpn }
22410
+ };
22411
+ }
22412
+ async function selectSecure(fds, socket, req) {
22413
+ const selecting = selections.get(socket);
22414
+ if (!selecting) throw new KernelError("EINVAL", "no handshake waits for a selection");
22415
+ selections.delete(socket);
22416
+ let choice;
22417
+ try {
22418
+ choice = choiceOf(fds, selecting.pause, req);
22419
+ } catch (err) {
22420
+ selecting.pause.choose({ refuse: 40 });
22421
+ throw err;
22422
+ }
22423
+ selecting.pause.choose(choice);
22424
+ try {
22425
+ return servedOf(await selecting.run);
22217
22426
  } finally {
22218
- if (!shared) server.release();
22427
+ choice.done?.();
22219
22428
  }
22220
22429
  }
22221
22430
 
@@ -22383,7 +22592,9 @@ var SOCKET_OPS = [
22383
22592
  "sock-tls",
22384
22593
  "sock-tls-upgrade",
22385
22594
  "sock-tls-serve",
22386
- "sock-tls-listen"
22595
+ "sock-tls-listen",
22596
+ "sock-tls-context",
22597
+ "sock-tls-select"
22387
22598
  ];
22388
22599
  function socketAt(fds, fd) {
22389
22600
  const file = anySocketAt(fds, fd);
@@ -22464,6 +22675,12 @@ async function socketSyscall(req, proc) {
22464
22675
  const listener = from === void 0 ? {} : { listener: socketAt(fds, from) };
22465
22676
  return ok3(await serveSecure(proc, socketAt(fds, req.fd), { ...rest2, ...listener }));
22466
22677
  }
22678
+ case "sock-tls-context":
22679
+ return ok3(await contextSecure(proc, req.cert));
22680
+ case "sock-tls-select": {
22681
+ const { fd, ...choice } = req;
22682
+ return ok3(await selectSecure(fds, socketAt(fds, fd), choice));
22683
+ }
22467
22684
  case "sock-tls-listen":
22468
22685
  await listenSecure(proc, socketAt(fds, req.fd), req);
22469
22686
  return ok3({});
@@ -23336,10 +23553,10 @@ function heldIo(fs, nodes, changed) {
23336
23553
  const held = await node(path);
23337
23554
  return held ? held.byName(() => held.pread(n, at)) : ranged2.pread(path, at, n, version);
23338
23555
  },
23339
- pwrite: (path, at, bytes, transfer) => write(
23556
+ pwrite: (path, at, bytes, transfer, options) => write(
23340
23557
  path,
23341
23558
  async (held) => void await held.pwrite(bytes, at),
23342
- () => ranged2.pwrite(path, at, bytes, transfer)
23559
+ () => ranged2.pwrite(path, at, bytes, transfer, options)
23343
23560
  ),
23344
23561
  truncate: (path, size) => write(
23345
23562
  path,
@@ -25624,8 +25841,9 @@ function holdingToken(opts, holds2, caller) {
25624
25841
  cwd: opts.cwd,
25625
25842
  ...opts.statfs ? { statfs: opts.statfs } : {},
25626
25843
  ...opts.renames ? { renames: opts.renames } : {},
25844
+ ...opts.fileId ? { fileId: opts.fileId } : {},
25627
25845
  ...opts.renameSeq ? { renameSeq: opts.renameSeq } : {},
25628
- hold: (path, on, open2, real, handle, ino) => holds2.hold(path, on, open2, real, handle, ino),
25846
+ hold: (path, on, open2, real, handle, ino, fid) => holds2.hold(path, on, open2, real, handle, ino, fid),
25629
25847
  boundIno: holds2.boundIno.bind(holds2),
25630
25848
  trust: holds2.trust.bind(holds2),
25631
25849
  keptFor: (handle) => holds2.keptFor(handle),
@@ -29428,7 +29646,7 @@ function targetOf(command) {
29428
29646
  ...command.variants ? { variants: command.variants } : {}
29429
29647
  };
29430
29648
  }
29431
- function chosen(planned, env) {
29649
+ function chosen2(planned, env) {
29432
29650
  const { target } = planned;
29433
29651
  const wasm = variantFor(target.variants, env);
29434
29652
  if (wasm === void 0) return planned;
@@ -30587,7 +30805,7 @@ var Launcher = class {
30587
30805
  delete env[TICKET_ENV];
30588
30806
  const own = env.HOSTNAME;
30589
30807
  if (asked.target.abi === "wasi") env.HOSTNAME = this.hostname;
30590
- const planned = chosen(asked, env);
30808
+ const planned = chosen2(asked, env);
30591
30809
  const { target, args } = planned;
30592
30810
  let program;
30593
30811
  let exeId;
@@ -30777,7 +30995,8 @@ var Launcher = class {
30777
30995
  opfsOps: this.rootFs.opfsOps?.() ?? 0,
30778
30996
  cwdChecks: this.cwds.checks,
30779
30997
  renameAcks: this.rootFs.renameWaits?.(),
30780
- staleFds: staleFdCount()
30998
+ staleFds: staleFdCount(),
30999
+ fileIds: this.rootFs.fileIdWrites?.() ?? 0
30781
31000
  }),
30782
31001
  sudo: (call, signal) => this.sudo(pid, call, signal),
30783
31002
  crontab: (call) => this.crontab(pid, call),
@@ -30788,6 +31007,7 @@ var Launcher = class {
30788
31007
  replacements: stack.unlinked,
30789
31008
  statfs: (path) => this.mounts.statfs(path),
30790
31009
  renames: (since, held) => this.renameLog.since(since, held),
31010
+ fileId: (path) => this.locks.shared?.() === true ? this.rootFileId(path) : void 0,
30791
31011
  renameSeq: (from, to) => this.renameLog.seqOf(from, to),
30792
31012
  mounts: () => this.mounts.list(),
30793
31013
  mount: (call, signal) => this.processMount(pid, call, signal),
@@ -31667,12 +31887,16 @@ var Launcher = class {
31667
31887
  this.writers ??= this.writerStack();
31668
31888
  return this.writers;
31669
31889
  }
31890
+ rootFileId(path) {
31891
+ return this.mounts.locate(path) === void 0 ? this.rootFs.fileId?.(path) : void 0;
31892
+ }
31670
31893
  flushingPeers(fs) {
31671
31894
  const peers = fs.peers?.();
31672
31895
  if (!peers) return void 0;
31673
31896
  peers.watch((crowded) => {
31674
31897
  this.changes.flush();
31675
31898
  this.changes.foreign(crowded);
31899
+ if (crowded) void this.held?.bindIds((path) => this.rootFileId(path));
31676
31900
  });
31677
31901
  return {
31678
31902
  ...peers,