@ai-ecoverse/slicc-vault 1.14.0 → 1.15.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 +11 -2
- package/dist/types/contract.d.ts +15 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -396,7 +396,7 @@ The vault never injects a secret into a model or MCP endpoint, whatever a rule s
|
|
|
396
396
|
| `model` | `provider?`, `request`: `{ method, url, headers: [name, value][], body?: string \| bytes }` | `{ request, hits, version }`, the request prepared for the agent's transport, below |
|
|
397
397
|
| `mcp` | `server`, `request` (the same shape) | `{ request, hits, version }`, below |
|
|
398
398
|
| `records` | — | `{ records: [{ kind, name, placeholder, state, provider?, server?, expiresAt?, settings? }] }`: the namespace's `model` and `mcp` records that aren't removed, `state` being `ready`, `expired` or `refreshing` (expired with a refresh source), and `settings` scrubbed over every value (an entry whose name holds a value is dropped; so is a record whose name, provider or server holds one, and an `expiresAt` whose digits are a value). Never a value, a `secret` or a `grant`; the records are read and scrubbed under one scrub version (retried, then `EAGAIN` while values keep changing); `EACCES` while the vault is locked before its first unlock |
|
|
399
|
-
| `grant` | `server`, `response` (the token endpoint's body, a string or bytes), `oauth` (`{ tokenUrl, clientId, scope?, resource? }`), `name?` (a record name; anything else is `EINVAL`) | `{ response, records: [{ kind, name, placeholder }], expiresAt? }`, below |
|
|
399
|
+
| `grant` | `provider`, `name`, `response`, `settings?` for a model-provider sign-in (below), or `server`, `response` (the token endpoint's body, a string or bytes), `oauth` (`{ tokenUrl, clientId, scope?, resource? }`), `name?` (a record name; anything else is `EINVAL`) | `{ response, records: [{ kind, name, placeholder }], expiresAt? }`, below |
|
|
400
400
|
| `refresh` | `server` or `provider`, `name?` | `{ state: 'refreshed', expiresAt? }`: refreshes that server's or provider's token in the vault, as Tokens and refresh describes (an `expiresAt` is left out when its digits are a value before or after the refresh, the replaced and the new token included, or when any other value changed while it ran); `ENOENT` when it has none, `EINVAL` when it has several and no `name` picks one |
|
|
401
401
|
|
|
402
402
|
**Preparing model and MCP requests (§3.12).** Every model and MCP request of the agent worker goes through `model` or `mcp`, with or without a credential; the response comes back to the worker directly.
|
|
@@ -411,9 +411,18 @@ The vault never injects a secret into a model or MCP endpoint, whatever a rule s
|
|
|
411
411
|
|
|
412
412
|
**MCP OAuth (`grant`).** The worker registers the server with its `tokenUrl`, sends the code exchange through `mcp` (to exactly `tokenUrl`, with no credential), and hands the token endpoint's body to `grant`, holding it only in transit.
|
|
413
413
|
- `oauth.tokenUrl` must be `https:` and the server's registered `tokenUrl` (`EACCES` otherwise). A server or record name that holds a stored value or one of the returned tokens, in any encoded form, is `EINVAL`. The body is checked as for a refresh (an object with a string `access_token`, no `error`, `token_type` Bearer if present, a valid `expires_in`), and every token must be a valid value (`EINVAL` otherwise), before anything is stored.
|
|
414
|
-
- The access token is sealed as an `mcp` record bound to `server`, named `name` or `<SERVER>_ACCESS_TOKEN` (upper-cased, other characters as `_`, `MCP_` in front of a leading digit), with source `oauth:mcp` and `expiresAt` from `expires_in`. A `refresh_token` is sealed as a `grant` named `<name>_GRANT` with `oauth` and the same `server`, and becomes the token's refresh source.
|
|
414
|
+
- The access token is sealed as an `mcp` record bound to `server`, named `name` or `<SERVER>_ACCESS_TOKEN` (upper-cased, other characters as `_`, `MCP_` in front of a leading digit), with source `oauth:mcp` and `expiresAt` from `expires_in`. A `refresh_token` is sealed as a `grant` named `<name>_GRANT` with `oauth` and the same `server`, and becomes the token's refresh source. A live `<name>_GRANT` that isn't this token's own (a sign-in's, say) is `EEXIST`, checked in the writing transaction; a re-grant without a `refresh_token` removes the token's old grant unless another live credential shares it.
|
|
415
415
|
- Both records are written in one transaction, so `grant` stores all or nothing. The reply's `response` is JSON, a string or bytes like the input, with only `access_token` and `refresh_token` (as their placeholders), any string `token_type`, a numeric `expires_in` that isn't a stored value, and string `scope` scrubbed over every value; every other field, `id_token` included, is dropped. A `grant` whose access-token name is already bound to another server is `EEXIST`, checked in the same transaction.
|
|
416
416
|
|
|
417
|
+
**Model-provider sign-ins (`grant { provider }`, §3.12.1).** For the subscription sign-ins `xai`, `kimi-coding` and `openai-codex`, the worker runs the device-code flow and hands the vault the final token response: `grant { provider, name, response, settings? }`, with `name` the account's record name. The worker holds that response only in memory and never logs, stores, licks or transcribes it.
|
|
418
|
+
- **The token endpoint and client id come from the vault's own table** (`src/providers.ts`, taken from pi-ai's OAuth modules and checked against the installed pi-ai by a test): `https://auth.x.ai/oauth2/token`, `https://auth.kimi.com/api/oauth/token`, `https://auth.openai.com/oauth/token`. A `server` or `oauth` in the request is `EINVAL`, and any other provider is `ENOTSUP`.
|
|
419
|
+
- **The response** is checked like an MCP grant's, except that a missing `token_type` is taken as `Bearer` (none of these providers' clients reads it); one that's present and not `Bearer` is still refused.
|
|
420
|
+
- **The records:** the access token becomes a `model` record of `provider`, source `oauth:<provider>`, with its `expiresAt`; the refresh token, if any, a `grant` record `<name>_GRANT` that refreshes through `egress` as usual. Both are written in one transaction. The reply is `{ records: [{ kind, name, placeholder }], expiresAt? }`.
|
|
421
|
+
- **Its name and settings** (its own or, without any, those it inherits from the name's record, removed or not, checked in the writing transaction, which is `EAGAIN` if a value was sealed meanwhile) may hold neither a returned token nor a stored value (`EINVAL`), and `<name>_GRANT` must be free or this record's own grant, checked in the transaction that writes them (`EEXIST`); a removed grant still counts as another credential's while that removed credential can be restored. Over an earlier OAuth key whose grant has another name, that grant is removed unless shared. `response` is a string or bytes (any `ArrayBufferView`).
|
|
422
|
+
- **Under an existing name:** a record of another provider, even a removed one, is `EEXIST`. An earlier sign-in of the same provider is rotated, keeping its placeholder; if the new response has no `refresh_token`, the old grant is removed with it unless another live credential still refreshes from it. A key the user pasted (any source but `oauth:`), live or removed within the 30 days it can still be restored (one removed longer ago is gone, whatever its provider), is kept restorable: the sign-in replaces it (same placeholder), and `restore` of that name brings the pasted key back with its own source, settings, expiry and refresh link, and removes the sign-in's grant (unless shared). `list` marks such a record `replaced: true`. The stashed key stays encrypted under the vault key (re-sealed when the lock changes), is scrubbed like any value, and is what `export` writes for that name with the pasted key's own settings and expiry (a backup carries the pasted key, not the sign-in).
|
|
423
|
+
- **Remove** of the `model` record removes its grant too; `restore` restores both (for a record without a pasted key underneath).
|
|
424
|
+
- **Refresh failures:** the provider refusing the refresh is `EACCES "refresh failed: <code>"`; after `invalid_grant`, the grant is dead (not asked again), `records` shows the key `expired` and it isn't filled any more (`EACCES` "NAME expired; sign in again"), whatever its `expiresAt`, until a new sign-in. A network failure (`ENOTCONN`, `ETIMEDOUT`) keeps it `refreshing`.
|
|
425
|
+
|
|
417
426
|
**Bytes** are any `ArrayBufferView` (`Uint8Array`, `Int8Array`, `DataView`, a `Float32Array`, a Node `Buffer`), read as exactly its own bytes from `byteOffset` for `byteLength`, never the rest of its buffer. Anything else, a bare `ArrayBuffer` included, is `EINVAL` ("chunk must be bytes", or "texts must be a list of strings and bytes").
|
|
418
427
|
|
|
419
428
|
A stream is compiled against the namespace's values when it opens. If a value is added, rotated or removed while it's open, its next `push`, `idle` or `end` answers `EAGAIN` and the stream is gone: open it again and push the output from its start.
|
package/dist/types/contract.d.ts
CHANGED
|
@@ -90,6 +90,7 @@ export interface SecretView {
|
|
|
90
90
|
server?: string;
|
|
91
91
|
url?: string;
|
|
92
92
|
pinned?: true;
|
|
93
|
+
replaced?: true;
|
|
93
94
|
settings?: Record<string, string>;
|
|
94
95
|
domains: string[];
|
|
95
96
|
rules: Rule[];
|
|
@@ -604,6 +605,12 @@ export type AgentOp = {
|
|
|
604
605
|
response: string | Uint8Array;
|
|
605
606
|
oauth: OAuthConfig;
|
|
606
607
|
name?: string;
|
|
608
|
+
} | {
|
|
609
|
+
op: 'grant';
|
|
610
|
+
provider: 'xai' | 'kimi-coding' | 'openai-codex';
|
|
611
|
+
name: string;
|
|
612
|
+
response: string | Uint8Array;
|
|
613
|
+
settings?: Record<string, string>;
|
|
607
614
|
} | {
|
|
608
615
|
op: 'refresh';
|
|
609
616
|
server: string;
|
|
@@ -652,6 +659,14 @@ export type AgentReply = {
|
|
|
652
659
|
placeholder: string;
|
|
653
660
|
}[];
|
|
654
661
|
expiresAt?: EpochMs;
|
|
662
|
+
} | {
|
|
663
|
+
op: 'grant';
|
|
664
|
+
records: {
|
|
665
|
+
kind: RecordKind;
|
|
666
|
+
name: string;
|
|
667
|
+
placeholder: string;
|
|
668
|
+
}[];
|
|
669
|
+
expiresAt?: EpochMs;
|
|
655
670
|
} | {
|
|
656
671
|
op: 'refresh';
|
|
657
672
|
state: 'refreshed' | 'requested';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-vault",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.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.3.0",
|
|
6
6
|
"type": "module",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@ai-ecoverse/jerk-oauth": "1.0.0",
|
|
51
|
-
"@ai-ecoverse/slicc-kernel": "1.
|
|
51
|
+
"@ai-ecoverse/slicc-kernel": "1.132.1",
|
|
52
52
|
"@ai-ecoverse/slicc-scrub": "1.0.0",
|
|
53
|
-
"@ai-ecoverse/slicc-shared-web": "1.
|
|
54
|
-
"@ai-ecoverse/wasm-bash": "5.3.0-
|
|
53
|
+
"@ai-ecoverse/slicc-shared-web": "1.25.1",
|
|
54
|
+
"@ai-ecoverse/wasm-bash": "5.3.0-21",
|
|
55
55
|
"@ai-ecoverse/wasm-coreutils": "9.12.0-10",
|
|
56
56
|
"@biomejs/biome": "2.5.15",
|
|
57
57
|
"@earendil-works/pi-ai": "1.3.0",
|