@ai-ecoverse/slicc-vault 1.11.0 → 1.12.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
@@ -112,7 +112,8 @@ Otherwise the vault keeps the policy it last accepted for that file, so the rule
112
112
  - **Session rules** (§3.19) add `protocol` and `port` (an integer from 1 to 65535), and are used only by `sessionMaterialize`, never by `forward`; a rule without `protocol` is used only by `forward`:
113
113
  - `protocol = "http1"`: `header`, `methods` and `paths` as above; the position is the header. A `position` is a warning.
114
114
  - `protocol = "smtp"`, with `position = "auth-plain"` or `"auth-login"`, or `protocol = "pg"`, with `position = "password-cleartext"` or `"scram-sha-256"` (each allowed only where a rule names it): no `header`, `methods` or `paths` (a warning otherwise). Its `header` reads `''`.
115
- - `port` or `position` without `protocol`, another `protocol`, or a missing or wrong `port` or `position` is a warning, and the rule is dropped.
115
+ - **Trust anchors** (§3.19.6), on any session rule: `anchors` (base64 DER certificates, each starting with a SEQUENCE and at most 8 KB), `pins` (base64 SHA-256 of a public key, 32 bytes) and `roots` (`true` or `false`), at most 8 anchors and 8 pins. A rule with anchors or pins accepts only sessions the kernel verified against them, never the public roots, unless it says `roots = true`; a rule without them accepts the public roots, and `roots = false` there is a warning. The vault checks only their format: the kernel checks that an anchor is a CA (CA:true and keyCertSign) and the chain. Like every rule, they come only from the attested root-owned file, so only an approved `sudo` write sets them: no program, agent or page op can.
116
+ - `port`, `position`, `anchors`, `pins` or `roots` without `protocol`, another `protocol`, or a missing or wrong `port`, `position`, `anchors`, `pins` or `roots` is a warning, and the rule is dropped.
116
117
  - A record of kind `secret` is `active` when the file's `[secrets]` entry of its name holds the record's own placeholder and at least one rule names it. A placeholder from another vault or an old record doesn't activate anything.
117
118
  - Nothing else in the file counts: no imports, profiles or other providers.
118
119
 
@@ -184,7 +185,8 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
184
185
  | `policy` | `files` | the attested policy files, below (Policy) |
185
186
  | `forward` | `head`, `body?`, `owner`, `exit`, `rawTraits`, with a `MessagePort` transferred alongside | the injected head, below (Forward) |
186
187
  | `materializeConsume` | `ticket`, `stamp`, `path`, `resolved`, `argv`, `cwd`, `interpreters`, `trusted` | the values for the child's environment, below (Exec) |
187
- | `sessionMaterialize` | `stamp`, `session`, `host`, `port`, `protocol`, `position`, `verified`, `handshakeAt`, `exit`, then `placeholders` (and `head` for http1) at every position but `scram-sha-256`, which takes `scram` and no `placeholders` | `{ values }` for a kernel-verified TLS session, below (Sessions) |
188
+ | `sessionMaterialize` | `stamp`, `session`, `host`, `port`, `protocol`, `position`, `verified`, `handshakeAt`, `exit`, `verifiedBy?`, `anchor?`, then `placeholders` (and `head` for http1) at every position but `scram-sha-256`, which takes `scram` and no `placeholders` | `{ values }` for a kernel-verified TLS session, below (Sessions) |
189
+ | `sessionAnchors` | `stamp`, `session`, `host`, `port`, `protocol`, `exit` | `{ anchors, pins }`, below (Sessions) |
188
190
  | `import` | `kind`, `bytes`, `password?`, `pick?` | below (Import and export) |
189
191
  | `export` | `passphrase` | `{ bundle, count }`, below |
190
192
  | anything else | | `ENOTSUP` |
@@ -329,6 +331,7 @@ The fields are the kernel's session attestation:
329
331
  - `host`: the name the kernel verified the leaf against, compared lower-cased without one trailing dot; `port`; `protocol` (`http1`, `smtp` or `pg`).
330
332
  - `position`: `header:<name>` (lower-cased) for http1, else `auth-plain`, `auth-login`, `password-cleartext` or `scram-sha-256` (below).
331
333
  - `verified`: the kernel's own verdict on the chain and the name; `handshakeAt`: when the handshake finished (epoch ms).
334
+ - `verifiedBy`: `roots` (the default; the kernel's bundled public roots) or `anchored` (a rule's anchors or pins, below), with `anchor`: base64 of the SHA-256 of the whole DER of the anchor that verified the chain, as `sessionAnchors` gave it, or the pin itself. `anchor` with `roots`, or a missing or malformed one with `anchored`, is `EINVAL`.
332
335
  - `exit`: the exit the session leaves by (`page`, `proxy`, `tailscale` or `device:<id>`, with the device's `DeviceId`).
333
336
  - `placeholders` (every position but `scram-sha-256`, which takes `scram` instead, below): 1 to 16 placeholders, every one found at the position, in one request. `head` (http1 only, required there): `{ method, path }`, the request line's method and target.
334
337
  - A malformed field is `EINVAL`, and so is a `protocol` other than these or a `position` that isn't one of its protocol's; neither burns the session.
@@ -344,6 +347,8 @@ Each placeholder then gets its value only when it is a `secret` record's, holdin
344
347
 
345
348
  It answers `{ values: { [placeholder]: value } }` for those, and leaves the others out, so the kernel sends them as written. When none gets a value, it's `EACCES` with the first placeholder's reason: "NAME isn't a secret", "a placeholder of no record of this vault", "NAME's placeholder isn't the one /etc/fnox/config.toml names", "NAME was removed", "NAME expired", "no session rule in /etc/fnox/config.toml lets NAME be used", "no session rule lets NAME at PROTOCOL POSITION on HOST:PORT" (with " for METHOD PATH" for http1), "NAME isn't allowed on EXIT", or "the secret or its rule changed meanwhile". Each value handed out adds a `session` audit entry `{ name, session, domain, port, protocol, position, rule, exit, uid, tag }`, `rule` being the number of the rule that allowed it, and sets the record's `usedAt`. It never includes the value. Stored values never hold CR, LF or NUL (Records), so a value can't break out of its position.
346
349
 
350
+ **`sessionAnchors`** (§3.19.6) is value-free: when the kernel's bundled roots fail a session only on trust (the leaf names the host and the chain is otherwise valid), it asks with `{ stamp, session, host, port, protocol, exit }` (checked as above; `users` must be `enforce`), and the vault answers `{ anchors, pins }`: every anchor and pin of the session rules for that `protocol`, `domain` and `port` whose secret's `[exits]` line, if any, lists `exit`, without duplicates, or empty lists. It burns nothing and needs no unlocked vault. The kernel then verifies the chain against those anchors only (never the program's `ca`), and asks `sessionMaterialize` with `verifiedBy: 'anchored'` and the `anchor` it used. A placeholder gets its value only by a rule that trusts the session: a rule with anchors or pins needs an `anchored` session whose `anchor` is one of that rule's own ("the anchor that verified the session isn't one of NAME's session rule" otherwise), or `roots = true` for a `roots` session ("NAME's session rule accepts only its anchors and pins, not public roots"); a rule without them takes a `roots` session only. So one rule's anchor never opens another rule's secret, and a pinned host can't fall back to a public CA.
351
+
347
352
  **pg SCRAM-SHA-256** (`position: 'scram-sha-256'`, §3.19.5). The program's pg client computes its `ClientProof` from the password it holds, a placeholder, so the kernel can't see which one it used; the vault finds it from the proof. The request carries `scram: { clientFirstBare, serverFirst, clientFinalBare, proof }` and no `placeholders` (`EINVAL` otherwise, and `scram` at any other position is `EINVAL` too):
348
353
  - `clientFirstBare` is `n=<user>,r=<client nonce>` and `serverFirst` is `r=<nonce>,s=<salt>,i=<iterations>`, with no extensions; the server's nonce must extend the client's, and `i` must be 1 to 100000.
349
354
  - `clientFinalBare` is `c=biws,r=<the server's nonce>`, without `p=`: `biws` is the GS2 header `n,,`, so channel binding (`-PLUS`, `p=…`, `y,,`) is refused. The kernel offers the program only `SCRAM-SHA-256`, because the program's binding data couldn't match the kernel's TLS.
@@ -452,7 +457,7 @@ The button writes its intent to `intents` under a random 128-bit nonce and opens
452
457
  The npm package `@ai-ecoverse/slicc-vault` holds only TypeScript types, built from `src/contract.ts`: the port API (`Hello`, `Greeting`, `Request`, `Reply`, `VaultEvent`, `Role`), the button (`ButtonConfig`, `ButtonMessage`, `ButtonEvent`), the popup (`PopupIntent`, `PopupState`), the page's results (`SealResult`, `ImportResult`), and the data Settings shows (`SecretView`, `VaultStatus`, `ScrubState`, `PendingRequest`, `AuditEntry`, `ExitPolicyView`, `VaultList`). `Availability` (`connecting`, `waiting`, `locked`, `ready`) is for bios to tell spectrum "waiting for the vault" and "unlock the vault to continue". They mean:
453
458
 
454
459
  - **`EpochMs`**: milliseconds since the epoch. **`DeviceId`**: the stable id of a linked device's link certificate, the same id bios uses for linked devices and trays, in `sourceDevice`, `device:<id>` exits (and `[exits]` lines) and `ExitPolicyView.devices[].device`; never a display name.
455
- - **`Rule`**: `domain` is an exact lower-case host; `header` a lower-case header name; **empty `methods` or `paths` mean any**. A rule with `protocol` (`http1`, `smtp`, `pg`) is a session rule, with its `port` and, for smtp and pg, its `position`; show it as "PROTOCOL on host:port".
460
+ - **`Rule`**: `domain` is an exact lower-case host; `header` a lower-case header name; **empty `methods` or `paths` mean any**. A rule with `protocol` (`http1`, `smtp`, `pg`) is a session rule, with its `port` and, for smtp and pg, its `position`; show it as "PROTOCOL on host:port". `anchors`, `pins` and `roots` are its trust anchors (Policy).
456
461
  - **`exec`**: `false` means the value is never revealed to a program; `any`, `argv` (with `execArgv`, the allowed argv prefixes) and `shell` are the `[exec]` modes under which `fnox exec` may hand it to a program.
457
462
  - **`exits`** is present only when `/etc/slicc/secrets-policy.toml` restricts that secret; absent means every exit (`page`, `proxy`, `tailscale`, `device:<id>`).
458
463
  - **Bindings in `list`:** read under one scrub version (retried, then `EAGAIN`), `provider`, `server` and `settings` are scrubbed over every value (a setting whose name holds one is dropped), and left out while the vault is locked before its first unlock.
@@ -69,6 +69,9 @@ export interface Rule {
69
69
  protocol?: SessionProtocol;
70
70
  position?: SessionPosition;
71
71
  port?: number;
72
+ anchors?: string[];
73
+ pins?: string[];
74
+ roots?: boolean;
72
75
  }
73
76
  export interface RefusedRule {
74
77
  secret: string;
@@ -492,7 +495,21 @@ export type Op = {
492
495
  verified: boolean;
493
496
  handshakeAt: EpochMs;
494
497
  exit: ExitName;
495
- } & SessionTarget);
498
+ } & SessionTarget & ({
499
+ verifiedBy?: 'roots';
500
+ anchor?: never;
501
+ } | {
502
+ verifiedBy: 'anchored';
503
+ anchor: string;
504
+ })) | {
505
+ op: 'sessionAnchors';
506
+ stamp: Stamp;
507
+ session: string;
508
+ host: string;
509
+ port: number;
510
+ protocol: SessionProtocol;
511
+ exit: ExitName;
512
+ };
496
513
  export type AgentOp = {
497
514
  op: 'redact';
498
515
  texts: (string | Uint8Array)[];
@@ -634,6 +651,10 @@ export type OpReply = {
634
651
  } | {
635
652
  op: 'sessionMaterialize';
636
653
  values: Record<string, string>;
654
+ } | {
655
+ op: 'sessionAnchors';
656
+ anchors: string[];
657
+ pins: string[];
637
658
  };
638
659
  export type Request = Op & {
639
660
  v: Protocol;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-vault",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
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",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@ai-ecoverse/jerk-oauth": "1.0.0",
50
- "@ai-ecoverse/slicc-kernel": "1.125.12",
50
+ "@ai-ecoverse/slicc-kernel": "1.126.0",
51
51
  "@ai-ecoverse/slicc-scrub": "1.0.0",
52
52
  "@ai-ecoverse/slicc-shared-web": "1.24.2",
53
53
  "@ai-ecoverse/wasm-bash": "5.3.0-20",