@ai-ecoverse/slicc-vault 1.3.0 → 1.4.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 +25 -4
- package/dist/types/contract.d.ts +13 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The separate-origin vault at `vault.sliccy.ai`. It holds the sealed secrets, OAuth tokens, and model and MCP credentials of SLICC seven, so that no value ever reaches the model provider. The design is `secrets-tokens-design.md` (revision 4) in slicc-agent-plan. This README is the spec of what is built.
|
|
4
4
|
|
|
5
|
-
- **Built:** the key store, records, origin namespacing, auto-unlock with an optional passkey or passphrase lock, the embedding API with its `page` and `agent` roles, the page's `put`, `seal`, `remove`, `restore`, `cancel` and `lock`, the scrub state, the lock button and its popup, the attested policy (rules, `[exec]`, `[exits]`, `[limits]`), the injector and `forward` with the response scrub (S1), the agent's `redact`, `redactStream` and `endpoints` (S3), the model and MCP refusal, and the vault side of the `/dev/sealed` ops `seal`, `placeholder`, `list`, `test`, `request`, `status`, `cancel` and `materialize`.
|
|
6
|
-
- **Not built yet:**
|
|
5
|
+
- **Built:** the key store, records, origin namespacing, auto-unlock with an optional passkey or passphrase lock, the embedding API with its `page` and `agent` roles, the page's `put`, `seal`, `remove`, `restore`, `cancel` and `lock`, the scrub state, the lock button and its popup, the attested policy (rules, `[exec]`, `[exits]`, `[limits]`), the injector and `forward` with the response scrub (S1), the agent's `redact`, `redactStream` and `endpoints` (S3), the model and MCP refusal, exec tickets (`materialize` and `materializeConsume`), and the vault side of the `/dev/sealed` ops `seal`, `placeholder`, `list`, `test`, `request`, `status`, `cancel` and `materialize`.
|
|
6
|
+
- **Not built yet:** `import` and `export`, and the agent's `model`, `mcp`, `records`, `grant` and `refresh`. They answer as described below.
|
|
7
7
|
- **The vault has no network stack.** It never fetches anything but its own `config.json` and `embedders.json`: bios sends every request the vault prepares.
|
|
8
8
|
|
|
9
9
|
## Documents and hosting
|
|
@@ -160,7 +160,7 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
|
|
|
160
160
|
- On `port2` it answers `{ v: 1, op: 'welcome', role, status }`, or `{ v: 1, op: 'refused', error }` for another protocol version or an unknown role.
|
|
161
161
|
- Everything after that goes over the port. The namespace and the role are fixed by the hello. Requests are `{ v: 1, id, op, ...fields }`; replies are `{ v: 1, id, op, ...fields }` or `{ v: 1, id, error: { code, message } }`. Each port is answered in order (FIFO).
|
|
162
162
|
- **Roles:** `page` (the default) is bios and spectrum. bios may hand spectrum a page port of its own, or relay; the messages are the same. `agent` is the port bios hands the agent worker: it may use only `redact`, `redactStream`, `push`, `idle`, `end`, `drop`, `endpoints`, `model`, `mcp`, `records`, `grant` and `refresh` (the last five `ENOTSUP` in this version), and anything else is `EACCES`. The agent port gets no events. Its ops are below (The agent's ops).
|
|
163
|
-
- **Values:** only `forward`
|
|
163
|
+
- **Values:** only `forward` (inside the head it returns, where an accepted rule allows it) and `materializeConsume` (for an `[exec]` grant) put a value into the page. No other op, event or reply carries one, and nothing on the agent port ever does.
|
|
164
164
|
|
|
165
165
|
| op (page role) | fields | reply |
|
|
166
166
|
|---|---|---|
|
|
@@ -177,6 +177,7 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
|
|
|
177
177
|
| `dismissBackupReminder` | — | `{ backupReminder }`; the reminder becomes `dismissed` for this namespace |
|
|
178
178
|
| `policy` | `files` | the attested policy files, below (Policy) |
|
|
179
179
|
| `forward` | `head`, `body?`, `owner`, `exit`, `rawTraits`, with a `MessagePort` transferred alongside | the injected head, below (Forward) |
|
|
180
|
+
| `materializeConsume` | `ticket`, `stamp`, `path`, `argv`, `cwd`, `interpreters` | the values for the child's environment, below (Exec) |
|
|
180
181
|
| `import`, `export`, anything else | | `ENOTSUP` |
|
|
181
182
|
|
|
182
183
|
`put` and `seal` need the vault unlocked. They answer `EACCES` with a message starting `vault is locked` otherwise.
|
|
@@ -203,11 +204,31 @@ bios relays each kernel request unchanged as the `sealed` op, with the kernel's
|
|
|
203
204
|
| `request` | `kind: "jerk-seal"`, `provider` | the same |
|
|
204
205
|
| `status` | `id` | `{ state }`, with `placeholder` and `record` once a value request is `done` |
|
|
205
206
|
| `cancel` | `id` | `{ state }`: `cancelled`, or the state it had already reached |
|
|
206
|
-
| `materialize` | `names
|
|
207
|
+
| `materialize` | `names` (or `shellGrants: true`), `path`, `argv`, `cwd`, `interpreter?` | `{ ticket, expiresAt, real: { NAME: true \| false } }`, below (Exec) |
|
|
207
208
|
|
|
208
209
|
- **Requests** expire 300 s after they're made. Each uid has at most one pending request per namespace; a second one is `EAGAIN`. `status` and `cancel` see only the caller's own requests (uid 0 sees all), and others get `ENOENT`.
|
|
209
210
|
- A value request shows up in `status.waiting` and is answered by the page's `put` with its `request` id, or ended by `cancel`. `oauth` and `jerk-seal` requests can only be cancelled in this version.
|
|
210
211
|
|
|
212
|
+
## Exec
|
|
213
|
+
|
|
214
|
+
`fnox exec` (§3.5) asks for a one-shot ticket, and the kernel redeems it when it execs the child.
|
|
215
|
+
|
|
216
|
+
**`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.
|
|
217
|
+
- 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.
|
|
218
|
+
- **`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.
|
|
219
|
+
- **`{ argv = [[…]] }`**: `real` only when `[path, ...argv[1..]]` starts with one of the listed prefixes. A listed shell or interpreter is allowed.
|
|
220
|
+
- **`{ shell = true }`**: always `real`, with no program refusal.
|
|
221
|
+
- **Limits,** per secret and uid, counted from the audit log: `materialize_per_minute` (10) and `materialize_per_day` (200), and `shell_per_day` (5,000, with no per-minute limit) for shell grants. Over a limit is `EAGAIN: materialize limit for NAME (10/min)`.
|
|
222
|
+
- A locked vault answers `EACCES` (`vault is locked`).
|
|
223
|
+
- Each real name is audited as `materialize { name, exec, path, argv, argvSha256, cwd, pid, ppid, uid, tag, ticket, consumed: false }`. `argv` is joined with spaces, scrubbed, and cut to 200 characters; `argvSha256` is the SHA-256 of each argument followed by a NUL byte.
|
|
224
|
+
- The reply never carries a value. The ticket is 32 hex characters and expires after 30 s.
|
|
225
|
+
|
|
226
|
+
**`materializeConsume`** (a page op, from bios for the kernel at exec) takes `{ ticket, stamp, path, argv, cwd, interpreters }`, with `stamp` the exec'ing process and `interpreters` the absolute, symlink-resolved shebang/binfmt chain the kernel actually resolved for `path` (empty for a native binary), relayed unchanged. The vault burns the ticket, then requires that:
|
|
227
|
+
- 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;
|
|
228
|
+
- `interpreters` is a list of absolute paths, and fnox's claim matches it: a claimed `interpreter` equals `interpreters[0]`, and no claim means `interpreters` is empty;
|
|
229
|
+
- under each real name's `[exec]` mode, in the policy as stored now (reloaded at materialize and at consume), no program in `path` and `interpreters` is refused: for `true` none may be on the refusal list; for `{ argv }` a refused one must itself be the program of one of the grant's prefixes; `{ shell = true }` allows any;
|
|
230
|
+
- each real name is still granted by the policy in force now (a removed grant, or an `{ argv }` grant that no longer lists the command, refuses), and no real record has expired meanwhile. Any mismatch is `EACCES: ticket refused: …`, and the ticket stays burned. It answers `{ values: { NAME: value }, placeholders: { NAME: placeholder } }` for the names that were real and still exist, and marks their audit entries `consumed`. The kernel swaps each env var whose value is exactly one of those placeholders, and compiles its own scrubber over these values for the child's output (S2).
|
|
231
|
+
|
|
211
232
|
## Forward
|
|
212
233
|
|
|
213
234
|
bios's exit router asks the vault to inject a request the kernel forwarded (an owner-bearing request that carries a placeholder), then sends it itself:
|
package/dist/types/contract.d.ts
CHANGED
|
@@ -363,6 +363,14 @@ export type Op = {
|
|
|
363
363
|
owner: Stamp;
|
|
364
364
|
exit: ExitName;
|
|
365
365
|
rawTraits: RawTraits;
|
|
366
|
+
} | {
|
|
367
|
+
op: 'materializeConsume';
|
|
368
|
+
ticket: string;
|
|
369
|
+
stamp: Stamp;
|
|
370
|
+
path: string;
|
|
371
|
+
argv: string[];
|
|
372
|
+
cwd: string;
|
|
373
|
+
interpreters: string[];
|
|
366
374
|
};
|
|
367
375
|
export type AgentOp = {
|
|
368
376
|
op: 'redact';
|
|
@@ -445,7 +453,11 @@ export type OpReply = {
|
|
|
445
453
|
op: 'policy';
|
|
446
454
|
} & PolicyResult) | ({
|
|
447
455
|
op: 'forward';
|
|
448
|
-
} & ForwardResult)
|
|
456
|
+
} & ForwardResult) | {
|
|
457
|
+
op: 'materializeConsume';
|
|
458
|
+
values: Record<string, string>;
|
|
459
|
+
placeholders: Record<string, string>;
|
|
460
|
+
};
|
|
449
461
|
export type Request = Op & {
|
|
450
462
|
v: Protocol;
|
|
451
463
|
id: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-vault",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|