@ai-ecoverse/slicc-vault 1.8.4 → 1.9.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 +3 -3
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ The build (`npm run build`) writes a flat static tree to `dist/site/`:
|
|
|
17
17
|
| `vault.css`, `fonts/` | Spectrum 2 from the CSS-only `@spectrum-css` packages, and Adobe Clean |
|
|
18
18
|
| `config.json` | `{ version, embedders }`. `embedders` is `vault.sliccy.ai`'s fallback list, `["https://seven.sliccy.ai"]` |
|
|
19
19
|
|
|
20
|
-
The bundles hold this repo's code
|
|
20
|
+
The bundles hold this repo's code, `@ai-ecoverse/slicc-scrub`, and `@ai-ecoverse/jerk-oauth` (certified 0.15.1, exact-pinned, for token refresh): its wasm is inlined into a separate chunk under `chunks/` (about 970 KB) that loads only on the first refresh or MCP `grant`, never fetched. 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:
|
|
21
21
|
- `Content-Security-Policy` with `script-src 'self'`, no inline scripts or styles, and `frame-ancestors` listing the seven origins;
|
|
22
22
|
- `Cross-Origin-Embedder-Policy: require-corp`, `Cross-Origin-Resource-Policy: same-site`, `Cross-Origin-Opener-Policy: same-origin`;
|
|
23
23
|
- `Origin-Agent-Cluster: ?1`, `nosniff`, `Referrer-Policy: no-referrer`, `no-cache` with an ETag.
|
|
@@ -222,13 +222,13 @@ bios relays each kernel request unchanged as the `sealed` op, with the kernel's
|
|
|
222
222
|
|
|
223
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
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:`, without userinfo (`user:pw@`, `user@`, even an empty `:@`, also when hidden by whitespace or backslashes the URL parser normalises: any `@` in the authority refuses);
|
|
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:`, without userinfo (`user:pw@`, `user@`, even an empty `:@`, also when hidden by whitespace or backslashes the URL parser normalises: any `@` in the authority refuses), and a `resource` must be an `https:` URL (or `http:` on 127.0.0.1, localhost or [::1]) without userinfo or fragment;
|
|
226
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
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
228
|
- **A J1 lease:** `{ name, value, source: 'jerk:<provider>@<device>', expiresAt, refresh: 'jerk-seal' }`.
|
|
229
229
|
- **When a token is due** (5 minutes before `expiresAt`): the core frame checks once a minute, and right after a page says hello, for every connected namespace and for `secret`, `model` and `mcp` records alike, 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
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:
|
|
231
|
+
- An OAuth token is refreshed in the vault with jerk-oauth: `refreshTokenRequestBytes` builds `POST <tokenUrl, serialized>` with `grant_type=refresh_token`, the refresh token, `client_id` and any `scope` and `resource` (form-encoded), from a byte buffer it zeroes, and the vault 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 checks the response with its own rules below and then with jerk-oauth's `parseRefreshResponse` (with the clock as whole seconds; a clock that isn't a safe integer is `EACCES: refresh failed: the vault's clock is invalid`), 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 whole number from 0 to ten years is `EACCES: refresh failed: the IdP returned an invalid expires_in` (likewise a `refresh_token` that is present but not a string, a `token_type` that isn't `Bearer` in any case or is missing, and `access_token`, `refresh_token`, `token_type` or `expires_in` given twice: `EACCES: refresh failed: the IdP returned NAME twice`); 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; a code that isn't 1–128 characters of RFC 6749's error charset reads "the IdP sent an unreadable error", and `error_description` is never shown. Any other response jerk-oauth refuses is `EACCES: refresh failed: the IdP response is invalid`. MCP `grant` responses are parsed the same way. Without a connected page it's `ENOTCONN`.
|
|
232
232
|
- `egress` carries the refresh token to the page, in transit only; the response goes back to the vault, never to the kernel.
|
|
233
233
|
|
|
234
234
|
## Import and export
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-vault",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.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",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"dist/types"
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
|
-
"prepare": "node scripts/endpoints.mjs && { [ ! -e .git ] || lefthook install; }",
|
|
35
|
-
"build": "node scripts/endpoints.mjs && node build.mjs && tsc -p tsconfig.build.json",
|
|
34
|
+
"prepare": "node scripts/endpoints.mjs && node scripts/jerk-wasm.mjs && { [ ! -e .git ] || lefthook install; }",
|
|
35
|
+
"build": "node scripts/endpoints.mjs && node scripts/jerk-wasm.mjs && node build.mjs && tsc -p tsconfig.build.json",
|
|
36
36
|
"prepack": "npm run build",
|
|
37
37
|
"start": "npm run build && node test/integration/serve.mjs",
|
|
38
38
|
"pretest": "npm run build && playwright-core install --no-shell chromium",
|
|
39
39
|
"test": "node --test --test-timeout=300000 --test-global-setup=node_modules/@ai-ecoverse/slicc-shared-web/harness/global.mjs 'test/integration/*.test.mjs'",
|
|
40
|
-
"test:unit": "node scripts/endpoints.mjs && NODE_NO_WARNINGS=1 mcr -c node_modules/@ai-ecoverse/slicc-shared-web/mcr.config.mjs node --import fake-indexeddb/auto --test --test-force-exit 'test/unit/**/*.test.mjs'",
|
|
40
|
+
"test:unit": "node scripts/endpoints.mjs && node scripts/jerk-wasm.mjs && NODE_NO_WARNINGS=1 mcr -c node_modules/@ai-ecoverse/slicc-shared-web/mcr.config.mjs node --import fake-indexeddb/auto --test --test-force-exit 'test/unit/**/*.test.mjs'",
|
|
41
41
|
"typecheck": "tsc",
|
|
42
42
|
"lint": "slicc-lint",
|
|
43
43
|
"format": "biome check --write .",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"release": "semantic-release"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
+
"@ai-ecoverse/jerk-oauth": "0.15.1",
|
|
49
50
|
"@ai-ecoverse/slicc-scrub": "1.0.0",
|
|
50
51
|
"@ai-ecoverse/slicc-shared-web": "1.13.1",
|
|
51
52
|
"@biomejs/biome": "2.5.15",
|