@ai-ecoverse/slicc-kernel 1.139.0 → 1.140.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/README.md CHANGED
@@ -610,7 +610,7 @@ The kernel answers DNS itself, so DNS clients (BIND's `dig`, `host` and `nslooku
610
610
  ### Node name
611
611
 
612
612
  The kernel has one node name, `slicc` unless `createKernel({ hostname })` (or `createNodeKernel`) names another; it must be a valid host name, and not `localhost`, a `*.localhost` name, `host.slicc.internal`, `wasmer.sh` or an IPv4 address, or `createKernel` rejects. `no_proxy` lists it (and `emscripten`), so HTTP clients reach the kernel's own servers by it directly.
613
- - **Where programs see it:** every process gets it in `HOSTNAME`, which the kernel sets on every start whatever the caller's environment says. Emscripten programs also see it in `/etc/hostname` (next to the synthetic `/etc/hosts` and `/etc/passwd`, which only they have; WASI programs resolve names through the kernel instead) and in `Module.sliccKernel.hostname`.
613
+ - **Where programs see it:** every process gets it in `HOSTNAME`, which the kernel sets on every start whatever the caller's environment says. Every program can read it from `/etc/hostname`: the node name and a newline, a `0644` root-owned file that a real `/etc/hostname` in the root replaces ([#1297](https://github.com/ai-ecoverse/slicc-kernel/issues/1297)). Emscripten programs also have the synthetic `/etc/hosts` and `/etc/passwd`, which only they have (WASI programs resolve names through the kernel instead), and `Module.sliccKernel.hostname`.
614
614
  - **What `uname` says:** for an Emscripten program built with homescoop's `__syscall_uname` import (wasm-coreutils 9.12.0-7 on), the kernel answers `uname(2)`: sysname `Linux`, nodename the node name, release `6.0.0-slicc` (as `/proc/sys/kernel/osrelease`), version `#1 SMP PREEMPT_DYNAMIC` (as `/proc/version`), machine `wasm32` and domainname `(none)`, so `uname -a` and musl's `gethostname()` see them. `uname -o` is coreutils' compiled-in `Emscripten`. Older Emscripten builds still report their C library's `Emscripten 4.0.23` and `emscripten`, and WASIX programs `wasmer.sh`; the kernel has nothing to answer there.
615
615
  - **Resolving it:** `/etc/hosts` maps the node name and both of those names to `127.0.1.1` (Debian's convention), and name resolution answers them, and `localhost.localdomain`, itself, as `127.0.1.1` and `::1`. So a program that looks up its own host name, as git does for every commit, never asks the [uplink](#uplink), and `127.0.1.1` is the kernel's loopback.
616
616
  - **The trade-off:** a raw connection to the real `wasmer.sh` resolves to loopback too. HTTP(S) to it still goes through the realm proxy and the transport, which resolve names themselves; `wasmer.sh` is deliberately not in `no_proxy`.
@@ -1235,7 +1235,7 @@ Releases are cut by semantic-release on every push to `main`, in two jobs, and o
1235
1235
 
1236
1236
  Every change is certified in its pull request, before it merges (`cert-review-pr.yml`, next section), and `cert-review` is a required check of the merge queue, so every commit on `main`, and every release built from it, was certified first. A release is then **verified on npm** (`release / verify on npm` in `release.yml`, `tools/cert-review/verify.mjs`), in code, for every tag the run made:
1237
1237
 
1238
- - It waits up to an hour for npm to serve the version (an accepted publish can take a while to appear; 1.125.4 took 55 min). `release-recover` logs `npm publish`'s own output every time, and says PASS for a version it published only once npm serves it (the tarball answers) with `dist.integrity` equal to the tarball it packed; the reference is the `integrity` of `npm publish --json`'s own report, the bytes npm uploaded (a separate pack isn't byte-identical; 1.136.1 was served fine and still read as not served), and it publishes from the folder, never a tarball, so the packument keeps `gitHead`; an exit 0 without a report fails, and a version npm says is already published has no report, so it's only checked for being served; a version npm took but doesn't serve so after 25 min is `release-recover: INCOMPLETE`, and the publish job fails (#1228: 1.134.2 was taken, never served, and still read as PASS). A version semantic-release itself just published is handed to this job instead. A version npm still doesn't serve after the hour, and doesn't list, while a newer one of ours is its `latest`, ends with `verify: SKIPPED <name>@<version>: never served by npm; superseded by <latest>` (the gate takes SKIPPED like PASS); only after the whole wait, since a slow version can appear late and must not go unverified. SKIPPED writes no certified line, so nothing downstream reads it as certified; if npm serves a skipped version later, the coordinator's merge watchdog notes it within 10 min (any version that appears below npm's `latest`), and the coordinator re-runs `release / verify on npm` in that version's Release run for the normal verdict, the certified line or the supply-chain alert. One npm still doesn't serve, with nothing newer or a version list it can't read, is a "not served yet, re-run" failure, never a supply-chain alert. It then downloads the tarball and checks `dist.integrity` (sha512), the SLSA v1 provenance (the tarball's sha512 is its subject; `release.yml` on `refs/heads/main`, a GitHub-hosted builder; `gitCommit` = `gitHead` = the tag, on `main`), and `npm audit signatures`. npm serves a version's attestations a little after the version itself, so a 404 from the attestations, a missing `dist.attestations`, an `npm view` or network error while looking for them, or `npm audit signatures`' E404 on them, is retried within the same hour: it's a supply-chain alert only once the version has been served for 30 min, its packument has stopped changing for 10, and the attestations still answer 404, or the hour has run out. Any other status, or attestations that don't match, fail at once. A version that `release-recover.mjs` skipped because npm's `latest` is newer (#837) has nothing to verify.
1238
+ - It waits up to an hour for npm to serve the version (an accepted publish can take a while to appear; 1.125.4 took 55 min). `release-recover` publishes a version it has to recover with a plain `npm publish --provenance` from the folder (so the packument keeps `gitHead`) and logs npm's own exit code and output; it fails only on npm's own non-zero exit, and npm's "cannot publish over the previously published version" on a re-run counts as already done. Whether npm serves the version is this job's to check. A version npm still doesn't serve after the hour, and doesn't list, while a newer one of ours is its `latest`, ends with `verify: SKIPPED <name>@<version>: never served by npm; superseded by <latest>` (the gate takes SKIPPED like PASS); only after the whole wait, since a slow version can appear late and must not go unverified. SKIPPED writes no certified line, so nothing downstream reads it as certified; if npm serves a skipped version later, the coordinator's merge watchdog notes it within 10 min (any version that appears below npm's `latest`), and the coordinator re-runs `release / verify on npm` in that version's Release run for the normal verdict, the certified line or the supply-chain alert. One npm still doesn't serve, with nothing newer or a version list it can't read, is a "not served yet, re-run" failure, never a supply-chain alert. It then downloads the tarball and checks `dist.integrity` (sha512), the SLSA v1 provenance (the tarball's sha512 is its subject; `release.yml` on `refs/heads/main`, a GitHub-hosted builder; `gitCommit` = `gitHead` = the tag, on `main`), and `npm audit signatures`. npm serves a version's attestations a little after the version itself, so a 404 from the attestations, a missing `dist.attestations`, an `npm view` or network error while looking for them, or `npm audit signatures`' E404 on them, is retried within the same hour: it's a supply-chain alert only once the version has been served for 30 min, its packument has stopped changing for 10, and the attestations still answer 404, or the hour has run out. Any other status, or attestations that don't match, fail at once. A version that `release-recover.mjs` skipped because npm's `latest` is newer (#837) has nothing to verify.
1239
1239
  - It then writes `certified @ai-ecoverse/slicc-kernel@<v> sha256=<64 hex> (covers <first>–<v>; ci: <run>; pr-certified: <each merged pull request's cert-review at its merge>; known: <open known-issue issues>)` into the GitHub release's notes, between `<!-- cert-verify -->` markers. homescoop's ci reads exactly that prefix and re-hashes the npm tarball against it. `covers` starts after the last version npm serves, so a skipped version is covered by the next. Notes that already carry the identical line stay unchanged. A pull request's `cert-review` is the newest finished one (success, failure or error) on its head at or before its merge, or the newest pending one if none had finished. A re-review that a later `ready_for_review`, `reopened` or `cert-review-approved` event starts on the same head, still pending at the merge or ending after it, doesn't change what the merge queue saw, and a status posted after the merge can't change a release's line.
1240
1240
  - A mismatch writes no line. The job is red with a `supply-chain alert`, and so is a line already in the notes with another sha256.
1241
1241
 
@@ -178,13 +178,13 @@ var IndexedDbMeta = class {
178
178
  return [transaction, transaction.objectStore(META_STORE)];
179
179
  }
180
180
  async get(paths) {
181
- const missing4 = paths.filter((path) => !this.cache.has(path));
182
- if (missing4.length > 0) {
181
+ const missing5 = paths.filter((path) => !this.cache.has(path));
182
+ if (missing5.length > 0) {
183
183
  this.settleRemoves();
184
184
  const generation = this.generation;
185
185
  const [transaction, store] = this.transaction("readonly");
186
186
  const found = /* @__PURE__ */ new Map();
187
- for (const path of missing4) {
187
+ for (const path of missing5) {
188
188
  const request3 = store.get(path);
189
189
  request3.onsuccess = () => void found.set(path, request3.result ?? null);
190
190
  }
@@ -7125,7 +7125,7 @@ function satisfies(version, range2) {
7125
7125
  }
7126
7126
 
7127
7127
  // src/version.ts
7128
- var KERNEL_VERSION = true ? "1.139.0" : "0.0.0-dev";
7128
+ var KERNEL_VERSION = true ? "1.140.0" : "0.0.0-dev";
7129
7129
  var OS_RELEASE = "6.0.0-slicc";
7130
7130
  function procVersion() {
7131
7131
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9805,6 +9805,70 @@ function devCreation(path, cred) {
9805
9805
  return cred && cred.euid !== 0 ? "EACCES" : "EPERM";
9806
9806
  }
9807
9807
 
9808
+ // src/fs/etc-files.ts
9809
+ var HOSTNAME_PATH = "/etc/hostname";
9810
+ var S_IFREG3 = 32768;
9811
+ function missing4(err) {
9812
+ return err?.code === "ENOENT";
9813
+ }
9814
+ function statOf(path, bytes) {
9815
+ const epoch = /* @__PURE__ */ new Date(0);
9816
+ return {
9817
+ isFile: true,
9818
+ isDirectory: false,
9819
+ isSymbolicLink: false,
9820
+ size: bytes.length,
9821
+ mode: S_IFREG3 | 420,
9822
+ mtime: epoch,
9823
+ atime: epoch,
9824
+ ctime: epoch,
9825
+ ino: inodeOf(path),
9826
+ nlink: 1,
9827
+ uid: 0,
9828
+ gid: 0
9829
+ };
9830
+ }
9831
+ function withEtcFiles(fs, files) {
9832
+ const bytesOf2 = (path) => {
9833
+ const text = files.get(path)?.();
9834
+ return text === void 0 ? void 0 : new TextEncoder().encode(text);
9835
+ };
9836
+ const absent2 = (path) => fs.lstat(path).then(() => false, missing4);
9837
+ const fallback = (path, make) => async (err) => {
9838
+ const bytes = missing4(err) && files.has(path) && await absent2(path) ? bytesOf2(path) : void 0;
9839
+ if (bytes === void 0) throw err;
9840
+ return make(bytes);
9841
+ };
9842
+ const ranged2 = rangedOps(fs);
9843
+ const own = {
9844
+ stat: (path) => fs.stat(path).catch(fallback(path, (b) => statOf(path, b))),
9845
+ lstat: (path) => fs.lstat(path).catch(fallback(path, (b) => statOf(path, b))),
9846
+ exists: async (path) => await fs.exists(path) || files.has(path) && await absent2(path),
9847
+ ...fs.createExclusive ? {
9848
+ createExclusive: async (path, owner) => {
9849
+ if (files.has(path) && await absent2(path)) throw fsError("EEXIST", path);
9850
+ return fs.createExclusive.call(
9851
+ fs,
9852
+ path,
9853
+ owner
9854
+ );
9855
+ }
9856
+ } : {},
9857
+ readFile: (path) => fs.readFile(path).catch(fallback(path, (b) => new TextDecoder().decode(b))),
9858
+ readFileBuffer: (path) => fs.readFileBuffer(path).catch(fallback(path, (b) => b)),
9859
+ ...ranged2 ? {
9860
+ pread: (path, offset, length2, version) => ranged2.pread(path, offset, length2, version).catch(fallback(path, (b) => b.slice(offset, offset + length2)))
9861
+ } : {}
9862
+ };
9863
+ return new Proxy(fs, {
9864
+ get(target, key, receiver) {
9865
+ if (Object.hasOwn(own, key)) return own[key];
9866
+ const value2 = Reflect.get(target, key, receiver);
9867
+ return typeof value2 === "function" ? value2.bind(target) : value2;
9868
+ }
9869
+ });
9870
+ }
9871
+
9808
9872
  // src/fs/fresh-modules.ts
9809
9873
  var MAGIC = [0, 97, 115, 109];
9810
9874
  var MAX_FRESH = 65536;
@@ -11260,7 +11324,7 @@ function inodeless(mount, attr2) {
11260
11324
  if (mount.caps.links) mount.caps = { ...mount.caps, links: false };
11261
11325
  return true;
11262
11326
  }
11263
- function statOf(attr2, path, mount) {
11327
+ function statOf2(attr2, path, mount) {
11264
11328
  const mtime = new Date(attr2.mtime);
11265
11329
  const fallback = attr2.kind === "directory" ? 16877 : attr2.kind === "symlink" ? 41471 : attr2.kind === "fifo" ? 4516 : 33188;
11266
11330
  return {
@@ -11732,7 +11796,7 @@ var MountFs = class _MountFs {
11732
11796
  const { mount, rel: rel2 } = found;
11733
11797
  if (!owner || !mount.owners || !mount.caps.owners) return void 0;
11734
11798
  const dir = parentOf7(real);
11735
- const above = statOf(await this.table.getattr(mount, dir, parentOf7(rel2)), dir, mount);
11799
+ const above = statOf2(await this.table.getattr(mount, dir, parentOf7(rel2)), dir, mount);
11736
11800
  const setgid = (above.mode & S_ISGID4) !== 0;
11737
11801
  let mode = owner.mode;
11738
11802
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
@@ -12064,7 +12128,7 @@ var MountFs = class _MountFs {
12064
12128
  }
12065
12129
  statWriting(attr2, real, mount) {
12066
12130
  const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
12067
- return statOf({ ...attr2, size: Math.max(attr2.size, ...ends) }, real, mount);
12131
+ return statOf2({ ...attr2, size: Math.max(attr2.size, ...ends) }, real, mount);
12068
12132
  }
12069
12133
  async stat(path, redirects = 0) {
12070
12134
  const real = await this.follow(path);
@@ -15475,11 +15539,11 @@ function ipv6(host) {
15475
15539
  const back = halves.length === 2 ? groups(halves[1]) : [];
15476
15540
  if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
15477
15541
  const words2 = [...head, ...back].map((g) => Number.parseInt(g, 16));
15478
- const missing4 = 8 - words2.length - tail2.length;
15479
- if (halves.length === 2 ? missing4 < 1 : missing4 !== 0) return void 0;
15542
+ const missing5 = 8 - words2.length - tail2.length;
15543
+ if (halves.length === 2 ? missing5 < 1 : missing5 !== 0) return void 0;
15480
15544
  const all = [
15481
15545
  ...head.map((g) => Number.parseInt(g, 16)),
15482
- ...new Array(halves.length === 2 ? missing4 : 0).fill(0),
15546
+ ...new Array(halves.length === 2 ? missing5 : 0).fill(0),
15483
15547
  ...back.map((g) => Number.parseInt(g, 16)),
15484
15548
  ...tail2
15485
15549
  ];
@@ -27888,13 +27952,13 @@ function interpreter(head) {
27888
27952
  function expandDefaults(defaults2, env) {
27889
27953
  const out = {};
27890
27954
  for (const [key, value2] of Object.entries(defaults2 ?? {})) {
27891
- let missing4 = false;
27955
+ let missing5 = false;
27892
27956
  const expanded = value2.replace(ENV_REFERENCE, (_, name2) => {
27893
27957
  const set3 = Object.hasOwn(env, name2) ? env[name2] : void 0;
27894
- if (set3 === void 0) missing4 = true;
27958
+ if (set3 === void 0) missing5 = true;
27895
27959
  return set3 ?? "";
27896
27960
  });
27897
- if (!missing4) out[key] = expanded;
27961
+ if (!missing5) out[key] = expanded;
27898
27962
  }
27899
27963
  return out;
27900
27964
  }
@@ -30065,7 +30129,8 @@ var Launcher = class {
30065
30129
  }
30066
30130
  commandFs(root) {
30067
30131
  return withCommandDirs(
30068
- root,
30132
+ withEtcFiles(root, /* @__PURE__ */ new Map([[HOSTNAME_PATH, () => `${this.hostname}
30133
+ `]])),
30069
30134
  async () => this.commandNames(await this.commands()),
30070
30135
  (dir) => this.userCommandDir(dir),
30071
30136
  this.owners,