@ai-ecoverse/slicc-kernel 1.136.8 → 1.136.9
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 +3 -1
- package/dist/kernel-worker.js +12 -2
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +12 -2
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +1 -1
- package/dist/types/kernel/net/sealed-pg.d.ts +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -225,7 +225,7 @@ A WASI preview1 or WASIX program (Zig, Rust, Go, wasi-libc or wasix-libc C) has
|
|
|
225
225
|
{ "slicc": { "abi": "wasi", "commands": { "rg": { "wasm": "bin/rg.wasm" }, "rustc": { "script": "bin/rustc" } } } }
|
|
226
226
|
```
|
|
227
227
|
|
|
228
|
-
A command's name is any single path component without whitespace or control characters, so coreutils' `[` is in `/usr/bin` too; a name that is empty, `.` or `..`, or has a `/`, a space, a tab, a newline, NUL or another control character is skipped. Entries may also set `argv0`, `args` and `env`, and a package can set `slicc.env` for all of its commands. A WASI entry with `"preopenRoot": true` also gets `/` as a preopen (see [WASI programs](#wasi-programs)). An entry with `"argv0Path": true` gets the absolute path it was run by as `argv[0]` when that path is a link outside `/bin` and `/usr/bin` to the command, as a venv's `bin/python` is (the `#!` line of a script naming such a link included), so it can find what sits beside that link; other entries keep their `argv0`. In `env`, `${package}` is the package directory, `${cwd}` the process's working directory, and `${NAME}` the caller's `NAME` when the program starts (a default naming an unset variable is left out); a relative path that exists in the package (`lib/python3.14`, `./`) becomes that absolute path. The caller's environment wins over these defaults; a `null` value removes the variable for the command, after the caller's environment is applied (a host-networked program can drop the kernel's proxy settings that way), and a command entry can set a variable its package's `slicc.env` removes. The catalog reads its roots in order, and the first root that provides a name wins it: a user's own global packages (for that user's processes), `/node_modules`, root's global packages, then each appended root. Within a root, packages are read by name and the first wins (compared as strings by UTF-16 code unit, so a scoped `@scope/name` comes before names that start with a letter, but after those that start with a digit), whatever order the file system lists them in; a `PNPM_HOME`'s install groups count as one root, read group by group (by the group directory's name, effectively arbitrary for pnpm's hashed names), each by package name. A variant can take over from the package it extends with `"slicc": { "replaces": ["@ai-ecoverse/wasm-imagemagick"] }`: when it and a package it names are in the same root, it wins every command name they share, whatever the order; names only the other provides stay the other's, a third package that provides the name and sorts first still wins it, and packages that replace each other fall back to the name order. A replace never reaches into an earlier root, so root's global packages can't take a name from `/node_modules` or a user's own packages. The field must be an array of package names; anything else is ignored. `sh` runs bash as `sh` unless a package provides its own. A package's `engines.slicc-kernel` is a floor the kernel holds it to: when the running kernel's version doesn't satisfy that npm range (comparators, `^`, `~`, x-ranges, hyphen ranges, `||`), or the range can't be read, its commands are refused. They stay in `/usr/bin`, and running one by name, by path or as a `#!` interpreter exits 126 with `<cmd>: <pkg>@<version> needs slicc-kernel <range>; this kernel is <version>` on stderr. Each refused package is logged once to the console. A non-string `engines.slicc-kernel`, or none, is no constraint. A development build reports the newest release tag as `<tag>+dev` and checks against that; a build of unknown version (`0.0.0`) checks nothing. The refusal follows the program, not the name: its glue or `.wasm` run by path (directly or through a symlink), and a byte-identical copy of its glue beside it named for one of its commands (#390, #899), exit 126 with the same message ([#1235](https://github.com/ai-ecoverse/slicc-kernel/issues/1235)). Only commands are refused: a package without commands (a `py-*` library, a side module) isn't, and a refused package that replaces base commands makes those commands fail with 126 rather than fall back to the package it replaces. The installed commands appear as executables in a virtual `/usr/bin` and `/bin`, so `PATH` lookups, `command -v` and `ls /usr/bin` work without writing anything to OPFS. An entry reads as its program, as a PATH search that opens what it finds expects (`ruby -S rake`, `perl -S`, Pod::Usage looking itself up, bundler checking that `/usr/bin/ruby` isn't empty). A script command reads as its script, a JS command as its module, and a WASI or Emscripten command as its `.wasm`, so `cat /usr/bin/hello` prints the script and `head -c4 /usr/bin/cat` gives `\0asm`. The entry is a plain file and no link: it stays a root-owned `0755` regular file with its own inode, `readlink` of it is `EINVAL`, and `which` and `command -v` print `/usr/bin/<name>`. Its size and times are the program's when the caller may read the program (anyone, for a kernel-internal look or with users off, only when everyone may), and 0 and the epoch otherwise. Running it still runs the catalog command, with its `env`, `args` and `argv0`. The read is the caller's: with users on, reading an entry also needs `r` on its program, and that includes a user's own commands in `~/.local/share/pnpm/bin`, so an entry whose program the caller can't read is `EACCES`. A process can also run a program by the path of its glue when the `.wasm` sits next to it, a wasm module by its own path (as a WASI program; when it is the `wasm` of a WASI command in the caller's catalog, the user's own packages included, it runs as that command, with its `env` defaults, `args` and `argv0`, the command whose name matches the file's, without `.wasm`, if several share it, while a copy elsewhere runs bare), and a script through its `#!` line (including `#!/usr/bin/env name`). Executing any other file fails with `ENOEXEC`, so bash runs it as a shell script, as POSIX shells do. A spawn by a bare name (no `/`) that the catalog doesn't have, and that didn't ask for no search (a WASIX `posix_spawn` or `execve`, as opposed to its `posix_spawnp`/`execvp`, keeps its bare name), searches the `PATH` of the spawn's own environment, in order, with an empty entry meaning the working directory; each candidate is run exactly as a spawn of that path would be. The first that runs wins; if one was found but refused and nothing else matched, the spawn fails with `EACCES` (126), otherwise `ENOENT` (127). A catalog command still wins over the same name on `PATH`, the reverse of a shell's order. A searched bare name is never looked up in the working directory itself: a file of that name there runs only through an empty `PATH` entry, as with `execvp` ([#1234](https://github.com/ai-ecoverse/slicc-kernel/issues/1234)). The `PATH` searched is the new process's, from the spawn's environment; POSIX `posix_spawnp` uses the caller's, which is the same unless the caller hands the child another one. This is
|
|
228
|
+
A command's name is any single path component without whitespace or control characters, so coreutils' `[` is in `/usr/bin` too; a name that is empty, `.` or `..`, or has a `/`, a space, a tab, a newline, NUL or another control character is skipped. Entries may also set `argv0`, `args` and `env`, and a package can set `slicc.env` for all of its commands. A WASI entry with `"preopenRoot": true` also gets `/` as a preopen (see [WASI programs](#wasi-programs)). An entry with `"argv0Path": true` gets the absolute path it was run by as `argv[0]` when that path is a link outside `/bin` and `/usr/bin` to the command, as a venv's `bin/python` is (the `#!` line of a script naming such a link included), so it can find what sits beside that link; other entries keep their `argv0`. In `env`, `${package}` is the package directory, `${cwd}` the process's working directory, and `${NAME}` the caller's `NAME` when the program starts (a default naming an unset variable is left out); a relative path that exists in the package (`lib/python3.14`, `./`) becomes that absolute path. The caller's environment wins over these defaults; a `null` value removes the variable for the command, after the caller's environment is applied (a host-networked program can drop the kernel's proxy settings that way), and a command entry can set a variable its package's `slicc.env` removes. The catalog reads its roots in order, and the first root that provides a name wins it: a user's own global packages (for that user's processes), `/node_modules`, root's global packages, then each appended root. Within a root, packages are read by name and the first wins (compared as strings by UTF-16 code unit, so a scoped `@scope/name` comes before names that start with a letter, but after those that start with a digit), whatever order the file system lists them in; a `PNPM_HOME`'s install groups count as one root, read group by group (by the group directory's name, effectively arbitrary for pnpm's hashed names), each by package name. A variant can take over from the package it extends with `"slicc": { "replaces": ["@ai-ecoverse/wasm-imagemagick"] }`: when it and a package it names are in the same root, it wins every command name they share, whatever the order; names only the other provides stay the other's, a third package that provides the name and sorts first still wins it, and packages that replace each other fall back to the name order. A replace never reaches into an earlier root, so root's global packages can't take a name from `/node_modules` or a user's own packages. The field must be an array of package names; anything else is ignored. `sh` runs bash as `sh` unless a package provides its own. A package's `engines.slicc-kernel` is a floor the kernel holds it to: when the running kernel's version doesn't satisfy that npm range (comparators, `^`, `~`, x-ranges, hyphen ranges, `||`), or the range can't be read, its commands are refused. They stay in `/usr/bin`, and running one by name, by path or as a `#!` interpreter exits 126 with `<cmd>: <pkg>@<version> needs slicc-kernel <range>; this kernel is <version>` on stderr. Each refused package is logged once to the console. A non-string `engines.slicc-kernel`, or none, is no constraint. A development build reports the newest release tag as `<tag>+dev` and checks against that; a build of unknown version (`0.0.0`) checks nothing. The refusal follows the program, not the name: its glue or `.wasm` run by path (directly or through a symlink), and a byte-identical copy of its glue beside it named for one of its commands (#390, #899), exit 126 with the same message ([#1235](https://github.com/ai-ecoverse/slicc-kernel/issues/1235)). Only commands are refused: a package without commands (a `py-*` library, a side module) isn't, and a refused package that replaces base commands makes those commands fail with 126 rather than fall back to the package it replaces. The installed commands appear as executables in a virtual `/usr/bin` and `/bin`, so `PATH` lookups, `command -v` and `ls /usr/bin` work without writing anything to OPFS. An entry reads as its program, as a PATH search that opens what it finds expects (`ruby -S rake`, `perl -S`, Pod::Usage looking itself up, bundler checking that `/usr/bin/ruby` isn't empty). A script command reads as its script, a JS command as its module, and a WASI or Emscripten command as its `.wasm`, so `cat /usr/bin/hello` prints the script and `head -c4 /usr/bin/cat` gives `\0asm`. The entry is a plain file and no link: it stays a root-owned `0755` regular file with its own inode, `readlink` of it is `EINVAL`, and `which` and `command -v` print `/usr/bin/<name>`. Its size and times are the program's when the caller may read the program (anyone, for a kernel-internal look or with users off, only when everyone may), and 0 and the epoch otherwise. Running it still runs the catalog command, with its `env`, `args` and `argv0`. The read is the caller's: with users on, reading an entry also needs `r` on its program, and that includes a user's own commands in `~/.local/share/pnpm/bin`, so an entry whose program the caller can't read is `EACCES`. A process can also run a program by the path of its glue when the `.wasm` sits next to it, a wasm module by its own path (as a WASI program; when it is the `wasm` of a WASI command in the caller's catalog, the user's own packages included, it runs as that command, with its `env` defaults, `args` and `argv0`, the command whose name matches the file's, without `.wasm`, if several share it, while a copy elsewhere runs bare), and a script through its `#!` line (including `#!/usr/bin/env name`). Executing any other file fails with `ENOEXEC`, so bash runs it as a shell script, as POSIX shells do. A spawn by a bare name (no `/`) that the catalog doesn't have, and that didn't ask for no search (a WASIX `posix_spawn` or `execve`, as opposed to its `posix_spawnp`/`execvp`, keeps its bare name), searches the `PATH` of the spawn's own environment, in order, with an empty entry meaning the working directory; each candidate is run exactly as a spawn of that path would be. The first that runs wins; if one was found but refused and nothing else matched, the spawn fails with `EACCES` (126), otherwise `ENOENT` (127). A catalog command still wins over the same name on `PATH`, the reverse of a shell's order. A searched bare name is never looked up in the working directory itself: a file of that name there runs only through an empty `PATH` entry, as with `execvp` ([#1234](https://github.com/ai-ecoverse/slicc-kernel/issues/1234)). The `PATH` searched is the new process's, from the spawn's environment; POSIX `posix_spawnp` uses the caller's, which is the same unless the caller hands the child another one. This is `posix_spawnp`/`execvp` semantics for every caller that asks for a search: JS programs' `ctx.spawn`, WASI programs' spawn import, WASIX `posix_spawnp`, and Emscripten programs built on homescoop's shim before its own `PATH` search (gmake, tar, git and others hand the kernel the bare name). wasm-coreutils from 9.12.0-11 searches `PATH` itself and hands the kernel a path ([#1165](https://github.com/ai-ecoverse/slicc-kernel/issues/1165), [#1168](https://github.com/ai-ecoverse/slicc-kernel/issues/1168)). A `..` in a path that is run is resolved physically, as the file system resolves it: `pkg/../tools/x` after a symlinked `pkg` is the `tools/x` beside the link's target, and its exec permission is checked there. The walk is the caller's: with users on, each directory it passes needs search (`x`) permission (`EACCES`, 126 in bash, as on Linux), and a sudo process follows only the links it trusts, as for any other path. Commands also come from pnpm's global directory. Every process gets a `PNPM_HOME` and has `$PNPM_HOME/bin` on `PATH`: root's is `/usr/local/share/pnpm` (unless the embedder sets its own), a user's `~/.local/share/pnpm` (see [Users](#users)). The kernel scans each of pnpm's global install groups (`$PNPM_HOME/global/*/<group>/node_modules`) after `/node_modules`, which wins when both provide a command. The catalog is re-read whenever anything under either changes, so `pnpm add -g @ai-ecoverse/wasi-ripgrep` makes `rg` work in the same shell, and `pnpm remove -g` removes it again.
|
|
229
229
|
|
|
230
230
|
**More roots:** `createKernel({ commands: { append: ['/opt/user/node_modules'] } })` (and `createNodeKernel`) scans more directories, each absolute and normalised (anything else throws a `TypeError`). A name is looked up in a user's own global packages first, then in `modules`, then in root's global packages, then in the appended roots in the order given, and the first match wins, so an appended root never shadows another command (or `sudo`). Their commands are in `/usr/bin` for every user, like root's global packages, and their `binfmt` and file system entries count too. A package in an appended root also gets a command for each `bin` entry of its `package.json` (a string `bin` is named after the package, without its scope), which runs as a `#!` script; a name its `slicc.commands` gives wins. A `bin` entry is skipped when its name isn't a command name (see above), or its file, with symlinks resolved, isn't a regular file inside the package. `/node_modules` and the global packages keep reading only `slicc.commands`. A missing root counts as empty until it appears, and an install or removal in a root is picked up as it happens. With `users: 'enforce'` a root counts only when it and every directory above it are root's and not writable by group or others; another root is skipped without a word. Under `'report'` such a root is still scanned, and reported once to `onDenial` and the report log as `{ pid: 0, uid: 0, op: 'commands', path, errno: 'EACCES' }`. Running a `bin` command needs `r` and `x` on its file under `users` (see [Permissions](#permissions)), checked when it runs, so a `0644` file is `EACCES` under `'enforce'`.
|
|
231
231
|
|
|
@@ -505,6 +505,8 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
505
505
|
- `AUTH LOGIN`: the password line (the second line, or the first after `AUTH LOGIN <user>`) is decoded, substituted (`auth-login`) and re-encoded; the user line goes out as written.
|
|
506
506
|
- Every other line goes out as written. After the first `AUTH`, or once `MAIL`, `RCPT`, `DATA` or `BDAT` is seen, so is the rest of the session. A line still unfinished after 2 s, or longer than 4 KiB, is sent as written, and so is the rest.
|
|
507
507
|
- **pg:** a session that starts with a protocol 3 `StartupMessage` (after `sslmode`'s `SSLRequest` upgrade, or with direct TLS) is followed message by message. The only position is the **`PasswordMessage` answering `AuthenticationCleartextPassword` (`R` 3)**, as the program read it from the server. The password is substituted (`password-cleartext`) and the message is framed again. **SCRAM-SHA-256** (`R` 10, the pg 14+ default) works with an unmodified client:
|
|
508
|
+
- **Only where a value can be substituted:** the kernel follows SCRAM only in a session where a sealed value could reach this host: one the bundled roots verified, or one a vault anchor verified (`sessionAnchors` found an anchor for this destination; it's asked once, when the pg session starts). In any other session, such as one verified by the program's own `ca` (a private-CA server, `sslrootcert`) or not verified at all (`sslmode=require`), the mechanisms, the GS2 header and `c=` pass through byte for byte, so a program's own password with any `channel_binding` works as it does without the kernel ([slicc-jsh#584](https://github.com/ai-ecoverse/slicc-jsh/issues/584)).
|
|
509
|
+
- **In an anchored session:** `-PLUS` is not offered and the server side is `n,,`, so a program's own password with `channel_binding=prefer` against a `-PLUS` server is refused there, and `require` fails closed, until the binding is taken from the anchor-verified leaf ([#1259](https://github.com/ai-ecoverse/slicc-kernel/issues/1259)).
|
|
508
510
|
- **What the program is offered:** the server's mechanisms. `SCRAM-SHA-256-PLUS` is offered only when the kernel can bind to the server: a verified session whose leaf certificate's signature hash is SHA-256, SHA-384 or SHA-512, with MD5 and SHA-1 counting as SHA-256 (RFC 5929 §4.1). With any other hash (RSA-PSS or Ed25519, say), `-PLUS` is dropped. So libpq's default `channel_binding=prefer`, `require`, and node-postgres' `enableChannelBinding` all work.
|
|
509
511
|
- **Channel binding (`tls-server-end-point`):** the kernel changes only what the server would refuse.
|
|
510
512
|
- `n,,` goes out as written.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -7098,7 +7098,7 @@ function satisfies(version, range2) {
|
|
|
7098
7098
|
}
|
|
7099
7099
|
|
|
7100
7100
|
// src/version.ts
|
|
7101
|
-
var KERNEL_VERSION = true ? "1.136.
|
|
7101
|
+
var KERNEL_VERSION = true ? "1.136.9" : "0.0.0-dev";
|
|
7102
7102
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7103
7103
|
function procVersion() {
|
|
7104
7104
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -19484,6 +19484,7 @@ function latin2(bytes) {
|
|
|
19484
19484
|
var PgServer = class {
|
|
19485
19485
|
binding;
|
|
19486
19486
|
plus = false;
|
|
19487
|
+
touch;
|
|
19487
19488
|
constructor(binding) {
|
|
19488
19489
|
this.binding = binding;
|
|
19489
19490
|
}
|
|
@@ -19505,6 +19506,7 @@ var PgServer = class {
|
|
|
19505
19506
|
return message3;
|
|
19506
19507
|
}
|
|
19507
19508
|
mechanisms(message3) {
|
|
19509
|
+
if (this.touch !== true) return message3;
|
|
19508
19510
|
this.sasl = true;
|
|
19509
19511
|
const offered = latin2(message3.subarray(9)).split("\0").filter((n) => n !== "");
|
|
19510
19512
|
this.plus = offered.includes(PLUS);
|
|
@@ -19864,6 +19866,10 @@ var SessionWriter = class {
|
|
|
19864
19866
|
this.proven ??= this.prove(protocol).catch(() => void 0);
|
|
19865
19867
|
return this.proven;
|
|
19866
19868
|
}
|
|
19869
|
+
async mayTouch() {
|
|
19870
|
+
if (this.s.verified) return true;
|
|
19871
|
+
return (await this.proof("pg"))?.anchor !== void 0;
|
|
19872
|
+
}
|
|
19867
19873
|
async prove(protocol) {
|
|
19868
19874
|
const s = this.s;
|
|
19869
19875
|
if (s.anchoring) {
|
|
@@ -19913,7 +19919,10 @@ var SessionWriter = class {
|
|
|
19913
19919
|
this.buffer.fill(0);
|
|
19914
19920
|
this.buffer = joined4;
|
|
19915
19921
|
if (this.mode === "sniff") this.mode = modeOf(latin12(joined4.subarray(0, 8))) ?? "sniff";
|
|
19916
|
-
if (this.mode === "pg"
|
|
19922
|
+
if (this.mode === "pg" && !this.sawPg) {
|
|
19923
|
+
this.sawPg = true;
|
|
19924
|
+
if (!this.s.verified) void this.proof("pg");
|
|
19925
|
+
}
|
|
19917
19926
|
if (this.mode === "pass") await this.release();
|
|
19918
19927
|
else if (this.mode === "http1") await this.http1();
|
|
19919
19928
|
else if (this.mode === "smtp") await this.lines();
|
|
@@ -20093,6 +20102,7 @@ function sealedConn(conn, s) {
|
|
|
20093
20102
|
for (; ; ) {
|
|
20094
20103
|
const bytes = await conn.read();
|
|
20095
20104
|
if (!bytes) return bytes;
|
|
20105
|
+
if (writer?.sawPg && pg.touch === void 0) pg.touch = await writer.mayTouch();
|
|
20096
20106
|
const out = writer?.sawPg ? pg.filter(bytes) : bytes;
|
|
20097
20107
|
if (pg.failed) {
|
|
20098
20108
|
conn.close();
|