@ai-ecoverse/slicc-vault 1.9.0 → 1.9.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.
Files changed (2) hide show
  1. package/README.md +7 -7
  2. package/package.json +6 -3
package/README.md CHANGED
@@ -164,7 +164,7 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
164
164
 
165
165
  | op (page role) | fields | reply |
166
166
  |---|---|---|
167
- | `sealed` | `req` (the `/dev/sealed` request object, or its JSON text), `stamp` (`{ pid, ppid, uid, gid, tag }` from the kernel) | `{ reply }`, the §3.7 reply, below |
167
+ | `sealed` | `req` (the `/dev/sealed` request object, or its JSON text), `stamp` (the kernel's `{ pid, ppid, uid, gid, tag, users, groups?, user? }`) | `{ reply }`, the §3.7 reply, below |
168
168
  | `status` | — | `{ status: { version, protocol, origin, locked, lock, scrub, waiting, exportedAt, backupReminder, policy } }` |
169
169
  | `placeholders` | — | `{ placeholders, scrub }`: every placeholder of a record that isn't removed, active and inert, sorted |
170
170
  | `list` | — | `{ list: { secrets, refused, audit, auditMore, exits } }` for Settings › Secrets |
@@ -262,7 +262,7 @@ Values are checked as at `put` (at least 8 bytes, no CR, LF or NUL, no placehold
262
262
 
263
263
  `fnox exec` (§3.5) asks for a one-shot ticket, and the kernel redeems it when it execs the child.
264
264
 
265
- **`materialize`** (a `/dev/sealed` op) takes `names`, `path` (the absolute program fnox will exec), `argv` (exact, `argv[0]` included), `cwd`, and optionally `interpreter`: fnox's claim of the absolute shebang or binfmt interpreter of `path`, omitted for a native program. It's only a claim; the kernel's chain at consume decides. `shellGrants: true` (`fnox exec --shell-grants`) adds every `shell = true` secret to `names`, which may then be omitted.
265
+ **`materialize`** (a `/dev/sealed` op) takes `names`, `path` (the absolute program fnox will exec), `argv` (exact, `argv[0]` included), `cwd`, and optionally `interpreter`: fnox's claim of the absolute shebang or binfmt interpreter of `path`, omitted for a native program. It's only a claim; the kernel's chain at consume decides. `shellGrants: true` (`fnox exec --shell-grants`) adds every `shell = true` secret to `names`, which may then be omitted. While the caller's stamp doesn't say `users: 'enforce'`, it's `EACCES` "kernel users must be enforce" in every `[exec]` mode, `shell` included (§3.16.2): with users off, every process is effectively root and the stamp proves nothing.
266
266
  - Each name must be a `secret` record (`ENOENT` otherwise) that hasn't expired (`EACCES: NAME expired; refreshing`). `real` says which will get their value: removed records and secrets without an `[exec]` grant stay `false` and keep their placeholder.
267
267
  - **`true`** (exec `any`): refused with `EACCES` when `path`, or `interpreter`, is a shell (`sh`, `bash`, `dash`, `zsh`, `fish`, `busybox`, `jsh`), an interpreter (`python*`, `node`, `deno`, `bun`, `perl`, `ruby`, `php`, `lua`, `wasmer`, `awk` and `gawk`/`mawk`/`nawk`, `sed`, `tclsh`, `expect`, `R`, `Rscript`, `irb`) or an env dumper or wrapper (`env`, `printenv`, `xargs`, `sudo`, `su`, `nohup`, `timeout`, `time`, `nice`, `strace`), by basename.
268
268
  - **`{ argv = [[…]] }`**: `real` only when `[path, ...argv[1..]]` starts with one of the listed prefixes. A listed shell or interpreter is allowed.
@@ -276,10 +276,10 @@ Values are checked as at `put` (at least 8 bytes, no CR, LF or NUL, no placehold
276
276
  - `stamp` is the exec'ing process.
277
277
  - `resolved` is the kernel-normalised file that actually runs: `path` with every symlink resolved.
278
278
  - `interpreters` is the absolute, symlink-resolved shebang/binfmt chain the kernel resolved for `path`, empty for a native binary. A plain `#!/usr/bin/env X` (or `env -S`) is reported as the resolved `X`; `env` with any other option or a `NAME=value` assignment is reported as `env` itself.
279
- - `trusted` is true when the program and every chain entry are root-owned regular files, not group- or other-writable, under root-owned, non-writable parents (the attestation rule). Under users `off` it's always false.
279
+ - `trusted` is true when the program and every chain entry are root-owned regular files, not group- or other-writable, under root-owned, non-writable parents (the attestation rule). Under users `off` it's always false, and the consume is refused anyway.
280
280
 
281
281
  The vault burns the ticket, then requires that:
282
- - it's unexpired and from this namespace, `stamp.ppid` is the pid that asked, and `path`, `argv` (element by element) and `cwd` are exactly the ones it was issued for;
282
+ - `stamp.users` is `enforce` (else "kernel users must be enforce"), it's unexpired and from this namespace, `stamp.ppid` is the pid that asked, and `path`, `argv` (element by element) and `cwd` are exactly the ones it was issued for;
283
283
  - `resolved` is an absolute path (missing or malformed refuses), `interpreters` a list of absolute paths and `trusted` a boolean; a claimed `interpreter` from fnox equals `interpreters[0]` (without a claim, the kernel's chain is taken as it is);
284
284
  - each real name is still granted by the policy as stored now (reloaded at materialize and at consume): a removed grant, or an `{ argv }` grant that no longer lists the command, refuses;
285
285
  - under that name's mode, the chain passes:
@@ -299,13 +299,13 @@ const channel = new MessageChannel();
299
299
  port.postMessage({ v: 1, id, op: 'forward', head, body, owner, exit, rawTraits }, [channel.port2]);
300
300
  ```
301
301
 
302
- - `head` is `{ method, url, headers: [name, value][] }`; `owner` the kernel's `{ pid, ppid, uid, gid, tag }`; `exit` the exit bios chose (`page`, `proxy`, `tailscale` or `device:<name>`); `rawTraits` that exit's `{ manualRedirects?, encodedBodies? }`. `body` (bytes or a string) is needed only for MCP requests (below); for every other request it stays in bios, because bodies are never injected.
302
+ - `head` is `{ method, url, headers: [name, value][] }`; `owner` the kernel's `{ pid, ppid, uid, gid, tag, users, groups?, user? }` (`users` must be `enforce`, else nothing is injected); `exit` the exit bios chose (`page`, `proxy`, `tailscale` or `device:<name>`); `rawTraits` that exit's `{ manualRedirects?, encodedBodies? }`. `body` (bytes or a string) is needed only for MCP requests (below); for every other request it stays in bios, because bodies are never injected.
303
303
  - **The reply** is `{ head }`, the same head with the values filled in, or `{ refused: { status, reason } }`: `403` with a one-line reason, or `502` for an exit with encoded bodies. A refused request must not be sent anywhere. Malformed fields are `EINVAL`.
304
304
  - **The response** goes back over `channel.port1`, scrubbed: bios posts `{ op: 'response', status, statusText, url, redirected?, headers }`, then any number of `{ op: 'body', chunk: Uint8Array }`, then `{ op: 'end' }` or `{ op: 'error', message }`. The vault answers with the same messages, with `statusText`, `url`, every header name and value, the body and the error scrubbed over every value of the namespace. A value in its raw form comes back as its placeholder, anything else as `[redacted:NAME]`. The body is scrubbed as a stream; after 250 ms without input, a held tail shorter than 16 bytes is let through.
305
305
 
306
306
  **The injector**, fnox's proxy semantics:
307
307
  - **Hosts** are compared lower-cased, without a trailing dot, in rules, the refusal list and MCP servers alike.
308
- - **Refused before anything is sent:** a placeholder in the URL or in a header name; credentials in the URL; a scheme other than `https:` or a port other than 443 (except §3.16.3's kernel servers, below); an ambiguous path (`.`/`..` segments, `\`, `%2e`, `%2f`, `%5c`, `%25`, checked on the raw URL); an exit with encoded bodies; no placeholder of this vault at all; a record that is removed, expired ("NAME expired; refreshing") or not a `secret`; a locked vault; no rule (in `/etc/fnox/config.toml`) whose exact domain, header, method and path allow it; an exit the secret's `[exits]` line doesn't list; the same placeholder twice in one header value; a header other than `authorization` on an exit that follows redirects (`manualRedirects` false); CR, LF or NUL after substitution.
308
+ - **Refused before anything is sent:** an `owner` whose `users` isn't `enforce` ("kernel users must be enforce", §3.16.2; this covers kernel programs calling a registered MCP server, and the `/dev/sealed` `test` verdict, too); a placeholder in the URL or in a header name; credentials in the URL; a scheme other than `https:` or a port other than 443 (except §3.16.3's kernel servers, below); an ambiguous path (`.`/`..` segments, `\`, `%2e`, `%2f`, `%5c`, `%25`, checked on the raw URL); an exit with encoded bodies; no placeholder of this vault at all; a record that is removed, expired ("NAME expired; refreshing") or not a `secret`; a locked vault; no rule (in `/etc/fnox/config.toml`) whose exact domain, header, method and path allow it; an exit the secret's `[exits]` line doesn't list; the same placeholder twice in one header value; a header other than `authorization` on an exit that follows redirects (`manualRedirects` false); CR, LF or NUL after substitution.
309
309
  - **Paths** in rules are globs: `*` stays within a segment, `**` crosses segments, `?` is one character.
310
310
  - **Injected:** each placeholder is replaced inside the rule's header value, so `Bearer …` and `token …` templates work. In `Authorization: Basic`, the credential is decoded, the user and password parts substituted (a substituted user may not contain `:`), and re-encoded. A Basic value that holds a placeholder but isn't valid base64 of UTF-8 is refused.
311
311
  - **Audited:** each injection adds a `forward` audit entry `{ name, domain, method, path, rule, exit, uid, tag }` and sets the record's `usedAt`. Never headers, bodies or values.
@@ -430,7 +430,7 @@ npm test # Chromium integration tests
430
430
  npm start # vault on http://vault.sliccy.localhost:8790, a test embedder on seven.sliccy.localhost
431
431
  ```
432
432
 
433
- - **Integration tests** (`test/integration/`) run the built vault on a local server that sends the production headers, at `http://vault.sliccy.localhost:<port>`, with test embedders on `seven.`, `preview.` and `evil.sliccy.localhost`. Chromium treats `*.sliccy.localhost` as one site and as a secure context, so the embedded frame gets the same first-party storage it gets under `sliccy.ai`. They use slicc-shared-web's CDP harness, plus a second CDP session for popups and the virtual WebAuthn authenticator.
433
+ - **Integration tests** (`test/integration/`) run the built vault on a local server that sends the production headers, at `http://vault.sliccy.localhost:<port>`, with test embedders on `seven.`, `preview.` and `evil.sliccy.localhost`. Chromium treats `*.sliccy.localhost` as one site and as a secure context, so the embedded frame gets the same first-party storage it gets under `sliccy.ai`. They use slicc-shared-web's CDP harness, plus a second CDP session for popups and the virtual WebAuthn authenticator. `kernel.test.mjs` runs exec tickets end to end against the real kernel (`@ai-ecoverse/slicc-kernel/node` with wasm-bash and wasm-coreutils, test dependencies only): its `/dev/sealed` hook relays to the built vault, so a `materialize`, the spawn with `SLICC_SEALED_TICKET`, the kernel's `materializeConsume`, the value swap and S2's scrubbing of the child's output all run as in seven, and with users `off` nothing is revealed.
434
434
  - **Unit tests** stay in the gitignored `test/unit/` (`npm run test:unit`), and the pre-commit hook requires them to cover every changed line of `src/`.
435
435
  - The Biome, TypeScript, lefthook and Renovate configuration comes from [slicc-shared-web](https://github.com/ai-ecoverse/slicc-shared-web). There are no comments in any file.
436
436
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-vault",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
4
4
  "description": "The separate-origin vault at vault.sliccy.ai: sealed secrets, OAuth tokens, and model and MCP credentials for SLICC seven, used only by its injector",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -46,9 +46,12 @@
46
46
  "release": "semantic-release"
47
47
  },
48
48
  "devDependencies": {
49
- "@ai-ecoverse/jerk-oauth": "0.15.1",
49
+ "@ai-ecoverse/jerk-oauth": "0.15.2",
50
+ "@ai-ecoverse/slicc-kernel": "1.83.0",
50
51
  "@ai-ecoverse/slicc-scrub": "1.0.0",
51
52
  "@ai-ecoverse/slicc-shared-web": "1.13.1",
53
+ "@ai-ecoverse/wasm-bash": "5.3.0-18",
54
+ "@ai-ecoverse/wasm-coreutils": "9.12.0-5",
52
55
  "@biomejs/biome": "2.5.15",
53
56
  "@earendil-works/pi-ai": "1.0.4",
54
57
  "@happy-dom/global-registrator": "20.14.5",
@@ -61,7 +64,7 @@
61
64
  "hash-wasm": "4.12.0",
62
65
  "kdbxweb": "2.1.1",
63
66
  "lefthook": "2.1.17",
64
- "monocart-coverage-reports": "2.13.1",
67
+ "monocart-coverage-reports": "2.13.2",
65
68
  "playwright-core": "1.63.0",
66
69
  "semantic-release": "25.0.9",
67
70
  "typescript": "7.0.2"