@ai-ecoverse/slicc 1.44.5 → 1.44.7
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 +17 -9
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -318,6 +318,7 @@ Release builds (a plain `X.Y.Z` version, linked in or, for `go install`, from th
|
|
|
318
318
|
| `SLICC_DISCOVERY` | test-only: limits browser discovery, see [Development](#development) |
|
|
319
319
|
| `SLICC_OPEN_COMMAND` | test-only: `slicc open` runs this program with the pairing URL as its only argument instead of opening the default browser; the integration tests always set it |
|
|
320
320
|
| `SLICC_PROTECTED_PORTS` | test-only: `<first>-<last>` (or one port), more [debugging ports](#network-policy) denied in every mode on top of 9222-9229 (never instead of them), so tests can prove the range on fake ports without touching the real ones. A malformed value exits 1 |
|
|
321
|
+
| `SLICC_CDP_GUARD_IDLE`, `SLICC_CDP_GUARD_LIMIT` | test-only: Go durations (such as `2s`) that shorten the [`cdp`](#cdp-browser-control) guard's idle bound (30 s) and outer bound (5 min) on a target's setup. A longer value counts as the default, and so does 0, a negative value or one that doesn't parse, so the variables can never widen or turn off the guard |
|
|
321
322
|
| `SLICC_LOOPBACK_PORTS` | test-only: `<first>-<last>`, the port range of the [loopback listener](#linking-seven-in-your-everyday-browser) instead of 17117-17126 |
|
|
322
323
|
| `SLICC_LINK_URL` | inside seven only: the link facade's address, `tcp://127.0.0.1:<port>` (see [Inside seven](#inside-seven)) |
|
|
323
324
|
| `SLICC_USER_DATA_DIR`, `XDG_STATE_HOME` | read, as SLICC six reads them, only to find six's Chrome profile (see [Using SLICC six's Chrome profile](#using-slicc-sixs-chrome-profile)) |
|
|
@@ -415,7 +416,7 @@ Lists running CDP endpoints and installed browsers, best match first. The browse
|
|
|
415
416
|
**Running endpoints** come from four places:
|
|
416
417
|
- `DevToolsActivePort` files in the default user-data dirs of the browsers below, in each SLICC profile under the profiles dir (see `slicc launch`), in SLICC six's profiles (see [Using SLICC six's Chrome profile](#using-slicc-sixs-chrome-profile)), and on Linux in the flatpak (`~/.var/app/<id>/config/…`) and snap (`~/snap/chromium/common/chromium`) dirs. The file's port must answer `/json/version` with the websocket path on the file's second line, otherwise the file is stale and ignored. A file without a second line starting with `/` is ignored too.
|
|
417
418
|
- `GET /json/version` on `127.0.0.1:9222` to `9229`.
|
|
418
|
-
- The process list (`ps -ax -o pid= -o command=`; on Windows `Get-CimInstance Win32_Process` with `ProcessId` and `CommandLine`, as JSON): each process started with `--remote-debugging-port=<n>` or `--remote-debugging-port <n>`. Pid 0, `slicc` itself, and commands run by a wrapper (`node`, `npx`, `tsx`, `npm`, `open`, `bash`, `zsh`, `sh`, `csh`, `fish`, `dash`, `timeout`, `env`, `sudo`, `caffeinate`, `cmd`, `powershell`, `pwsh`) are skipped. Port 0 means the port is in the `DevToolsActivePort` of the process's `--user-data-dir` (its value runs to the next whitespace followed by `--` or to the end, so paths with spaces work, and a quoted value runs to the matching quote), checked as above; without one, or with one under a `slicc/profiles` folder that isn't one of six's profiles, the process is skipped. Any other port is asked for `/json/version` on `127.0.0.1`. The exact rules are in [slicc-browsers](https://github.com/ai-ecoverse/slicc-browsers#the-process-source).
|
|
419
|
+
- The process list (`ps -ax -o pid= -o command=`; on Linux, when that command fails (missing, or a BusyBox `ps` without `-ax`), `/proc/<pid>/cmdline` of every numeric pid, its NUL-separated arguments joined by spaces; on Windows a process snapshot (`CreateToolhelp32Snapshot`, each process's command line from `NtQueryInformationProcess`), and only when that fails `Get-CimInstance Win32_Process` with `ProcessId` and `CommandLine`, as JSON, through PowerShell within 5 s): each process started with `--remote-debugging-port=<n>` or `--remote-debugging-port <n>`. Pid 0, `slicc` itself, and commands run by a wrapper (`node`, `npx`, `tsx`, `npm`, `open`, `bash`, `zsh`, `sh`, `csh`, `fish`, `dash`, `timeout`, `env`, `sudo`, `caffeinate`, `cmd`, `powershell`, `pwsh`) are skipped. Port 0 means the port is in the `DevToolsActivePort` of the process's `--user-data-dir` (its value runs to the next whitespace followed by `--` or to the end, so paths with spaces work, and a quoted value runs to the matching quote), checked as above; without one, or with one under a `slicc/profiles` folder that isn't one of six's profiles, the process is skipped. Any other port is asked for `/json/version` on `127.0.0.1`. The exact rules are in [slicc-browsers](https://github.com/ai-ecoverse/slicc-browsers#the-process-source).
|
|
419
420
|
- Each `--cdp` URL: `http(s)://host:port`, `ws(s)://host:port/devtools/browser/<id>`, `host:port` or a bare port. One that doesn't answer is reported on stderr and left out; the exit status stays 0.
|
|
420
421
|
|
|
421
422
|
Endpoints are merged by host and port. A `DevToolsActivePort` file wins over `--cdp`, which wins over the process list, which wins over the port probe. For each endpoint, `/json/version` gives the name, version and `webSocketDebuggerUrl`, and `/json/list` gives the seven tabs: `page` targets whose origin is `https://<label>.sliccy.ai`, other than `www` (`:443` is the same origin, any other port isn't). An endpoint's `cdp` has its host in lower case, with `localhost` and `::1` written as `127.0.0.1`. A `DevToolsActivePort` whose port is open but serves no `/json/version` (Chrome's opt-in remote debugging of a running browser) is listed as `wsOnly`, with the websocket URL from the file and no tab check.
|
|
@@ -788,7 +789,7 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
|
|
|
788
789
|
- RESET from the page, or the link going down: the socket is closed at once.
|
|
789
790
|
|
|
790
791
|
**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`).
|
|
791
|
-
- **Privileged pages** (slicc-browsers ≥ 1.8.0) are protected the same way, as if they were seven's: every URL whose scheme is in `protectedSchemes` (`chrome:`, `chrome-untrusted:`, `chrome-extension:`, `devtools:`, `file:`, and the UI schemes of the catalog's other Chromium browsers, `edge:`, `brave:`, `opera:`, `vivaldi:` and `arc:`), on any host and path, except the new tab pages in `schemeExceptions` (`chrome://newtab`, `chrome://new-tab-page` and its `chrome-untrusted://new-tab-page` frames, `edge://newtab`, `brave://newtab`, `opera://startpage`), matched by the parsed scheme and exact host, so `chrome-untrusted://new-tab-page.evil/` stays protected. So `chrome://settings`, `chrome://password-manager`, an extension's pages, frames and workers (a password manager's or a wallet's vault included) and any local file are refused, hidden from auto-attach and detached like a seven tab. A wrapper is judged by what it wraps: `view-source:chrome://settings/` and `view-source:file:///…` by their inner URL, `blob:` and `filesystem:` by their origin. An `about:` URL other than `about:blank` and `about:srcdoc` is judged as the `chrome://` URL Chrome makes of it, so `about:settings` is `chrome://settings`. A target whose `type` is in `targetTypes` (`browser_ui`, Chrome's own UI such as `chrome://omnibox-popup.top-chrome/`) is protected whatever its URL, and hidden altogether (see *Listing*).
|
|
792
|
+
- **Privileged pages** (slicc-browsers ≥ 1.8.0) are protected the same way, as if they were seven's: every URL whose scheme is in `protectedSchemes` (`chrome:`, `chrome-untrusted:`, `chrome-extension:`, `devtools:`, `file:`, from 1.8.5 also `chrome-search:`, `chrome-native:`, `chrome-distiller:`, `isolated-app:`, `chrome-experimental-site-token-provider:`, `externalfile:` and `content:`, and the UI schemes of the catalog's other Chromium browsers, `edge:`, `brave:`, `opera:`, `vivaldi:` and `arc:`), on any host and path, except the new tab pages in `schemeExceptions` (`chrome://newtab`, `chrome://new-tab-page` and its `chrome-untrusted://new-tab-page` frames, `edge://newtab`, `brave://newtab`, `opera://startpage`), matched by the parsed scheme and exact host, so `chrome-untrusted://new-tab-page.evil/` stays protected. `chrome-error:` is not protected: Chrome's network and frame error pages stay readable, snapshottable, reattachable and navigable away from, like the ordinary pages they stand in for. So `chrome://settings`, `chrome://password-manager`, an extension's pages, frames and workers (a password manager's or a wallet's vault included) and any local file are refused, hidden from auto-attach and detached like a seven tab. A wrapper is judged by what it wraps: `view-source:chrome://settings/` and `view-source:file:///…` by their inner URL, `blob:` and `filesystem:` by their origin. An `about:` URL other than `about:blank` and `about:srcdoc` is judged as the `chrome://` URL Chrome makes of it, so `about:settings` is `chrome://settings`. A target whose `type` is in `targetTypes` (`browser_ui`, Chrome's own UI such as `chrome://omnibox-popup.top-chrome/`) is protected whatever its URL, and hidden altogether (see *Listing*).
|
|
792
793
|
- **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.
|
|
793
794
|
- **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, except `Target.getTargetInfo` on a hidden target (see *Listing*).
|
|
794
795
|
- **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.
|
|
@@ -806,7 +807,12 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
|
|
|
806
807
|
- On each touched page, iframe, background page or webview target the node attaches a session of its own, which the page never sees. On it, the node enables `Fetch` for document requests to protected hosts (`resourceType: Document`, request stage), and `Network` with `Network.setBypassServiceWorker`, so a service worker can't hand out a document without a request.
|
|
807
808
|
- The node fails each such request into a protected origin with `Fetch.failRequest` (`BlockedByClient`), and the target shows Chrome's error page instead. Every other request continues untouched. So a touched target never loads a protected document, and nothing the stream installed on it (`Page.addScriptToEvaluateOnNewDocument`, `Fetch`, `Network`, `Page.setBypassCSP`, emulation, overrides) ever runs there.
|
|
808
809
|
- Seven's own tab and its vault frame are never touched, because attaching to them is refused.
|
|
809
|
-
- Page commands on a touched target's session wait, in order, until the node's own session there is ready; commands on other sessions of the stream don't wait. The node's `Network.enable` and `Network.setBypassServiceWorker` answer only once the target's renderer does, which a
|
|
810
|
+
- Page commands on a touched target's session wait, in order, until the node's own session there is ready; commands on other sessions of the stream don't wait. No page command on that session reaches the browser before the node's session is ready. The node's `Network.enable` and `Network.setBypassServiceWorker` answer only once the target's renderer does, which a loaded host can take a minute or more to start, so the wait has no fixed length. It fails closed on no progress, or at an outer bound:
|
|
811
|
+
- **Progress** for a target whose setup is under way is any message from the browser on the stream's socket that is an answer to any command (the page's or the node's, on any session), an event on one of the target's sessions (the node's or the page's), or a `Target.*` event on the browser or the node's browser session whose `targetId` or `targetInfo.targetId` is that target. The two `Network.webSocketFrame…` events the node drops unread (see *Bytes*) don't count; they need `Network` enabled on a session, which no session on a target whose setup is under way has. The node checks the bounds every quarter of the shorter one (7.5 s by default). At each check while any setup is under way, it sends `Browser.getVersion` on its own browser session, one at a time, so a loaded but live browser makes progress even while the page sends nothing. A target that is stuck while the browser keeps answering is making progress; a browser that answers nothing isn't.
|
|
812
|
+
- **Idle bound:** a setup with no progress for 30 s fails, within one check of it (37.5 s).
|
|
813
|
+
- **Outer bound:** a setup still under way 5 min after it started fails, whatever its progress.
|
|
814
|
+
- **Refusals:** when a setup call fails, the node's session is lost or the target closes, the waiting commands, and any after them, are refused at once with `slicc: refused: slicc can't keep seven's origin out of this target`. When a bound fires, the message names it: `…: its guard made no progress in 30s` or `…: its guard wasn't ready in 5m0s`.
|
|
815
|
+
- **Caps:** more than 4096 waiting commands, or 64 MiB of them, on one stream reset it with `ENOBUFS`.
|
|
810
816
|
- In touched targets, service workers are bypassed for every request, not only for protected ones.
|
|
811
817
|
- The guard covers every frame that shares the touched target's process. A cross-site iframe's navigation pauses in its parent's target, before the iframe becomes a target of its own, with the frame's id as `frameId` (the id the iframe target gets later). A popup's request pauses in the popup's target, with its `targetId` as `frameId`. A redirect into a protected origin pauses again at the request stage, with `redirectedRequestId`, even when the first URL didn't match.
|
|
812
818
|
- The page's own `Fetch.enable` on a page session still works. Chrome chains the interceptors of a target's sessions, the most recently enabled first, so the page's interceptor could see a protected document's request before the node's does, or after the node failed it, at the response stage. The node fails such a request itself, and the page never sees it (see *Paused requests to seven* above).
|
|
@@ -1013,7 +1019,7 @@ A node checks every address it connects to for the link's `tcp`, `http`, `ws` an
|
|
|
1013
1019
|
**Local default** (the bridge to the browser `slicc` controls). The remote default, with loopback and the private ranges (`127.0.0.0/8`, `::1/128`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7`) allowed instead of denied, so this machine and the LAN are reachable. `--deny 127.0.0.0/8` and `--deny 192.168.0.0/16` take loopback and the LAN back out.
|
|
1014
1020
|
|
|
1015
1021
|
**Protected ports**, in both defaults and in every mode (`slicc`, `attach`, `open`, `pair`, `--default-browser` and `follow`). Two kinds:
|
|
1016
|
-
- **Debugging ports:** ports 9222–9229; every port in a `DevToolsActivePort` file that browser discovery would read (default user-data dirs, SLICC profiles and six's profiles); the port of every running process started with `--remote-debugging-port=<n>` (from the process list, as discovery reads it: wrappers skipped, and for port 0 the `DevToolsActivePort` of its `--user-data-dir`); and the debugging port of every browser the node was given or launched (`slicc`'s browsers, `attach --cdp` and `--cdp-extra`, `follow --cdp`). Nothing is probed: files and the process list are only read.
|
|
1022
|
+
- **Debugging ports:** ports 9222–9229; every port in a `DevToolsActivePort` file that browser discovery would read (default user-data dirs, SLICC profiles and six's profiles); the port of every running process started with `--remote-debugging-port=<n>` (from the process list, as discovery reads it: wrappers skipped, and for port 0 the `DevToolsActivePort` of its `--user-data-dir`); and the debugging port of every browser the node was given or launched (`slicc`'s browsers, `attach --cdp` and `--cdp-extra`, `follow --cdp`). Nothing is probed: files and the process list are only read. The files are read at startup, every 5 s, and again whenever a `tcp`, `http`, `ws` or `ssh` connection goes to an address of this machine on a port not yet denied. The process list is read at startup, then in the background, one scan at a time: about every second while such connections keep coming (until 30 s pass without one; between two scans it pauses at least as long as the last one took), and every 30 s while idle. A connection uses the list only if its scan started less than about 1 s earlier. With an older list (the first connection after a quiet spell, say) it waits for a scan that started after it came, bounded by the scan's timeout (5 s, plus the rest of a scan already running), and if that scan fails or times out the connection is refused (`EACCES`, reason `a browser debugging port: couldn't list running browsers`). Freshness counts from the start of a scan, on purpose: on a host whose scan takes longer than about a second (a slow PowerShell `Win32_Process` query), most connections to this machine wait for a scan, which is the price of refusing the first connection to any browser that has been running for about a second. If no process list can be read at startup (no working `ps`, and on Linux no readable `/proc`), the node says so once on stderr: `slicc: can't list running browsers here (<why>); connects to this machine's own addresses are refused`. Such a host is fail-closed by design: every connection to its own addresses is refused until a scan works. A scan that fails later is reported once (`couldn't list running browsers for their debugging ports`), and again when scanning works again; the last good list is kept, but an old list never lets a connection through by itself. Connections to other hosts never wait. So a browser that starts later is denied before its first connection, once it has been running for about a second. No `--allow` reopens one, not even `--allow 127.0.0.1:9222` or `--allow 127.0.0.0/8`: a debugging port wins over every other rule. Only `--expose-debug-port <port>` (repeatable, every mode) leaves one open, and only a port that is a debugging port when the node starts; any other port, or the port of a browser the node was given or launched, is an error (exit 1), since `cdp` is the way to those browsers. So seven never reaches raw CDP on this machine, `<host>.slicc.internal:9222` included, unless the user named that port.
|
|
1017
1023
|
- **The node's own listeners** (the kernel listener and, for `open` and `pair`, the loopback listener): denied the same way, but an `--allow` for exactly the same address and port reopens one.
|
|
1018
1024
|
|
|
1019
1025
|
Both are denied:
|
|
@@ -1636,8 +1642,9 @@ On a [§1.3](#13-local-setup-without-cdp-loopback-signalling) loopback link, `un
|
|
|
1636
1642
|
- **Protected ports:** every listed browser's debugging port is protected in the policy on both commands, so a `tcp` stream can't reach the raw port even under `--allow` or `--expose-debug-port` ([§6.5](#65-debugging-ports-every-mode)).
|
|
1637
1643
|
|
|
1638
1644
|
- **Protected origins (PRE-1):** the node filters every `cdp` stream against `protectedOrigins` from slicc-browsers (≥ 1.4.0: `*.sliccy.ai` at any depth, any port, http and https; `vault.sliccy.ai` by name; plus the setup's extra origins). It refuses navigating to, attaching to, or driving targets on those origins, and storage, cookie and permission access for them, as **CDP errors** `{"error":{"code":-32000,"message":"slicc: refused: …"}}`, not RESETs. It detaches sessions that land on them and redacts their `url`/`title` in listings to the origin.
|
|
1639
|
-
- **Privileged browser pages** (≥ 1.8.0) count as protected origins: every URL in `protectedSchemes` (`chrome:`, `chrome-untrusted:`, `chrome-extension:`, `devtools:`, `file:`, `edge:`, `brave:`, `opera:`, `vivaldi:`, `arc:`) except the new tab pages in `schemeExceptions
|
|
1645
|
+
- **Privileged browser pages** (≥ 1.8.0) count as protected origins: every URL in `protectedSchemes` (`chrome:`, `chrome-untrusted:`, `chrome-extension:`, `devtools:`, `file:`, from 1.8.5 `chrome-search:`, `chrome-native:`, `chrome-distiller:`, `isolated-app:`, `chrome-experimental-site-token-provider:`, `externalfile:` and `content:`, `edge:`, `brave:`, `opera:`, `vivaldi:`, `arc:`) except the new tab pages in `schemeExceptions`, and never `chrome-error:`; `view-source:`, `blob:` and `filesystem:` by what they wrap; `about:` aliases as the `chrome://` URL they become (`aboutScheme`). Targets of a `targetTypes` type (`browser_ui`) are protected whatever their URL, and never listed or announced to the page. The refusal text is the same.
|
|
1640
1646
|
- **In targets the stream touched** (attached to or created): the node fails every protected document load with `net::ERR_BLOCKED_BY_CLIENT`, so instrumentation installed before a navigation can never run in a protected document. That covers redirects, service-worker-served navigations and the page's own Fetch interception.
|
|
1647
|
+
- **Waiting for that guard:** page commands on a touched target's session wait while the node sets its guard up there. The wait fails closed after 30 s without progress (any answer from the browser on the stream's socket, the node's own `Browser.getVersion` probe included, or any event for that target or its sessions) or 5 min in all, whichever comes first, and the refusal names the bound. At most 4096 commands or 64 MiB wait on a stream; more is `RESET ENOBUFS`.
|
|
1641
1648
|
- **Auto-attached targets with no URL yet** (OOPIFs, popups) are held until the URL is known, then detached if protected. After 10 s with no URL they are treated as protected. The node's own setup connection ([§1.1](#11-local-setup-over-cdp)) isn't a `cdp` stream and is the one exception. [`cdp` (browser control)](#cdp-browser-control) has the details.
|
|
1642
1649
|
|
|
1643
1650
|
#### 5.8.1 `browsers` updates (page cap `browsers`), node → page
|
|
@@ -1992,7 +1999,7 @@ The remote default, but it **also allows loopback and the private ranges** (`10/
|
|
|
1992
1999
|
|
|
1993
2000
|
Both defaults, in every mode (local and remote), deny this machine's browser debugging ports on `127.0.0.0/8`, `::1` and **every address of this machine**, so `<host>.slicc.internal:<port>` (which the page sends as `127.0.0.1`) is refused too:
|
|
1994
2001
|
- **Which:** `9222`–`9229`; every port in a `DevToolsActivePort` file the node finds read-only; the port of every running process started with `--remote-debugging-port` (for port 0, the `DevToolsActivePort` of its `--user-data-dir`); and the debugging port of each browser the node was given or launched.
|
|
1995
|
-
- **When:** at startup, every 5 s, and on each `tcp` connect (`net`, `http`, `ws`, `ssh` forwarding) to an address of this machine on a port not yet denied: the
|
|
2002
|
+
- **When:** the files at startup, every 5 s, and on each `tcp` connect (`net`, `http`, `ws`, `ssh` forwarding) to an address of this machine on a port not yet denied. The process list at startup, then in the background (one scan at a time): about every 1 s while such connects keep coming (until 30 s without one; a slow scan is followed by a pause as long), every 30 s while idle. A connect uses the list only if it is under about 1 s old; otherwise it waits for a fresh scan, bounded by the scan's timeout, and is refused if that scan fails or times out (reason `a browser debugging port: couldn't list running browsers`). Connects to other hosts never wait. On Linux the list falls back to `/proc` when `ps` fails. On Windows it is read in-process, not through PowerShell, which can take more than its 5 s to start on a busy or freshly booted machine; until then every connect to this machine was refused. With no list at all at startup, the node says `can't list running browsers here (…); connects to this machine's own addresses are refused` once on stderr; a later failed scan is reported once, and again when it recovers. So a browser that starts later is refused before its first connection, once it has run for about a second.
|
|
1996
2003
|
- **Above every rule:** no `--allow` reopens one, `--allow 127.0.0.1:9222` included. A refusal is `EACCES`, reason `a browser debugging port`.
|
|
1997
2004
|
- **Opt-in:** only `--expose-debug-port <port>` (repeatable) leaves a port of the first three kinds open, and only one that is a debugging port at startup. Any other port is an error, and so is a browser the node was given or launched: `cdp` is the way to them.
|
|
1998
2005
|
- **HELLO:** `policy.deny` lists the loopback denies of the ports known at startup; `policy.summary` ends in `exposes debugging ports <ports>`, the ports actually exposed.
|
|
@@ -2013,7 +2020,7 @@ CI installs a pinned release: `go install github.com/ai-ecoverse/slicc-cli/cmd/s
|
|
|
2013
2020
|
|
|
2014
2021
|
Go 1.26 and Node ≥ 24 (for the lint tools). `npm run build` writes `bin/slicc`. `npm run lint` runs the slicc lint tools, `gofmt`, `go mod tidy -diff`, `go vet` and golangci-lint. `npm test` runs the integration tests in `test/integration/`, which build the binary and drive it; run `npm ci` first, since the [Inside seven](#inside-seven) tests need the pinned `@ai-ecoverse/slicc-kernel`. Unit tests stay next to the code and out of git (`npm run test:unit`). `go build` and `npm run build` link no version, so these binaries report `dev`. `npm run dist` cross-builds all six targets into `artifacts/release/` with `SHA256SUMS`, unsigned. `npm run npm:stage` then stages the npm packages into `dist/npm/`, and `npm run npm:check` packs them with `npm publish --dry-run`.
|
|
2015
2022
|
|
|
2016
|
-
**Test shards.** CI splits the integration tests into shards with `scripts/test-shard.mjs <index>/<count> <go test args>`. The split is greedy by measured time: `test/integration/shard-times.json` holds each top-level test's seconds per platform (`darwin`, `linux`, `win32`), each test goes to the shard with the least time so far, and a test without a time counts as the platform's median. The last shard runs everything the others don't, so a test that isn't in any list yet still runs. CI has two matrices. **The light matrix** runs on pull requests, the merge queue (`merge_group`) and every push to `main`, which is the run the release waits for: the
|
|
2023
|
+
**Test shards.** CI splits the integration tests into shards with `scripts/test-shard.mjs <index>/<count> <go test args>`. The split is greedy by measured time: `test/integration/shard-times.json` holds each top-level test's seconds per platform (`darwin`, `linux`, `win32`), each test goes to the shard with the least time so far, and a test without a time counts as the platform's median. The last shard runs everything the others don't, so a test that isn't in any list yet still runs. CI has two matrices. **The light matrix** runs on pull requests, the merge queue (`merge_group`) and every push to `main`, which is the run the release waits for: the three Linux shards with `-race`, and one leg each on macOS and Windows (shard `1/1`) without `-race` (about 25 minutes), so a queue group or a push needs only one macOS test runner. **The full matrix** runs in the Nightly workflow (`.github/workflows/nightly.yml`), once a day (03:17 UTC) and on `workflow_dispatch`, on the head of `main` only: its `full` job calls `ci.yml` (`workflow_call` with `full: true`), which then runs three shards on Linux, three on Windows and four on macOS, whose runners vary the most (the same shard took from 400 s to 570 s) and run the guard vectors slowest, so each stays well inside its 12 minutes, every leg with `-race`, and the installer's macOS curl check too. On the org's hosted macOS pool the full matrix takes an hour or more, longer than merges often arrive, so a push run that waited for it was stale by the time it passed and nothing released; a red nightly instead blocks the next release (see [Releases](#releases)). Only one push run on `main` runs at a time, and only the newest push waits: a newer push replaces a pending one, never the one running, so a steady stream of merges queues at most two light matrices. The [guard vectors](https://github.com/ai-ecoverse/slicc-browsers#guard-vectors) (`TestCDPProtectVectorsPrompt` and `TestCDPProtectVectorsLagging`, one per detach variant) run on Linux in the light matrix, and on every OS in the full matrix: the light matrix's macOS and Windows legs skip them with `SLICC_GUARD_VECTORS=0`. CI runs them with `-v`, so `node scripts/shard-times.mjs --run <CI run id>` refreshes the file from a finished `main` run's logs (`<platform>=<log file>` reads local `go test -v` output instead); commit the result when tests get much slower or faster. `TestEachCIShardFitsItsTimeout` fails when a shard that runs with `-race` and the guard vectors adds up to more than three quarters of its `-timeout` by those times: then add a shard. Linux has three for that reason: with two, a shard ran 719 s of its 12 minutes, and the next run hit the timeout. When a shard does hit its `-timeout`, `go test` panics naming whichever test was running then, so `test-shard.mjs` adds a line with how long that test had run, what the finished tests took against the times' prediction, and the slowest of them. **Runners.** CI's first job, `runner`, picks where the Linux jobs run with `.github/runner.mjs`: the pool named by `vars.CERT_RUNNER` (now `cloud-run-job`, one Cloud Run task per job; unset, `ubuntu-latest`) for pushes, the nightly (`schedule` and `workflow_dispatch`), same-repo pull requests and merge groups whose entries are all same-repo, and GitHub's hosted `ubuntu-latest` for fork pull requests, merge groups with a fork entry, and when the decider fails. It gives two labels through the same rule: `runner` for the heavy jobs (the Linux test shards and `installer-assets`) and `light` (`vars.CERT_LIGHT_RUNNER`, unset `ubuntu-latest`) for `lint`, `installer (ubuntu-curl)` and the aggregate `ci`, so where a fork gets `ubuntu-latest` it gets it for both. `vars.CI_RUNNER` forces one runner, for both labels, for every event, fork pull requests and merge groups included, so it is never a rollback; `vars.CERT_RUNNER` is the one pool variable, and changing it is the rollback, with every rule here still applied. The `runner` job itself runs hosted for fork pull requests, otherwise on `vars.CERT_LIGHT_RUNNER` (or `vars.CERT_RUNNER`); it loads `runner.mjs` from `main`, never from the code under test, which may be a fork's: a pull request's base commit, and for a merge group the head of `main` (`merge_group.base_ref`), not the group's parent commit (`base_sha`), which can be the speculative commit of the group ahead of it in the queue, with that group's pull requests in it. That keeps the decider's own answer trusted; a fork pull request that edits `ci.yml` itself is stopped only by the repository's approval rule for outside contributors' workflows and the pool's runner-group access. The Alpine installer check stays hosted (it needs Docker), and macOS and Windows always run hosted. In `release.yml`, the dispatch-only `ci` wait, the `fresh` check and the `nightly` gate run on `vars.CERT_LIGHT_RUNNER`, as does the nightly's `report` job, the Linux legs of `smoke` (hosted for fork pull requests) and `install-smoke` on `vars.CERT_RUNNER`, each falling back to `ubuntu-latest`, and every job with `id-token: write` (`release`, `sliccy-next`) on a GitHub-hosted runner. No workflow names a pool literally.
|
|
2017
2024
|
|
|
2018
2025
|
**Test names: `--hosts <file>`** (test-only, left out of `--help`). `slicc attach`, `slicc open`, `slicc pair` and `slicc <join-url> follow` take a hosts file, so tests get deterministic names without public DNS. Each line is `<ip> <name> [<name>…]`, and `#` starts a comment. Names match case-insensitively, with or without a trailing dot, and a name on several lines gets every address, in file order. Wherever the node resolves a name (`resolve`, `tcp` by name, `http`, and the policy's [browser endpoints](#network-policy)), a name in the file gets the file's addresses of the requested family, possibly none, and never the system resolver's. Other names go to the system resolver. The policy applies afterwards as usual: `resolve` drops refused addresses, and a dial to a refused address is `EACCES`. `tcp` and `http` try the addresses in file order. A file that can't be read or parsed exits 2. A name mapped to a loopback address only helps the node's own dials, `http` and `tcp` by name: a seven kernel that gets a loopback address from `resolve` dials its own loopback, because loopback never goes out through an exit. For end-to-end exit tests, map test names to a non-loopback address of the machine.
|
|
2019
2026
|
|
|
@@ -2026,7 +2033,7 @@ Front ends and capabilities follow a node's links with `node.Node.Subscribe(func
|
|
|
2026
2033
|
|
|
2027
2034
|
In the same process, probe ports, dirs and a process list passed in explicitly (`Options.ProbePorts`, `Options.Home`, `Options.ProfilesDir`, and `Options.Processes`, which returns the output of the process-list command) are still used. `Options.Transport` and `Options.Dial` replace discovery's HTTP transport and TCP dial; the corpus test fakes every server with them, and its process list is fake too. `test/integration`'s `TestMain` sets `SLICC_DISCOVERY=isolated` and unsets `SLICC_USER_DATA_DIR`, and points `HOME`, `APPDATA` and the XDG dirs, `XDG_STATE_HOME` included (and `LOCALAPPDATA`, and `USERPROFILE` except on Windows, where a Chrome started with a temporary one never writes `DevToolsActivePort`; the tests that have `slicc` launch a real Chrome give it the real `LOCALAPPDATA` on Windows, which they only do on CI) at a temporary directory for itself and every `slicc` it starts, so no test can reach the browsers of the machine it runs on. It also puts a stand-in `jerk` first on `PATH` that fails `--version` (on Windows one for every `PATHEXT` extension), so slicc never finds the machine's own slicc-jerk there, while every other tool keeps its own path. The tests that want a jerk put the fake jerk's dir before it. Tests opt in to more with a fake home or fake endpoints. The Chrome the harness starts itself gets the real environment.
|
|
2028
2035
|
|
|
2029
|
-
**Debugging ports in tests: `SLICC_PROTECTED_PORTS`** (test-only). `<first>-<last>` (or one port) adds to 9222–9229 the range every mode denies (see [Network policy](#network-policy)); 9222–9229 stay denied. The debugging-port tests (`test/integration/debugports_test.go`) set it to two free ports where fakes listen, write fake `DevToolsActivePort` files for two more under `SLICC_DISCOVERY=home`, give a fake CDP endpoint as `--cdp-extra` or `--cdp`, and after link-up write another `DevToolsActivePort` file and start two fake browsers with `--remote-debugging-port` (one with a port, one with 0 and a `--user-data-dir`). They prove, in every mode, that a link `net` or `http` request to each of them is refused, an exact `--allow` included, that a browser that starts later is refused
|
|
2036
|
+
**Debugging ports in tests: `SLICC_PROTECTED_PORTS`** (test-only). `<first>-<last>` (or one port) adds to 9222–9229 the range every mode denies (see [Network policy](#network-policy)); 9222–9229 stay denied. The debugging-port tests (`test/integration/debugports_test.go`) set it to two free ports where fakes listen, write fake `DevToolsActivePort` files for two more under `SLICC_DISCOVERY=home`, give a fake CDP endpoint as `--cdp-extra` or `--cdp`, and after link-up write another `DevToolsActivePort` file and start two fake browsers with `--remote-debugging-port` (one with a port, one with 0 and a `--user-data-dir`). They prove, in every mode, that a link `net` or `http` request to each of them is refused, an exact `--allow` included, that a browser that starts later is refused on the first connection after more than 1 s without one, and that `--expose-debug-port` opens only what it names, without ever touching 9222–9229 on the machine they run on. Tests of `browser.Guard` with a fake process table prove that a connect to this machine with a stale list waits for a gated, slow scan and is decided by it while connects to other hosts don't wait, that the list is rescanned about every second after a connect and connects then don't wait, that a scan that fails or times out refuses the connect, that failure and recovery are each reported once, that an idle node scans at most twice in 5 s, and that shutdown reports no failure; every connect in them has a deadline. **`SLICC_PROC_ROOT`** (test-only) replaces `/proc` for the Linux fallback. With a failing `ps` first on `PATH`, the tests prove that `slicc attach` reads a fake `/proc` on Linux (its browser's port refused, a browser added later refused on its first connection, other loopback ports open, no failure line) and that with no `/proc` either it prints the startup line once and refuses connects to this machine.
|
|
2030
2037
|
|
|
2031
2038
|
**SLICC six's profile in tests.** The `--profile six` tests build fake six profile trees in a fake home only: each OS's layout, a mixed-case name, a symlinked profile and root, `SingletonLock` (or `lockfile`) live, stale and absent, and debuggable endpoints that are fakes or the harness's own headless Chrome on a free port. They check the trees byte for byte after a launch, an attach and an exit. A fake browser (`testdata/fakechrome`) stands in for Chrome where a real one would write to the profile itself. The one real-Chrome launch into a fake six profile checks that nothing was seeded; on Windows it puts that profile at `SLICC_USER_DATA_DIR` in a temporary dir, since that Chrome keeps the real `LOCALAPPDATA` (see below).
|
|
2032
2039
|
|
|
@@ -2069,8 +2076,9 @@ semantic-release cuts releases from `main` in `.github/workflows/release.yml`, o
|
|
|
2069
2076
|
- **rebuild** (heavy runner) runs [the recipe](#reproducing-a-release) on a `git archive` of the merge commit, in another path on another machine; every release file must be byte-identical to build's.
|
|
2070
2077
|
- **delta** names the Go tests the pull request adds or changes in `test/integration/*_test.go` (the `func Test…` a hunk falls in). A changed helper counts as changing every test in its file; a helper in a file with no tests is a test gap (`helper <name>`), never a guess.
|
|
2071
2078
|
- **suite** (heavy runner): the candidate leg runs the whole integration suite on the merge commit on Linux (`go test -json`); the base leg is the NEGATIVE: main's tree with the pull request's whole `test/` diff applied, running only its tests, against main's own binary. A fix whose test fails there and passes on the candidate is `negative`; passing on both is `no-negative`; not compiling or skipping on main is `prev-not-run`. Gaps are known issues, never a FAIL by themselves.
|
|
2079
|
+
- **bios** runs seven's own canary on the candidate wasm when classify sees `slicc-wasip1-wasm.wasm` or the `@ai-ecoverse/wasi-slicc` tarball differ from main's build: it calls slicc-bios's `canary.yml` as a reusable workflow, pinned to the commit in `tools/cert-review/bios.json`, with build's `cert-build-wasi-slicc-<v>` artifact as the candidate (strict binding: its run, artifact id, artifact digest and tarball sha256, never its name alone) and the shards that install the in-kernel slicc (`follow-chat`, whose `follow-slicc.test.mjs` follows another seven). It passes no secret. The canary's shards run on hosted runners.
|
|
2072
2080
|
- **review** (light runner) runs `claude-code-action/base-action` on that evidence with `AWS_BEARER_TOKEN_BEDROCK`, the workflow's only secret, in that step only; without the key the step skips and the line says `INCOMPLETE: no reviewer key`. Its read-only `gh` calls use the runner's own `gh`; nothing downloads one, and a runner without it makes the review INCOMPLETE. The reviewer writes only `verdict.json` and `comment.md`, in a folder of its own: a `fail` can add a defect, but nothing it writes can clear one decided in code.
|
|
2073
|
-
- **record** (code only) recomputes the findings from the code-made evidence (delta, suite and rebuild artifacts), re-hashes the build artifact against build's `SHA256SUMS` hash, checks the rebuild, reads CI's results on the head (the macOS, Windows and Linux test legs and `ci`, waiting up to an hour) and the verdict. A byte or reproducibility mismatch, or the reviewer's fail, is `cert-ci: FAIL` (failure); missing evidence (a known issue that can't be read or doesn't exist included) is `cert-ci: INCOMPLETE` (error: re-run the failed jobs); otherwise `certified ai-ecoverse/slicc-cli PR #N at <head>, build <v> SHA256SUMS sha256=<hex> (wasm sha256=<hex>; reproducible: …; ci: …; known: …; test gaps: …)`. **report** posts it as the status and a sticky comment.
|
|
2081
|
+
- **record** (code only) recomputes the findings from the code-made evidence (delta, suite and rebuild artifacts), re-hashes the build artifact against build's `SHA256SUMS` hash, checks the rebuild, reads CI's results on the head (the macOS, Windows and Linux test legs and `ci`, waiting up to an hour), bios's canary report (`bios-canary`, by the id and digest the call returned, its pin, this run, this build's tarball and every pinned shard) and the verdict. A byte or reproducibility mismatch, a failing canary test, or the reviewer's fail, is `cert-ci: FAIL` (failure); missing evidence (a known issue that can't be read or doesn't exist included, and a canary that left no result, missed a shard, or was flaky) is `cert-ci: INCOMPLETE` (error: re-run the failed jobs); otherwise `certified ai-ecoverse/slicc-cli PR #N at <head>, build <v> SHA256SUMS sha256=<hex> (wasm sha256=<hex>; bios: follow-chat pass at slicc-bios@<sha> or bios: not needed, wasm unchanged; reproducible: …; ci: …; known: …; test gaps: …)`. **report** posts it as the status and a sticky comment.
|
|
2074
2082
|
- **No silent pass.** Every step that runs a cert tool requires that tool's verdict: plan's `build=`, a posted status from classify and the lookup, a `certified …` line and a `success` record before report posts success, and in `cert-verify.yml` the reproduction's and the darwin check's verdict lines, verify's `certified slicc-cli@<version> binaries …` line and the notes job's `notes: …` line. A tool that exits 0 and prints nothing fails its step, and the status is `error`.
|
|
2075
2083
|
- **Evidence by artifact id and digest.** Every job that uploads evidence hands on the artifact's id and digest as job outputs, one pair per matrix side. Every consumer downloads by `artifact-ids` with `digest-mismatch: error`, never by name or pattern, and no upload overwrites another. A download that failed counts as no evidence. The review and record take from each producer's artifact only its own files (`gather.mjs`: the candidate suite gives `candidate.*`, main's gives `prev.*`), so one job can't plant another's evidence. classify, record and cert-verify's verify list the run's artifacts: an artifact under a declared id with another name or digest, or a `cert-*` artifact from this attempt that no producer declared, is `cert-ci: INCOMPLETE` (classify posts `error`). Artifacts left by an earlier attempt of the run are ignored.
|
|
2076
2084
|
- **merge group**: CI's deterministic suites, plus a code-only lookup that each pull request in the group has a green status on the head being merged; otherwise red, naming it. No build, review or model.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc",
|
|
3
|
-
"version": "1.44.
|
|
3
|
+
"version": "1.44.7",
|
|
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,12 +18,12 @@
|
|
|
18
18
|
"node": ">=18"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@ai-ecoverse/slicc-darwin-arm64": "1.44.
|
|
22
|
-
"@ai-ecoverse/slicc-darwin-amd64": "1.44.
|
|
23
|
-
"@ai-ecoverse/slicc-linux-arm64": "1.44.
|
|
24
|
-
"@ai-ecoverse/slicc-linux-amd64": "1.44.
|
|
25
|
-
"@ai-ecoverse/slicc-windows-arm64": "1.44.
|
|
26
|
-
"@ai-ecoverse/slicc-windows-amd64": "1.44.
|
|
21
|
+
"@ai-ecoverse/slicc-darwin-arm64": "1.44.7",
|
|
22
|
+
"@ai-ecoverse/slicc-darwin-amd64": "1.44.7",
|
|
23
|
+
"@ai-ecoverse/slicc-linux-arm64": "1.44.7",
|
|
24
|
+
"@ai-ecoverse/slicc-linux-amd64": "1.44.7",
|
|
25
|
+
"@ai-ecoverse/slicc-windows-arm64": "1.44.7",
|
|
26
|
+
"@ai-ecoverse/slicc-windows-amd64": "1.44.7",
|
|
27
27
|
"@ai-ecoverse/slicc-jerk": "^1.0.0"
|
|
28
28
|
}
|
|
29
29
|
}
|