@ai-ecoverse/slicc-kernel 1.67.0 → 1.68.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.
@@ -2019,6 +2019,7 @@ function kernelFdKind(file) {
2019
2019
  if (file.held) return "held";
2020
2020
  if (file.heldMeta && "device" in file.heldMeta) return "device";
2021
2021
  if (file.tty) return "tty";
2022
+ if (file.sealed) return "sealed";
2022
2023
  return file.seek ? "file" : "stream";
2023
2024
  }
2024
2025
  var FdTable = class _FdTable {
@@ -10543,8 +10544,8 @@ function ptySyscall(req, ctx) {
10543
10544
  var RUN_CAPTURE_CAP = 64 * 1024 * 1024;
10544
10545
  var RUN_MAX_BUFFER = 1024 * 1024;
10545
10546
  function capLimit(maxBuffer) {
10546
- const asked = maxBuffer === void 0 ? RUN_MAX_BUFFER : maxBuffer;
10547
- return Math.max(0, Math.min(asked, RUN_CAPTURE_CAP));
10547
+ const asked = typeof maxBuffer === "number" && maxBuffer >= 0 ? maxBuffer : RUN_MAX_BUFFER;
10548
+ return Math.min(asked, RUN_CAPTURE_CAP);
10548
10549
  }
10549
10550
  function slot(how, n, extra) {
10550
10551
  if (how === "inherit") return { fd: n };
@@ -10570,6 +10571,7 @@ async function reaped(deps, pid) {
10570
10571
  }
10571
10572
  }
10572
10573
  async function runChild(req, deps) {
10574
+ if (!isSignal(req.killSignal)) throw new KernelError("EINVAL");
10573
10575
  const max = capLimit(req.maxBuffer);
10574
10576
  let failure2;
10575
10577
  let pid = 0;
@@ -11406,6 +11408,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
11406
11408
  "fd-open-tty",
11407
11409
  "fd-tty-names",
11408
11410
  "fd-tty-owner",
11411
+ "fd-open-sealed",
11409
11412
  "tty-get",
11410
11413
  "tty-set",
11411
11414
  "tty-winsz",
@@ -11461,6 +11464,7 @@ var WasmProcess = class {
11461
11464
  ignored = 0;
11462
11465
  inherited = 0;
11463
11466
  interrupt = new AbortController();
11467
+ gone = new AbortController();
11464
11468
  dying = false;
11465
11469
  umask = DEFAULT_UMASK;
11466
11470
  cred;
@@ -11683,6 +11687,7 @@ var WasmProcess = class {
11683
11687
  }
11684
11688
  async write(fd, body, nonblock = false, cancelled) {
11685
11689
  const file = this.fds.get(fd).file;
11690
+ if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
11686
11691
  if (!file.write) throw new KernelError("EBADF");
11687
11692
  if (!pollFile(file).writable) {
11688
11693
  if (nonblock) throw new KernelError("EAGAIN");
@@ -11764,6 +11769,24 @@ var WasmProcess = class {
11764
11769
  if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
11765
11770
  return this.fds.install(tty.file(), 3);
11766
11771
  }
11772
+ openSealed() {
11773
+ const broker = this.options.sealed;
11774
+ if (!broker) throw new KernelError("ENOENT");
11775
+ return this.fds.install(broker.open(), 3);
11776
+ }
11777
+ async sealedCaller() {
11778
+ const shown = await this.options.identity?.();
11779
+ const broker = this.options.sealed;
11780
+ const tag = broker.tagOf(this.pid);
11781
+ return {
11782
+ pid: shown?.pid ?? this.pid,
11783
+ ppid: shown?.ppid ?? 0,
11784
+ uid: this.cred.euid,
11785
+ gid: this.cred.egid,
11786
+ ...tag !== void 0 ? { tag } : {},
11787
+ users: broker.users
11788
+ };
11789
+ }
11767
11790
  guarded() {
11768
11791
  const { guard: guard2 } = this.options;
11769
11792
  return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
@@ -11884,6 +11907,8 @@ var WasmProcess = class {
11884
11907
  return { ok: true, kind: "json", json: this.openTty(req.name) };
11885
11908
  case "fd-tty-names":
11886
11909
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
11910
+ case "fd-open-sealed":
11911
+ return { ok: true, kind: "json", json: this.openSealed() };
11887
11912
  case "fd-tty-owner":
11888
11913
  return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
11889
11914
  case "fd-select": {
@@ -12319,6 +12344,7 @@ var WasmProcess = class {
12319
12344
  async exit() {
12320
12345
  if (this.exited) return;
12321
12346
  this.exited = true;
12347
+ this.gone.abort();
12322
12348
  this.die();
12323
12349
  this.clearAlarm();
12324
12350
  this.interrupt.abort();
@@ -12506,6 +12532,7 @@ function processOptions(opts, header3, hooks, created3) {
12506
12532
  return {
12507
12533
  ...inherited(opts),
12508
12534
  ...opts.owners ? { owners: true } : {},
12535
+ ...opts.sealed ? { sealed: opts.sealed } : {},
12509
12536
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
12510
12537
  ...opts.identity ? { identity: opts.identity } : {},
12511
12538
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
@@ -12988,6 +13015,178 @@ function replayingHops(inner, heldMs = HELD_MS) {
12988
13015
  };
12989
13016
  }
12990
13017
 
13018
+ // src/kernel/sealed.ts
13019
+ var SEALED_MAX_BYTES = 64 * 1024;
13020
+ var SEALED_TIMEOUT_MS = 1e4;
13021
+ var SEALED_OPS = /* @__PURE__ */ new Set([
13022
+ "seal",
13023
+ "placeholder",
13024
+ "list",
13025
+ "test",
13026
+ "request",
13027
+ "status",
13028
+ "cancel",
13029
+ "materialize"
13030
+ ]);
13031
+ var encoder3 = new TextEncoder();
13032
+ var decoder2 = new TextDecoder("utf-8", { fatal: true });
13033
+ function parseSealed(bytes) {
13034
+ try {
13035
+ if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
13036
+ let parsed2;
13037
+ try {
13038
+ parsed2 = JSON.parse(decoder2.decode(bytes));
13039
+ } catch {
13040
+ throw new KernelError("EINVAL");
13041
+ }
13042
+ if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2))
13043
+ throw new KernelError("EINVAL");
13044
+ const req = parsed2;
13045
+ if (req.v !== 1 || typeof req.op !== "string" || !SEALED_OPS.has(req.op))
13046
+ throw new KernelError("EINVAL");
13047
+ return req;
13048
+ } finally {
13049
+ bytes.fill(0);
13050
+ }
13051
+ }
13052
+ function errorReply(code, message3) {
13053
+ return { v: 1, error: { code, message: message3 } };
13054
+ }
13055
+ function isRecord(value) {
13056
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13057
+ }
13058
+ function serialized(reply) {
13059
+ let plain;
13060
+ try {
13061
+ const text = JSON.stringify(reply);
13062
+ plain = text === void 0 ? void 0 : JSON.parse(text);
13063
+ } catch (err) {
13064
+ return JSON.stringify(errorReply("EIO", lineOf(err)));
13065
+ }
13066
+ if (!isRecord(plain)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
13067
+ const { v: _, ...rest2 } = plain;
13068
+ return JSON.stringify({ v: 1, ...rest2 });
13069
+ }
13070
+ function lineOf(err) {
13071
+ const text = err instanceof Error ? err.message : String(err);
13072
+ return (text.split("\n")[0] ?? "").slice(0, 200);
13073
+ }
13074
+ var SealedChannel = class {
13075
+ reply;
13076
+ offset = 0;
13077
+ inflight;
13078
+ broker;
13079
+ constructor(broker) {
13080
+ this.broker = broker;
13081
+ }
13082
+ send(bytes, caller, gone) {
13083
+ const length2 = bytes.length;
13084
+ const fields = parseSealed(bytes);
13085
+ this.reply = void 0;
13086
+ this.offset = 0;
13087
+ this.inflight?.control.abort("replaced");
13088
+ const control = new AbortController();
13089
+ const withdraw = () => control.abort("withdrawn");
13090
+ if (gone.aborted) withdraw();
13091
+ else gone.addEventListener("abort", withdraw, { once: true });
13092
+ const timer = setTimeout(() => control.abort("timeout"), this.broker.timeoutMs);
13093
+ const req = { ...fields, v: 1, op: fields.op, caller };
13094
+ const flight = { control, done: Promise.resolve() };
13095
+ flight.done = this.answer(req, control.signal).then((reply) => {
13096
+ clearTimeout(timer);
13097
+ gone.removeEventListener("abort", withdraw);
13098
+ if (this.inflight !== flight) return;
13099
+ this.inflight = void 0;
13100
+ if (reply) this.reply = encoder3.encode(serialized(reply));
13101
+ });
13102
+ this.inflight = flight;
13103
+ return length2;
13104
+ }
13105
+ async answer(req, signal) {
13106
+ const hook = this.broker.hook;
13107
+ if (!hook) return errorReply("ENOTCONN", "no vault is connected");
13108
+ const stopped = new Promise((resolve) => {
13109
+ signal.addEventListener("abort", () => resolve(void 0), { once: true });
13110
+ });
13111
+ if (signal.aborted) return void 0;
13112
+ const asked = Promise.resolve().then(() => hook(req, { signal })).then(
13113
+ (reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
13114
+ (err) => errorReply("EIO", lineOf(err))
13115
+ );
13116
+ const reply = await Promise.race([asked, stopped]);
13117
+ if (!signal.aborted) return reply;
13118
+ return signal.reason === "timeout" ? errorReply("ETIMEDOUT", "the vault did not answer within 10 s") : void 0;
13119
+ }
13120
+ async read(max, signal) {
13121
+ for (; ; ) {
13122
+ if (this.reply) {
13123
+ const out = this.reply.slice(this.offset, this.offset + max);
13124
+ this.offset += out.length;
13125
+ if (this.offset >= this.reply.length) {
13126
+ this.reply = void 0;
13127
+ this.offset = 0;
13128
+ }
13129
+ return out;
13130
+ }
13131
+ const flight = this.inflight;
13132
+ if (!flight) return new Uint8Array(0);
13133
+ await this.until(flight, signal);
13134
+ }
13135
+ }
13136
+ async until(flight, signal) {
13137
+ if (!signal) return flight.done;
13138
+ if (signal.aborted) throw new KernelError("EINTR");
13139
+ let stop = () => {
13140
+ };
13141
+ const interrupted2 = new Promise((_, reject) => {
13142
+ stop = () => reject(new KernelError("EINTR"));
13143
+ signal.addEventListener("abort", stop, { once: true });
13144
+ });
13145
+ try {
13146
+ await Promise.race([flight.done, interrupted2]);
13147
+ } finally {
13148
+ signal.removeEventListener("abort", stop);
13149
+ }
13150
+ }
13151
+ poll() {
13152
+ return { readable: this.reply !== void 0 || !this.inflight, writable: true, hangup: false };
13153
+ }
13154
+ async changed(signal) {
13155
+ const flight = this.inflight;
13156
+ if (flight) await this.until(flight, signal);
13157
+ }
13158
+ close() {
13159
+ this.inflight?.control.abort("withdrawn");
13160
+ this.inflight = void 0;
13161
+ this.reply = void 0;
13162
+ }
13163
+ };
13164
+ var SealedBroker = class {
13165
+ hook;
13166
+ users;
13167
+ timeoutMs;
13168
+ tagOf;
13169
+ constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
13170
+ this.hook = hook;
13171
+ this.users = users;
13172
+ this.timeoutMs = timeoutMs;
13173
+ this.tagOf = tagOf;
13174
+ }
13175
+ open() {
13176
+ const channel = new SealedChannel(this);
13177
+ return new OpenFile({
13178
+ read: (max, signal) => channel.read(max, signal),
13179
+ write: async () => {
13180
+ throw new KernelError("EINVAL");
13181
+ },
13182
+ poll: () => channel.poll(),
13183
+ changed: (signal) => channel.changed(signal),
13184
+ close: () => channel.close(),
13185
+ sealed: channel
13186
+ });
13187
+ }
13188
+ };
13189
+
12991
13190
  // src/kernel/settling.ts
12992
13191
  var SETUP_OPS = /* @__PURE__ */ new Set([
12993
13192
  "proc-list",
@@ -13635,13 +13834,13 @@ var HostfsClient = class {
13635
13834
  const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
13636
13835
  onOpen();
13637
13836
  const reader = response.body.getReader();
13638
- const decoder2 = new TextDecoder();
13837
+ const decoder3 = new TextDecoder();
13639
13838
  let buffered = "";
13640
13839
  try {
13641
13840
  for (; ; ) {
13642
13841
  const { done: done4, value } = await reader.read();
13643
13842
  if (done4) return;
13644
- buffered += decoder2.decode(value, { stream: true });
13843
+ buffered += decoder3.decode(value, { stream: true });
13645
13844
  let end = buffered.indexOf("\n");
13646
13845
  while (end >= 0) {
13647
13846
  const line = buffered.slice(0, end).trim();
@@ -15019,7 +15218,7 @@ var LAYOUT = [
15019
15218
  ];
15020
15219
  var LOAD_SAMPLE_MS = 5e3;
15021
15220
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
15022
- var encoder3 = new TextEncoder();
15221
+ var encoder4 = new TextEncoder();
15023
15222
  var USERS_MODES = ["off", "report", "enforce"];
15024
15223
  function usersMode(mode = "off") {
15025
15224
  if (USERS_MODES.includes(mode)) return mode;
@@ -15169,12 +15368,19 @@ var Launcher = class {
15169
15368
  fstab = Promise.resolve([]);
15170
15369
  usersMode;
15171
15370
  dac;
15371
+ sealed;
15172
15372
  nextPid = 1e3;
15173
15373
  boot = Date.now();
15174
15374
  terminals = 0;
15175
15375
  constructor(options) {
15176
15376
  this.usersMode = usersMode(options.users);
15177
15377
  this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
15378
+ this.sealed = new SealedBroker(
15379
+ options.sealed,
15380
+ this.usersMode,
15381
+ void 0,
15382
+ (pid) => this.tagOf(pid)
15383
+ );
15178
15384
  if (this.owners) options.fs.trackOwners?.();
15179
15385
  this.mounts = new MountTable({
15180
15386
  open: (type, spec) => this.driver(type, spec),
@@ -15574,6 +15780,7 @@ var Launcher = class {
15574
15780
  ...req.umask !== void 0 ? { umask: req.umask } : {},
15575
15781
  cred: req.cred,
15576
15782
  ...this.owners ? { owners: true } : {},
15783
+ sealed: this.sealed,
15577
15784
  ...this.dac ? { dac: this.dac } : {},
15578
15785
  program: req.program,
15579
15786
  argv0: req.argv0,
@@ -16212,7 +16419,7 @@ var Launcher = class {
16212
16419
  },
16213
16420
  account
16214
16421
  );
16215
- const report = (message3) => options.onData(encoder3.encode(`${message3}\r
16422
+ const report = (message3) => options.onData(encoder4.encode(`${message3}\r
16216
16423
  `));
16217
16424
  const handle = await this.launch(planned, {
16218
16425
  env,
@@ -16249,7 +16456,7 @@ var Launcher = class {
16249
16456
  };
16250
16457
  const stdout = collect(out, options.onStdout);
16251
16458
  const stderr = collect(err, options.onStderr);
16252
- const report = (message3) => stderr(encoder3.encode(`${message3}
16459
+ const report = (message3) => stderr(encoder4.encode(`${message3}
16253
16460
  `));
16254
16461
  if (!planned) {
16255
16462
  report(`${file}: command not found`);
@@ -16274,6 +16481,33 @@ var Launcher = class {
16274
16481
  }
16275
16482
  };
16276
16483
 
16484
+ // src/sealed-relay.ts
16485
+ function sealedAsker(port) {
16486
+ const asks = /* @__PURE__ */ new Map();
16487
+ let next2 = 0;
16488
+ const hook = (req, { signal }) => {
16489
+ const id2 = ++next2;
16490
+ const waiting = Promise.withResolvers();
16491
+ asks.set(id2, waiting);
16492
+ const abort = () => {
16493
+ if (!asks.delete(id2)) return;
16494
+ port.postMessage({ sealedAbort: { id: id2, reason: signal.reason } });
16495
+ waiting.reject(new Error(String(signal.reason)));
16496
+ };
16497
+ signal.addEventListener("abort", abort, { once: true });
16498
+ port.postMessage({ sealedAsk: { id: id2, req } });
16499
+ return waiting.promise.finally(() => signal.removeEventListener("abort", abort));
16500
+ };
16501
+ const answer = ({ id: id2, reply, error }) => {
16502
+ const waiting = asks.get(id2);
16503
+ if (!waiting) return;
16504
+ asks.delete(id2);
16505
+ if (reply) waiting.resolve(reply);
16506
+ else waiting.reject(new Error(error ?? "no reply"));
16507
+ };
16508
+ return { hook, answer };
16509
+ }
16510
+
16277
16511
  // src/serve.ts
16278
16512
  async function inOpfs(deps, root) {
16279
16513
  if (deps.syncAccess !== true) return false;
@@ -16388,11 +16622,31 @@ function usersOp(l, req) {
16388
16622
  if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
16389
16623
  return l.users.list();
16390
16624
  }
16391
- function hostOptions(req, port) {
16625
+ function hostOptions(req, port, sealed) {
16392
16626
  return {
16393
16627
  ...req.hostname !== void 0 ? { hostname: req.hostname } : {},
16394
16628
  ...req.users !== void 0 ? { users: req.users } : {},
16395
- ...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {}
16629
+ ...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
16630
+ ...req.sealed ? { sealed } : {}
16631
+ };
16632
+ }
16633
+ function policyAsker(port) {
16634
+ const policies = /* @__PURE__ */ new Map();
16635
+ let nextPolicy = 0;
16636
+ const askPolicy = (req) => new Promise((resolve) => {
16637
+ const id2 = ++nextPolicy;
16638
+ policies.set(id2, resolve);
16639
+ port.postMessage({ mountPolicy: { id: id2, req } });
16640
+ });
16641
+ return {
16642
+ mountPolicy: (policy) => policy === void 0 ? {} : { processMounts: policy === "ask" ? askPolicy : policy },
16643
+ allowed(data) {
16644
+ const answer = data.mountAllowed;
16645
+ if (!answer) return false;
16646
+ policies.get(answer.id)?.(answer.allowed);
16647
+ policies.delete(answer.id);
16648
+ return true;
16649
+ }
16396
16650
  };
16397
16651
  }
16398
16652
  function serveKernel(port, deps) {
@@ -16402,6 +16656,7 @@ function serveKernel(port, deps) {
16402
16656
  const terminals = /* @__PURE__ */ new Map();
16403
16657
  const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
16404
16658
  const clients = /* @__PURE__ */ new Set();
16659
+ const sealed = sealedAsker(port);
16405
16660
  const approvals = sudoApprovals(port);
16406
16661
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
16407
16662
  let holding;
@@ -16466,7 +16721,7 @@ function serveKernel(port, deps) {
16466
16721
  ...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
16467
16722
  ...req.modules ? { modules: req.modules } : {},
16468
16723
  ...req.env ? { env: req.env } : {},
16469
- ...hostOptions(req, port),
16724
+ ...hostOptions(req, port, sealed.hook),
16470
16725
  ...req.approve ? { approve: approvals.ask } : {},
16471
16726
  ...net,
16472
16727
  ...media ? { media } : {},
@@ -16529,23 +16784,13 @@ function serveKernel(port, deps) {
16529
16784
  if (grant) waiting.resolve(grant);
16530
16785
  else waiting.reject(Object.assign(new Error(error ?? "no grant"), { code: "EACCES" }));
16531
16786
  }
16532
- const policies = /* @__PURE__ */ new Map();
16533
- let nextPolicy = 0;
16534
- const askPolicy = (req) => new Promise((resolve) => {
16535
- const id2 = ++nextPolicy;
16536
- policies.set(id2, resolve);
16537
- port.postMessage({ mountPolicy: { id: id2, req } });
16538
- });
16539
- const mountPolicy = (policy) => policy === void 0 ? {} : { processMounts: policy === "ask" ? askPolicy : policy };
16787
+ const { mountPolicy, allowed } = policyAsker(port);
16540
16788
  port.addEventListener("message", (event) => {
16789
+ const sealedReply = event.data.sealedReply;
16790
+ if (sealedReply) return sealed.answer(sealedReply);
16541
16791
  const grant = event.data.hostfsGrant;
16542
16792
  if (grant) return granted2(grant);
16543
- const answer = event.data.mountAllowed;
16544
- if (answer) {
16545
- policies.get(answer.id)?.(answer.allowed);
16546
- policies.delete(answer.id);
16547
- return;
16548
- }
16793
+ if (allowed(event.data)) return;
16549
16794
  if (answered(event.data, remote, uplink, approvals)) return;
16550
16795
  const req = event.data;
16551
16796
  handle(req).then(