@ai-ecoverse/slicc 1.41.1 → 1.42.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 +53 -16
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -150,7 +150,7 @@ Six's `--version`, `version`, `--profile` and `--mount` are seven flags too, and
150
150
  | `--kernel-port <n>`, `--no-kernel` | the same flags (see [Kernel services](#kernel-services)) |
151
151
  | `--cdp <url>`, the `/cdp` relay to a running browser | the [`cdp`](#cdp-browser-control) capability. The browser seven runs in is always listed. More come from `--browser` (with `--cdp <url>` for an endpoint) on `slicc`, `--cdp-extra <url>` on `attach`, and `--cdp <url>` on `follow`. `--cdp-extra` and `follow --cdp` must answer when they start |
152
152
  | `--quiet` | no request log. `--plain` prints status lines, and `SLICC_DEBUG=1` logs each `http` stream's method, scheme and host |
153
- | `/api/fetch-proxy` | `http`, `tcp`, `resolve` and `ws` streams under the [local policy](#network-policy): the internet, this machine and the LAN, but not the browsers' debugging ports or `slicc`'s own ports |
153
+ | `/api/fetch-proxy` | `http`, `tcp`, `resolve`, `ws` and `udp` streams under the [local policy](#network-policy): the internet, this machine and the LAN, but not the browsers' debugging ports or `slicc`'s own ports |
154
154
  | `/api/hostfs/*`, tokens re-granted after a `403` | `fs` streams, with no tokens. In seven, a mount has no medium while the link is down, and fills in when the link is back |
155
155
  | `/api/kernel-tunnel` | `kernel` streams into the page |
156
156
  | `/auth/callback`, `/api/oauth-state`, `/api/oauth-result` | not needed on `https://<label>.sliccy.ai`, where seven signs in through the relay itself. A seven on another origin needs slicc-extension to sign in |
@@ -332,7 +332,7 @@ slicc [--browser <id>]... [--cdp <url>]... [--page <url>] [--origin <origin>]...
332
332
 
333
333
  Without `--browser`, the first row of the ranking that can host seven is chosen, an endpoint slicc didn't launch included, but never one in SLICC six's profile. When that happens without the picker, slicc says so on stderr, for an endpoint that isn't in a SLICC profile and that no `--cdp` named: `slicc: using Google Chrome (profile default) at http://127.0.0.1:9222, which slicc didn't launch; opened a new seven tab. Use --browser or --cdp to choose another.` It says `found a seven tab` when one was open already. The profile is `default` for a browser's default user-data dir, the base name of the user-data dir otherwise, and left out (with its parentheses) when unknown. With `--json` the line comes as the `foreign-browser` event too. Picking a row in the picker, `--browser`, or a `--cdp` for that endpoint means the user chose it, and there is no notice. An Electron app can't host seven. Each further browser is an extra: an installed one is launched without a start page, and an endpoint is used as it is. Then the link is set up as for `slicc attach` (see [Local setup](#local-setup-slicc-attach)): over CDP with the browser that hosts seven, the first page tab on an allowed origin.
334
334
 
335
- **What the link offers:** `net` (tcp, resolve), `http`, `ws`, and `cdp` with the browser that hosts seven (`seven: true`) and every extra, under the local policy with all of their debugging ports protected; and kernel services on `--kernel-port` (default 80; `--no-kernel` turns them off) when seven offers `kernel-in` (see [Kernel services](#kernel-services)); and `ssh` only with `--runner` (see [SSH server](#ssh-server)); and `fs` with each `--mount` folder (see [Host folders](#host-folders)). seven gets full control of every listed browser, the one it runs in too. The `cdp` list (HELLO `browsers`) has only the browser that hosts seven and the extras picked with `--browser` or in the picker; the other browsers discovery found are never offered.
335
+ **What the link offers:** `net` (tcp, resolve), `http`, `ws`, `udp`, and `cdp` with the browser that hosts seven (`seven: true`) and every extra, under the local policy with all of their debugging ports protected; and kernel services on `--kernel-port` (default 80; `--no-kernel` turns them off) when seven offers `kernel-in` (see [Kernel services](#kernel-services)); and `ssh` only with `--runner` (see [SSH server](#ssh-server)); and `fs` with each `--mount` folder (see [Host folders](#host-folders)). seven gets full control of every listed browser, the one it runs in too. The `cdp` list (HELLO `browsers`) has only the browser that hosts seven and the extras picked with `--browser` or in the picker; the other browsers discovery found are never offered.
336
336
 
337
337
  The cdp list follows the browsers: an extra that stops answering is taken off the list (`browser-gone`), and listed again with the same id when it answers on the same endpoint again (`browser-back`).
338
338
 
@@ -361,7 +361,7 @@ The cdp list follows the browsers: an extra that stops answering is taken off th
361
361
 
362
362
  **The dashboard** follows: the linked browsers (launched or attached, their CDP port, hosts seven or extra, and `gone` while one doesn't answer), the seven tab, the link state (`waiting for seven`, `connecting`, `linked` with its uptime, or `reconnecting` after a link is lost, while slicc waits for seven to offer again), the capabilities offered, the policy summary, `compute ssh via <runner>` with `--runner`, the kernel services URL, stream counts per kind (open and total, never contents), the last status lines, and the update notice, which then isn't printed on stderr. `NO_COLOR` turns colors off.
363
363
 
364
- **Plain mode** (no terminal, `--yes`, `--browser` or `--plain`): no picker and no dashboard, just the default, with status lines on stderr (`slicc: launched …`, `slicc: attached to …`, `slicc: offering …; policy: …; compute: ssh via <runner>`, `slicc: linked with "<name>" in <tab>`, `slicc: link lost: …`, `slicc: <name> quit …`, `slicc: <name> is back …`, `slicc: closed <ids>`). It never writes ANSI escapes or cursor codes. `SLICC_DEBUG=1` adds diagnostics.
364
+ **Plain mode** (no terminal, `--yes`, `--browser` or `--plain`): no picker and no dashboard, just the default, with status lines on stderr (`slicc: <name> is still starting (slow disk?), <n> s so far`, `slicc: launched …`, `slicc: attached to …`, `slicc: offering …; policy: …; compute: ssh via <runner>`, `slicc: linked with "<name>" in <tab>`, `slicc: link lost: …`, `slicc: <name> quit …`, `slicc: <name> is back …`, `slicc: closed <ids>`). It never writes ANSI escapes or cursor codes. `SLICC_DEBUG=1` adds diagnostics.
365
365
 
366
366
  **`--json`** prints NDJSON events on stdout and nothing else there, for Sliccstart (`slicc --json --browser <id>`). Errors are also printed on stderr. Every event has `"schema": 1` and `event`. Fields marked `?` are left out when empty; new fields and events may be added, and existing ones keep their meaning.
367
367
  - `discovered`: `entries`, the ranked list in the shape of `slicc browsers --json` (always a list).
@@ -371,6 +371,7 @@ The cdp list follows the browsers: an extra that stops answering is taken off th
371
371
  - `linked`: `peer?` (the name in seven's HELLO), `tab?` (the seven tab's URL), `caps`, `policy` (the summary).
372
372
  - `link-lost`: `reason?` when slicc or seven closed the link (`slicc is exiting`, seven's `bye` reason, `replaced by a new offer`), or `message?` for a failure. seven sets the link up again after a reload, and a new `linked` follows.
373
373
  - `browser-gone`, `browser-back`: `browser`, `name`, `role`. A browser stopped answering and left the cdp list, or answers again and is back on it.
374
+ - `browser-slow`: `browser`, `name`, `role`, `elapsed` (seconds). A browser slicc launched hasn't written its `DevToolsActivePort` yet, every 10 s from 10 s on (see [`slicc launch`](#slicc-launch---browser-id---profile-name---url-url---headless---no-seven-flags---json)). The dashboard and plain mode show it as `<name> is still starting (slow disk?), <n> s so far`.
374
375
  - `closed`: `browsers`, the ids slicc closed (always a list). It is the last event of every run that got past choosing its browsers.
375
376
  - `error`: `message`, and `browser?` when closing that browser failed. A setup error ends the run with exit 1.
376
377
  - `mode`: `mode` (`loopback`) and `reason` (`no-browser`), when the run [falls back](#the-default-slicc) to the default browser. Then come `pairing`, `linked` and `link-lost` events (each `linked` and `link-lost` with `mode` and `device`, the page's fingerprint), and `closed` with `browsers: []`.
@@ -484,12 +485,13 @@ Launches a browser with CDP and keeps running until interrupted (Ctrl-C, `SIGINT
484
485
  - Before each launch, the seven prefs are merged into the profile's `Default/Preferences`, which is created if missing: `tab_freezing_enabled: false`, `performance_tuning.tab_freezing.enabled: false`, Memory Saver off (`performance_tuning.high_efficiency_mode.state: 0`), and `[*.]sliccy.ai` plus each extra allowed origin's host as tab-discard exceptions. The exceptions go into the list `performance_tuning.tab_discarding.exceptions` (six's form) and into the map `performance_tuning.tab_discarding.exceptions_with_time`, which Chrome 154 reads and shows under "Always keep these sites active". Existing keys, exceptions and timestamps are kept, and a file that already has everything isn't rewritten. A Preferences file that isn't a JSON object is left alone. `--no-seven-flags` skips these prefs too.
485
486
  - Chromium flags: `--remote-debugging-port=0 --user-data-dir=<profile> --no-first-run --no-default-browser-check`, the throwaway flags for a throwaway profile, the seven flags below, `--headless=new` with `--headless`, then `--url`.
486
487
  - A throwaway profile is one inside the system temp dir (Go's `os.TempDir()`: `TMPDIR` on macOS and Linux, `TMP` or `TEMP` on Windows; symlinks resolved, case ignored on macOS and Windows). It gets `--use-mock-keychain --password-store=basic` (`browser.ThrowawaySwitches`), so Chrome never asks for the macOS keychain or a Linux password store; without a default keychain, as under a temporary `HOME`, macOS would show "Keychain Not Found". Every other profile, SLICC's and six's included, headless or not, keeps the real keychain, so its saved cookies and passwords stay readable. `browser.Throwaway(dir)` tells which is which.
487
- - The seven flags keep a seven tab's kernel running while the tab is in the background: `--disable-background-timer-throttling`, `--disable-backgrounding-occluded-windows`, `--disable-renderer-backgrounding`, `--disable-background-tracing`, and `--disable-features=LocalNetworkAccessChecks,LocalNetworkAccessChecksWebSockets,IntensiveWakeUpThrottling,HighEfficiencyModeAvailable,InfiniteTabsFreezing,InfiniteTabsFreezingOnMemoryPressure,CPUMeasurementInFreezingPolicy,MemoryMeasurementInFreezingPolicy,AllowDevtoolsConnectedDiscard`. Chromium ignores feature names it doesn't know. `--no-seven-flags` (`LaunchOptions.NoSevenFlags`) leaves them all out, along with the seven prefs above. Chromium keeps only one `--disable-features` value, so every `--disable-features` from the seven flags and from `LaunchOptions.ExtraArgs` is merged into one switch, in order and without duplicates. The CDP port is read from the profile's `DevToolsActivePort` once `/json/version` answers with the same websocket path (30 s limit). On macOS a browser with a window starts through `open -n -W -a <app> --args …`, so camera and microphone permissions belong to the browser, not the terminal; `--headless` runs the executable directly. A flatpak starts with `flatpak run --filesystem=<profile> <id>`.
488
+ - The seven flags keep a seven tab's kernel running while the tab is in the background: `--disable-background-timer-throttling`, `--disable-backgrounding-occluded-windows`, `--disable-renderer-backgrounding`, `--disable-background-tracing`, and `--disable-features=LocalNetworkAccessChecks,LocalNetworkAccessChecksWebSockets,IntensiveWakeUpThrottling,HighEfficiencyModeAvailable,InfiniteTabsFreezing,InfiniteTabsFreezingOnMemoryPressure,CPUMeasurementInFreezingPolicy,MemoryMeasurementInFreezingPolicy,AllowDevtoolsConnectedDiscard`. Chromium ignores feature names it doesn't know. `--no-seven-flags` (`LaunchOptions.NoSevenFlags`) leaves them all out, along with the seven prefs above. Chromium keeps only one `--disable-features` value, so every `--disable-features` from the seven flags and from `LaunchOptions.ExtraArgs` is merged into one switch, in order and without duplicates. The CDP port is read from the profile's `DevToolsActivePort` once `/json/version` answers with the same websocket path. slicc waits for that as long as the browser it started is running, up to 2 min, since a first start from a cold or slow disk can take well over 30 s. Every 10 s of the wait it says so: `slicc: Google Chrome is still starting (slow disk?), 10 s so far` on stderr, or the `browser-slow` event with `--json`. A browser that exits first fails the launch at once, with its exit code and the end of its output (`Google Chrome exited with code 21 after 0.4s, before writing DevToolsActivePort; its output: …`). One still running after 2 min is stopped, and the launch fails with `… is still running after 2m0s but has not written <profile>/DevToolsActivePort`. Ctrl-C, `SIGINT` or `SIGTERM` during the wait stops the browser at once, and `slicc launch` says `slicc: interrupted while <name> was starting; closed it` (`{"event":"closed"}` with `--json`) and exits 0. A browser that `open` started on macOS isn't a child of slicc, so a launch that fails or is interrupted also stops the processes that hold the profile: the `SingletonLock` owner and every process started with its `--user-data-dir`. For an Electron app, which has no SLICC profile, it stops the process running the app's executable; the launch refuses an app that is already running, so that process is the one slicc started. One that is still running 15 s later is named in the error (`… is still running as process [<pid>]`), and `slicc launch` exits 1 instead of reporting `closed`. Electron apps get the same wait for their CDP port. On macOS a browser with a window starts through `open -n -W -a <app> --args …`, so camera and microphone permissions belong to the browser, not the terminal; `--headless` runs the executable directly. A flatpak starts with `flatpak run --filesystem=<profile> <id>`.
488
489
  - Electron apps keep their own data and get `--remote-debugging-port=<port>`, the first free port from 9333 up, so they never take 9222–9229. An app that is already running is refused, since a second instance would hand off to it; quit it first. Apps with `debugSupport: "disabled"` are refused.
489
490
  - Shutdown: `Browser.close` over CDP; if the browser is still running 5 s later, a signal (`SIGTERM` to the browser's process group and main process; `taskkill /T` on Windows), then after 3 s a kill (`SIGKILL`; `taskkill /F /T`). The browser runs in its own process group (`CREATE_NEW_PROCESS_GROUP` on Windows), so a terminal Ctrl-C reaches only `slicc`.
490
491
 
491
492
  Plain output: the CDP HTTP URL on stdout, status lines on stderr. With `--json`, NDJSON events on stdout:
492
493
  - `{"event":"launched","id","name","version","engine","pid","profile","profileDir","cdp","webSocketDebuggerUrl"}` (`version`, `profile` and `profileDir` are left out when empty);
494
+ - `{"event":"browser-slow","id","name","elapsed"}` every 10 s while the browser is still starting (`elapsed` in seconds, see above);
493
495
  - `{"event":"closed"}` after `slicc launch` closed the browser;
494
496
  - `{"event":"exited"}` when the browser exited by itself;
495
497
  - with `--profile six`, when six's Chrome was already running: `{"event":"attached","id","name","version","engine","profile","profileDir","cdp","webSocketDebuggerUrl"}` instead of `launched`, then `{"event":"detached"}` on Ctrl-C (six's Chrome keeps running) or `exited` when it quits;
@@ -604,7 +606,7 @@ The URL is the secret, and it carries the node's fingerprint, so a process that
604
606
  - **The answer:** non-trickled. The node answers through its link stack, waits until ICE gathering is complete (10 s at most, after which it refuses), and returns its SDP with every host candidate and `a=end-of-candidates`. The page checks that the answer's fingerprint is the one it pinned before it uses the answer.
605
607
 
606
608
  **The link in this mode** is local (HELLO `mode: "local"`), with the node's loopback certificate:
607
- - **Caps:** `net`, `http`, `credentialed`, `ws`, `unlink-loopback`, `fs` with `--mount`, `ssh` with `--runner`, `jerk` and `jerk-seal` unless `--no-jerk`, and kernel services on `--kernel-port` for a page that offers `kernel-in` (see [Kernel services](#kernel-services)). There is no `cdp`, since the node controls no browser, so a `cdp` OPEN gets `EOPNOTSUPP`. `jerk`'s `--seven-origin` is the pinned origin.
609
+ - **Caps:** `net`, `http`, `credentialed`, `ws`, `udp`, `unlink-loopback`, `fs` with `--mount`, `ssh` with `--runner`, `jerk` and `jerk-seal` unless `--no-jerk`, and kernel services on `--kernel-port` for a page that offers `kernel-in` (see [Kernel services](#kernel-services)). There is no `cdp`, since the node controls no browser, so a `cdp` OPEN gets `EOPNOTSUPP`. `jerk`'s `--seven-origin` is the pinned origin.
608
610
  - **Policy:** the local default (see [Network policy](#network-policy)). The protected ports are 9222–9229, every port in a `DevToolsActivePort` file that browser discovery would read (default user-data dirs, SLICC profiles and six's profiles; files are only read, nothing is probed), the listener's port and the kernel port. Those files are read again every 5 s, and a new port is protected from then on as these are: on loopback and on every address of this machine. It isn't added to HELLO's `policy.deny`, which was sent already. So a kernel program can't reach a debugging port the user turned on.
609
611
  - **ICE:** host candidates, loopback included, as for `slicc attach`.
610
612
  - **Several sevens:** the node keeps one link per paired page fingerprint. A new offer for a fingerprint replaces that fingerprint's link only. Each link is an exit of its own, and kernel connections go to the page that linked last, as for `slicc attach`.
@@ -636,7 +638,7 @@ One WebRTC peer connection. seven offers and the node answers. The node:
636
638
  - **Resets with `EOPNOTSUPP`:** an OPEN for a kind it doesn't serve.
637
639
  - **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.
638
640
 
639
- HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "local", "node", "caps": ["net", "http", "credentialed", "ws", "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)). With host jerk, `caps` adds `"jerk"` and `"jerk-seal"` and HELLO adds `"jerk": { "version" }` (see [`jerk`](#jerk-slicc-jerk-over-the-link)).
641
+ HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "local", "node", "caps": ["net", "http", "credentialed", "ws", "udp", "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)). With host jerk, `caps` adds `"jerk"` and `"jerk-seal"` and HELLO adds `"jerk": { "version" }` (see [`jerk`](#jerk-slicc-jerk-over-the-link)).
640
642
 
641
643
  **`ssh`** (cap `ssh`), page → node, with `--runner` only:
642
644
  - OPEN `{ "kind": "ssh" }`, OPENED `{}` at once. The stream's bytes are one SSH connection to the node's SSH server.
@@ -692,6 +694,13 @@ Any value of `credentialed` other than `false` or `null` counts as true, so a ma
692
694
  - **Flow:** the stream window, nothing more. The node reads the far end one message at a time, and holds a message until the page's window takes it. It holds nothing of a page message but the window, so a record header alone reserves nothing.
693
695
  - **Privacy:** the node logs a stream only as `ws <scheme>://<host><path>`, without the query, and never a handshake header, a message, or a close reason, `SLICC_DEBUG` included. `ws` streams are never `credentialed`.
694
696
 
697
+ **`udp`** (cap `udp`), page → node. A UDP socket for a kernel program (link spec [§5.12](#512-udp-cap-udp-page--node), with the node-side details there):
698
+ - OPEN `{ "kind": "udp", "family": 4 }` or `6`, OPENED `{ "localAddr" }`: one unconnected socket on an ephemeral port.
699
+ - **Bytes, both ways:** records of a `u8` type, a `u16` BE host length, the host, a `u16` BE port, a `u32` BE length, then the payload. Type `1` is a datagram, to that destination from the page or from that source to the page. Type `2`, from the node only, says a destination was refused or unreachable, with the errno text as its payload.
700
+ - **Policy:** each destination is checked as a `tcp` connect would be, protected ports included. A refused one drops that datagram with a type-2 `EACCES` record, and the socket stays open. Broadcast, multicast and the unspecified address are always refused.
701
+ - **Receiving:** only from an `ip:port` the socket has sent to.
702
+ - END from either side closes the socket, and so does 5 min without traffic. A malformed or oversize record, or the wrong family, is `RESET EPROTO`.
703
+
695
704
  **`kernel`** (page cap `kernel-in`), node → page:
696
705
  - 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).
697
706
  - 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`.
@@ -957,7 +966,7 @@ The watcher per OS:
957
966
 
958
967
  ## Network policy
959
968
 
960
- A node checks every address it connects to for the link's `tcp`, `http`, `ws` and `ssh` forwarding, and drops refused addresses from `resolve` answers. The check runs on each address actually connected, after DNS and for every Happy Eyeballs attempt, so a name can't rebind past it. A refusal is `EACCES`.
969
+ A node checks every address it connects to for the link's `tcp`, `http`, `ws` and `ssh` forwarding and every `udp` datagram's destination, and drops refused addresses from `resolve` answers. The check runs on each address actually connected, after DNS and for every Happy Eyeballs attempt, so a name can't rebind past it. A refusal is `EACCES`.
961
970
 
962
971
  **Rules.** `--allow` and `--deny` take `CIDR` or `CIDR:port`, repeated or comma-separated: `10.0.0.0/8`, `127.0.0.1/32:9222`, `[::1]/128:9222`. A bare address is a single-host rule (`127.0.0.1`, `[::1]:22`). Host bits are masked off, and `::ffff:a.b.c.d` rules become IPv4 rules. A rule that can never match is rejected: one inside `64:ff9b::/96`, `64:ff9b:1::/48`, `2002::/16`, `2001::/32`, `0.0.0.0/8` or `::/128`.
963
972
 
@@ -1038,15 +1047,15 @@ slicc <join-url> follow [--exit] [--allow <rule>]... [--deny <rule>]... [--name
1038
1047
  `follow` links this machine to a seven on another machine through the tray hub (see [Tray hub signaling](#tray-hub-signaling-remote-setup)). It serves the link until interrupted (`SIGINT`, `SIGTERM`) and reconnects when the link drops. Status lines go to stderr, and `SLICC_DEBUG=1` adds diagnostics. The join URL is never printed.
1039
1048
 
1040
1049
  **Flags.**
1041
- - `--exit`: offer this machine's network to seven, as `tcp` and `resolve` streams (cap `net`), `http` streams (cap `http`) and `ws` streams (cap `ws`) under the remote policy, so public addresses only by default. Without `--exit`, all four are refused with `EOPNOTSUPP`.
1050
+ - `--exit`: offer this machine's network to seven, as `tcp` and `resolve` streams (cap `net`), `http` streams (cap `http`), `ws` streams (cap `ws`) and `udp` streams (cap `udp`) under the remote policy, so public addresses only by default. Without `--exit`, all five are refused with `EOPNOTSUPP`.
1042
1051
  - `--allow`, `--deny`: policy rules on top of the remote default (see [Network policy](#network-policy)).
1043
1052
  - `--name`: the link's `name` and `host` in HELLO, as for `attach`.
1044
1053
  - `--runner`, `--shell`, `--eval`, `--eval-quiet`, `--sftp`, `--no-sftp`: compute over SSH (cap `ssh`), exactly as for `attach`. `direct-tcpip` forwarding goes through the remote policy, so it reaches public addresses only unless `--allow` says otherwise. `--runner` works with or without `--exit`.
1045
- - `--cdp <url>`: let seven drive this browser over the link (repeatable, in any form `slicc browsers --cdp` takes; each must answer when `follow` starts). See [`cdp`](#cdp-browser-control), and its warning: the seven on the other machine gets full control of every browser listed. No browser is `seven: true`. Without `--cdp`, `follow` doesn't offer `cdp`, and a `cdp` OPEN is refused with `EOPNOTSUPP`. Each browser's debugging port and its own endpoint addresses are [denied](#network-policy) to `tcp`, `http`, `ws` and `ssh` forwarding.
1054
+ - `--cdp <url>`: let seven drive this browser over the link (repeatable, in any form `slicc browsers --cdp` takes; each must answer when `follow` starts). See [`cdp`](#cdp-browser-control), and its warning: the seven on the other machine gets full control of every browser listed. No browser is `seven: true`. Without `--cdp`, `follow` doesn't offer `cdp`, and a `cdp` OPEN is refused with `EOPNOTSUPP`. Each browser's debugging port and its own endpoint addresses are [denied](#network-policy) to `tcp`, `http`, `ws`, `udp` and `ssh` forwarding.
1046
1055
  - `--mount <path>[:<name>][:ro]`: share this folder with the seven on the other machine (cap `fs`, repeatable); see [Host folders](#host-folders). It can read the folder, and change it unless `:ro`.
1047
1056
  - `--jerk`: offer this machine's slicc-jerk for status and sign-ins (cap `jerk`). `--jerk-seal` also lets the seven on the other machine seal this machine's tokens into its vault (cap `jerk-seal`) and implies `--jerk`. Without host jerk, `follow` says `jerk: no slicc-jerk 0.6.0 or later on PATH or in slicc's data folder; not offering it` (or, for an older jerk, `jerk: host jerk <version> is older than 0.6.0; run slicc install jerk`) and runs on without it. See [`jerk`](#jerk-slicc-jerk-over-the-link).
1048
1057
 
1049
- HELLO from `follow`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "remote", "node", "caps", "routes": { "prefixes": [], "exit" }, "policy" }`. `caps` holds `net`, `http`, `credentialed` and `ws` with `--exit`, `ssh` with `--runner` (plus `sshHostKey`), `cdp` (and HELLO `browsers`) with `--cdp`, `fs` (and HELLO `mounts`) with `--mount`, `jerk` (and HELLO `jerk`) with `--jerk`, `jerk-seal` with `--jerk-seal`, and always `join`.
1058
+ HELLO from `follow`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "remote", "node", "caps", "routes": { "prefixes": [], "exit" }, "policy" }`. `caps` holds `net`, `http`, `credentialed`, `ws` and `udp` with `--exit`, `ssh` with `--runner` (plus `sshHostKey`), `cdp` (and HELLO `browsers`) with `--cdp`, `fs` (and HELLO `mounts`) with `--mount`, `jerk` (and HELLO `jerk`) with `--jerk`, `jerk-seal` with `--jerk-seal`, and always `join`.
1050
1059
 
1051
1060
  **Identity.** The device is its DTLS certificate. `follow` keeps one ECDSA P-256 certificate per hub origin (`scheme://host:port`), valid for 20 years, in `<config>/slicc/tray-certs.json` (mode `0600`). Every link to that hub uses it, so the fingerprint stays the same across reconnects, restarts and updates. The first run on a hub mints it and says `new device, fingerprint sha-256 …`; later runs say `device, fingerprint …`. Every dial attaches with a fresh `controllerId`, which is only a routing handle.
1052
1061
 
@@ -1318,7 +1327,7 @@ The scenario: seven is open in the user's **everyday browser** (any vendor, no C
1318
1327
 
1319
1328
  **The link in this mode**
1320
1329
  - **ICE:** host candidates on loopback only, as in [§1.1](#11-local-setup-over-cdp).
1321
- - **Caps:** `net`, `http`, `ws`, `credentialed`, `fs` (with `--mount`), `kernel-in` services ([§5.9](#59-kernel-listener-on-the-node-local-only), listener on `--kernel-port`), `ssh` with `--runner`, `jerk`/`jerk-seal`, and `agent`. **No `cdp`:** the node controls no browser.
1330
+ - **Caps:** `net`, `http`, `ws`, `udp`, `credentialed`, `fs` (with `--mount`), `kernel-in` services ([§5.9](#59-kernel-listener-on-the-node-local-only), listener on `--kernel-port`), `ssh` with `--runner`, `jerk`/`jerk-seal`, and `agent`. **No `cdp`:** the node controls no browser.
1322
1331
  - **Policy:** the local default ([§6.4](#64-local-default-slicc-the-bridge-to-the-browser-it-controls)). Its protected ports have no served browser here, so they become 9222–9229 plus every port in a `DevToolsActivePort` file the node finds read-only, plus the node's own listeners. A kernel program can't reach a debugging port the user enabled.
1323
1332
  - **PRE-1** ([§5.8](#58-cdp-cap-cdp-page--node)) protects seven's origin from seven's own browser control. In this mode there's none, so PRE-1 doesn't apply. The `http` cap is the node's own client and carries no browser cookies or storage, so fetching a `*.sliccy.ai` URL through it exposes nothing of the user's session.
1324
1333
  - **Restarts:** the node keeps its certificate and port, and the page keeps its certificate and pin. On link loss the page retries `/signal` with backoff (1 s up to 30 s, and on `online` or `visibilitychange`). A restarted `slicc` is reachable again with no new pairing. The `node` id changes, and the page keys the device on the fingerprint, so the name stays.
@@ -1416,7 +1425,7 @@ Page-opened streams are odd and node-opened streams even, never 0, and never reu
1416
1425
  - **`name`:** for display. **`host`** (node only): the machine's short hostname, lower-cased, as a hint for the DNS label. The page makes it DNS-safe and unique per link (`laptop-2`) and serves it as `<host>.slicc.internal` on a synthetic address in `198.18.57.0/24`. `--name` overrides both.
1417
1426
  - **`mode`:** `local` (CDP setup) or `remote` (hub).
1418
1427
  - **`caps`:** what this side accepts OPENs for.
1419
- - node: `net` (`tcp` and `resolve`), `http`, `ssh`, `cdp`, `fs`, `join`, `jerk` and `jerk-seal` ([§5.14](#514-jerk-caps-jerk-and-jerk-seal-page--node-j1)).
1428
+ - node: `net` (`tcp` and `resolve`), `http`, `ws` ([§5.15](#515-ws-cap-ws-page--node-websockets-for-kernel-programs)), `udp` ([§5.12](#512-udp-cap-udp-page--node)), `ssh`, `cdp`, `fs`, `join`, `jerk` and `jerk-seal` ([§5.14](#514-jerk-caps-jerk-and-jerk-seal-page--node-j1)).
1420
1429
  - node, without a kind: `credentialed` ([§5.13](#513-credentialed-node-cap-http-streams-that-carry-a-value)), a promise about how the node handles `http` streams.
1421
1430
  - page: `kernel-in` ([§5.4](#54-kernel-page-cap-kernel-in-node--page)).
1422
1431
  - **`routes`** (node only): the prefixes it offers as specific routes, plus `exit: true` when it's willing to be a default exit.
@@ -1663,8 +1672,33 @@ slicc-node's hostfs ([its README, *Host folders*](https://github.com/ai-ecoverse
1663
1672
  - **Rules:** `summary` is at most 200 characters, with secrets redacted.
1664
1673
  - **Never sent:** credentials, or tool output beyond the summary. A tool's summary is its name and main argument (the command, path, pattern or URL). A result's summary is its line count, or the first line of an error.
1665
1674
 
1666
- #### 5.12 Reserved
1667
- `exec` (a command in seven's kernel shell, like six's `exec`) and `udp` (datagram frames, once the kernel has datagram sockets).
1675
+ #### 5.12 `udp` (cap `udp`), page → node
1676
+ - **OPEN:** `{ "kind": "udp", "family": 4 | 6 }` opens one unconnected UDP socket on the node, bound to an ephemeral port on all addresses of that family. **OPENED:** `{ "localAddr": "ip:port" }`.
1677
+ - **Records, both ways,** independent of DATA boundaries: `u8 type`, `u16` BE host length, host, `u16` BE port, `u32` BE length, payload.
1678
+ - **Type `1`, a datagram:**
1679
+ - page → node: the destination. The kernel always sends an IP literal of the socket's family; a hostname or the wrong family is `EPROTO`;
1680
+ - node → page: the source of a received datagram.
1681
+ - One record is one datagram, payload at most 65 507 bytes (IPv4) or 65 527 (IPv6).
1682
+ - **Type `2`, node → page only, unreachable:** host and port of a destination the node refused or that answered ICMP unreachable. The payload is the errno text (`EACCES: …` for policy, `ECONNREFUSED`, `ENETUNREACH`). The kernel may surface it on a connected socket and otherwise drop it.
1683
+ - **Policy, per datagram:** the node's dial policy and protected ports apply to every destination ([§6](#6-policy-node)).
1684
+ - **A refused destination drops that datagram only,** with a type-2 record back. The socket stays open, because one resolver socket legitimately tries several servers.
1685
+ - **Broadcast, multicast and the unspecified address are always refused.**
1686
+ - **Receiving: address-and-port-dependent filtering.** Like a NAT, the node passes on a received datagram only if its source `ip:port` is one the socket has sent to. Everything else is dropped silently.
1687
+ - **Close:** END from either side closes the socket. An idle socket (nothing sent or received for 5 min) is closed by the node with END.
1688
+ - **RESET codes:** `EPROTO` (a malformed or oversize record, or the wrong family) and `ENETUNREACH` (no usable interface of that family at OPEN).
1689
+ - **Flow:** no credit back-pressure for datagrams. Either side drops a datagram that doesn't fit the stream window instead of waiting, as UDP would, and counts it. The count is in the dashboard and `SLICC_DEBUG` logs.
1690
+ - **Privacy:** never log payloads; log only destination host and port at debug.
1691
+ - **HELLO:** nodes that implement it add `"udp"` to `caps`, on `slicc`, `attach` and `follow --exit` (as `ws`).
1692
+
1693
+ **Node-side details:**
1694
+ - **Socket:** `udp4` on `0.0.0.0` or `udp6` (IPv6 only) on `::`, so `localAddr` is `0.0.0.0:<port>` or `[::]:<port>`. "No usable interface" means no interface that is up has an address of that family, or the bind fails.
1695
+ - **`EPROTO` also covers:** a record type other than `1` from the page, a host that is empty, longer than 64 bytes, carries a zone (`%eth0`) or is IPv4-mapped (`::ffff:a.b.c.d`) on an IPv6 socket, port `0`, END inside a record, and an OPEN whose `family` isn't the number `4` or `6`.
1696
+ - **Always refused, with `EACCES`:** `255.255.255.255`, the directed broadcast address of every IPv4 interface of this machine (enumerated as for protected ports; if the interfaces can't be enumerated, every IPv4 destination is refused), `224.0.0.0/4`, `ff00::/8`, `0.0.0.0` and `::`. They're checked before and after [§6.1](#61-where-its-checked)'s normalization, so a NAT64 or 6to4 address can't embed them. `--allow` doesn't reopen them.
1697
+ - **Type 2, other than policy:** a send the OS fails at once (`ENETUNREACH`, `EHOSTUNREACH`, `EMSGSIZE`, …). ICMP unreachable comes only on Linux (`IP_RECVERR`), for destinations the socket has sent to. macOS and Windows don't report it on an unconnected socket, so there the datagram is just lost.
1698
+ - **Filtering:** the node remembers the 4096 destinations a socket sent to most recently. A destination counts from the moment its datagram goes out, so a reply that comes back at once isn't dropped.
1699
+ - **Idle:** a datagram the page sends, or one the node passes on, counts as activity. Unsolicited datagrams don't.
1700
+ - **Drops:** only node → page, when a record doesn't fit the page's window. The dashboard's `streams` line shows `udp … dropped`, and `SLICC_DEBUG` logs `udp <localAddr>: dropped N datagrams …` at most once a second, and the totals when the socket closes. The node reads page records all the time, so it never makes the page drop.
1701
+ - **Logs** (`SLICC_DEBUG`): `udp <localAddr>: open`, the first datagram to each destination (`to <ip:port>`), refusals, and `closed, N sent, M received, K dropped`. Never a payload.
1668
1702
 
1669
1703
  #### 5.13 `credentialed` (node cap): `http` streams that carry a value
1670
1704
  Values live in the vault (`vault.sliccy.ai`). A request reaches a node with a value in it only through a **transit exit**: the vault's `forward` hands bios the injected head, and bios sends it over the link.
@@ -1815,6 +1849,9 @@ How a kernel program in seven reaches [§1.4](#14-a-seven-as-follower-leader-to-
1815
1849
  - `EACCES`: B refused (`UNLINKED`, or no *Allow remote control*), with B's message.
1816
1850
  - **Not offered:** exits, kernel streams, CDP or anything else on B. Six's `exec` and `list` are parked.
1817
1851
 
1852
+ #### 5.17 Reserved
1853
+ `exec` is reserved (six's leader `exec`, parked in [slicc-cli#102](https://github.com/ai-ecoverse/slicc-cli/issues/102)).
1854
+
1818
1855
  ### 6. Policy (node)
1819
1856
 
1820
1857
  #### 6.1 Where it's checked
@@ -1869,7 +1906,7 @@ CI installs a pinned release: `go install github.com/ai-ecoverse/slicc-cli/cmd/s
1869
1906
 
1870
1907
  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`.
1871
1908
 
1872
- **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 runs two shards on Linux and Windows and three on macOS, whose runners vary the most (the same shard took from 400 s to 570 s), so each stays well inside its 12 minutes. CI runs them with `-v`, so `node scripts/shard-times.mjs --run <CI run id>` refreshes the file from a finished run's logs (`<platform>=<log file>` reads local `go test -v` output instead); commit the result when tests get much slower or faster.
1909
+ **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 runs two shards on Linux and Windows and three on macOS, whose runners vary the most (the same shard took from 400 s to 570 s), so each stays well inside its 12 minutes. That full matrix, all with `-race`, runs on pushes to `main`, which is the run the release waits for, so nothing ships without it. Pull requests and the merge queue (`merge_group`) run a lighter one, so a queue group needs only one macOS test runner: the two Linux shards with `-race`, and one leg each on macOS and Windows without `-race` (25 minutes). 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.
1873
1910
 
1874
1911
  **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.
1875
1912
 
@@ -1906,7 +1943,7 @@ The real-Chrome tests in `test/integration/` launch headless, so they run the ex
1906
1943
 
1907
1944
  PRs merge through the merge queue. CI also runs on `merge_group`, and the queue requires one check, the `ci` job, which fails unless every other CI job passed. Merge-queue runs never start or satisfy a release: the release starts only from CI's `push` run on `main`.
1908
1945
 
1909
- semantic-release cuts releases from `main` in `.github/workflows/release.yml`, on a macOS runner. The release doesn't run the tests again: the workflow starts when the CI workflow's run for a push to `main` completes (`workflow_run`), and the release job runs only when that run passed, sharded tests on all three OSes included, on the commit that run tested. A CI run that failed or was cancelled releases nothing; when a re-run of its failed jobs passes, the release follows by itself. When `main` has moved on by then, the checkout is behind `main`, so semantic-release (in `release-plan.mjs` and again in the release) skips without failing, and the newer commit's run releases. A pull request's dry run and a manual run instead wait in the `ci` job for the CI workflow's run on the same commit (`pull_request`, or `push` on `main`), and fail unless it passed. Only a run on `main` publishes: anywhere else (a pull request, a manual run from another branch) the release job is a dry run (`DRY_RUN` is true whenever `github.ref` isn't `refs/heads/main`), and the attestation, semantic-release (the GitHub release and the npm packages), `go-warm.sh`, `install-smoke` and the `sliccy-next` recovery each also require `refs/heads/main` themselves. The release job then runs these steps:
1946
+ semantic-release cuts releases from `main` in `.github/workflows/release.yml`, on a macOS runner. The release doesn't run the tests again: the workflow starts when the CI workflow's run for a push to `main` completes (`workflow_run`), and the release job runs only when that run passed, sharded tests on all three OSes included, on the commit that run tested. A CI run that failed or was cancelled releases nothing; when a re-run of its failed jobs passes, the release follows by itself. When `main` has moved on by then, the checkout is behind `main`, so semantic-release (in `release-plan.mjs` and again in the release) skips without failing, and the newer commit's run releases. So that such a run doesn't wait for a macOS runner first, its `fresh` job on `cloud-run-bench` compares the tested commit with `git ls-remote` of `main` and skips the release job when `main` has moved; when it can't tell, the release job runs and semantic-release decides. A pull request's dry run and a manual run instead wait in the `ci` job for the CI workflow's run on the same commit (`pull_request`, or `push` on `main`), and fail unless it passed. Only a run on `main` publishes: anywhere else (a pull request, a manual run from another branch) the release job is a dry run (`DRY_RUN` is true whenever `github.ref` isn't `refs/heads/main`), and the attestation, semantic-release (the GitHub release and the npm packages), `go-warm.sh`, `install-smoke` and the `sliccy-next` recovery each also require `refs/heads/main` themselves. The release job then runs these steps:
1910
1947
  1. `scripts/release-plan.mjs` asks semantic-release (dry run) for the next version, and stops when there is none.
1911
1948
  2. `scripts/package.sh <version>` builds `darwin`, `linux` and `windows` × `amd64`, `arm64` with `CGO_ENABLED=0`, `-trimpath` and `-X main.version=<version>`, and `slicc-wasip1-wasm.wasm` (`GOOS=wasip1 GOARCH=wasm`) the same way. It signs and notarizes the darwin binaries and writes `SHA256SUMS`. The windows and linux binaries are not signed. Without the Apple signing secrets (forks, pull requests) it warns and leaves the darwin binaries unsigned.
1912
1949
  3. `scripts/npm-stage.mjs` stages `@ai-ecoverse/slicc` and the six platform packages into `dist/npm/`. It also stages the same launcher as `sliccy` (with two bins, `sliccy` and `slicc`, and a short preview README) into `dist/npm-next/`, as version `7.0.0-next.<version>`, with exact `optionalDependencies` on this release's platform packages. `npx sliccy@next` runs the bin named after the package, `sliccy`, and both bins run the same launcher. `--bin <name>` (repeatable) overrides the bins of any staged launcher. `--wasi` also stages `@ai-ecoverse/wasi-slicc` into `dist/npm/wasi-slicc/` from `slicc-wasip1-wasm.wasm` and `npm/wasi-slicc/host/slicc-host.mjs`, with README.md's [Inside seven](#inside-seven) section as its README; `--wasi-only` stages only that package.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc",
3
- "version": "1.41.1",
3
+ "version": "1.42.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,12 +18,12 @@
18
18
  "node": ">=18"
19
19
  },
20
20
  "optionalDependencies": {
21
- "@ai-ecoverse/slicc-darwin-arm64": "1.41.1",
22
- "@ai-ecoverse/slicc-darwin-amd64": "1.41.1",
23
- "@ai-ecoverse/slicc-linux-arm64": "1.41.1",
24
- "@ai-ecoverse/slicc-linux-amd64": "1.41.1",
25
- "@ai-ecoverse/slicc-windows-arm64": "1.41.1",
26
- "@ai-ecoverse/slicc-windows-amd64": "1.41.1",
21
+ "@ai-ecoverse/slicc-darwin-arm64": "1.42.0",
22
+ "@ai-ecoverse/slicc-darwin-amd64": "1.42.0",
23
+ "@ai-ecoverse/slicc-linux-arm64": "1.42.0",
24
+ "@ai-ecoverse/slicc-linux-amd64": "1.42.0",
25
+ "@ai-ecoverse/slicc-windows-arm64": "1.42.0",
26
+ "@ai-ecoverse/slicc-windows-amd64": "1.42.0",
27
27
  "@ai-ecoverse/slicc-jerk": "^0.19.0"
28
28
  }
29
29
  }