@ai-ecoverse/slicc-vault 1.17.4 → 1.18.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
@@ -176,9 +176,9 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
176
176
  | `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 |
177
177
  | `status` | — | `{ status: { version, protocol, origin, locked, lock, scrub, waiting, exportedAt, backupReminder, policy } }` |
178
178
  | `placeholders` | — | `{ placeholders, scrub }`: every placeholder of a record that isn't removed, active and inert, sorted |
179
- | `list` | — | `{ list: { secrets, refused, audit, auditMore, exits } }` for Settings › Secrets |
179
+ | `list` | — | `{ list: { secrets, mcp, refused, audit, auditMore, exits } }` for Settings › Secrets. `mcp` is `[{ name, server, state }]`, sorted by name, the `mcp` records bound to a server that can be used now: not removed, and `state` `ready` or `refreshing` (expired with a refresh source). An expired one with no refresh source or with a dead grant, a bridge token (source `process`), one with no `server`, one whose `mcp:<server>` is longer than a request's 128-character `provider`, and one whose name or server holds a value are left out, and so is every record while the vault is locked before its first unlock |
180
180
  | `audit` | `before?` (a `seq`), `limit?` (1–1000, default 100) | `{ audit, more, next? }`, newest first; pass `next` as `before` for the next page |
181
- | `put` | `name`, `value`, `kind?` (`secret`, the default, `model` or `mcp`), `provider?`, `server?`, `settings?`, `request?` | `{ name, placeholder, state }`. A value typed in Settings (Settings › Accounts puts model keys as `kind: 'model'` with their `provider`), with source `settings`; on an existing name it's a rotation that keeps the placeholder. Without `value`, for an existing `model` or `mcp` record, it only updates `provider`, `server` or `settings` (say, a new Cloudflare account id), keeping the value and placeholder (`ENOENT` if there's no such live record; like every `put`, it needs the vault unlocked), audited as a `seal` entry with `rebound: true`; a new `server` clears a first-use pin, and a new `provider` drops the record's custom base URLs. With `request`, it answers that value request, which must still be pending and ask for `name`, and the record must be a `secret` (else `EINVAL`, and nothing is stored), so its `/dev/sealed` `status` becomes `done` |
181
+ | `put` | `name`, `value`, `kind?` (`secret`, the default, `model` or `mcp`), `provider?`, `server?`, `settings?`, `request?` | `{ name, placeholder, state }`. A value typed in Settings (Settings › Accounts puts model keys as `kind: 'model'` with their `provider`), with source `settings`; on an existing name it's a rotation that keeps the placeholder, except over a `process` record that isn't a `secret`, which gets a new one (below, `seal` in `/dev/sealed`). Without `value`, for an existing `model` or `mcp` record, it only updates `provider`, `server` or `settings` (say, a new Cloudflare account id), keeping the value and placeholder (`ENOENT` if there's no such live record; like every `put`, it needs the vault unlocked), audited as a `seal` entry with `rebound: true`; a new `server` clears a first-use pin, and a new `provider` drops the record's custom base URLs. With `request`, it answers that value request, which must still be pending and ask for `name`, and the record must be a `secret` (else `EINVAL`, and nothing is stored), so its `/dev/sealed` `status` becomes `done` |
182
182
  | `seal` | `name`, `value`, `source`, `kind?` (default `secret`), `expiresAt?`, `refresh?`, `oauth?`, `provider?`, `server?`, `settings?`, `request?`, `jerk?` | `{ name, placeholder, state }`. bios's, for J1 leases and sign-in tokens, below (Tokens and refresh) |
183
183
  | `answer` | `request`, `state` (`progress` or `done`), `progress?` (≤ 200), `exit?`, `user?`, `scopes?`, `message?` (≤ 500), `rows?`, `placeholder?` | `{ state }`. The answer to a `jerk` or `unseal` request (or a sign-in that failed: for `oauth` and `jerk-seal`, `done` needs a nonzero `exit`, since only `seal` completes them); fields merge into the request, and `status` returns them. `rows` (for `jop: "list"`) are `{ provider, name?, env, device?, signedIn?, user?, scopes? }`, with any other field dropped, so no token can pass. For a `jop: "status"` answered `done` with `exit` 0, the vault adds the placeholder of the provider's own `oauth:<provider>` or `jerk:<provider>@…` record; a `placeholder` given by bios must be one of those records of the provider |
184
184
  | `refresh` | `name` | `{ state: 'refreshed', expiresAt? }` or `{ state: 'requested' }`, below |
@@ -213,8 +213,8 @@ bios relays each kernel request unchanged as the `sealed` op, with the kernel's
213
213
 
214
214
  | op | fields | reply |
215
215
  |---|---|---|
216
- | `seal` | `name`, `value`, and for an MCP bridge `kind: "mcp"`, `server`, `url` | `{ placeholder, state }`. A new name creates a `process` record. An existing one is rotated and keeps its placeholder, but only when its value came from a process; a value from Settings, jerk, OAuth or an import is replaced only by uid 0, and anyone else gets `EEXIST`. **A bridge token** (§3.16.3, e.g. mcpcat's `--bearer`) is an `mcp` record bound to `server` and `url`, which must be a kernel-loopback URL (`http://<port>.kernel.localhost/…`, never 9222; anything else is `EACCES`). A page `put` or `seal` of the same name turns it into an ordinary `mcp` record, without the URL. It records the sealing uid, and resealing it, even after it was removed, is allowed only for a `process` `mcp` record sealed by the same uid, or by uid 0; anything else, a user-entered record included, is `EEXIST` |
217
- | `placeholder` | `name` | `{ placeholder, state }`, including `removed`; `ENOENT` when there's no record |
216
+ | `seal` | `name`, `value`, and for an MCP bridge `kind: "mcp"`, `server`, `url` | `{ placeholder, state }`. A new name creates a `process` record. An existing one is rotated and keeps its placeholder, but only when its value came from a process; a value from Settings, jerk, OAuth or an import is replaced only by uid 0, and anyone else gets `EEXIST`. **A bridge token** (§3.16.3, e.g. mcpcat's `--bearer`) is an `mcp` record bound to `server` and `url`, which must be a kernel-loopback URL (`http://<port>.kernel.localhost/…`, never 9222; anything else is `EACCES`). Any write that isn't a process's over a `process` record of another kind than `secret` (a page `put` or `seal`, a sign-in, an import) makes it an ordinary record, without the URL, under a new placeholder, so whoever sealed it keeps no placeholder of the new value; a `secret` keeps its placeholder, which every uid can read and the rules name. It records the sealing uid, and resealing it, even after it was removed, is allowed only for a `process` `mcp` record sealed by the same uid, or by uid 0; anything else, a user-entered record included, is `EEXIST` |
217
+ | `placeholder` | `name`, `server?` | `{ placeholder, state }` of the `secret` record, including `removed`; `ENOENT` when there's no record. For root, with `server`, the live `mcp` record of that name bound to that server comes first, below (Root and `mcp` records) |
218
218
  | `list` | — | `{ secrets: [{ name, placeholder, state, exec, rules, expiresAt? }] }`, records of kind `secret` that aren't removed, with their rules and `[exec]` mode |
219
219
  | `test` | `name`, `url`, `method?` (`GET`), `header?` (`authorization`), `exit?` (`page`) | `{ verdict: 'inject' \| 'refuse', reason }`: the injector's verdict for `Bearer <placeholder>` in that header, without sending anything; `ENOENT` for an unknown name |
220
220
  | `request` | `kind: "value"`, `name`, `prompt?` (≤ 200 characters) | `{ state: "pending", id }` |
@@ -222,13 +222,17 @@ bios relays each kernel request unchanged as the `sealed` op, with the kernel's
222
222
  | `request` | `kind: "jerk-seal"`, `provider` | the same |
223
223
  | `request` | `kind: "jerk"`, `jop` (`list`, `status`, `create`), `provider?` (needed but for `list`), `scope?`, `params?` (an object) | the same, or `{ state: 'done', id, exit: 2, message }` for a J2 refusal |
224
224
  | `request` | `kind: "unseal"`, `provider` | the same, or the J2 refusal |
225
- | `status` | `id` | `{ state }`, with `placeholder` and `record` once the request is `done`, `jerk` for a sign-in or lease that reported it, and the latest `answer` fields (`progress`, `exit`, `user`, `scopes`, `message`, `rows`, `placeholder`) |
225
+ | `status` | `id` | `{ state }`, with `placeholder` and `record` once the request is `done`, `jerk` for a sign-in or lease that reported it, and the latest `answer` fields (`progress`, `exit`, `user`, `scopes`, `message`, `rows`, `placeholder`). A `jerk` `status` of `mcp:<server>` is decided below (Root and `mcp` records) |
226
226
  | `cancel` | `id` | `{ state }`: `cancelled`, or the state it had already reached |
227
227
  | `materialize` | `names` (or `shellGrants: true`), `path`, `argv`, `cwd`, `interpreter?` | `{ ticket, expiresAt, real: { NAME: true \| false } }`, below (Exec) |
228
228
 
229
229
  - **Requests** expire 300 s after they're made. Each uid has at most one pending request per namespace that waits for a human (`value`, `oauth`, `jerk-seal`, and `jerk` with `jop: "create"`); a second one is `EAGAIN`. `status` and `cancel` see only the caller's own requests (uid 0 sees all), and others get `ENOENT`. Only requests that wait for a human show in `status.waiting`.
230
230
  - A value request is answered by the page's `put` with its `request` id. A sign-in or lease (`oauth`, `jerk-seal`) is answered by `seal` with `request` (Tokens and refresh). Every other kind is answered by the page's `answer`. `cancel` ends any of them and pushes `{ v: 1, event: 'cancelled', request, kind }` to the page, which passes it on to J1 where it can.
231
231
  - **J-8's jerk requests** (§3.7.1): the vault only keeps their state; bios learns of them from the relay and answers. `jop: "create"` and `unseal` are for root and cones only (J2): the kernel stamp must have `users: 'enforce'`, and either uid 0 or the `sudo` group among its `groups`. bios names that group's gid as `sudoGid` in its hello; a page hello without it clears the namespace's gid. A stamp without `groups`, a vault without `sudoGid`, or any other mode refuses: the request is `done` with `exit: 2` at once, before bios sees anything. The tag is kept for audit and display only.
232
+ - **Root and `mcp` records:** the placeholder of an `mcp` record a process didn't seal is for root only, since a process holding one can call that server through `forward` past a role's tool limits. Whoever seals a bridge token holds its placeholder. Root is a stamp with uid 0 and `users: 'enforce'`, not `mayManage`'s `sudo` group: a process a human approved (sudo, including standing approvals), or the judge the owner chose to delegate sudo to (slicc-agent's `sudo.approver = agent`, whose default is `user`, a person). That root can already do more than read a placeholder. A cone or scoop isn't root, and neither is any uid under another mode, where the stamp proves nothing. Records match as kind `mcp`, source other than `process`, with `server` equal to the name given.
233
+ - `placeholder` with `name` and `server` answers for root when such a record, not removed, has that name and server. Anyone else, a wrong server, a removed record, a bridge token and a record with no `server` get the reply the op gives without `server`, the same as for no record. A `seal` under a taken name still answers `EEXIST`, as above.
234
+ - `status` of a `done` `jerk` `status` request for `mcp:<server>` with no placeholder from a `secret` record: when both the request's stored stamp and the caller are root, and bios answered exit 0, and exactly one usable record (the rule of `list.mcp`) is bound to that server, the reply adds its `placeholder` and `record`. Several usable ones are `EINVAL` for root ("several mcp records are bound to SERVER; ask for one by name"), as for the agent's `refresh`; `placeholder` by name needs no such rule. When both are root and a record is usable but bios didn't answer exit 0, bios's reply stands. Every other reader, and any reader with no usable record, gets `{ state: 'done', exit: 3, message: "no token for mcp:<server> in seven's vault: run jerk mcp:<server> create" }`, whatever bios answered. The page and bios never see this rule: bios answers from `list.mcp`, before the caller is checked.
235
+ - Neither reply carries a value, and neither read is audited, like every read of a placeholder; each use of one is, as `mcp` (Model and MCP endpoints). Where the value is filled is Which MCP credential goes where (§3.16.1): for a Settings record, an `https:` URL under the server URL the page registered; with no page server for that name, it's pinned on first use to the agent's URL for that server, until the page registers one; for a sign-in (`oauth:mcp`), any registered `https:` URL of that server.
232
236
 
233
237
  ## Tokens and refresh
234
238
 
@@ -483,7 +487,7 @@ The button writes its intent to `intents` under a random 128-bit nonce and opens
483
487
 
484
488
  ## Types for bios and spectrum
485
489
 
486
- 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:
490
+ 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`, `McpView`, `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:
487
491
 
488
492
  - **`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.
489
493
  - **`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).
@@ -495,6 +499,7 @@ The npm package `@ai-ecoverse/slicc-vault` holds only TypeScript types, built fr
495
499
  - **Who did it:** `uid` and `tag` are the kernel stamp for entries a program or the agent caused (`/dev/sealed` seals, `forward`, `mcp`, `materialize`, `session`). Every entry from a page-role op (`put`, `seal`, `remove`, `restore`, `import`, `policy`) and from the vault itself (`refresh`, `endpoints`) has `uid: null, tag: null`, older entries included. Spectrum shows page ops as "by you", `refresh` and `endpoints` as "automatically", and `policy` entries with no "by …".
496
500
  - **`AuditEntry`**: `endpoints` is vault-originated: the upgrade's `purged` (show it as "Removed custom model URLs an agent set") and `adopted` ("Kept the MCP servers your tokens use"), a `pinned` MCP token ("pinned to <host>", with an Unpin action) and an `unpinned` one; `forward` is a use of a value in a request, with the exit that carried it; `mcp` a kernel program's call to a registered MCP server; `session` a value handed to the kernel for a verified TLS session (show it as "Sent over PROTOCOL to host", with "trusted by public roots" or "trusted by its pinned anchor" from `verifiedBy`, and nothing when an older entry has no `verifiedBy`); **`materialize` is shown as "Revealed to a program"**; `seal`, `remove`, `restore` and `policy` (with its `changes`) record changes. Every entry has a `seq` for paging.
497
501
  - **`SessionTarget`**: a `sessionMaterialize` request's protocol, position and what goes with it: `placeholders` (and `head` for http1) for every position but `scram-sha-256`, which takes `scram` (a `ScramExchange`) and no `placeholders`.
502
+ - **`VaultList.mcp`** lists usable `mcp` records for bios's jerk `status`, without a placeholder or source (`list`); treat every entry as usable, whatever its `state`.
498
503
  - **`VaultList.refused`** lists rules the vault dropped because they name a model or MCP endpoint.
499
504
  - **`PendingRequest.prompt`** is untrusted, agent-written text of at most 200 characters.
500
505
  - **`ExitPolicyView`** is read-only: the secrets `/etc/slicc/secrets-policy.toml` restricts to certain exits.
@@ -371,8 +371,14 @@ export interface PolicyResult {
371
371
  })[];
372
372
  warnings: string[];
373
373
  }
374
+ export interface McpView {
375
+ name: string;
376
+ server: string;
377
+ state: 'ready' | 'refreshing';
378
+ }
374
379
  export interface VaultList {
375
380
  secrets: SecretView[];
381
+ mcp: McpView[];
376
382
  refused: RefusedRule[];
377
383
  audit: AuditEntry[];
378
384
  auditMore: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-vault",
3
- "version": "1.17.4",
3
+ "version": "1.18.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
  "piAi": "1.4.0",
6
6
  "type": "module",
@@ -47,7 +47,7 @@
47
47
  "release": "semantic-release"
48
48
  },
49
49
  "dependencies": {
50
- "@ai-ecoverse/jerk-oauth": "1.3.6",
50
+ "@ai-ecoverse/jerk-oauth": "1.3.7",
51
51
  "@ai-ecoverse/slicc-scrub": "1.1.0"
52
52
  },
53
53
  "devDependencies": {