@ai-ecoverse/slicc-vault 1.5.0 → 1.6.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 +35 -5
- package/dist/types/contract.d.ts +17 -1
- package/package.json +3 -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, 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:**
|
|
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`), `import` (backups, KeePass, six's `secrets.env`) and `export`, and the vault side of the `/dev/sealed` ops `seal`, `placeholder`, `list`, `test`, `request`, `status`, `cancel` and `materialize`.
|
|
6
|
+
- **Not built yet:** 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
|
|
@@ -18,7 +18,7 @@ The build (`npm run build`) writes a flat static tree to `dist/site/`:
|
|
|
18
18
|
| `vault.css`, `fonts/` | Spectrum 2 from the CSS-only `@spectrum-css` packages, and Adobe Clean |
|
|
19
19
|
| `config.json` | `{ version, embedders }`. `embedders` is the fallback list, `["https://seven.sliccy.ai"]` |
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
The bundles hold this repo's code and `@ai-ecoverse/slicc-scrub`. The only third-party code is for KeePass imports: `kdbxweb` (with `@xmldom/xmldom` and `fflate`) and `hash-wasm`'s argon2, exact-pinned and bundled into a separate chunk under `chunks/` that the core frame loads only when a KeePass import starts; its wasm is inlined, never fetched. The sliccy-ai Worker serves the tree from the network on every load, never from OPFS, with:
|
|
22
22
|
- `Content-Security-Policy` with `script-src 'self'`, no inline scripts or styles, and `frame-ancestors` listing the seven origins;
|
|
23
23
|
- `Cross-Origin-Embedder-Policy: require-corp`, `Cross-Origin-Resource-Policy: same-site`, `Cross-Origin-Opener-Policy: same-origin`;
|
|
24
24
|
- `Origin-Agent-Cluster: ?1`, `nosniff`, `Referrer-Policy: no-referrer`, `no-cache` with an ETag.
|
|
@@ -71,7 +71,7 @@ Everything lives in the vault origin's IndexedDB, database `slicc-vault`, versio
|
|
|
71
71
|
|
|
72
72
|
## Backup reminder
|
|
73
73
|
|
|
74
|
-
`status.exportedAt` is the time of the namespace's last successful `export`, or `null` if it never exported. `status.backupReminder` is `due` once the namespace holds a `secret` record and has never exported, `dismissed` after `dismissBackupReminder`, and `null` otherwise (no secrets yet, or exported).
|
|
74
|
+
`status.exportedAt` is the time of the namespace's last successful `export`, or `null` if it never exported. `status.backupReminder` is `due` once the namespace holds a `secret` record and has never exported, `dismissed` after `dismissBackupReminder`, and `null` otherwise (no secrets yet, or exported).
|
|
75
75
|
|
|
76
76
|
## Policy
|
|
77
77
|
|
|
@@ -178,7 +178,9 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
|
|
|
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
180
|
| `materializeConsume` | `ticket`, `stamp`, `path`, `argv`, `cwd`, `interpreters`, `trusted` | the values for the child's environment, below (Exec) |
|
|
181
|
-
| `import`, `
|
|
181
|
+
| `import` | `kind`, `bytes`, `password?`, `pick?` | below (Import and export) |
|
|
182
|
+
| `export` | `passphrase` | `{ bundle, count }`, below |
|
|
183
|
+
| anything else | | `ENOTSUP` |
|
|
182
184
|
|
|
183
185
|
`put` and `seal` need the vault unlocked. They answer `EACCES` with a message starting `vault is locked` otherwise.
|
|
184
186
|
|
|
@@ -209,6 +211,33 @@ bios relays each kernel request unchanged as the `sealed` op, with the kernel's
|
|
|
209
211
|
- **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`.
|
|
210
212
|
- 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.
|
|
211
213
|
|
|
214
|
+
## Import and export
|
|
215
|
+
|
|
216
|
+
Spectrum's Settings › Secrets reads the file and the password, and bios hands them over. Both need the vault unlocked. Like every page op, `export` trusts the page: under rev 4 seven's page is trusted with values in transit (§8), and a compromised seven origin is out of scope.
|
|
217
|
+
|
|
218
|
+
**`export { passphrase }`** returns `{ bundle: Uint8Array, count }`, a backup of the namespace's records of kind `secret`, `model` and `mcp` that aren't removed. Grants aren't included, because they need a fresh sign-in. The passphrase needs at least 12 characters. The bundle is UTF-8 JSON:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{ "format": "slicc-vault-bundle", "v": 1,
|
|
222
|
+
"kdf": { "name": "PBKDF2", "hash": "SHA-256", "iterations": 600000, "salt": "<base64url>" },
|
|
223
|
+
"cipher": { "name": "AES-GCM", "iv": "<base64url>" },
|
|
224
|
+
"data": "<base64url>" }
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`data` is AES-GCM 256 (additional data `slicc-vault-bundle`) over `{ "records": [{ name, kind, value, placeholder, source, createdAt, expiresAt? }] }`. A successful export sets `status.exportedAt`.
|
|
228
|
+
|
|
229
|
+
**`import { kind, bytes, password?, pick? }`** runs in two steps:
|
|
230
|
+
- Without `pick` it answers `{ stage: 'pick', candidates: [{ name, kind, exists, suggestedRules?, refused? }] }`, for the human to choose from. `refused` says why a candidate can't be imported.
|
|
231
|
+
- With `pick` it seals the candidates picked, each given as `{ name, kind }` or, for kind `secret`, as just its name, and answers `{ stage: 'done', imported: [{ name, placeholder, state }], skipped }`. `skipped` lists picked names that were refused or not in the file, and `reasons` maps each to a short reason ("not a valid name", "values shorter than 8 bytes can't be protected from the model", "it isn't in the file", …), never a value or part of one. `outcomes` lists every picked candidate as `{ name, kind?, imported, reason? }`, which tells two candidates of the same name and different kinds apart (`kind` is absent only for a picked name that isn't in the file). Each record is stored in its own transaction, which also reserves a bundle's placeholder, so a record either lands whole or not at all; one that can't be stored (its placeholder was taken meanwhile, the vault was re-keyed) is listed in `skipped`, and the rest still import.
|
|
232
|
+
|
|
233
|
+
| `kind` | `password` | what becomes a candidate |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| `bundle` | the export passphrase | each record, with its kind, source (`import:bundle` when it isn't a valid source), expiry and **placeholder**, so the policy files still match. A placeholder used by another record of the namespace, or by another embedder of this vault, is refused, as is a name whose record here has another placeholder |
|
|
236
|
+
| `keepass` | the master password (KDBX 3 or 4, as kdbxweb reads them; Argon2 through hash-wasm) | each entry with a password, named after its title (upper-cased, other characters as `_`, a leading digit prefixed with `_`; a name already taken by an earlier entry gets `_2`, `_3`, …), source `import:keepass` |
|
|
237
|
+
| `six` | — | each `NAME=value` line of six's `secrets.env` (`export` and quotes allowed), source `import:six`. `NAME_DOMAINS` becomes `suggestedRules`: a `[[proxy.rules]]` stanza per exact domain, for the human to add with `sudo`, and a note for each wildcard, which isn't carried over |
|
|
238
|
+
|
|
239
|
+
Values are checked as at `put` (at least 8 bytes, no CR, LF or NUL, no placeholder). An existing name is rotated, keeping its placeholder. A wrong password or passphrase is `EACCES`.
|
|
240
|
+
|
|
212
241
|
## Exec
|
|
213
242
|
|
|
214
243
|
`fnox exec` (§3.5) asks for a one-shot ticket, and the kernel redeems it when it execs the child.
|
|
@@ -343,6 +372,7 @@ The npm package `@ai-ecoverse/slicc-vault` holds only TypeScript types, built fr
|
|
|
343
372
|
- **`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.
|
|
344
373
|
- **`exits`** is present only when `/etc/slicc/secrets-policy.toml` restricts that secret; absent means every exit (`page`, `proxy`, `tailscale`, `device:<id>`).
|
|
345
374
|
- **`kind`**: `secret` (a credential programs use through rules), **`grant`** (a sign-in's refresh token, never injected; show it as "Sign-in"), `model` (a model provider key), `mcp` (an MCP server credential). `provider` and `account` name the service and the signed-in account where one is known.
|
|
375
|
+
- **Who did it:** `uid` and `tag` are the kernel stamp for entries a program or the agent caused (`/dev/sealed` seals, `forward`, `mcp`, `materialize`). Every entry from a page-role op (`put`, `seal`, `remove`, `restore`, `import`, `policy`) and from the vault itself (`refresh`) has `uid: null, tag: null`, older entries included. Spectrum shows page ops as "by you", `refresh` as "automatically", and `policy` entries with no "by …".
|
|
346
376
|
- **`AuditEntry`**: `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; **`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.
|
|
347
377
|
- **`VaultList.refused`** lists rules the vault dropped because they name a model or MCP endpoint.
|
|
348
378
|
- **`PendingRequest.prompt`** is untrusted, agent-written text of at most 200 characters.
|
package/dist/types/contract.d.ts
CHANGED
|
@@ -154,6 +154,8 @@ export interface PolicyEntry {
|
|
|
154
154
|
seq: number;
|
|
155
155
|
id: string;
|
|
156
156
|
at: EpochMs;
|
|
157
|
+
uid: null;
|
|
158
|
+
tag: null;
|
|
157
159
|
file: PolicyFile;
|
|
158
160
|
sha256: string;
|
|
159
161
|
accepted: boolean;
|
|
@@ -265,8 +267,10 @@ export interface SealResult {
|
|
|
265
267
|
export type ImportKind = 'bundle' | 'keepass' | 'six';
|
|
266
268
|
export interface ImportCandidate {
|
|
267
269
|
name: string;
|
|
270
|
+
kind: RecordKind;
|
|
268
271
|
exists: boolean;
|
|
269
272
|
suggestedRules?: string;
|
|
273
|
+
refused?: string;
|
|
270
274
|
}
|
|
271
275
|
export type ImportResult = {
|
|
272
276
|
stage: 'pick';
|
|
@@ -275,7 +279,15 @@ export type ImportResult = {
|
|
|
275
279
|
stage: 'done';
|
|
276
280
|
imported: SealResult[];
|
|
277
281
|
skipped: string[];
|
|
282
|
+
reasons?: Record<string, string>;
|
|
283
|
+
outcomes?: ImportOutcome[];
|
|
278
284
|
};
|
|
285
|
+
export interface ImportOutcome {
|
|
286
|
+
name: string;
|
|
287
|
+
kind?: RecordKind;
|
|
288
|
+
imported: boolean;
|
|
289
|
+
reason?: string;
|
|
290
|
+
}
|
|
279
291
|
export type PopupIntent = {
|
|
280
292
|
kind: 'unlock';
|
|
281
293
|
} | {
|
|
@@ -345,7 +357,10 @@ export type Op = {
|
|
|
345
357
|
kind: ImportKind;
|
|
346
358
|
bytes: Uint8Array;
|
|
347
359
|
password?: string;
|
|
348
|
-
pick?: string
|
|
360
|
+
pick?: (string | {
|
|
361
|
+
name: string;
|
|
362
|
+
kind: RecordKind;
|
|
363
|
+
})[];
|
|
349
364
|
} | {
|
|
350
365
|
op: 'export';
|
|
351
366
|
passphrase: string;
|
|
@@ -445,6 +460,7 @@ export type OpReply = {
|
|
|
445
460
|
} & ImportResult) | {
|
|
446
461
|
op: 'export';
|
|
447
462
|
bundle: Uint8Array;
|
|
463
|
+
count: number;
|
|
448
464
|
} | {
|
|
449
465
|
op: 'lock';
|
|
450
466
|
} | {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-vault",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
"esbuild": "0.28.2",
|
|
58
58
|
"fake-indexeddb": "6.2.5",
|
|
59
59
|
"happy-dom": "20.14.5",
|
|
60
|
+
"hash-wasm": "4.12.0",
|
|
61
|
+
"kdbxweb": "2.1.1",
|
|
60
62
|
"lefthook": "2.1.17",
|
|
61
63
|
"monocart-coverage-reports": "2.13.0",
|
|
62
64
|
"playwright-core": "1.63.0",
|