@ai-ecoverse/slicc 1.30.1 → 1.32.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.
Files changed (2) hide show
  1. package/README.md +32 -4
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -426,7 +426,7 @@ One WebRTC peer connection. seven offers and the node answers. The node:
426
426
  - **Resets with `EOPNOTSUPP`:** an OPEN for a kind it doesn't serve.
427
427
  - **Closes the link:** an unknown frame type, a short or malformed frame (END with a payload, CREDIT that isn't 4 bytes), stream id 0, a reused id, an id with the wrong parity (page streams are odd, node streams even), or a second OPEN on a stream channel.
428
428
 
429
- HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "local", "node", "caps": ["net", "http", "cdp"], "routes": { "prefixes": [], "exit": true }, "policy": { "allow", "deny", "summary" }, "browsers": [ … ] }`. `cdp` and `browsers` are there when at least one browser is listed (see [`cdp`](#cdp-browser-control)). With `--runner`, `caps` adds `"ssh"` and HELLO adds `"sshHostKey": "ssh-ed25519 AAAA…"`, the host key in `authorized_keys` form, so the page can write `<host>.slicc.internal <key>` into the kernel's `known_hosts`. With `--mount`, `caps` adds `"fs"` and HELLO adds `"mounts"` (see [Host folders](#host-folders)).
429
+ HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "local", "node", "caps": ["net", "http", "credentialed", "cdp"], "routes": { "prefixes": [], "exit": true }, "policy": { "allow", "deny", "summary" }, "browsers": [ … ] }`. `cdp` and `browsers` are there when at least one browser is listed (see [`cdp`](#cdp-browser-control)). With `--runner`, `caps` adds `"ssh"` and HELLO adds `"sshHostKey": "ssh-ed25519 AAAA…"`, the host key in `authorized_keys` form, so the page can write `<host>.slicc.internal <key>` into the kernel's `known_hosts`. With `--mount`, `caps` adds `"fs"` and HELLO adds `"mounts"` (see [Host folders](#host-folders)).
430
430
 
431
431
  **`ssh`** (cap `ssh`), page → node, with `--runner` only:
432
432
  - OPEN `{ "kind": "ssh" }`, OPENED `{}` at once. The stream's bytes are one SSH connection to the node's SSH server.
@@ -460,13 +460,21 @@ HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode
460
460
  - **RESET before the response head:** `EPROTO` for a malformed head or one of the refusals above, and for a `206` with a coding the node would decode, since its `Content-Range` wouldn't match. A failed fetch is `<code>: fetch failed: <reason>`, with `tcp`'s codes: `EACCES` for a policy refusal, `ENOTFOUND` for a name that doesn't resolve, `ETIMEDOUT`, `ECONNRESET`, and `ECONNREFUSED` for anything else, TLS failures included.
461
461
  - **After the head:** an upstream error or a broken coding mid-body is `RESET ECONNRESET`.
462
462
 
463
+ **`credentialed`** (node cap, no kind of its own), on every node that serves `http`. An `http` OPEN with `"credentialed": true` carries a credential the vault injected for a transit exit (link spec §5.13). For such a stream the node:
464
+ - refuses a URL that isn't `https:` on port 443 with `RESET EACCES: credentialed requests go only to https on port 443`, before anything is dialled;
465
+ - logs the request only as its method, scheme and host, as for every `http` stream, and never its path, query, headers or body, `SLICC_DEBUG` included;
466
+ - scrubs RESET messages: they carry the error's reason, never the URL's path or query, a header or a body byte;
467
+ - follows no redirects and keeps nothing: no cache, no cookies, nothing on disk.
468
+
469
+ Any value of `credentialed` other than `false` or `null` counts as true, so a malformed flag never sends a credential out as an ordinary request.
470
+
463
471
  **`kernel`** (page cap `kernel-in`), node → page:
464
472
  - OPEN `{ "kind": "kernel", "port" }`, sent only to a page whose HELLO lists `kernel-in`. The page dials that kernel port and answers OPENED or RESET (`ECONNREFUSED` when nothing listens). See [Kernel services](#kernel-services).
465
473
  - Then raw bytes both ways: the node's first DATA is everything it has read from the browser's connection so far, request head included. END half-closes, as for `tcp`.
466
474
 
467
475
  ### `cdp` (browser control)
468
476
 
469
- > **Warning:** a seven with the `cdp` capability has full control of every listed browser: its tabs, cookies, stored passwords and logged-in sessions, files it can download, and anything a page in it can reach. That includes the browser seven runs in (`seven: true`), whether slicc launched it or not: with `slicc` or `slicc attach` on your everyday browser, seven can drive all of that browser, without a further prompt. List only browsers you would let that seven use as you. For `follow` that is a seven on another machine.
477
+ > **Warning:** a seven with the `cdp` capability has full control of every listed browser: its tabs, cookies, stored passwords and logged-in sessions, files it can download, and anything a page in it can reach. That includes the browser seven runs in (`seven: true`), whether slicc launched it or not: with `slicc` or `slicc attach` on your everyday browser, seven can drive all of that browser, without a further prompt. List only browsers you would let that seven use as you. For `follow` that is a seven on another machine. The one exception is seven's own origin, which the node keeps out of reach (see below).
470
478
 
471
479
  Only browsers the user named are listed: the one seven runs in, `--cdp` and `--cdp-extra` for `attach`, the extras picked with `--browser` or in the picker for `slicc`, and `--cdp` for `follow`. A browser discovery found but nobody picked is not.
472
480
 
@@ -480,7 +488,7 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
480
488
  - **OPENED:** `{ "product", "protocolVersion" }`, the `Browser` and `Protocol-Version` fields of `/json/version` (from `Browser.getVersion` for a `wsOnly` browser).
481
489
  - **RESET:** `ENOENT` for an id that isn't listed, `ECONNREFUSED` when the browser doesn't answer, `EPROTO` for an OPEN without `browser`.
482
490
 
483
- **Bytes**, both directions: a sequence of messages, each a `u32` BE length plus one UTF-8 CDP JSON message of at most 64 MiB (67 108 864 bytes). The node sends each page message as one WebSocket text message, and each text message from the browser as one stream message. A length over 64 MiB from the page, or a browser message over 64 MiB, is `RESET EMSGSIZE`, and a page END in the middle of a message is `RESET EPROTO`. Browser messages that start with `{"method":"Network.webSocketFrameReceived"` or `{"method":"Network.webSocketFrameSent"` are dropped: seven runs inside a browser it may debug, so those events would carry the CDP traffic itself and grow without bound (parity with slicc-node's `/cdp`).
491
+ **Bytes**, both directions: a sequence of messages, each a `u32` BE length plus one UTF-8 CDP JSON message of at most 64 MiB (67 108 864 bytes). The node sends each page message as one WebSocket text message, and each text message from the browser as one stream message, after the filter below. A length over 64 MiB from the page, or a browser message over 64 MiB, is `RESET EMSGSIZE`, and a page END in the middle of a message is `RESET EPROTO`. Browser messages that start with `{"method":"Network.webSocketFrameReceived"` or `{"method":"Network.webSocketFrameSent"` are dropped: seven runs inside a browser it may debug, so those events would carry the CDP traffic itself and grow without bound (parity with slicc-node's `/cdp`).
484
492
 
485
493
  **Closing.**
486
494
  - The browser closes the socket with a normal close (1000): the node sends END, then discards whatever the page still sends until its END.
@@ -488,11 +496,23 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
488
496
  - END from the page: the node closes the socket with a normal close, then sends END.
489
497
  - RESET from the page, or the link going down: the socket is closed at once.
490
498
 
499
+ **Seven's origin is off limits.** The node relays CDP through a filter, so seven's browser control never navigates to, opens or attaches to a target on seven's own origin, and never reads or writes its cookies or storage. That holds for every listed browser, not only the one seven runs in. What is protected comes from [slicc-browsers](https://github.com/ai-ecoverse/slicc-browsers#protected-origins)' `protectedOrigins`: every `sliccy.ai` host (the apex and `www` included, any port, `https` and `http`), `vault.sliccy.ai` by name (the vault is never a CDP target for seven's tools), `blob:`, `filesystem:` and `view-source:` URLs by the URL inside them, `devtools:` URLs, and the extra origins: `--origin` for `attach`, and `--origin` and the `--page` origin for `slicc`. `follow` has none. `*.kernel.localhost` is not protected. URLs are parsed the way the browser parses them (WHATWG), and the node forwards its own re-encoding of each command, so the browser runs exactly what was checked: a key given twice counts once, as its last value, and a message that isn't a JSON object with an integer `id` and a string `method` is refused (or dropped, without an `id`).
500
+ - **Targets.** Before the stream opens, the node attaches its own session to the browser target and calls `Target.setDiscoverTargets` and `Target.getTargets` on it with ids of its own. Neither the session nor its messages ever reach the page. From then on it knows every target's URL and opener. A target on an inheriting URL (`about:blank`, `about:srcdoc`, none) is protected when it was protected before or when its opener is protected or unknown, so a blank popup that seven opens counts as seven. On a page session, the main frame's `securityOrigin` (`Page.frameNavigated`) and the default context's `origin` (`Runtime.executionContextCreated`) count too.
501
+ - **Refused**, with `{"id":…,"sessionId"?:…,"error":{"code":-32000,"message":"slicc: refused: target is on seven's origin"}}`, never forwarded: `Target.createTarget`, `Page.navigate` and anything else whose `url` is protected; `Page.navigateToHistoryEntry` to an entry whose URL is protected (the node looks it up with `Page.getNavigationHistory`); `Target.attachToTarget`, `activateTarget`, `closeTarget`, `autoAttachRelated`, `Browser.getWindowForTarget` and any other command whose `targetId` or `frameId` is a protected target; and every command on a session whose target is protected, or became so. `Target.getTargetInfo` and `Target.detachFromTarget` stay allowed.
502
+ - **Origins and storage.** A command whose `origin`, `securityOrigin`, `ownerOrigin`, `issuerOrigin`, `topLevelOrigin`, `topLevelSite`, `embeddedOrigin`, `requestingOrigin`, `scopeURL` or `manifestId` is protected, or whose `storageKey` or `cacheId` names seven's storage, is refused the same way. This covers `Storage.*`, `IndexedDB.*`, `CacheStorage.*`, `DOMStorage.*`, `FileSystem.*`, `ServiceWorker.*`, `PWA.*`, `Browser.setPermission` and `Browser.grantPermissions`. Free-form values (`expression`, `arguments`, `value`, `headers`, `postData` and the like) aren't searched.
503
+ - **Cookies.** `Network.getCookies`, `Network.getAllCookies`, `Storage.getCookies` and `Page.getCookies` answer without the cookies whose domain is protected (`sliccy.ai` and every subdomain, and each extra origin's host and its parents: cookies ignore ports), and without partitioned cookies whose `partitionKey.topLevelSite` is such a site. `Network.setCookie`, `setCookies`, `deleteCookies`, `Storage.setCookies` and `Page.deleteCookie` for such a domain, a URL on such a host, or a partition key under such a site, are refused with `slicc: refused: cookies on seven's origin`. So are `Network.clearBrowserCookies` and `Storage.clearCookies`, except in a browser context this stream created with `Target.createBrowserContext`.
504
+ - **Requests a page makes to seven.** For a request whose URL is protected, `Network.*` and `Fetch.*` events lose their `Cookie` and `Set-Cookie` headers, their raw header texts and their cookie lists, and `requestWillBeSentExtraInfo` drops seven's cookies from `associatedCookies` for any request. For a WebSocket, WebTransport or EventSource on a protected origin (`ws:` and `wss:` count as `http:` and `https:`), `webSocketWillSendHandshakeRequest`, `webSocketHandshakeResponseReceived`, `webSocketFrameError`, `eventSourceMessageReceived` and `dataReceived` are dropped. Reading such a request's body (`Network.getResponseBody`, `Fetch.getResponseBody`, `takeResponseBody…AsStream`, `Audits.getEncodedResponse` and the like) is refused. Continuing, failing or fulfilling it is allowed.
505
+ - **Browser-wide commands**, refused with their own reason after `slicc: refused: `: `Target.exposeDevToolsProtocol` (it would give a page the whole browser) and `Target.sendMessageToTarget` (attach with `flatten: true` instead), on any target; `Fetch.enable` on the browser itself (a page session is fine); `Tracing.start`; `Extensions.loadUnpacked`; and `Browser.setPermission`, `Browser.grantPermissions` without an `origin` and `Browser.resetPermissions`, except in a browser context this stream created.
506
+ - **Auto-attach.** With `Target.setAutoAttach`, a session to a protected target is detached by the node at once (`Target.detachFromTarget` on its parent session), and the page never sees its `attachedToTarget` or `detachedFromTarget`.
507
+ - **Navigation by other means.** A script's `location = …`, a link, `window.open` or a server redirect can't be stopped: the node sees the target arrive (`Target.targetInfoChanged`) only after the browser committed it. Then the node detaches every page session on it, drops their events and results, and refuses further commands on them; the page gets `Target.detachedFromTarget`. Commands that were already on their way may still have run in the new page.
508
+ - **Listing.** `Target.getTargets`, `Target.getTargetInfo`, `Target.targetCreated` and `Target.targetInfoChanged` still show protected targets, so seven knows its tab exists, but their `url` and `title` are only the origin (`https://seven.sliccy.ai`, or `about:blank`). Service worker registrations and versions on a protected origin are left out of `ServiceWorker` events, and `Storage`, `DOMStorage` and `BackgroundService` events that name a protected origin are dropped.
509
+ - **Limits.** The filter relies on Chrome's site isolation: a seven frame inside a page of another site is a target of its own (`iframe`), which the node protects, but a page can still frame it, and a screenshot of that page shows it. A same-site frame shares its parent's process and isn't a target. Every `sliccy.ai` host is protected, so this only matters for an extra origin, such as one port of `127.0.0.1` framed by a page on another. If the node loses its own session (`ECONNRESET: slicc lost track of the browser's targets`), it refuses every command and resets the stream unless the browser closes within 2 s. The node's own setup connection (see [Local setup](#local-setup-slicc-attach)) is not a `cdp` stream and isn't filtered.
510
+
491
511
  **List changes.** The node checks every listed browser's debugging port every 2 s. One that fails twice in a row is removed: its open streams get `RESET ECONNRESET: browser quit`, and a later OPEN for it gets `ENOENT`. Once its port answers again it is listed again under the same id. A browser removed through the API (`Registry.Remove`, for the TUI) has its streams reset with `ECONNRESET: browser removed`.
492
512
 
493
513
  **`browsers` updates** (page cap `browsers`), node → page. When the list changes while a link is up and the page's HELLO has `browsers` in `caps`, the node opens `OPEN { "kind": "browsers", "browsers": [ … ] }` on the control channel with the full current list in HELLO's shape. It replaces the previous list; there are no deltas. The page answers OPENED `{}` and END, and the node sends END. Updates are sent one at a time per link, a change that comes while one is in flight is sent after it, with the list as it is then, and an update the page doesn't finish within 10 s is reset with `ETIMEDOUT`. A change between the HELLO and the link coming up is sent as an update right away.
494
514
 
495
- `test/integration/testdata/link/link.js` is a page-side implementation of the link (framing, credit, both channel layouts, the violation rules, and `resolve` and raw-fetch helpers) that the integration tests run in Chrome against `slicc attach`. `cdp.js` next to it is a CDP client over `cdp` streams, `fs.js` an op-shaped client for `fs` streams (`call`, `list`, `read`, `write`, `watch`), and `page.js` answers `browsers` and `mounts` updates. `page.js` keeps one link per `node` id from the hello. For tray links (no hello `node`) it replaces an older link with the same fingerprint only when HELLO's `node` matches, as seven does. `agent.js` is a fake agent behind the page's `agent` cap: the tests queue scripted runs, emit transcript events, hold and release a run, reset watch streams, and turn the cap off. Loaded with `?kernel`, the test page lists `kernel-in` and answers `kernel` OPENs with `kernel.js`, a small HTTP and WebSocket echo server in the page.
515
+ `test/integration/testdata/link/link.js` is a page-side implementation of the link (framing, credit, both channel layouts, the violation rules, and `resolve` and raw-fetch helpers) that the integration tests run in Chrome against `slicc attach`. `cdp.js` next to it is a CDP client over `cdp` streams, `protect.js` probes the filter that keeps seven's origin out of reach, `fs.js` an op-shaped client for `fs` streams (`call`, `list`, `read`, `write`, `watch`), and `page.js` answers `browsers` and `mounts` updates. `page.js` keeps one link per `node` id from the hello. For tray links (no hello `node`) it replaces an older link with the same fingerprint only when HELLO's `node` matches, as seven does. `agent.js` is a fake agent behind the page's `agent` cap: the tests queue scripted runs, emit transcript events, hold and release a run, reset watch streams, and turn the cap off. Loaded with `?kernel`, the test page lists `kernel-in` and answers `kernel` OPENs with `kernel.js`, a small HTTP and WebSocket echo server in the page.
496
516
 
497
517
  ### Kernel services
498
518
 
@@ -539,6 +559,14 @@ The browser's connection waits unread for `OPENED`.
539
559
  - Names never reveal host paths, and neither does anything else on the link.
540
560
  - Without a folder, the node doesn't advertise `fs`, and an `fs` OPEN gets `EOPNOTSUPP`.
541
561
 
562
+ **Private paths.** Some folders are never shared, even inside a folder that is:
563
+ - slicc's own config, data and cache folders (`<config>/slicc`, which holds the DTLS certificates, the SSH host key, the tray state and the vault pairing keys);
564
+ - browser profiles: slicc's `seven-profiles` and `profiles`, SLICC six's profiles and `SLICC_USER_DATA_DIR`, and the user data folders of the browsers slicc knows, since the vault's storage lives there;
565
+ - slicc-jerk's files: `$XDG_CONFIG_HOME/slicc-jerk` and `$XDG_STATE_HOME/slicc-jerk` (`~/.config` and `~/.local/state` when unset, macOS included), `/etc/slicc-jerk`, and `SLICC_JERK_CONFIG`; and on Windows `slicc-jerk` under `%APPDATA%`, `%LOCALAPPDATA%` and `%ProgramData%`;
566
+ - host fnox's files: `FNOX_CONFIG_DIR` and `FNOX_STATE_DIR` with fnox's own defaults (`$XDG_CONFIG_HOME/fnox` and `$XDG_STATE_HOME/fnox`, else `~/.config/fnox` and `~/.local/state/fnox`, or `~\AppData\Local\fnox` on Windows), `FNOX_AGE_KEY_FILE`, and the daemon's socket folders.
567
+
568
+ The paths are read from slicc's own environment at start. A `--mount` of one of them, or of a folder inside one, is skipped with `--mount <spec>: slicc never shares this folder, which holds its own keys or a credential tool's files, skipping`. In a folder that contains one, every operation on a private path or below it is `EACCES` with `slicc never shares this path`. That holds when the path is reached through a symlink, through a Windows 8.3 short name, or, on macOS and Windows, typed with a different case. A `rename`, `rmdir`, `unlink` or `symlink` of a folder that holds a private path (`.config`, for example) is refused too, so a private folder can't be moved out of the fence or replaced. `list` still shows a private folder's name. `watch` drops a change inside a private folder before it is batched, so it adds no line, no parent folder and no `all`.
569
+
542
570
  **HELLO.** `caps` lists `fs`, and `"mounts"` lists the folders:
543
571
 
544
572
  ```json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc",
3
- "version": "1.30.1",
3
+ "version": "1.32.0",
4
4
  "description": "SLICC's command line for seven: links a machine's network, compute and browsers to seven over WebRTC",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -18,11 +18,11 @@
18
18
  "node": ">=18"
19
19
  },
20
20
  "optionalDependencies": {
21
- "@ai-ecoverse/slicc-darwin-arm64": "1.30.1",
22
- "@ai-ecoverse/slicc-darwin-amd64": "1.30.1",
23
- "@ai-ecoverse/slicc-linux-arm64": "1.30.1",
24
- "@ai-ecoverse/slicc-linux-amd64": "1.30.1",
25
- "@ai-ecoverse/slicc-windows-arm64": "1.30.1",
26
- "@ai-ecoverse/slicc-windows-amd64": "1.30.1"
21
+ "@ai-ecoverse/slicc-darwin-arm64": "1.32.0",
22
+ "@ai-ecoverse/slicc-darwin-amd64": "1.32.0",
23
+ "@ai-ecoverse/slicc-linux-arm64": "1.32.0",
24
+ "@ai-ecoverse/slicc-linux-amd64": "1.32.0",
25
+ "@ai-ecoverse/slicc-windows-arm64": "1.32.0",
26
+ "@ai-ecoverse/slicc-windows-amd64": "1.32.0"
27
27
  }
28
28
  }