@ai-ecoverse/slicc-vault 1.6.0 → 1.7.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.
- package/README.md +37 -16
- package/dist/types/contract.d.ts +86 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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`), `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`.
|
|
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`, sign-in tokens with their grants and refresh through `egress`, and the vault side of the `/dev/sealed` ops `seal`, `placeholder`, `list`, `test`, `request`, `status`, `cancel` and `materialize`.
|
|
6
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
|
|
|
@@ -153,14 +153,14 @@ Every record, request, audit entry and scrub version belongs to the origin that
|
|
|
153
153
|
The embedder (bios) loads `https://vault.sliccy.ai/` in an iframe and, after `load`, posts a hello carrying a port:
|
|
154
154
|
|
|
155
155
|
```js
|
|
156
|
-
iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role: 'page' }, 'https://vault.sliccy.ai', [port2]);
|
|
156
|
+
iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role: 'page', sudoGid }, 'https://vault.sliccy.ai', [port2]);
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
- The frame answers only when the message comes from its own parent window, from an allowed origin, matching `location.ancestorOrigins[0]` when the browser exposes it. Anything else gets no answer at all.
|
|
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` (inside the head it returns, where an accepted rule allows it)
|
|
163
|
+
- **Values:** only `forward` (inside the head it returns, where an accepted rule allows it), `materializeConsume` (for an `[exec]` grant) and `egress` (a refresh request) 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
|
|---|---|---|
|
|
@@ -170,14 +170,16 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
|
|
|
170
170
|
| `list` | — | `{ list: { secrets, refused, audit, auditMore, exits } }` for Settings › Secrets |
|
|
171
171
|
| `audit` | `before?` (a `seq`), `limit?` (1–1000, default 100) | `{ audit, more, next? }`, newest first; pass `next` as `before` for the next page |
|
|
172
172
|
| `put` | `name`, `value`, `request?` | `{ name, placeholder, state }`. A value typed in Settings, with source `settings`; on an existing name it's a rotation that keeps the placeholder. With `request`, it answers that value request, which must still be pending and ask for `name` (else `EINVAL`, and nothing is stored), so its `/dev/sealed` `status` becomes `done` |
|
|
173
|
-
| `seal` | `name`, `value`, `source`, `kind?` (default `secret`), `expiresAt?`, `refresh?` | `{ name, placeholder, state }`. bios's, for J1 leases and sign-in tokens |
|
|
173
|
+
| `seal` | `name`, `value`, `source`, `kind?` (default `secret`), `expiresAt?`, `refresh?`, `oauth?`, `request?`, `jerk?` | `{ name, placeholder, state }`. bios's, for J1 leases and sign-in tokens, below (Tokens and refresh) |
|
|
174
|
+
| `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 |
|
|
175
|
+
| `refresh` | `name` | `{ state: 'refreshed', expiresAt? }` or `{ state: 'requested' }`, below |
|
|
174
176
|
| `remove`, `restore` | `name`, `kind?` (default `secret`) | `{ name, placeholder, state }`. A record removed more than 30 days ago can't be restored (`ENOENT`) |
|
|
175
177
|
| `cancel` | `request` | `{ state }`; the request's `/dev/sealed` `status` becomes `cancelled` |
|
|
176
178
|
| `lock` | — | `{}`; drops the key everywhere when the lock is on |
|
|
177
179
|
| `dismissBackupReminder` | — | `{ backupReminder }`; the reminder becomes `dismissed` for this namespace |
|
|
178
180
|
| `policy` | `files` | the attested policy files, below (Policy) |
|
|
179
181
|
| `forward` | `head`, `body?`, `owner`, `exit`, `rawTraits`, with a `MessagePort` transferred alongside | the injected head, below (Forward) |
|
|
180
|
-
| `materializeConsume` | `ticket`, `stamp`, `path`, `argv`, `cwd`, `interpreters`, `trusted` | the values for the child's environment, below (Exec) |
|
|
182
|
+
| `materializeConsume` | `ticket`, `stamp`, `path`, `resolved`, `argv`, `cwd`, `interpreters`, `trusted` | the values for the child's environment, below (Exec) |
|
|
181
183
|
| `import` | `kind`, `bytes`, `password?`, `pick?` | below (Import and export) |
|
|
182
184
|
| `export` | `passphrase` | `{ bundle, count }`, below |
|
|
183
185
|
| anything else | | `ENOTSUP` |
|
|
@@ -187,7 +189,9 @@ iframe.contentWindow.postMessage({ type: 'slicc-vault', v: 1, op: 'hello', role:
|
|
|
187
189
|
**Events** on page ports:
|
|
188
190
|
- `{ v: 1, event: 'status', status }` whenever requests, records, the scrub state or the lock change;
|
|
189
191
|
- `{ v: 1, event: 'placeholders', placeholders, scrub }` whenever either changes;
|
|
190
|
-
- `{ v: 1, event: 'changed', parts }`, with `parts` among `secrets`, `audit` and `exits`, so a view knows what to reload
|
|
192
|
+
- `{ v: 1, event: 'changed', parts }`, with `parts` among `secrets`, `audit` and `exits`, so a view knows what to reload;
|
|
193
|
+
- `{ v: 1, event: 'refresh', name, source, expiresAt }`, when a J1 lease is due (below);
|
|
194
|
+
- `{ v: 1, event: 'egress', request }`, with a `MessagePort`, when the vault needs bios to send a request it built (below).
|
|
191
195
|
|
|
192
196
|
**Errors** use errno names: `EINVAL`, `ENOENT`, `EACCES`, `EEXIST`, `EAGAIN`, `ETIMEDOUT`, `ENOTCONN`, and `ENOTSUP` for ops this version doesn't have.
|
|
193
197
|
|
|
@@ -204,12 +208,28 @@ bios relays each kernel request unchanged as the `sealed` op, with the kernel's
|
|
|
204
208
|
| `request` | `kind: "value"`, `name`, `prompt?` (≤ 200 characters) | `{ state: "pending", id }` |
|
|
205
209
|
| `request` | `kind: "oauth"`, `provider`, `oauth` (an object) | the same |
|
|
206
210
|
| `request` | `kind: "jerk-seal"`, `provider` | the same |
|
|
207
|
-
| `
|
|
211
|
+
| `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 |
|
|
212
|
+
| `request` | `kind: "unseal"`, `provider` | the same, or the J2 refusal |
|
|
213
|
+
| `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`) |
|
|
208
214
|
| `cancel` | `id` | `{ state }`: `cancelled`, or the state it had already reached |
|
|
209
215
|
| `materialize` | `names` (or `shellGrants: true`), `path`, `argv`, `cwd`, `interpreter?` | `{ ticket, expiresAt, real: { NAME: true \| false } }`, below (Exec) |
|
|
210
216
|
|
|
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`.
|
|
212
|
-
- A value request
|
|
217
|
+
- **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`.
|
|
218
|
+
- 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.
|
|
219
|
+
- **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.
|
|
220
|
+
|
|
221
|
+
## Tokens and refresh
|
|
222
|
+
|
|
223
|
+
The vault has no network stack, so sign-ins run in seven's page (route (b), §3.11) and the vault keeps only the results.
|
|
224
|
+
- **A sign-in's tokens:** bios runs the sign-in, does the code exchange, and hands the results to `seal`, keeping no copy:
|
|
225
|
+
- the refresh token as `{ kind: 'grant', name, value, source: 'oauth:<provider>', oauth: { tokenUrl, clientId, scope?, resource? } }`. A grant is never injected and never exported; `tokenUrl` must be `https:`;
|
|
226
|
+
- the access token as `{ name, value, source: 'oauth:<provider>', expiresAt, refresh: '<grant name>' }`. A grant name needs an `oauth:` source and `jerk-seal` a `jerk:` one (`EINVAL`), and a refresh uses the grant only when its source is the token's own (else the grant is missing).
|
|
227
|
+
- With `request` (only for a `secret`; the id of a pending `oauth` request for the same provider as `oauth:<provider>`, or a `jerk-seal` request for the provider of `jerk:<provider>@…`) and `jerk` (`{ exit, user?, scopes?, expires_at? }`), `seal` also answers that request, in the same transaction that stores the record (a request that ended meanwhile refuses with `EINVAL`, and nothing is stored): its `/dev/sealed` `status` becomes `done`, with `jerk`, the record's `placeholder` and `record` state.
|
|
228
|
+
- **A J1 lease:** `{ name, value, source: 'jerk:<provider>@<device>', expiresAt, refresh: 'jerk-seal' }`.
|
|
229
|
+
- **When a token is due** (5 minutes before `expiresAt`): the core frame checks once a minute, for every connected namespace, and `refresh { name }` asks for it at once. Vault frames of the same storage (several tabs) take a Web Lock per refresh source, so one grant is sent to its IdP by one frame at a time; a frame that finds the token changed while it waited (refreshed or sealed again) sends nothing and answers with the stored token's `{ state: 'refreshed', expiresAt? }`.
|
|
230
|
+
- A `jerk-seal` token is announced once per `expiresAt` (kept in IndexedDB, so across frames), and again every 10 minutes while it stays unsealed, in case no live page heard it, with the `refresh` event, and bios runs J1 `seal` again (`{ state: 'requested' }`).
|
|
231
|
+
- An OAuth token is refreshed in the vault: it builds `POST tokenUrl` with `grant_type=refresh_token`, the refresh token, `client_id` and any `scope` and `resource` (form-encoded), and sends it through bios with the `egress` event. bios sends the request through its exits and posts `{ status, headers, body }` (or `{ error }`) on the event's port within 30 s. The event goes to the page port of the namespace that connected last. A port that doesn't answer in time (`ETIMEDOUT`), closed or not, keeps its ops and events but gets no more `egress`, and the next refresh goes to the page port before it. The vault reads `access_token`, `expires_in` and any new `refresh_token`, checks them as values, and rotates the records in one transaction with the `refresh` audit entry (their placeholders stay; if the token or its grant was changed, removed or restored during the round trip, nothing is written and it answers `EAGAIN`). An `expires_in` of 0 makes the token due at once, and one that isn't a non-negative finite number is `EACCES: refresh failed: the IdP returned an invalid expires_in` (likewise a `refresh_token` that is present but not a string, and a `token_type` other than `Bearer`); a response carrying `error` fails even with a token; a response without `expires_in` leaves the new token without an expiry, so it isn't refreshed again until a new one is sealed. It answers `{ state: 'refreshed', expiresAt? }`, without `expiresAt` when the token has none. A failed refresh is `EACCES: refresh failed: <the IdP's error code>`, scrubbed. Without a connected page it's `ENOTCONN`.
|
|
232
|
+
- `egress` carries the refresh token to the page, in transit only; the response goes back to the vault, never to the kernel.
|
|
213
233
|
|
|
214
234
|
## Import and export
|
|
215
235
|
|
|
@@ -232,7 +252,7 @@ Spectrum's Settings › Secrets reads the file and the password, and bios hands
|
|
|
232
252
|
|
|
233
253
|
| `kind` | `password` | what becomes a candidate |
|
|
234
254
|
|---|---|---|
|
|
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 |
|
|
255
|
+
| `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 ("this app already has this secret under another name", "another app that uses this vault already has this secret; use it there, or add it here by hand"), as is a name whose record here has another placeholder ("this app already has a different secret with this name") |
|
|
236
256
|
| `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
257
|
| `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
258
|
|
|
@@ -252,19 +272,20 @@ Values are checked as at `put` (at least 8 bytes, no CR, LF or NUL, no placehold
|
|
|
252
272
|
- 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.
|
|
253
273
|
- The reply never carries a value. The ticket is 32 hex characters and expires after 30 s.
|
|
254
274
|
|
|
255
|
-
**`materializeConsume`** (a page op, from bios for the kernel at exec) takes `{ ticket, stamp, path, argv, cwd, interpreters, trusted }`, all from the kernel and relayed unchanged:
|
|
275
|
+
**`materializeConsume`** (a page op, from bios for the kernel at exec) takes `{ ticket, stamp, path, resolved, argv, cwd, interpreters, trusted }`, all from the kernel and relayed unchanged:
|
|
256
276
|
- `stamp` is the exec'ing process.
|
|
277
|
+
- `resolved` is the kernel-normalised file that actually runs: `path` with every symlink resolved.
|
|
257
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.
|
|
258
|
-
- `trusted` is true when
|
|
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.
|
|
259
280
|
|
|
260
281
|
The vault burns the ticket, then requires that:
|
|
261
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;
|
|
262
|
-
- `
|
|
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);
|
|
263
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;
|
|
264
285
|
- under that name's mode, the chain passes:
|
|
265
|
-
- `true`: no entry of `path` and `interpreters` is on the refusal list, so a `#!/usr/bin/env node` script
|
|
266
|
-
- `{ argv }`: the listed program is `path`, and
|
|
267
|
-
- `{ shell = true }`: any chain;
|
|
286
|
+
- `true`: no entry of `path`, `resolved` and `interpreters` is on the refusal list, so neither a `#!/usr/bin/env node` script nor a symlink `/tmp/l → /bin/bash` passes, and `trusted` is true ("the program or its interpreter can be changed by a non-root user" otherwise), so a renamed copy of a shell, which its non-root maker owns, doesn't pass either;
|
|
287
|
+
- `{ argv }`: the listed program is `path`, and when `path` is a link to a refused program the rule must also list that `resolved` program by its real path ("list it in [exec] by its real path"), so a listed `/tmp/l` that is repointed to `/bin/bash` refuses. Whatever chain the kernel resolved for it is allowed, so a rule for `npm publish` allows `/usr/bin/npm`'s `node`. But when `resolved` or the chain is a refused program and `trusted` is false, it refuses ("the program or its interpreter can be changed by a non-root user"), and `env` and the other wrappers in the chain always refuse;
|
|
288
|
+
- `{ shell = true }`: any program and chain, the explicit exception;
|
|
268
289
|
- no real record has expired meanwhile.
|
|
269
290
|
|
|
270
291
|
Any failure 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).
|
package/dist/types/contract.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type RecordKind = 'secret' | 'grant' | 'model' | 'mcp';
|
|
|
6
6
|
export type RecordState = 'active' | 'inert' | 'removed';
|
|
7
7
|
export type RecordSource = 'settings' | 'process' | `jerk:${string}@${DeviceId}` | `oauth:${string}` | `import:${string}`;
|
|
8
8
|
export type LockMode = 'auto' | 'passkey' | 'passphrase';
|
|
9
|
-
export type RequestKind = 'value' | 'oauth' | 'jerk-seal';
|
|
9
|
+
export type RequestKind = 'value' | 'oauth' | 'jerk-seal' | 'jerk' | 'unseal';
|
|
10
10
|
export type RequestState = 'pending' | 'done' | 'cancelled' | 'expired';
|
|
11
11
|
export type ErrorCode = 'EINVAL' | 'ENOENT' | 'EACCES' | 'EEXIST' | 'EAGAIN' | 'ETIMEDOUT' | 'ENOTCONN' | 'ENOTSUP';
|
|
12
12
|
export type ExitName = 'page' | 'proxy' | 'tailscale' | `device:${DeviceId}`;
|
|
@@ -18,6 +18,8 @@ export interface Stamp {
|
|
|
18
18
|
uid: number;
|
|
19
19
|
gid: number;
|
|
20
20
|
tag: string | null;
|
|
21
|
+
users?: string;
|
|
22
|
+
groups?: number[];
|
|
21
23
|
}
|
|
22
24
|
export interface VaultError {
|
|
23
25
|
code: ErrorCode;
|
|
@@ -162,7 +164,11 @@ export interface PolicyEntry {
|
|
|
162
164
|
changes: PolicyChange[];
|
|
163
165
|
warning?: string;
|
|
164
166
|
}
|
|
165
|
-
export type AuditEntry = ForwardEntry | McpEntry | RevealEntry | SealEntry | RemoveEntry | PolicyEntry;
|
|
167
|
+
export type AuditEntry = ForwardEntry | McpEntry | RevealEntry | SealEntry | RemoveEntry | RefreshEntry | PolicyEntry;
|
|
168
|
+
export interface RefreshEntry extends AuditBase {
|
|
169
|
+
type: 'refresh';
|
|
170
|
+
grant: string;
|
|
171
|
+
}
|
|
166
172
|
export interface RequestHead {
|
|
167
173
|
method: string;
|
|
168
174
|
url: string;
|
|
@@ -352,7 +358,17 @@ export type Op = {
|
|
|
352
358
|
kind?: RecordKind;
|
|
353
359
|
expiresAt?: EpochMs;
|
|
354
360
|
refresh?: string;
|
|
361
|
+
oauth?: OAuthConfig;
|
|
362
|
+
request?: string;
|
|
363
|
+
jerk?: JerkResult;
|
|
355
364
|
} | {
|
|
365
|
+
op: 'refresh';
|
|
366
|
+
name: string;
|
|
367
|
+
} | ({
|
|
368
|
+
op: 'answer';
|
|
369
|
+
request: string;
|
|
370
|
+
state: 'progress' | 'done';
|
|
371
|
+
} & RequestAnswer) | {
|
|
356
372
|
op: 'import';
|
|
357
373
|
kind: ImportKind;
|
|
358
374
|
bytes: Uint8Array;
|
|
@@ -383,6 +399,7 @@ export type Op = {
|
|
|
383
399
|
ticket: string;
|
|
384
400
|
stamp: Stamp;
|
|
385
401
|
path: string;
|
|
402
|
+
resolved: string;
|
|
386
403
|
argv: string[];
|
|
387
404
|
cwd: string;
|
|
388
405
|
interpreters: string[];
|
|
@@ -468,7 +485,14 @@ export type OpReply = {
|
|
|
468
485
|
backupReminder: BackupReminder;
|
|
469
486
|
} | ({
|
|
470
487
|
op: 'policy';
|
|
471
|
-
} & PolicyResult) |
|
|
488
|
+
} & PolicyResult) | {
|
|
489
|
+
op: 'refresh';
|
|
490
|
+
state: 'refreshed' | 'requested';
|
|
491
|
+
expiresAt?: EpochMs;
|
|
492
|
+
} | {
|
|
493
|
+
op: 'answer';
|
|
494
|
+
state: RequestState;
|
|
495
|
+
} | ({
|
|
472
496
|
op: 'forward';
|
|
473
497
|
} & ForwardResult) | {
|
|
474
498
|
op: 'materializeConsume';
|
|
@@ -501,12 +525,71 @@ export type VaultEvent = {
|
|
|
501
525
|
v: Protocol;
|
|
502
526
|
event: 'changed';
|
|
503
527
|
parts: ChangedPart[];
|
|
528
|
+
} | {
|
|
529
|
+
v: Protocol;
|
|
530
|
+
event: 'refresh';
|
|
531
|
+
name: string;
|
|
532
|
+
source: RecordSource;
|
|
533
|
+
expiresAt: EpochMs;
|
|
534
|
+
} | {
|
|
535
|
+
v: Protocol;
|
|
536
|
+
event: 'egress';
|
|
537
|
+
request: EgressRequest;
|
|
538
|
+
} | {
|
|
539
|
+
v: Protocol;
|
|
540
|
+
event: 'cancelled';
|
|
541
|
+
request: string;
|
|
542
|
+
kind: RequestKind;
|
|
543
|
+
};
|
|
544
|
+
export interface JerkRow {
|
|
545
|
+
provider: string;
|
|
546
|
+
name?: string;
|
|
547
|
+
env: string[];
|
|
548
|
+
device?: string;
|
|
549
|
+
signedIn?: boolean;
|
|
550
|
+
user?: string;
|
|
551
|
+
scopes?: string[];
|
|
552
|
+
}
|
|
553
|
+
export interface RequestAnswer {
|
|
554
|
+
progress?: string;
|
|
555
|
+
exit?: number;
|
|
556
|
+
user?: string;
|
|
557
|
+
scopes?: string[];
|
|
558
|
+
message?: string;
|
|
559
|
+
rows?: JerkRow[];
|
|
560
|
+
placeholder?: string;
|
|
561
|
+
}
|
|
562
|
+
export interface EgressRequest {
|
|
563
|
+
method: string;
|
|
564
|
+
url: string;
|
|
565
|
+
headers: [string, string][];
|
|
566
|
+
body: string;
|
|
567
|
+
}
|
|
568
|
+
export type EgressAnswer = {
|
|
569
|
+
status: number;
|
|
570
|
+
headers: [string, string][];
|
|
571
|
+
body: Uint8Array | string;
|
|
572
|
+
} | {
|
|
573
|
+
error: string;
|
|
504
574
|
};
|
|
575
|
+
export interface OAuthConfig {
|
|
576
|
+
tokenUrl: string;
|
|
577
|
+
clientId: string;
|
|
578
|
+
scope?: string;
|
|
579
|
+
resource?: string;
|
|
580
|
+
}
|
|
581
|
+
export interface JerkResult {
|
|
582
|
+
exit: number;
|
|
583
|
+
user?: string;
|
|
584
|
+
scopes?: string[];
|
|
585
|
+
expires_at?: string;
|
|
586
|
+
}
|
|
505
587
|
export interface Hello {
|
|
506
588
|
type: MessageType;
|
|
507
589
|
v: Protocol;
|
|
508
590
|
op: 'hello';
|
|
509
591
|
role?: Role;
|
|
592
|
+
sudoGid?: number;
|
|
510
593
|
}
|
|
511
594
|
export type Greeting = {
|
|
512
595
|
v: Protocol;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-vault",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"hash-wasm": "4.12.0",
|
|
61
61
|
"kdbxweb": "2.1.1",
|
|
62
62
|
"lefthook": "2.1.17",
|
|
63
|
-
"monocart-coverage-reports": "2.13.
|
|
63
|
+
"monocart-coverage-reports": "2.13.1",
|
|
64
64
|
"playwright-core": "1.63.0",
|
|
65
65
|
"semantic-release": "25.0.9",
|
|
66
66
|
"typescript": "7.0.2"
|