@ai-ecoverse/slicc 1.42.4 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -35
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`slicc` is SLICC's command line for seven, the SLICC on `*.sliccy.ai`. It is one binary for macOS, Windows and Linux. It launches and links browsers, and shares a machine's network, compute and browsers with a running seven over a WebRTC link. `npx sliccy` and Sliccstart.app run this binary. It replaces slicc-node and slicc-swift (see [Coming from slicc-node](#coming-from-slicc-node)).
|
|
4
4
|
|
|
5
|
-
**Status:** being built. `slicc` (the default flow), `slicc attach` (with the `cdp` capability), `slicc open`, `slicc pair` and `slicc unpair` (seven in your everyday browser, without CDP), `slicc <join-url> follow`, remote control with `prompt`, `watch` and `abort` (also from inside seven's kernel, see [Inside seven](#inside-seven)), host folders with `--mount`, `slicc browsers`, `slicc launch`, `update`, `install`, `--version` and `--help` work so far, plus a hidden diagnostic, `slicc <join-url> follow --signal-only [--timeout 2m]`. It attaches through the tray hub, prints a summary of the leader's offer (its size, media sections, candidate count and DTLS fingerprint) and the ICE server URLs (credentials are never printed), and exits without answering. The link protocol is specified in [Link protocol](#link-protocol).
|
|
5
|
+
**Status:** being built. `slicc` (the default flow), `slicc attach` (with the `cdp` capability), `slicc open`, `slicc pair` and `slicc unpair` (seven in your everyday browser, without CDP), `slicc <join-url> follow`, `slicc tray` (on a machine and inside seven), remote control with `prompt`, `watch` and `abort` (also from inside seven's kernel, see [Inside seven](#inside-seven)), host folders with `--mount`, `slicc browsers`, `slicc launch`, `update`, `install`, `--version` and `--help` work so far, plus a hidden diagnostic, `slicc <join-url> follow --signal-only [--timeout 2m]`. It attaches through the tray hub, prints a summary of the leader's offer (its size, media sections, candidate count and DTLS fingerprint) and the ICE server URLs (credentials are never printed), and exits without answering. The link protocol is specified in [Link protocol](#link-protocol).
|
|
6
6
|
|
|
7
7
|
## Commands (planned)
|
|
8
8
|
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
| `slicc attach --cdp <url>` | link a seven that is open in an already-running CDP browser (works now; see [Local setup](#local-setup-slicc-attach)) |
|
|
13
13
|
| `slicc open`, `slicc --default-browser`, `slicc pair` | link a seven in your everyday browser, without CDP: open or print a pairing URL (works now; see [Linking seven in your everyday browser](#linking-seven-in-your-everyday-browser)) |
|
|
14
14
|
| `slicc unpair [<fingerprint> \| --all]` | list or forget the sevens paired with `open` or `pair` |
|
|
15
|
+
| `slicc tray [--json] [-q] [-a]` | list the `slicc` processes on this machine that hold a link: mode, seven, device, state and offers (works now; see [Running links](#running-links-slicc-tray)) |
|
|
15
16
|
| `slicc launch` | launch a browser with CDP in a separate SLICC profile and print its CDP URL |
|
|
16
|
-
| `slicc <join-url> follow [--exit] [--runner …] [--cdp <url>] [--mount <path>]` | follow a seven's tray from another machine and offer its network, compute (over SSH), browsers or folders (`--exit`, `--runner`, `--cdp` and `--mount` work now; see [Remote setup](#remote-setup-slicc-join-url-follow)) |
|
|
17
|
+
| `slicc <join-url> follow [--exit] [--runner …] [--cdp <url>] [--mount <path>]` | follow a seven's tray from another machine and offer its network, compute (over SSH), browsers or folders (`--exit`, `--runner`, `--cdp` and `--mount` work now; see [Remote setup](#remote-setup-slicc-join-url-follow)). `-` or `@<file>` in place of `<join-url>` reads it from stdin or a file, out of sight of `ps` |
|
|
17
18
|
| `slicc <join-url> prompt <text>`, `watch`, `abort` | send a prompt to seven's agent and stream its answer, follow the agent as it works, or abort its run; `--cdp <url>` instead of a join URL links locally (works now; see [Remote control](#remote-control-slicc-prompt-watch-and-abort)) |
|
|
18
19
|
| `slicc <join-url> prompt`, `watch`, `abort` inside seven | the same commands from seven's kernel, as `@ai-ecoverse/wasi-slicc` (works now; see [Inside seven](#inside-seven)) |
|
|
20
|
+
| `slicc tray [url \| rotate \| unlink <device> \| retry]` inside seven | this seven's tray: its state and devices, the join URL on request, rotate it, drop a device, reconnect (see [Inside seven](#inside-seven)) |
|
|
19
21
|
| `slicc browsers --json` | list running CDP endpoints and installed browsers, ranked |
|
|
20
22
|
| `slicc update [--check]` | replace this binary with the latest release (works now; see [Updates](#updates)) |
|
|
21
23
|
| `slicc install jerk` | install slicc-jerk into slicc's data folder (works now; see [Companion tools](#companion-tools)) |
|
|
@@ -214,6 +216,23 @@ Signing:
|
|
|
214
216
|
- **macOS:** the binaries are signed with a Developer ID, with the hardened runtime, and notarized. A bare binary can't carry a stapled ticket (only apps, disk images and installer packages can), so Gatekeeper checks the notarization online the first time it runs. Inside Sliccstart.app the binary is stapled with the app.
|
|
215
217
|
- **Windows:** Windows binaries are unsigned; SmartScreen may warn on first run. AppLocker, WDAC or Intune policies can block them.
|
|
216
218
|
|
|
219
|
+
## Reproducing a release
|
|
220
|
+
|
|
221
|
+
Every release asset but the signed darwin binaries can be rebuilt byte for byte from its tag:
|
|
222
|
+
```sh
|
|
223
|
+
git clone https://github.com/ai-ecoverse/slicc-cli && cd slicc-cli && git checkout v<version>
|
|
224
|
+
SLICC_SIGN=0 scripts/package.sh <version>
|
|
225
|
+
gh release download v<version> --repo ai-ecoverse/slicc-cli -p SHA256SUMS -O - | diff - artifacts/release/SHA256SUMS
|
|
226
|
+
```
|
|
227
|
+
`diff` shows only the two darwin lines: every `linux`, `windows` and `.wasm` asset and `install.sh` is identical. `@ai-ecoverse/wasi-slicc`'s `bin/slicc.wasm` is `slicc-wasip1-wasm.wasm`. A `git archive` of the tag, or a checkout with tags made later, gives the same bytes:
|
|
228
|
+
- `package.sh` builds with `-buildvcs=false`, so Go stamps no tag, revision or `.git` state into the binary. Nothing reads it there: a release links its version with `-X main.version`, and only a binary with no linked version reads its Go build info (see [Updates](#updates)).
|
|
229
|
+
- It uses `-trimpath`, so the checkout's path doesn't matter.
|
|
230
|
+
- It runs the Go release that `go.mod` names (its `toolchain` line, else its `go` line, which then needs a patch version such as `go 1.26.5`; without one `package.sh` refuses to build) through `GOTOOLCHAIN`, downloading it when another Go is installed. It clears `GOFLAGS`, `GOEXPERIMENT`, `GOAMD64`, `GOARM64`, `GOWASM` and `GOFIPS140`, sets `GOENV=off` so no `go env -w` setting applies, and sets `GOWORK=off`, so the environment doesn't change the build. Its log names the Go it used.
|
|
231
|
+
|
|
232
|
+
The darwin binaries differ only by their signature: the release signs the same unsigned bytes. The integration test `TestReleaseBuildIsReproducibleFromAnyCheckout` (on macOS, where the release builds) builds a checkout tagged after its commit, and a `git archive` of it with `GOAMD64`, `GOARM64`, `GOFIPS140`, `GOFLAGS` and a `go env -w` file set, and requires identical `SHA256SUMS`.
|
|
233
|
+
|
|
234
|
+
Releases whose `package.sh` lacks `-buildvcs=false` were built with Go's VCS stamping, so their bytes depend on the tags present at build time: rebuild one from its commit with that tag and every later one deleted locally (`git tag -d`), as the release saw it, with the Go that `go.mod` names at that tag (`GOTOOLCHAIN=go<version>`) and a clean Go environment (`GOENV=off`, and none of the variables above set).
|
|
235
|
+
|
|
217
236
|
## Companion tools
|
|
218
237
|
|
|
219
238
|
slicc works with slicc-jerk (`jerk`), which runs on this machine and gets tokens for seven's vault. slicc finds it on `PATH` or in `bin` of its data folder: `~/Library/Application Support/slicc` on macOS, `$XDG_DATA_HOME/slicc` (default `~/.local/share/slicc`) on Linux, `%LOCALAPPDATA%\slicc` on Windows. That folder is never shared over `fs` (see [Host folders](#host-folders)). When its `bin` exists, slicc appends it to its own `PATH` at start, so its children (the SSH runner, for one) find jerk too; a copy already on `PATH` wins.
|
|
@@ -298,6 +317,7 @@ Release builds (a plain `X.Y.Z` version, linked in or, for `go install`, from th
|
|
|
298
317
|
| `SLICC_LAUNCHER` | set to `npm` by the npm launcher; removed from the environment that `slicc` passes on |
|
|
299
318
|
| `SLICC_DISCOVERY` | test-only: limits browser discovery, see [Development](#development) |
|
|
300
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
|
+
| `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 |
|
|
301
321
|
| `SLICC_LOOPBACK_PORTS` | test-only: `<first>-<last>`, the port range of the [loopback listener](#linking-seven-in-your-everyday-browser) instead of 17117-17126 |
|
|
302
322
|
| `SLICC_LINK_URL` | inside seven only: the link facade's address, `tcp://127.0.0.1:<port>` (see [Inside seven](#inside-seven)) |
|
|
303
323
|
| `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)) |
|
|
@@ -306,7 +326,7 @@ Release builds (a plain `X.Y.Z` version, linked in or, for `go install`, from th
|
|
|
306
326
|
|
|
307
327
|
```
|
|
308
328
|
slicc [--browser <id>]... [--cdp <url>]... [--page <url>] [--origin <origin>]... [--profile <name> | --profile six[:<port>]] [--headless]
|
|
309
|
-
[--name <name>] [--kernel-port <n>] [--no-kernel] [--allow <rule>]... [--deny <rule>]... [--mount <path>[:<name>][:ro]]...
|
|
329
|
+
[--name <name>] [--kernel-port <n>] [--no-kernel] [--allow <rule>]... [--deny <rule>]... [--expose-debug-port <port>]... [--mount <path>[:<name>][:ro]]...
|
|
310
330
|
[--yes] [--plain] [--json] [--no-fallback] [--open] [--default-browser]
|
|
311
331
|
[--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp]
|
|
312
332
|
```
|
|
@@ -321,7 +341,7 @@ slicc [--browser <id>]... [--cdp <url>]... [--page <url>] [--origin <origin>]...
|
|
|
321
341
|
- `linked`: `peer?`, `caps` and `policy`, for each link that comes up;
|
|
322
342
|
- `link-lost`: `reason?` or `message?`, for each one that goes;
|
|
323
343
|
- `closed`, with `browsers: []`, last, when slicc exits.
|
|
324
|
-
- **Then** it serves loopback links with this run's `--page`, `--origin`, `--name`, `--allow`, `--deny`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount` and compute flags, until `SIGINT` or `SIGTERM`.
|
|
344
|
+
- **Then** it serves loopback links with this run's `--page`, `--origin`, `--name`, `--allow`, `--deny`, `--expose-debug-port`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount` and compute flags, until `SIGINT` or `SIGTERM`.
|
|
325
345
|
|
|
326
346
|
**Discovery** is `slicc browsers`' discovery and ranking (see [Browsers](#browsers)), with each `--cdp` URL added and the `--page` origin counted as a seven origin.
|
|
327
347
|
|
|
@@ -332,7 +352,7 @@ slicc [--browser <id>]... [--cdp <url>]... [--page <url>] [--origin <origin>]...
|
|
|
332
352
|
|
|
333
353
|
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
354
|
|
|
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.
|
|
355
|
+
**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 this machine's other debugging ports too (see [Network policy](#network-policy)); 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
356
|
|
|
337
357
|
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
358
|
|
|
@@ -340,7 +360,7 @@ The cdp list follows the browsers: an extra that stops answering is taken off th
|
|
|
340
360
|
- `--browser <id>`: an id from `slicc browsers` (an installed id such as `chrome`, or `cdp:<host>:<port>`), or a name, case-insensitively. Repeatable: the first hosts seven, the others are extras. Without it, the best match that can host seven. An endpoint in SLICC six's profile is refused (`<id> is SLICC six's Chrome; only --profile six uses it`).
|
|
341
361
|
- `--cdp <url>`: also consider this CDP endpoint, as `slicc browsers --cdp`.
|
|
342
362
|
- `--page <url>`: the seven page, default `https://seven.sliccy.ai/`. It must be `http` or `https`, and its origin is accepted as a seven origin.
|
|
343
|
-
- `--origin`, `--name`, `--allow`, `--deny`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount`, and the compute flags `--runner`, `--shell`, `--eval`, `--eval-quiet`, `--sftp`, `--no-sftp`: as for `slicc attach`. Without `--runner` there is no `ssh`.
|
|
363
|
+
- `--origin`, `--name`, `--allow`, `--deny`, `--expose-debug-port`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount`, and the compute flags `--runner`, `--shell`, `--eval`, `--eval-quiet`, `--sftp`, `--no-sftp`: as for `slicc attach`. Without `--runner` there is no `ssh`.
|
|
344
364
|
- `--profile <name>`: the SLICC profile for a launched browser that hosts seven. `--profile six` and `--profile six:<port>` use SLICC six's own Chrome profile instead, without the picker (see [Using SLICC six's Chrome profile](#using-slicc-sixs-chrome-profile)).
|
|
345
365
|
- `--headless`: launch browsers without a window.
|
|
346
366
|
- `--yes`, `--plain`: no picker. `--json`: NDJSON events, below.
|
|
@@ -390,7 +410,7 @@ The cdp list follows the browsers: an extra that stops answering is taken off th
|
|
|
390
410
|
|
|
391
411
|
### `slicc browsers [--json] [--cdp <url>]... [--no-bundle-scan]`
|
|
392
412
|
|
|
393
|
-
Lists running CDP endpoints and installed browsers, best match first. The browser tables (ids, names, bundle ids, install and user-data paths, Electron apps), the ranking tiers, the seven flags and prefs and the process-scan rules come from [slicc-browsers](https://github.com/ai-ecoverse/slicc-browsers)' `catalog.json`, which slicc-cdp's `playwright-cli` reads too. Its conformance corpus runs in `test/integration/corpus_test.go
|
|
413
|
+
Lists running CDP endpoints and installed browsers, best match first. The browser tables (ids, names, bundle ids, install and user-data paths, Electron apps), the ranking tiers, the seven flags and prefs and the process-scan rules come from [slicc-browsers](https://github.com/ai-ecoverse/slicc-browsers)' `catalog.json`, which slicc-cdp's `playwright-cli` reads too. Its conformance corpus runs in `test/integration/corpus_test.go`, with every need but `tabs`: discovery drops every page that isn't a seven tab (see below), so it reports no `tabs` list, and the cases that need one skip.
|
|
394
414
|
|
|
395
415
|
**Running endpoints** come from four places:
|
|
396
416
|
- `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.
|
|
@@ -500,7 +520,7 @@ Plain output: the CDP HTTP URL on stdout, status lines on stderr. With `--json`,
|
|
|
500
520
|
## Local setup: `slicc attach`
|
|
501
521
|
|
|
502
522
|
```
|
|
503
|
-
slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal cdp|stdio] [--name <name>] [--allow <rule>]... [--deny <rule>]...
|
|
523
|
+
slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal cdp|stdio] [--name <name>] [--allow <rule>]... [--deny <rule>]... [--expose-debug-port <port>]...
|
|
504
524
|
[--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp] [--kernel-port <n>] [--no-kernel]
|
|
505
525
|
[--mount <path>[:<name>][:ro]]...
|
|
506
526
|
```
|
|
@@ -513,7 +533,8 @@ slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal
|
|
|
513
533
|
- `--origin`: also accept seven on this origin (`scheme://host[:port]`), repeatable. `https://<label>.sliccy.ai` is always accepted, except `www`.
|
|
514
534
|
- `--signal`: `cdp` (default) or `stdio`.
|
|
515
535
|
- `--name`: the link's `name` and `host` in HELLO. The default name is `slicc on <host>`, with this machine's short host name, lower-cased.
|
|
516
|
-
- `--allow`, `--deny`: policy rules on top of the local default (see [Network policy](#network-policy)). The `--cdp` and `--cdp-extra` ports and endpoint addresses are denied.
|
|
536
|
+
- `--allow`, `--deny`: policy rules on top of the local default (see [Network policy](#network-policy)). The `--cdp` and `--cdp-extra` ports and endpoint addresses are denied, and so are this machine's other [debugging ports](#network-policy), whatever `--allow` says.
|
|
537
|
+
- `--expose-debug-port <port>`: let seven reach this one debugging port after all (repeatable). It must be one of this machine's debugging ports when `attach` starts (9222–9229, a `DevToolsActivePort` port, or a running browser's `--remote-debugging-port`), or `attach` exits 1. The `--cdp` and `--cdp-extra` ports can't be exposed (also exit 1).
|
|
517
538
|
- `--runner <argv…>`: offer compute over SSH (cap `ssh`, see [SSH server](#ssh-server)). Each command runs as `<argv…> <command>`. The argv is every word after `--runner` up to a lone `--` or the end of the command line, so `--runner bash -c` or `--runner docker exec -i box sh -c -- --name box`. An argv can't contain a lone `--` itself. `--runner=…` is refused. At start, slicc resolves the argv's first word as exec does (on `PATH`, or a path to an executable; a relative path is made absolute, since commands run in the home directory) and exits 2 before linking when it doesn't: `slicc: --runner <x>: program not found`, or, when the word holds whitespace (`--runner "bash -c"`), `slicc: --runner "bash -c": no program "bash -c"; did you mean --runner bash -c?`. Without `--runner` the link has no `ssh` capability, and the flags below are refused.
|
|
518
539
|
- `--shell <argv…>`: the interactive form for `ssh` without a command, in the same argv syntax. The default is derived from the runner.
|
|
519
540
|
- `--eval <argv…>` and `--eval-quiet <duration>`: the REPL behind the `slicc-eval` subsystem (for example `--eval python3 -i`), and the silence that ends one response (default `500ms`).
|
|
@@ -555,7 +576,7 @@ slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal
|
|
|
555
576
|
## Linking seven in your everyday browser
|
|
556
577
|
|
|
557
578
|
```
|
|
558
|
-
slicc open [--page <url>] [--origin <origin>]... [--name <name>] [--allow <rule>]... [--deny <rule>]...
|
|
579
|
+
slicc open [--page <url>] [--origin <origin>]... [--name <name>] [--allow <rule>]... [--deny <rule>]... [--expose-debug-port <port>]...
|
|
559
580
|
[--kernel-port <n>] [--no-kernel] [--no-jerk] [--mount <path>[:<name>][:ro]]...
|
|
560
581
|
[--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp]
|
|
561
582
|
slicc pair [the same flags]
|
|
@@ -569,7 +590,7 @@ Link spec §1.3. These commands link a seven that runs in the browser you use ev
|
|
|
569
590
|
- **`slicc pair`** only prints the pairing URL, for a seven that's already open. Paste it into seven's address bar, or into its *Link this computer* field.
|
|
570
591
|
- **Plain `slicc`** falls back to this mode by itself when it finds no SLICC profile and no CDP endpoint (see [The default](#the-default-slicc)).
|
|
571
592
|
- Both serve links until `SIGINT` or `SIGTERM`, then exit 0. A listener that can't bind, or a missing config directory, exits 1, and bad flags exit 2. Status lines go to stderr, and `SLICC_DEBUG=1` adds diagnostics.
|
|
572
|
-
- **Flags** are as for `slicc attach`: `--origin`, `--name`, `--allow`, `--deny`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount` and the compute flags. `--page <url>` is the seven page to pair (default `https://seven.sliccy.ai/`), and its origin is accepted as a seven origin. `--default-browser` takes these flags too. The flags of `slicc` that pick or launch browsers (`--browser`, `--cdp`, `--profile`, `--headless`, `--yes`, `--plain`, `--json`) are errors here. A flag of SLICC six's points back to six, as for `slicc`.
|
|
593
|
+
- **Flags** are as for `slicc attach`: `--origin`, `--name`, `--allow`, `--deny`, `--expose-debug-port`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount` and the compute flags. `--page <url>` is the seven page to pair (default `https://seven.sliccy.ai/`), and its origin is accepted as a seven origin. `--default-browser` takes these flags too. The flags of `slicc` that pick or launch browsers (`--browser`, `--cdp`, `--profile`, `--headless`, `--yes`, `--plain`, `--json`) are errors here. A flag of SLICC six's points back to six, as for `slicc`.
|
|
573
594
|
|
|
574
595
|
**Identity.** The node's DTLS certificate (ECDSA P-256, valid for 20 years) lives in `<config>/slicc/loopback-identity.json` (mode `0600`, directory `0700`). The same file holds the port the node last listened on. It is minted on first use, and slicc says `new loopback identity for this computer: <name> (ab12…ef), fingerprint sha-256 AB:12:…`, or `this computer: …` on later runs. It is separate from the tray hub's certificates and survives restarts and updates. A file that exists but can't be read as an identity (another version, broken JSON or PEM) is left alone: slicc says ``<path> is not a loopback identity slicc can read; …`` and exits 1. Only a certificate within a day of expiring is replaced. Whenever slicc mints a new identity (none yet, or a replacement), it forgets every pin in `loopback-peers.json` right after writing it, since every seven has to pair with the new certificate anyway. A `loopback-peers.json` that can't be read is left alone the same way: pairings and `slicc unpair` fail with ``<path> is not a list of paired sevens slicc can read; …``, and signals are refused. `<name>` is `--name`, or this machine's short host name in lower case.
|
|
575
596
|
|
|
@@ -607,7 +628,7 @@ The URL is the secret, and it carries the node's fingerprint, so a process that
|
|
|
607
628
|
|
|
608
629
|
**The link in this mode** is local (HELLO `mode: "local"`), with the node's loopback certificate:
|
|
609
630
|
- **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.
|
|
610
|
-
- **Policy:** the local default (see [Network policy](#network-policy))
|
|
631
|
+
- **Policy:** the local default (see [Network policy](#network-policy)), with this machine's [debugging ports](#network-policy) (`--allow` never reopens them, only `--expose-debug-port`), the listener's port and the kernel port denied. So a kernel program can't reach a debugging port the user turned on.
|
|
611
632
|
- **ICE:** host candidates, loopback included, as for `slicc attach`.
|
|
612
633
|
- **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`.
|
|
613
634
|
- **Restarts:** the node keeps its certificate and its port, and the page keeps its certificate and its pin. A restarted `slicc open` or `slicc pair` is reachable again at once, and a paired page signals again without a new token. Each process has a new `node` id, so seven keys the device on the fingerprint.
|
|
@@ -806,7 +827,7 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
|
|
|
806
827
|
|
|
807
828
|
**`browsers` updates** (page cap `browsers`), node → page. When the list changes while a link is up and the page's HELLO has `browsers` in `caps`, the node opens `OPEN { "kind": "browsers", "browsers": [ … ] }` on the control channel with the full current list in HELLO's shape. It replaces the previous list; there are no deltas. The page answers OPENED `{}` and END, and the node sends END. Updates are sent one at a time per link, a change that comes while one is in flight is sent after it, with the list as it is then, and an update the page doesn't finish within 10 s is reset with `ETIMEDOUT`. A change between the HELLO and the link coming up is sent as an update right away.
|
|
808
829
|
|
|
809
|
-
`test/integration/testdata/link/link.js` is a page-side implementation of the link (framing, credit, both channel layouts, the violation rules, and `resolve` and raw-fetch helpers) that the integration tests run in Chrome against `slicc attach`. `cdp.js` next to it is a CDP client over `cdp` streams, `protect.js` probes the filter that keeps seven's origin out of reach, `guard.js` runs [slicc-browsers' guard vectors](https://github.com/ai-ecoverse/slicc-browsers#guard-vectors) through its `guard-runner.mjs`, with a second client straight to the test Chrome as the unguarded observer, `fs.js` an op-shaped client for `fs` streams (`call`, `list`, `read`, `write`, `watch`), `loopback.js` is the page side of [loopback signalling](#linking-seven-in-your-everyday-browser), and `page.js` answers `browsers` and `mounts` updates. Through `linkTest.loopback`, `page.js` pairs from the fragment (`confirm`), signals again (`signal`, `reconnect`), unlinks in-band (`unlink`) and posts raw requests (`post`), with an optional `?ports=<first>-<last>` scan range. `page.js` keeps one link per `node` id from the hello. For tray links (no hello `node`) it replaces an older link with the same fingerprint only when HELLO's `node` matches, as seven does. `agent.js` is a fake agent behind the page's `agent` cap: the tests queue scripted runs, emit transcript events, hold and release a run, reset watch streams, and turn the cap off. Loaded with `?kernel`, the test page lists `kernel-in` and answers `kernel` OPENs with `kernel.js`, a small HTTP and WebSocket echo server in the page.
|
|
830
|
+
`test/integration/testdata/link/link.js` is a page-side implementation of the link (framing, credit, both channel layouts, the violation rules, and `resolve` and raw-fetch helpers) that the integration tests run in Chrome against `slicc attach`. `cdp.js` next to it is a CDP client over `cdp` streams, `protect.js` probes the filter that keeps seven's origin out of reach, `guard.js` runs [slicc-browsers' guard vectors](https://github.com/ai-ecoverse/slicc-browsers#guard-vectors) through its `guard-runner.mjs`, with a second client straight to the test Chrome as the unguarded observer (a row still running after 3 minutes fails on its own with the CDP calls it was waiting for, and the test fails after 4 minutes without a finished row, naming each row in flight and its pending calls), `fs.js` an op-shaped client for `fs` streams (`call`, `list`, `read`, `write`, `watch`), `loopback.js` is the page side of [loopback signalling](#linking-seven-in-your-everyday-browser), and `page.js` answers `browsers` and `mounts` updates. Through `linkTest.loopback`, `page.js` pairs from the fragment (`confirm`), signals again (`signal`, `reconnect`), unlinks in-band (`unlink`) and posts raw requests (`post`), with an optional `?ports=<first>-<last>` scan range. `page.js` keeps one link per `node` id from the hello. For tray links (no hello `node`) it replaces an older link with the same fingerprint only when HELLO's `node` matches, as seven does. `agent.js` is a fake agent behind the page's `agent` cap: the tests queue scripted runs, emit transcript events, hold and release a run, reset watch streams, and turn the cap off. Loaded with `?kernel`, the test page lists `kernel-in` and answers `kernel` OPENs with `kernel.js`, a small HTTP and WebSocket echo server in the page.
|
|
810
831
|
|
|
811
832
|
### Kernel services
|
|
812
833
|
|
|
@@ -989,7 +1010,11 @@ A node checks every address it connects to for the link's `tcp`, `http`, `ws` an
|
|
|
989
1010
|
|
|
990
1011
|
**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.
|
|
991
1012
|
|
|
992
|
-
**Protected ports**, in both defaults
|
|
1013
|
+
**Protected ports**, in both defaults and in every mode (`slicc`, `attach`, `open`, `pair`, `--default-browser` and `follow`). Two kinds:
|
|
1014
|
+
- **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. They are read at startup, again whenever a `tcp`, `http`, `ws` or `ssh` connection goes to an address of this machine on a port not yet denied (the files each time, the process list at most once a second), and every 5 s, so a browser that starts later is denied before its first connection. 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.
|
|
1015
|
+
- **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.
|
|
1016
|
+
|
|
1017
|
+
Both are denied:
|
|
993
1018
|
- on loopback, each as `127.0.0.0/8:port`, `127.0.0.1/32:port` and `[::1]/128:port`, so `--allow 127.0.0.1/32` doesn't reopen them;
|
|
994
1019
|
- on every address of this machine, as `<address>/32:port` or `<address>/128:port`. The interfaces are enumerated at check time and cached for 5 seconds, so a new interface counts. If they can't be enumerated, those ports are denied on every address.
|
|
995
1020
|
|
|
@@ -1001,11 +1026,11 @@ A node checks every address it connects to for the link's `tcp`, `http`, `ws` an
|
|
|
1001
1026
|
|
|
1002
1027
|
They are listed in HELLO's `policy.deny` as `<address>/32:port` or `[<address>]/128:port`.
|
|
1003
1028
|
|
|
1004
|
-
Only a user allow for exactly the same address and port reopens one of these.
|
|
1029
|
+
Only a user allow for exactly the same address and port reopens one of these, and only on another machine: on this machine the browser's debugging port wins.
|
|
1005
1030
|
|
|
1006
1031
|
**`resolve` filtering.** An address is kept when the policy allows it on at least one port. When every address is dropped, the answer is an empty list.
|
|
1007
1032
|
|
|
1008
|
-
**HELLO.** `policy.summary` names the default and lists the `--allow` and `--deny` rules on top of it
|
|
1033
|
+
**HELLO.** `policy.summary` names the default and lists the `--allow` and `--deny` rules on top of it, and `exposes debugging ports <ports>` after them with `--expose-debug-port`. `policy.allow` and `policy.deny` list the `--allow` and `--deny` rules plus the protected ports' loopback denies (debugging ports found at startup included, later ones not, since HELLO was sent already) and the browser endpoints, not the default ranges or the per-interface denies.
|
|
1009
1034
|
|
|
1010
1035
|
## SSH server
|
|
1011
1036
|
|
|
@@ -1039,16 +1064,21 @@ A command that can't start exits 127 with the reason on stderr.
|
|
|
1039
1064
|
## Remote setup: `slicc <join-url> follow`
|
|
1040
1065
|
|
|
1041
1066
|
```
|
|
1042
|
-
slicc <join-url> follow [--exit] [--allow <rule>]... [--deny <rule>]... [--name <name>] [--cdp <url>]...
|
|
1067
|
+
slicc <join-url> follow [--exit] [--allow <rule>]... [--deny <rule>]... [--expose-debug-port <port>]... [--name <name>] [--cdp <url>]...
|
|
1043
1068
|
[--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp]
|
|
1044
1069
|
[--mount <path>[:<name>][:ro]]... [--jerk] [--jerk-seal]
|
|
1070
|
+
slicc - follow [flags]
|
|
1071
|
+
slicc @<file> follow [flags]
|
|
1045
1072
|
```
|
|
1046
1073
|
|
|
1047
1074
|
`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.
|
|
1048
1075
|
|
|
1076
|
+
**The join URL off the command line.** On the command line, `ps` shows the join URL to every user of this machine. In its place, `-` reads it from stdin and `@<file>` from a file: the first non-empty line, trimmed. From stdin slicc reads only up to that line's end, so the rest of stdin is left for `prompt -`. The file must be a regular file, not a symlink. On macOS and Linux it must also belong to you and be readable by no one else (`chmod 600`). Exit 2 follows when there's no join URL, when the file can't be read, breaks those rules or is over 64 KiB, or when what's there isn't a join URL. The message names the file but never shows what it holds, inside seven too. `prompt`, `watch` and `abort` take `-` and `@<file>` the same way, inside seven too. There is no environment variable for it, because `--runner`'s processes would inherit it.
|
|
1077
|
+
|
|
1049
1078
|
**Flags.**
|
|
1050
1079
|
- `--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`.
|
|
1051
|
-
- `--allow`, `--deny`: policy rules on top of the remote default (see [Network policy](#network-policy)).
|
|
1080
|
+
- `--allow`, `--deny`: policy rules on top of the remote default (see [Network policy](#network-policy)). This machine's debugging ports stay denied on every address of it, whatever `--allow` says, `--allow 127.0.0.1/32` included.
|
|
1081
|
+
- `--expose-debug-port <port>`: let seven reach this one debugging port after all (repeatable), as for `attach`. The `--cdp` ports stay denied.
|
|
1052
1082
|
- `--name`: the link's `name` and `host` in HELLO, as for `attach`.
|
|
1053
1083
|
- `--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`.
|
|
1054
1084
|
- `--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.
|
|
@@ -1170,14 +1200,59 @@ Summaries are one line of at most 200 characters. Escape sequences and control c
|
|
|
1170
1200
|
- **Limits:** `summary` is at most 200 characters. Credentials and tool output beyond the summary are never sent.
|
|
1171
1201
|
- **Unknown events:** the node ignores events and fields it doesn't know.
|
|
1172
1202
|
|
|
1203
|
+
## Running links: `slicc tray`
|
|
1204
|
+
|
|
1205
|
+
```
|
|
1206
|
+
slicc tray [--json] [-q] [-a]
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
`slicc tray` lists the `slicc` processes on this machine that hold a link, as `screen -ls` and `tmux ls` do: `follow` (remote, through the tray hub), and the default flow, `attach`, `open` and `pair` (local). It replaces SLICC six's `host` on the machine side. Inside seven, `slicc tray` is that seven's own tray instead (see [Inside seven](#inside-seven)).
|
|
1210
|
+
|
|
1211
|
+
```
|
|
1212
|
+
PID MODE SEVEN DEVICE STATE OFFERS
|
|
1213
|
+
4121 follow SLICC on macOS 3fa9…c210 connected 2h exit,ssh,fs:proj
|
|
1214
|
+
5530 follow SLICC on Linux 88d1…07be reconnecting (#4, 30s) ssh
|
|
1215
|
+
6012 attach seven - connected 1d exit,cdp,kernel:80
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
- **`SEVEN`:** the name from seven's HELLO, else its origin's host, else the hub's host in parentheses. Never the join URL.
|
|
1219
|
+
- **`DEVICE`:** this machine's device, as `follow` and seven's tray see it: for `follow`, the DTLS certificate for that hub, and for `open` and `pair`, the loopback identity. It is the SHA-256 fingerprint, shown as its first and last four hex digits, so a row here matches a row in the leader's `slicc tray`. Over CDP there is none (`-`).
|
|
1220
|
+
- **`STATE`:** `waiting` (a local mode, until seven links), `dialing`, `waiting for offer`, `connecting`, `connected` with how long, `reconnecting (#<attempt>, <next wait>)`, or `stopped (<code>)` with the hub's or seven's code (`TRAY_EXPIRED`, `UNLINKED`, …) as the process exits.
|
|
1221
|
+
- **`OFFERS`:** `exit` (the network, `--exit` for `follow`), `ssh`, `cdp`, `jerk`, `fs:<name>` per folder, and `kernel:<port>`.
|
|
1222
|
+
- **`--json`:** a JSON array, one object per process: `{ "v": 1, "pid", "mode", "version", "started", "seven"?, "origin"?, "hub"?, "device"?, "state", "since", "attempt"?, "retryAt"?, "code"?, "error"?, "offers", "dials", "links", "lastLinked"? }`. `hub` is the hub's origin, `error` the last failure with secrets redacted, `dials` the dials so far and `links` the links that came up. These are SLICC six's follower counters.
|
|
1223
|
+
- **`-q`:** only the PIDs, one per line, for `slicc tray -q | xargs kill`. There's no `stop`.
|
|
1224
|
+
- **`-a`:** also the trays this machine knows but isn't following now, from `tray-certs.json` and `tray-join.json`, as `docker ps -a` does: PID `-`, state `not running`, the leader's name from the last link and the device for that hub. Neither file's join URL is shown.
|
|
1225
|
+
|
|
1226
|
+
**Control sockets.** Each of those processes serves a control socket while it runs, `<dir>/<pid>.sock`, and removes it on exit.
|
|
1227
|
+
- **Where `<dir>` is:**
|
|
1228
|
+
- **Linux:** `$XDG_RUNTIME_DIR/slicc`, otherwise `$TMPDIR/slicc-<uid>`.
|
|
1229
|
+
- **macOS:** `$TMPDIR/slicc-<uid>`.
|
|
1230
|
+
- **Either, when that path is too long for a socket:** `/tmp/slicc-<uid>`.
|
|
1231
|
+
- **Windows:** `%LocalAppData%\slicc\run`, or `%UserProfile%\.slicc-run` when that is too long, as AF_UNIX (Windows 10 and later).
|
|
1232
|
+
- **Who can reach it:**
|
|
1233
|
+
- **macOS and Linux:** the directory is `0700`, must belong to the user and not be a symlink, and is tightened when it's more open. Each connection checks the peer's uid (`SO_PEERCRED` on Linux, `LOCAL_PEERCRED` on macOS) and closes on anyone else.
|
|
1234
|
+
- **Windows:** slicc sets the directory's DACL to the user and SYSTEM only, protected from inheritance, so the sockets in it inherit it. `slicc tray` skips a directory whose DACL is anything else.
|
|
1235
|
+
- **Either way,** a process that can't serve a socket there runs on without one.
|
|
1236
|
+
- **The protocol:** one line of JSON each way, `{ "v": 1, "op": "status" }` and the status object above, then the process closes the connection.
|
|
1237
|
+
- **Stale sockets:** liveness comes from connecting, not from the PID, so a reused PID can't fool `slicc tray`. A socket that refuses a connection is stale, and `slicc tray` removes it and says so on stderr, as `screen -wipe` does.
|
|
1238
|
+
- **The lock:** each process holds an `flock` on `<pid>.sock.lock` while it runs, and `slicc tray` removes a socket only while it holds that lock itself and the socket still refuses. So it never removes a live socket.
|
|
1239
|
+
- **A lock file deleted underneath:** both sides check after locking that the lock file on disk is still the one they hold, and start over when it isn't.
|
|
1240
|
+
- **Untrusted text:** `slicc tray` and `--json` strip control characters and terminal escapes from what a process reports, and replace any join URL with `<join-url>`.
|
|
1241
|
+
- **Why sockets:** link state changes all the time, and a socket answers with the current state where a state file would lag. A `ps` scan sees no link state.
|
|
1242
|
+
|
|
1173
1243
|
## Inside seven
|
|
1174
1244
|
|
|
1175
|
-
`slicc` also runs inside seven's kernel, so that one seven's agent or user can prompt, watch and abort another seven's agent. That build is `slicc.wasm`, slicc-cli's client commands compiled with `GOOS=wasip1 GOARCH=wasm` and `CGO_ENABLED=0`, and it ships as the npm package `@ai-ecoverse/wasi-slicc`, at slicc-cli's own version. seven's page holds the WebRTC link; slicc only reaches the page's link facade.
|
|
1245
|
+
`slicc` also runs inside seven's kernel, so that one seven's agent or user can prompt, watch and abort another seven's agent, and see and manage this seven's own tray. That build is `slicc.wasm`, slicc-cli's client commands compiled with `GOOS=wasip1 GOARCH=wasm` and `CGO_ENABLED=0`, and it ships as the npm package `@ai-ecoverse/wasi-slicc`, at slicc-cli's own version. seven's page holds the WebRTC link; slicc only reaches the page's link facade.
|
|
1176
1246
|
|
|
1177
1247
|
```
|
|
1178
1248
|
slicc <join-url> prompt [--steer | --reject] [--thinking] [--json] [--plain] <text>|@file|-
|
|
1179
1249
|
slicc <join-url> watch [--thinking] [--json] [--plain]
|
|
1180
1250
|
slicc <join-url> abort
|
|
1251
|
+
slicc tray [--json]
|
|
1252
|
+
slicc tray url
|
|
1253
|
+
slicc tray rotate
|
|
1254
|
+
slicc tray unlink <device>
|
|
1255
|
+
slicc tray retry
|
|
1181
1256
|
slicc --version
|
|
1182
1257
|
slicc --help
|
|
1183
1258
|
```
|
|
@@ -1186,29 +1261,39 @@ slicc --help
|
|
|
1186
1261
|
- **Ctrl-C** ends slicc with 130 (the kernel's default for `SIGINT`), `watch` included, and a running prompt goes on in the other seven. `--abort-on-interrupt` isn't offered.
|
|
1187
1262
|
- **Exit 3** covers the facade's own refusals too. When the facade gives a message (`unlock the vault to continue`, or the other seven's refusal), slicc prints it instead of the Linked devices hint.
|
|
1188
1263
|
- **A terminal** is a standard stream that is a character device. Styling then follows `TERM`, `NO_COLOR` and `--plain` as usual.
|
|
1264
|
+
- **`slicc tray`:** this seven's tray, the leader's view (SLICC six's `host`). It asks the facade with the `tray` kind ([§5.16](#516-the-link-facade-page-side-not-on-the-wire-the-in-kernel-slicc)), so it takes no join URL.
|
|
1265
|
+
- **`slicc tray`** prints the tray's state (`off`, `connecting`, `ready`, `reconnecting` or `failed`) and its hub origin, the current exit when it is a device or a tailnet node, and one line per device: `DEVICE` (its fingerprint, as `3fa9…c210`), `NAME`, `HOST` (`<label>.slicc.internal`), `MODE`, `STATE` (`connecting`, `connected` or `offline`) and `OFFERS` (`exit` when the device offers to be the exit). `--json` prints seven's answer as one line of JSON.
|
|
1266
|
+
- **`slicc tray url`** prints the join URL and nothing else, so `slicc tray url > /dev/clipboard` copies it. It is the one deliberate exception to "the join URL is never printed": nothing else prints it, `slicc tray` and `--json` included, so it reaches a transcript only when asked for. It prints only an answer that is a valid join URL without control characters, and exits 1 otherwise.
|
|
1267
|
+
- **Whatever seven answers**, slicc doesn't trust it to keep the rule. `slicc tray`, `--json` and the error messages replace any join URL in the answer with `<join-url>`, and strip control characters and terminal escapes from names and messages. `--json` also drops `url`, `join`, `joinUrl`, `joinCommand`, `followUrl` and `webhookUrl` keys, wherever they are, and prints one line.
|
|
1268
|
+
- **`slicc tray rotate`** makes a new join URL; the old one stops working (SLICC six's `host reset`). It prints nothing on stdout, and `made a new join URL; …` on stderr. `unlink` and `retry` also confirm on stderr.
|
|
1269
|
+
- **`slicc tray unlink <device>`** drops one device, as Linked devices' Unlink does. `<device>` is a fingerprint, a unique prefix of one (colons and case ignored), or exactly the short form `slicc tray` shows (`3fa9…c210`, or `3fa9...c210`). slicc asks for the status first and matches there, then sends the full id. No match or more than one exits 1.
|
|
1270
|
+
- **`slicc tray retry`** reconnects the tray's links, as the Reconnect button does.
|
|
1271
|
+
- **Not here:** choosing the exit (that's routing, not tray state), and SLICC six's `host join` and `host leave`: a seven follower runs no kernel or agent of its own, so following stays with `#follow=` and the UI's Leave.
|
|
1272
|
+
- **Exit status:** 0, 1 on failure (`ENOTCONN` when the tray isn't connected, a facade that doesn't answer within 1 minute, or 5 minutes for `rotate` and `unlink`, which seven may ask you to approve), 2 for bad usage, 3 when seven refuses (`EACCES`).
|
|
1189
1273
|
- **Everything else** exits 2 with `slicc: <command> isn't available inside seven`: `follow`, `attach`, `open`, `browsers`, the default flow, `update`, `install`, and `prompt`, `watch` or `abort` without a join URL. `--help` lists only what works there.
|
|
1190
1274
|
- **What the build leaves out** (build constraints): pion and the link, CDP, SSH, mounts, the kernel listener, the policy, browser discovery, telemetry and updates.
|
|
1191
1275
|
|
|
1192
1276
|
**The link facade.** slicc reads its address from `SLICC_LINK_URL` only, as `tcp://127.0.0.1:<port>` on the kernel's loopback (any `127.x` address, or `localhost`). Without it, or with another shape, slicc exits 1. Each `agent` stream (see [`agent`](#agent-page-cap-agent-node--page)) is one connection, carrying records of a `u8` type, a `u32` big-endian length and the payload, both ways:
|
|
1193
1277
|
- **`1`, control:** JSON, at most 64 KiB.
|
|
1194
1278
|
- slicc's first record is `{ "v": 1, "join": "<join-url>", "open": { "kind": "agent", "op": … } }`, where `open` is the stream's OPEN, `length` for long prompts included. The join URL goes only there, never in an address, a log or an error.
|
|
1279
|
+
- For `slicc tray`, it is `{ "v": 1, "open": { "kind": "tray", "op": … } }`, without `join`. The page answers with one control record, `{ "opened": <result> }` or a `reset`, and slicc closes the connection (see [§5.16](#516-the-link-facade-page-side-not-on-the-wire-the-in-kernel-slicc)).
|
|
1195
1280
|
- The page answers `{ "opened": { … } }` (the OPENED) or `{ "reset": "ERRNO: message" }`. A `reset` may also come later, as the stream's RESET.
|
|
1196
|
-
- Without an answer within 1 minute, slicc gives up: `seven's link facade didn't answer within 1m0s`, exit 1 (`watch` reconnects).
|
|
1281
|
+
- Without an answer within 1 minute, slicc gives up: `seven's link facade didn't answer within 1m0s`, exit 1 (`watch` reconnects). `slicc tray rotate` and `unlink` wait 5 minutes, since seven may ask for approval first.
|
|
1197
1282
|
- **`2`, data:** the stream's DATA: a long prompt's bytes up, and NDJSON events down. slicc sends records of at most 64 KiB, and takes records of up to 16 MiB; record boundaries carry no meaning.
|
|
1198
1283
|
- **`3`, end:** that side's END. slicc closes the connection after both ends or a reset.
|
|
1199
1284
|
- **Errors:** an unknown type, a control record over 64 KiB, any record over 16 MiB, a control record that isn't JSON, or a later control record that isn't a `reset` makes slicc send `{ "reset": "EPROTO" }` and fail the stream. `ENOTCONN` (the tray can't be reached) fails `prompt` and `abort` with exit 1; `watch` says `lost the link to seven; reconnecting` and retries with its usual backoff, as it does when a connection drops.
|
|
1200
1285
|
|
|
1201
|
-
**The host module.** Go on `wasip1` can't open sockets, so the package's `host/slicc-host.mjs` gives `slicc.wasm` the `slicc_host` imports, on the kernel's loopback, through the imports-module API's `ctx.net.connect` and `ctx.net.ready` and the `fd-read`, `fd-write`, `fd-select` and `fd-close` calls (slicc-kernel 1.92.0 or later, the package's `engines.slicc-kernel`): `probe`, `connect(host, port)` (loopback only, non-blocking), `connected(fd, timeout)`, `read(fd, buffer, timeout)`, `write(fd, buffer, timeout)` (non-blocking) and `close(fd)`. Connects, reads and writes wait at most 100 ms at a time, so signals, timeouts and a reset that arrives while a long prompt uploads get through. Without the module, slicc exits 1 with `this runtime has no slicc_host module`. On a kernel older than 1.92.0, which has no `ctx.net`, `connect` answers `ENOSYS`, so slicc exits 1 with `can't reach seven's link facade`.
|
|
1286
|
+
**The host module.** Go on `wasip1` can't open sockets, so the package's `host/slicc-host.mjs` gives `slicc.wasm` the `slicc_host` imports, on the kernel's loopback, through the imports-module API's `ctx.net.connect` and `ctx.net.ready` and the `fd-read`, `fd-write`, `fd-select` and `fd-close` calls (slicc-kernel 1.92.0 or later, the package's `engines.slicc-kernel`): `probe`, `connect(host, port)` (loopback only, non-blocking; `localhost` is connected as `127.0.0.1`, never by name, so `/etc/hosts` can't point it elsewhere), `connected(fd, timeout)`, `read(fd, buffer, timeout)`, `write(fd, buffer, timeout)` (non-blocking) and `close(fd)`. Connects, reads and writes wait at most 100 ms at a time, so signals, timeouts and a reset that arrives while a long prompt uploads get through. Without the module, slicc exits 1 with `this runtime has no slicc_host module`. On a kernel older than 1.92.0, which has no `ctx.net`, `connect` answers `ENOSYS`, so slicc exits 1 with `can't reach seven's link facade`.
|
|
1202
1287
|
|
|
1203
1288
|
**The package.** `@ai-ecoverse/wasi-slicc` holds `bin/slicc.wasm`, `host/slicc-host.mjs`, this section as its README, and the LICENSE. Its `package.json` has `"slicc": { "abi": "wasi", "commands": { "slicc": { "wasm": "bin/slicc.wasm", "imports": "host/slicc-host.mjs" } } }` and `engines.slicc-kernel`. The release attaches `slicc.wasm` as `slicc-wasip1-wasm.wasm`, in `SHA256SUMS` and its attestation (see [Releases](#releases)).
|
|
1204
1289
|
|
|
1205
|
-
**Tests.** `test/integration/wasi_test.go` builds `slicc.wasm`, stages the package with `npm-stage.mjs --wasi-only`, and runs it in `@ai-ecoverse/slicc-kernel`'s Node kernel (an exact-pinned devDependency, so `npm ci` first) against a fake facade, a kernel JS program in `testdata/wasi/fakefacade/`. `wasi_realfacade_test.go` runs it against the kernel's own link facade (`createNodeKernel({ facades: { link } })`), served by a test page hook, `testdata/wasi/page.mjs`, that implements [§5.16](#516-the-link-facade-page-side-not-on-the-wire-the-in-kernel-slicc): it checks the stamp the hook receives (root and a cone in `sudo` pass, a scoop gets `EACCES`) and that the kernel sets `SLICC_LINK_URL`. `cmd/slicc` builds for both: a file there that needs a native-only package carries `//go:build !wasip1`, and `wasi_test.go` fails when the `wasip1` build breaks or links one of the left-out packages.
|
|
1290
|
+
**Tests.** `test/integration/wasi_test.go` builds `slicc.wasm`, stages the package with `npm-stage.mjs --wasi-only`, and runs it in `@ai-ecoverse/slicc-kernel`'s Node kernel (an exact-pinned devDependency, so `npm ci` first) against a fake facade, a kernel JS program in `testdata/wasi/fakefacade/`. One case writes `/etc/hosts` as root to map `localhost` to `203.0.113.5` and connects to `tcp://localhost:<port>`; `testdata/wasi/hostmodule.mjs` also loads the staged `host/slicc-host.mjs` with a `ctx.net` that records the host it is asked for. `wasi_realfacade_test.go` runs it against the kernel's own link facade (`createNodeKernel({ facades: { link } })`), served by a test page hook, `testdata/wasi/page.mjs`, that implements [§5.16](#516-the-link-facade-page-side-not-on-the-wire-the-in-kernel-slicc): it checks the stamp the hook receives (root and a cone in `sudo` pass, a scoop gets `EACCES`) and that the kernel sets `SLICC_LINK_URL`. `wasi_tray_test.go` runs `slicc tray` against both, and checks that its openings carry no join URL. `cmd/slicc` builds for both: a file there that needs a native-only package carries `//go:build !wasip1`, and `wasi_test.go` fails when the `wasip1` build breaks or links one of the left-out packages.
|
|
1206
1291
|
|
|
1207
1292
|
## Tray hub signaling (remote setup)
|
|
1208
1293
|
|
|
1209
1294
|
A node on another machine sets up its link through the tray hub, the `slicc-tray-hub` worker on `www.sliccy.ai`. seven is the tray leader and the node is a follower. The wire contract is six's `packages/shared-ts/src/tray-signaling.ts`. The node only signals over it and never speaks six's tray-sync messages.
|
|
1210
1295
|
|
|
1211
|
-
**Join URL.** `https://<host>/join/<token>`. Plain `http` is accepted only for loopback hosts. The join URL is the secret: the node never prints it, its token, or the token's secret part, in output, logs or errors.
|
|
1296
|
+
**Join URL.** `https://<host>/join/<token>`. Plain `http` is accepted only for loopback hosts. The join URL is the secret: the node never prints it, its token, or the token's secret part, in output, logs or errors. The one exception is `slicc tray url` inside seven, which prints the leader's own join URL when asked to (see [Inside seven](#inside-seven)).
|
|
1212
1297
|
|
|
1213
1298
|
**Attach.** `POST <join-url>` with `{ "controllerId", "runtime": "slicc-link/1" }`. The runtime tag lets seven tell link nodes from six's followers; the hub passes it on in `follower.join_requested`, and the node sends it again on `retry`. `wait` is retried after `retryAfterMs` (clamped to 50 ms–30 s) with the same controller id. `signal` starts a bootstrap and carries the TURN and STUN `iceServers` for the peer connection. `fail` (`INVALID_JOIN_CAPABILITY`, `TRAY_EXPIRED`) is terminal.
|
|
1214
1299
|
|
|
@@ -1328,7 +1413,7 @@ The scenario: seven is open in the user's **everyday browser** (any vendor, no C
|
|
|
1328
1413
|
**The link in this mode**
|
|
1329
1414
|
- **ICE:** host candidates on loopback only, as in [§1.1](#11-local-setup-over-cdp).
|
|
1330
1415
|
- **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.
|
|
1331
|
-
- **Policy:** the local default ([§6.4](#64-local-default-slicc-the-bridge-to-the-browser-it-controls))
|
|
1416
|
+
- **Policy:** the local default ([§6.4](#64-local-default-slicc-the-bridge-to-the-browser-it-controls)), with the debugging ports of [§6.5](#65-debugging-ports-every-mode) (no served browser here, so 9222–9229, every `DevToolsActivePort` port and every running browser's `--remote-debugging-port`) and the node's own listeners denied. A kernel program can't reach a debugging port the user enabled.
|
|
1332
1417
|
- **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.
|
|
1333
1418
|
- **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.
|
|
1334
1419
|
- **Several sevens:** the node holds one link per paired page fingerprint, and each is an exit on its own.
|
|
@@ -1546,7 +1631,7 @@ On a [§1.3](#13-local-setup-without-cdp-loopback-signalling) loopback link, `un
|
|
|
1546
1631
|
- **Which browsers:**
|
|
1547
1632
|
- **`attach`:** the browser it set up through (`--cdp <url>`, single-valued, `seven: true`), plus every `--cdp-extra <url>` (repeatable); the TUI's multi-select passes those. A second `--cdp` is an error that points to `--cdp-extra`.
|
|
1548
1633
|
- **`follow`:** only the browsers named with `--cdp <url>`. Without one, `follow` doesn't advertise `cdp`, because a remote seven gets full control of every browser listed. `follow` never marks a browser `seven: true`.
|
|
1549
|
-
- **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
|
|
1634
|
+
- **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)).
|
|
1550
1635
|
|
|
1551
1636
|
- **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.
|
|
1552
1637
|
- **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.
|
|
@@ -1815,10 +1900,10 @@ From [slicc-bios#360](https://github.com/ai-ecoverse/slicc-bios/pull/360) and [s
|
|
|
1815
1900
|
|
|
1816
1901
|
#### 5.16 The link facade (page side, not on the wire): the in-kernel `slicc`
|
|
1817
1902
|
|
|
1818
|
-
How a kernel program in seven reaches [§1.4](#14-a-seven-as-follower-leader-to-leader). This isn't a link stream. It's seven's page serving its own kernel. The page holds the WebRTC; the program never sees the hub, the join state or the peer connection.
|
|
1903
|
+
How a kernel program in seven reaches [§1.4](#14-a-seven-as-follower-leader-to-leader), and this seven's own tray. This isn't a link stream. It's seven's page serving its own kernel. The page holds the WebRTC; the program never sees the hub, the join state or the peer connection.
|
|
1819
1904
|
|
|
1820
1905
|
- **The program:** `@ai-ecoverse/wasi-slicc`, slicc-cli's client command layer built for `GOOS=wasip1` (published by slicc-cli, offered by bios's catalog).
|
|
1821
|
-
- **Commands:** `slicc <join-url> prompt|watch|abort`, with the CLI's own flags, output and `--json` events.
|
|
1906
|
+
- **Commands:** `slicc <join-url> prompt|watch|abort`, with the CLI's own flags, output and `--json` events, and `slicc tray` (below).
|
|
1822
1907
|
- **Anything else** exits 2 with `slicc: <command> isn't available inside seven`. A prompt without a join URL is refused the same way.
|
|
1823
1908
|
- **How it reaches the facade:** through its host module, at `SLICC_LINK_URL` (below).
|
|
1824
1909
|
- **Where it's served:** a page-served kernel facade with the caller's stamp on each connection, mirroring the CDP facade on `127.0.0.1:9222`. The mechanism is [slicc-kernel#526](https://github.com/ai-ecoverse/slicc-kernel/issues/526), shipped in [slicc-kernel 1.90.0](https://github.com/ai-ecoverse/slicc-kernel/tree/v1.90.0#page-facades) ([#556](https://github.com/ai-ecoverse/slicc-kernel/pull/556)):
|
|
@@ -1827,15 +1912,24 @@ How a kernel program in seven reaches [§1.4](#14-a-seven-as-follower-leader-to-
|
|
|
1827
1912
|
- **The port:** reserved from boot, so a program's `bind()` to it fails with `EADDRINUSE`, and kernel-only, so the page's `kernel.dial`, a client's `dial` and `loopbackFetch` are refused with `ECONNREFUSED`.
|
|
1828
1913
|
- **The limit:** bytes from the page wait in the kernel until the program reads them, up to 16 MiB per connection. Past that, the kernel closes the connection, and the hook's `close(reason)` gets `slicc-kernel: more than 16777216 bytes are waiting for the program to read`.
|
|
1829
1914
|
- **The connection:** a raw byte stream on the kernel's loopback, reached at `SLICC_LINK_URL` (`tcp://127.0.0.1:<port>`). The kernel hands the connection and the connector's stamp to the page's hook, as `SO_PEERCRED` would. The program's host module connects with the imports-module API's `ctx.net.connect` (slicc-kernel 1.92.0 or later).
|
|
1830
|
-
- **Framing, one connection per [§5.11](#511-agent-page-cap-agent-node--page-remote-control) stream:** records of `u8 type`, a `u32` BE length, then the payload (as [§5.15](#515-ws-cap-ws-page--node-websockets-for-kernel-programs)), in both directions.
|
|
1915
|
+
- **Framing, one connection per [§5.11](#511-agent-page-cap-agent-node--page-remote-control) stream or `tray` request:** records of `u8 type`, a `u32` BE length, then the payload (as [§5.15](#515-ws-cap-ws-page--node-websockets-for-kernel-programs)), in both directions.
|
|
1831
1916
|
- **`1` control (JSON):**
|
|
1832
1917
|
- the program's first record is `{ "v": 1, "join": "<join-url>", "open": { "kind": "agent", "op": "prompt" | "watch" | "abort", … } }`. `open` is exactly [§5.11](#511-agent-page-cap-agent-node--page-remote-control)'s OPEN, `length` for long prompts included;
|
|
1918
|
+
- or, for this seven's own tray, `{ "v": 1, "open": { "kind": "tray", "op": "status" | "url" | "rotate" | "unlink" | "retry", "id"? } }`, without `join` (below);
|
|
1833
1919
|
- the page answers with `{ "opened": { … } }` ([§5.11](#511-agent-page-cap-agent-node--page-remote-control)'s OPENED) or `{ "reset": "ERRNO: message" }`. After a `reset`, it closes the connection.
|
|
1834
1920
|
- **`2` data:** the stream's DATA, prompt bytes up and NDJSON down.
|
|
1835
1921
|
- **`3` end:** that side's END. The page closes the connection after both ends, or after a `reset`.
|
|
1836
1922
|
- **After the first exchange, the only control record allowed is a `reset`,** at any point: it's that stream's RESET. Any other later control record gets `reset` `EPROTO`.
|
|
1837
1923
|
- **Limits:** a control record is at most 64 KiB. A data record is at most 16 MiB, and senders keep them at 64 KiB or less. Any other type, or an oversized or malformed record, gets a `reset` with `EPROTO`.
|
|
1838
1924
|
- **Ctrl-C inside seven** ends the program with status 130 (the kernel's default), `watch` included. Go on `wasip1` can't catch signals, so `--abort-on-interrupt` isn't offered there.
|
|
1925
|
+
- **The `tray` kind:** this seven's own tray, the leader's view, with no join URL and no link stream behind it. The page serves it from what its Network port already has (`status().links`, `status().exit`, `rotateJoinUrl`, `unlink(id)`, `retryLinks`). One request per connection: the page answers with one control record, `{ "opened": <result> }` or `{ "reset": "ERRNO: message" }`, sends nothing else, and closes; slicc closes after the answer too.
|
|
1926
|
+
- **`status`:** `{ "tray": "idle" | "connecting" | "ready" | "reconnecting" | "failed", "error"?, "hub"?, "exit", "devices": [ … ] }`. `tray` is the tray's state (`idle` when it is off), `error` its last error, `hub` the hub's origin (never the join URL), and `exit` the Network port's current exit (`null`, `{ "kind": "link", "id" }` or `{ "kind": "tailnet", "node" }`). `devices` holds the Network port's devices, live and away, each `{ "id", "name", "kind"?, "host", "address", "mode", "offers", "policy"?, "exit", "state", "access"? }`; a remote device's `id` is its DTLS fingerprint, SHA-256 in lowercase hex without colons, the same id the device's own `slicc` shows. The answer never holds the join URL, `joinCommand`, `followUrl` or the webhook URL.
|
|
1927
|
+
- **`url`:** `{ "url": "<join-url>" }`, or `ENOTCONN` when the tray isn't `ready`. It is the only answer that carries the join URL.
|
|
1928
|
+
- **`rotate`:** `rotateJoinUrl`; `{}`. The new URL isn't in the answer.
|
|
1929
|
+
- **`unlink`:** `unlink(id)` for the device whose `id` equals `"id"` exactly; `{}`, or `ENOENT` when there's none. Matching a prefix is slicc's job.
|
|
1930
|
+
- **`retry`:** `retryLinks`; `{}`.
|
|
1931
|
+
- **Approval:** the page may ask the user to approve `rotate` and `unlink` first, as `sudo` does; a refusal is `EACCES: …`. slicc waits 5 minutes for those, and 1 minute for the others.
|
|
1932
|
+
- **Anything else** (another `op`, a `join` with `kind: "tray"`, a missing `id` for `unlink`) is `EINVAL`. A page without a tray answers every op with `EOPNOTSUPP`.
|
|
1839
1933
|
- **Who may:**
|
|
1840
1934
|
- **only under kernel users `enforce`;**
|
|
1841
1935
|
- **only uid 0 and members of `sudo`** (cones), from the stamp. Scoops are refused;
|
|
@@ -1868,7 +1962,7 @@ How a kernel program in seven reaches [§1.4](#14-a-seven-as-follower-leader-to-
|
|
|
1868
1962
|
- **Ties:** at a full tie, a **user flag beats a default rule**, so `--allow 192.168.0.0/16` opens exactly the range it names. Among the user's own flags, deny wins.
|
|
1869
1963
|
- **Refusal:** `EACCES`.
|
|
1870
1964
|
|
|
1871
|
-
#### 6.3 Remote default (`slicc
|
|
1965
|
+
#### 6.3 Remote default (`slicc <join-url> follow --exit`)
|
|
1872
1966
|
|
|
1873
1967
|
The node allows public unicast and denies:
|
|
1874
1968
|
- loopback: `127.0.0.0/8`, `::1/128`;
|
|
@@ -1878,18 +1972,28 @@ The node allows public unicast and denies:
|
|
|
1878
1972
|
- ULA: `fc00::/7`, which covers `fd7a:115c:a1e0::/48`;
|
|
1879
1973
|
- multicast: `224/4`, `ff00::/8`;
|
|
1880
1974
|
- reserved and documentation ranges: `240/4`, `255.255.255.255/32`, `192.0.0/24`, `192.0.2/24`, `198.18/15`, `198.51.100/24`, `203.0.113/24`, `2001:db8::/32`, `100::/64`;
|
|
1881
|
-
- deprecated IPv4-compatible `::/96` and SIIT `::ffff:0:0:0/96
|
|
1975
|
+
- deprecated IPv4-compatible `::/96` and SIIT `::ffff:0:0:0/96`;
|
|
1976
|
+
- this machine's debugging ports on every address of it ([§6.5](#65-debugging-ports-every-mode)), which no `--allow` reopens.
|
|
1882
1977
|
|
|
1883
1978
|
#### 6.4 Local default (`slicc`, the bridge to the browser it controls)
|
|
1884
1979
|
|
|
1885
1980
|
The remote default, but it **also allows loopback and the private ranges** (`10/8`, `172.16/12`, `192.168/16`, `fc00::/7`).
|
|
1886
1981
|
- **Why:** this is parity with slicc-node, whose `fetch-proxy` reaches the LAN today (a NAS, a dev server on another box).
|
|
1887
1982
|
- **Still denied:** link-local and metadata.
|
|
1888
|
-
- **Except on loopback:** the node denies the
|
|
1983
|
+
- **Except on loopback:** the node denies the debugging ports of [§6.5](#65-debugging-ports-every-mode) and its own listening ports (the kernel listener of [§5.9](#59-kernel-listener-on-the-node-local-only) included). Otherwise a kernel program could reach raw CDP on the host and get around the `cdp` capability.
|
|
1889
1984
|
- It denies them on **every address of this machine** too (interfaces enumerated at check time, cached briefly), in case something listens on `0.0.0.0`.
|
|
1890
1985
|
- Browsers the node launches bind their debugging port to loopback only, and so do the node's own listeners.
|
|
1891
1986
|
- **Opting out:** `--deny 127.0.0.0/8` and `--deny 192.168.0.0/16` restore the remote default.
|
|
1892
1987
|
|
|
1988
|
+
#### 6.5 Debugging ports (every mode)
|
|
1989
|
+
|
|
1990
|
+
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:
|
|
1991
|
+
- **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.
|
|
1992
|
+
- **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 files are read again then, and the process list when it is more than 1 s old. So a browser that starts later is refused before its first connection.
|
|
1993
|
+
- **Above every rule:** no `--allow` reopens one, `--allow 127.0.0.1:9222` included. A refusal is `EACCES`, reason `a browser debugging port`.
|
|
1994
|
+
- **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.
|
|
1995
|
+
- **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.
|
|
1996
|
+
|
|
1893
1997
|
### 7. Reference peer for seven's CI
|
|
1894
1998
|
|
|
1895
1999
|
The node is the reference. Two setup modes exist for tests:
|
|
@@ -1906,7 +2010,7 @@ CI installs a pinned release: `go install github.com/ai-ecoverse/slicc-cli/cmd/s
|
|
|
1906
2010
|
|
|
1907
2011
|
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`.
|
|
1908
2012
|
|
|
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, 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. 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). The [guard vectors](https://github.com/ai-ecoverse/slicc-browsers#guard-vectors) (`TestCDPProtectVectorsPrompt` and `TestCDPProtectVectorsLagging`, one per detach variant) run on Linux there, and on every OS in the full matrix: the 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. **Runners.** CI's first job, `runner`, picks where the Linux jobs run with `.github/runner.mjs`: `cloud-run-job
|
|
2013
|
+
**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, 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. That full matrix, all with `-race`, runs on pushes to `main`, which is the run the release waits for, so nothing ships without it. 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 full matrices. 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). The [guard vectors](https://github.com/ai-ecoverse/slicc-browsers#guard-vectors) (`TestCDPProtectVectorsPrompt` and `TestCDPProtectVectorsLagging`, one per detach variant) run on Linux there, and on every OS in the full matrix: the 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. **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, 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 and the `fresh` check run on `vars.CERT_LIGHT_RUNNER`, 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.
|
|
1910
2014
|
|
|
1911
2015
|
**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.
|
|
1912
2016
|
|
|
@@ -1919,6 +2023,8 @@ Front ends and capabilities follow a node's links with `node.Node.Subscribe(func
|
|
|
1919
2023
|
|
|
1920
2024
|
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.
|
|
1921
2025
|
|
|
2026
|
+
**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 at once, and that `--expose-debug-port` opens only what it names, without ever touching 9222–9229 on the machine they run on.
|
|
2027
|
+
|
|
1922
2028
|
**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).
|
|
1923
2029
|
|
|
1924
2030
|
**Port guard.** On CI (`CI` set), or with `SLICC_TEST_PORT_GUARD=1`, `TestMain` holds `127.0.0.1:9222` to `9229` (and `[::1]` where it can) for the whole run, and the run fails if anything connects to them, other than the one test that lends a port to its own fake browser. On CI a port it can't hold fails the run. Off CI it is off by default, so a developer's own Chrome on those ports is never touched; the guarded tests then skip.
|
|
@@ -1941,23 +2047,56 @@ The real-Chrome tests in `test/integration/` launch headless, so they run the ex
|
|
|
1941
2047
|
|
|
1942
2048
|
## Releases
|
|
1943
2049
|
|
|
1944
|
-
PRs merge through the merge queue. Only a pull request's newer push cancels its older CI run
|
|
2050
|
+
PRs merge through the merge queue. Only a pull request's newer push cancels its older CI run. Pushes to `main` share one concurrency group (`ci-push-refs/heads/main`) without `cancel-in-progress`: the running push run always finishes, and a newer push replaces the one still pending, which is cancelled and releases nothing. The newer run releases instead, and since a release takes every commit since the last tag, it still includes the replaced run's commits. Merge-queue runs each have their own group, per queue ref, and are never cancelled or replaced by another run. 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`.
|
|
1945
2051
|
|
|
1946
2052
|
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 manual run instead waits in the `ci` job for the CI workflow's `push` run on the same commit, and fails unless it passed. A pull request's dry run doesn't wait for CI; it runs alongside it. 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:
|
|
1947
2053
|
1. `scripts/release-plan.mjs` asks semantic-release (dry run) for the next version, and stops when there is none.
|
|
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.
|
|
2054
|
+
2. `scripts/package.sh <version>` builds `darwin`, `linux` and `windows` × `amd64`, `arm64` with `CGO_ENABLED=0`, `-trimpath`, `-buildvcs=false` and `-X main.version=<version>`, and `slicc-wasip1-wasm.wasm` (`GOOS=wasip1 GOARCH=wasm`) the same way, with the Go toolchain `go.mod` names (see [Reproducing a release](#reproducing-a-release)). 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.
|
|
1949
2055
|
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.
|
|
1950
2056
|
4. `actions/attest-build-provenance` attests every binary, the `.wasm` included, through `SHA256SUMS`.
|
|
1951
2057
|
5. semantic-release checks the version again (`dist/VERSION`, and `scripts/go-major.mjs`, see below), tags, creates the GitHub release with every binary and `SHA256SUMS`, then `scripts/npm-publish.mjs` publishes the platform packages and the launcher through npm trusted publishing (OIDC, with provenance), and then `sliccy` with `--tag next`. Versions already on npm are skipped. Before publishing `sliccy`, the script checks that every exact-pinned `optionalDependencies` version is on the registry, waits up to `SLICC_REGISTRY_WAIT_SECONDS` (default 900) for freshly published ones to appear, and fails otherwise, so a `sliccy` version never points at missing platform packages.
|
|
1952
2058
|
6. `scripts/go-warm.sh <version>`, once the tag is on `origin`, asks `proxy.golang.org` for the module at `v<version>` (`go list -m` with a scratch module cache) and asks `pkg.go.dev` to fetch it. The proxy fetch is retried briefly; the `pkg.go.dev` fetch is one attempt of at most 10 s, since the proxy fetch already gets the version indexed. Both only warn, and the step can't fail the release.
|
|
1953
2059
|
|
|
2060
|
+
**Certification in the pull request** (`.github/workflows/cert-review-pr.yml`, `tools/cert-review/`). Every same-repo pull request gets the commit status `cert-review` on its head, which the merge queue requires beside `ci`. Fork pull requests get none: only Lars merges one. Every tool runs from the pull request's base commit, main's copy, never from the code under test, which is only built and tested; the review uses `refs/pull/N/merge` and records its first parent and the head.
|
|
2061
|
+
- **plan** (light runner) runs on opened, synchronize, reopened, and `merge-ok` or `cert-review-approved` being added (or the approval removed), and plans the whole run again, since such an event cancels a run in progress; other labels neither start nor cancel anything. A `pending` status left on the head by a run that ended without a result becomes `error` (re-run the failed jobs), so the check never waits forever. The shared self-guard (`cert-self-guard`, pinned) holds a change to the reviewer's own paths (`REVIEWER_GLOBS` in `tools/cert-review/lib.mjs`: all of `.github/**`, every workflow and action, so no pull request's own workflow can post `cert-review`, and `tools/cert-review/**`) until a coordinator puts `cert-review-approved` on that head. Such pull requests always go to the coordinator before queueing.
|
|
2062
|
+
- **build** (hosted `ubuntu-latest`, no secrets) builds the merge commit and its first parent the release's way, each in a full clone with its tags: `SLICC_SIGN=0 scripts/package.sh <v>`, `npm-stage.mjs` for every package, and `npm pack`, with one version `X.Y.(Z+1)-pr.N.<run>` on both sides. `outputs.mjs` hashes every release file and tarball.
|
|
2063
|
+
- **classify** decides by output where the build can see it: a pull request is behaviour when a binary, the wasm, `SHA256SUMS` or an npm tarball differs from main's build. Paths whose effect the unsigned build never shows are behaviour whatever it outputs: `scripts/**` (signing and notarization, publishing, the release plan, `go-*`, `sliccy-next.mjs`), `install.sh`, `npm/**`, `package.json` and `package-lock.json` (they choose the semantic-release code the release runs), `.releaserc*`, and the reviewer's paths. Otherwise the status is `not behaviour: identical build outputs` (success), so a docs or test change that ships nothing new needs nothing more. Renovate puts `merge-ok` on its pull requests (`renovate.json`), so a dev-pin bump is reviewed by itself and merges on green. A behaviour change without `merge-ok` is `waits for merge-ok to be reviewed` (failure); with it, the review starts.
|
|
2064
|
+
- **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.
|
|
2065
|
+
- **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.
|
|
2066
|
+
- **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.
|
|
2067
|
+
- **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.
|
|
2068
|
+
- **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.
|
|
2069
|
+
- **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`.
|
|
2070
|
+
- **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.
|
|
2071
|
+
|
|
2072
|
+
**Verify on npm** (`.github/workflows/cert-verify.yml`, `tools/cert-review/`). `release.yml` stays plain semantic-release. When a `Release` run on `main` succeeds, `cert-verify` takes the `v*` tag on the commit it built (or the version given to a manual run) and checks it, code only, with no secret and no `id-token`, in a concurrency group of its own, so the next release never waits for it:
|
|
2073
|
+
- `release / reproduce from the tag` (`vars.CERT_RUNNER`, no token): [the recipe](#reproducing-a-release) on a `git archive` of `v<version>` on Linux. Every `linux`, `windows` and `.wasm` asset and `install.sh` must match the release's `SHA256SUMS` line, built on macOS.
|
|
2074
|
+
- `release / darwin signature and notarization` (hosted `macos-latest`) checks both darwin binaries:
|
|
2075
|
+
- codesign first: `codesign -dv` (identifier `ai.sliccy.slicc`, a `Developer ID Application` authority, team `vars.CERT_APPLE_TEAM`, default `S8LB56P782`, the hardened runtime) and `codesign --verify --strict`. A failure there is FAIL whatever Gatekeeper says, and a codesign that didn't finish is INCOMPLETE.
|
|
2076
|
+
- On a codesign pass, `spctl -a -vv -t install` asks Gatekeeper online. Its first answer for a new binary is sometimes `Unnotarized`, so it asks up to three times with a growing pause and keeps every answer in the evidence. It passes only on `source=Notarized Developer ID`.
|
|
2077
|
+
- `release / verify on npm` (`vars.CERT_LIGHT_RUNNER`, read-only) covers each package a publish variable lets out: `@ai-ecoverse/slicc`, its six platform packages, `@ai-ecoverse/wasi-slicc` and `sliccy@7.0.0-next.<version>`. For each, it:
|
|
2078
|
+
- waits up to an hour for npm;
|
|
2079
|
+
- checks `dist.integrity` against the registry tarball;
|
|
2080
|
+
- checks the SLSA provenance: subject = the tarball's sha512, `release.yml` at `refs/heads/main`, a GitHub-hosted builder, `gitCommit` = `gitHead` = the tag's commit, which is on `main`;
|
|
2081
|
+
- checks that each platform package's binary and `wasi-slicc`'s `bin/slicc.wasm` are the release's assets, and that `@ai-ecoverse/slicc` and `sliccy` pin this version's platform packages;
|
|
2082
|
+
- then runs `npm audit signatures`.
|
|
2083
|
+
|
|
2084
|
+
On GitHub, the release must have exactly the eight assets and `SHA256SUMS`, each must re-hash to its line, and `gh attestation verify` must accept each one for `release.yml` at `refs/heads/main`. `get.sliccy.ai/slicc` must redirect to `releases/latest/download/install.sh`, whose bytes are this release's while it is the latest. The job also reads the rebuild and darwin evidence and the release notes. A mismatch anywhere, or notes that already certify another sha, is `supply-chain alert: …`. Missing evidence (no rebuild or darwin result, a check that didn't finish) is `cert-ci: INCOMPLETE …`. Its record (`verify.json`) is written last, with the final state.
|
|
2085
|
+
- `release / write the certified lines` (hosted `ubuntu-latest`, the only job with `contents: write`, after a verify that passed) does only the notes edit, from the lines verify handed over. Between `<!-- cert-verify -->` markers it writes one line per package and one for the binaries:
|
|
2086
|
+
- `certified <package>@<version> sha256=<the registry tarball's sha256> (covers …; ci: …; pr-certified: …; known: …)`;
|
|
2087
|
+
- `certified slicc-cli@<version> binaries SHA256SUMS sha256=<the SHA256SUMS file's sha256> (…)`.
|
|
2088
|
+
|
|
2089
|
+
`pr-certified` lists each pull request since the previous tag with its `cert-review` status on its head. When one of them has none (a pull request from before the check was required), the lines start with `verified` instead of `certified`, since they then cover only the supply chain; readers take only `certified` as a cert. A `verified` line with the same sha counts as written. Notes that already carry the same lines stay unchanged, and lines in the block for packages outside this run's set are kept. Another sha writes nothing and fails with a `supply-chain alert`.
|
|
2090
|
+
|
|
1954
2091
|
**Go module versions.** In Go a pushed tag is the release: `proxy.golang.org` caches it, `sum.golang.org` records its hash for good, and `pkg.go.dev` indexes it.
|
|
1955
2092
|
- **Never move or delete a pushed tag.** The checksum database would then reject that version for every user. A bad version gets a `retract` directive in `go.mod`, and a new release.
|
|
1956
2093
|
- **Major versions.** A module at v2 or above needs `/vN` at the end of its module path, or `go install …@latest` never sees it. `scripts/go-major.mjs <version>` fails the release when the version's major doesn't fit the module path in `go.mod` (no suffix allows v0 and v1, `/vN` allows only vN). `release-plan.mjs` runs the same check, so it fails before the build. A `BREAKING CHANGE` commit therefore needs a deliberate module-path change first.
|
|
1957
2094
|
|
|
2095
|
+
**Gate verdicts.** A gate's silence never reads as a pass. Each script that gates a merge or a release ends a pass with a `<name>: PASS …` line: `test-shard: PASS linux 1/2 ran 143 tests`, `go-major: PASS 1.4.0 (major 1) fits module path …`, `release-plan: PASS release 1.4.0` (or `no release due`), `npm-publish: PASS latest: published 7, 1 already published, 0 held by their gates, of 8 packages`, `sliccy-next: PASS sliccy@7.0.0-next.1.4.0`, and `install-check: PASS all installer checks passed`. Every caller in `ci.yml`, `release.yml` and `.releaserc.json` runs the gate through `sh scripts/verdict.sh <name> <command…>`, which fails with the gate's exit code when it fails, and fails when it exits 0 without its `<name>: PASS` line, so an early exit, a skipped main block or empty output fails the step. `test-shard.mjs` also fails a shard that selects no tests, or whose `go test -v` runs none, and `npm-publish.mjs` fails on a directory with no packages. The `fresh` job sets `verdict` to `current`, `stale`, or `unknown` when `git ls-remote` can't read `main` (`fresh: PASS unknown (semantic-release decides)`, which refuses a release behind the remote anyway); a release after CI needs `current` or `unknown`, so a missing verdict releases nothing. The scripts find their main block with `realpath` on both `process.argv[1]` and the module's own path (`scripts/is-main.mjs`), so they also run through a symlinked checkout or script.
|
|
2096
|
+
|
|
1958
2097
|
**Recovering `sliccy@next`.** When a release tagged and published its platform packages but `sliccy` didn't go out (a registry that lagged past the wait, say), `gh workflow run release.yml --ref main -f sliccy_next_only=true` publishes it without a new release. On `main` only, that run skips the release job and runs `scripts/sliccy-next.mjs`. The script takes the latest `vX.Y.Z` tag reachable from `HEAD`, stages `sliccy@7.0.0-next.<version>` with the launcher (`npm/slicc.js`) from that tag and exact `optionalDependencies` on that version's platform packages, and publishes it through `npm-publish.mjs` with `--tag next`. The same guards apply: the `SLICCY_NEXT_PUBLISH` gate, the tag and version checks, the registry check, and a version that is already published is skipped. Only the latest release can be recovered, so `next` never moves backwards.
|
|
1959
2098
|
|
|
1960
|
-
Every pull request's CI checks the packaging in its `installer-assets` job: an unsigned build of all six targets, the npm packages and `sliccy` (with `--tag next`) packed with `--dry-run`, and `scripts/sliccy-next.mjs --dry-run` (the recovery path against the latest real release, registry check included). The `installer` jobs then run `install.sh` against those assets with curl on Linux and with wget on Alpine; the macOS curl run is left out of pull requests and the merge queue, so a queue group needs only one macOS runner, and runs on every push to `main`. A pull request that changes `release.yml`, `.releaserc.json`, or a script only the release runs (`scripts/package.sh`, `scripts/npm-*.mjs`, `scripts/release-plan.mjs`, `scripts/sliccy-next.mjs`, `scripts/go
|
|
2099
|
+
Every pull request's CI checks the packaging in its `installer-assets` job: an unsigned build of all six targets, the npm packages and `sliccy` (with `--tag next`) packed with `--dry-run`, and `scripts/sliccy-next.mjs --dry-run` (the recovery path against the latest real release, registry check included). The `installer` jobs then run `install.sh` against those assets with curl on Linux and with wget on Alpine; the macOS curl run is left out of pull requests and the merge queue, so a queue group needs only one macOS runner, and runs on every push to `main`. A pull request that changes `release.yml`, `.releaserc.json`, or a script only the release runs (`scripts/package.sh`, `scripts/npm-*.mjs`, `scripts/release-plan.mjs`, `scripts/sliccy-next.mjs`, `scripts/go-*`, `scripts/is-main.mjs`, `scripts/verdict.sh`) also runs the release workflow as a dry run: the same checks on the release's macOS runner, and each binary run with `--version` on its own OS and CPU.
|
|
1961
2100
|
|
|
1962
2101
|
The workflow reads these repository secrets. Each group is optional: without the first the darwin binaries stay unsigned, without the second they are signed but not notarized:
|
|
1963
2102
|
- macOS signing: `APPLE_CERTIFICATE_BASE64`, `APPLE_DEVELOPER_CERTIFICATE_PASSWORD`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.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.
|
|
22
|
-
"@ai-ecoverse/slicc-darwin-amd64": "1.
|
|
23
|
-
"@ai-ecoverse/slicc-linux-arm64": "1.
|
|
24
|
-
"@ai-ecoverse/slicc-linux-amd64": "1.
|
|
25
|
-
"@ai-ecoverse/slicc-windows-arm64": "1.
|
|
26
|
-
"@ai-ecoverse/slicc-windows-amd64": "1.
|
|
27
|
-
"@ai-ecoverse/slicc-jerk": "^0.
|
|
21
|
+
"@ai-ecoverse/slicc-darwin-arm64": "1.44.0",
|
|
22
|
+
"@ai-ecoverse/slicc-darwin-amd64": "1.44.0",
|
|
23
|
+
"@ai-ecoverse/slicc-linux-arm64": "1.44.0",
|
|
24
|
+
"@ai-ecoverse/slicc-linux-amd64": "1.44.0",
|
|
25
|
+
"@ai-ecoverse/slicc-windows-arm64": "1.44.0",
|
|
26
|
+
"@ai-ecoverse/slicc-windows-amd64": "1.44.0",
|
|
27
|
+
"@ai-ecoverse/slicc-jerk": "^1.0.0"
|
|
28
28
|
}
|
|
29
29
|
}
|