@ai-ecoverse/slicc-bios 1.1.1 → 1.2.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
CHANGED
|
@@ -244,7 +244,7 @@ When no host can drive a browser, `/json/list` and the WebSocket handshake answe
|
|
|
244
244
|
|
|
245
245
|
### Tailscale (proof of concept)
|
|
246
246
|
|
|
247
|
-
seven can also join a tailnet, with a patched build of Tailscale's wasm client ([`@tailscale/connect`](https://www.npmjs.com/package/@tailscale/connect)) that adds a full HTTP `fetch`, raw `dial` (TCP and UDP, available to the page as `sliccTailscale.dial`), exit-node selection and status. It runs in its own module worker, `os/tailscale-worker.js`. No CORS applies, because Go makes the requests itself over the tailnet, and its relays (DERP) carry the traffic over WebSocket.
|
|
247
|
+
seven can also join a tailnet, with a patched build of Tailscale's wasm client ([`@tailscale/connect`](https://www.npmjs.com/package/@tailscale/connect)) that adds a full HTTP `fetch`, raw `dial` (TCP and UDP, available to the page as `sliccTailscale.dial`), WebSockets, exit-node selection and status. It runs in its own module worker, `os/tailscale-worker.js`. No CORS applies, because Go makes the requests itself over the tailnet, and its relays (DERP) carry the traffic over WebSocket.
|
|
248
248
|
|
|
249
249
|
- **Turning it on:** the Network panel's **Use Tailscale** switch (spectrum ≥ 1.38.0) starts it at once and remembers the choice. Opening seven with `#tailscale=on` or `#tailscale=off` does the same. The setting, the exit node and the node's state are kept in IndexedDB (`slicc-os`, store `transport`), so the node keeps its identity across reloads.
|
|
250
250
|
- **The Network panel** (`os/network.js` passes `os/tailscale.js`'s panel through as `NetworkStatus.tailnet` and the port's `setTailnet`, `setExitNode`, `submitAuthKey` and `logoutTailnet`) shows:
|
|
@@ -257,7 +257,7 @@ seven can also join a tailnet, with a patched build of Tailscale's wasm client (
|
|
|
257
257
|
`document.documentElement.dataset.tailscale` shows the backend state, or `off`. While an exit node is in use, the panel's Connection block reports `route: 'tailnet'` (spectrum ≥ 1.41.0), with health `ok` and a note that this computer's own services still go through the base route. Without one, it reports the base route.
|
|
258
258
|
- **Joining:** **Sign in to Tailscale** opens Tailscale's login in a new tab. Or paste an auth key into the panel's field. A key is held in memory for that one login and is never stored, put in a URL or logged. An auth key in the fragment is dropped with a warning. The integration test hands its key to the page in `globalThis.sliccTailscaleAuthKey` through an init script, and the page takes it once and deletes it. The tailnet object is exposed as `globalThis.sliccTailscale` only when an init script sets `globalThis.sliccTailscaleDebug`, as the integration test does.
|
|
259
259
|
- **Nothing inbound:** the node runs with shields up, so tailnet peers can't open connections to it. The build has no peerapi server, and the page listens on nothing. Peers can't reach the kernel's services, `*.kernel.localhost` or the CDP facade.
|
|
260
|
-
- **Where the wasm comes from:** shown as **Tailscale (/opt/tailscale)** in [Install / Update](#install--update). [`@ai-ecoverse/wasm-tailscale`](https://www.npmjs.com/package/@ai-ecoverse/wasm-tailscale) (built in [ai-ecoverse/wasm-tailscale](https://github.com/ai-ecoverse/wasm-tailscale), certified, 1.104.1-
|
|
260
|
+
- **Where the wasm comes from:** shown as **Tailscale (/opt/tailscale)** in [Install / Update](#install--update). [`@ai-ecoverse/wasm-tailscale`](https://www.npmjs.com/package/@ai-ecoverse/wasm-tailscale) (built in [ai-ecoverse/wasm-tailscale](https://github.com/ai-ecoverse/wasm-tailscale), certified, 1.104.1-9) is a pnpm project of its own, [`src/packages/tailscale/`](src/packages/tailscale/), installed like the grammars, never at boot. The first time Tailscale is turned on, `os/tailscale.js` writes the deployed manifest and lockfile to `/opt/tailscale` and runs `pnpm install --frozen-lockfile --trust-lockfile` there under the Web Lock `slicc-tailscale`. The panel shows the download progress. The lockfile is recorded in `var/lib/slicc/tailscale/pnpm-lock.yaml`, so later starts skip the install, and a new deployed lockfile installs the new version on the next start. The page then reads `main.wasm` and `wasm_exec.js` through the kernel. A host folder mounted at `/mnt/tailscale`, or a folder named in the stored setting's `from`, (`npx @ai-ecoverse/slicc-node --mount <dist>:tailscale:ro`, then `mount -t hostfs tailscale /mnt/tailscale`) wins over the package, for trying a local build.
|
|
261
261
|
- **Routing:** the kernel keeps the transport [picked above](#network), and Tailscale is layered over it. While an exit node is in use, every request goes through the tailnet, except what [never goes out](#network) (loopback keeps `host.slicc.internal` on the local proxy). Without one, only tailnet addresses (`100.64.0.0/10`, `fd7a:115c:a1e0::/48`) and MagicDNS names go there.
|
|
262
262
|
- **Exits:** `os/exits.js` routes traffic over any number of exits: Tailscale, and linked devices.
|
|
263
263
|
- **Linked devices:** `os/link.js` links a `slicc` CLI to the page over WebRTC, as one exit per link. It speaks the [slicc link protocol](https://github.com/ai-ecoverse/slicc-cli): slicc-node's tunnel frames over data channels, with one channel per stream and a 256 KiB credit window per stream and direction.
|
|
@@ -298,12 +298,12 @@ seven can also join a tailnet, with a patched build of Tailscale's wasm client (
|
|
|
298
298
|
|
|
299
299
|
`traits` is read fresh each time, and every change to the exits (a link coming up or going, the tray, Tailscale, a new choice) calls `kernel.updateTransportTraits()` (slicc-kernel ≥ 1.80.0), so connected clients such as the agent get the new value through `onTraits`. Routing never depends on it: every request still goes through the router. With `globalThis.sliccKernelDebug` set by an init script, `globalThis.sliccKernelClient()` attaches a kernel client, as the integration test does.
|
|
300
300
|
- **The host machine:** the kernel sends `host.slicc.internal:<port>` to the transport as `127.0.0.1:<port>`, and that goes to the CLI linked on this computer (its `http` streams, under its policy), so a dev server on the host is reachable without CORS or Chrome's Local Network Access prompt. This holds even under Directly, since it isn't internet traffic. A remote device's loopback is never used for it, and without a local CLI it stays on the page's transport.
|
|
301
|
-
- **WebSockets (`ws`):** a program's WebSocket (the kernel proxy's `Upgrade: websocket`, or `ctx.websocket`) goes to the exit that would carry that host's HTTP
|
|
301
|
+
- **WebSockets (`ws`):** a program's WebSocket (the kernel proxy's `Upgrade: websocket`, or `ctx.websocket`) goes to the exit that would carry that host's HTTP. A linked CLI with cap `ws` carries it as `OPEN { kind: 'ws', url, protocols, headers }` (`os/link-ws.js`). The Tailscale exit carries it too, for tailnet names and addresses and, with an exit node in use, for public hosts: `os/tailscale.js` asks the worker for wasm-tailscale's `ipn.websocket()` (Go's WebSocket over the tailnet, so no browser cookies), over a `MessagePort` of its own per socket, and the transport's traits name `ws` in the tailnet exit's caps. The same limits, close codes and placeholder refusal apply. The handshake gets 30 s (the stored setting's `handshakeMs` changes it, for tests); after that, or when the program goes away first, the page aborts the worker's `AbortSignal` and the program sees `502`.
|
|
302
302
|
- **The handshake:** the CLI dials it under its policy, with the system's TLS roots. OPENED answers `{ protocol }` with the subprotocol the far end chose, and a refusal is a RESET, which the program sees as `502`.
|
|
303
303
|
- **Messages:** both ways, each one is a record: a type byte (`1` text, `2` binary, `8` close), a big-endian `u32` length, then the payload. A close carries its code (`u16`) and reason. Messages are at most 64 MiB either way (a larger one closes with `1009`; a program's `send` of one also fails with `EMSGSIZE`), and at most 16 MiB wait unread before the socket closes with `1009` and drops what's queued. A close reason is cut to 123 bytes at a character boundary. Pings stay between the CLI and the far end.
|
|
304
|
-
- **When nothing carries it:** an exit without WebSockets (a
|
|
304
|
+
- **When nothing carries it:** an exit without WebSockets (a CLI without cap `ws`) answers `502`. A held exit waits as HTTP does.
|
|
305
305
|
- **No secrets yet:** a handshake whose URL or headers carry a vault placeholder (`fnox_proxy_…`) is refused (`EACCES`, "secrets in WebSocket handshakes aren't supported yet"), so the placeholder never leaves seven.
|
|
306
|
-
- **Never through the page:** seven never opens a browser WebSocket itself. Unlike `fetch`, a browser WebSocket can't leave out the user's cookies for the site it reaches, so a program could act as the user there. With **Directly**, or no exit, a WebSocket is `501` with a hint to link a slicc CLI.
|
|
306
|
+
- **Never through the page:** seven never opens a browser WebSocket itself. Unlike `fetch`, a browser WebSocket can't leave out the user's cookies for the site it reaches, so a program could act as the user there. With **Directly**, or no exit, a WebSocket is `501` with a hint to link a slicc CLI or use a Tailscale exit node.
|
|
307
307
|
- **Network panel:** `status().links.devices` lists the linked devices (name, `<host>.slicc.internal`, address, mode, what they offer, the CLI's policy summary, whether they can be the exit, and their state), `status().exit` is the current choice, and `unlink(id)` drops a device. On `*.sliccy.ai` the panel also has the join URL, `rotateJoinUrl` and `retryLinks`.
|
|
308
308
|
- **Remote devices (the tray hub):** on `*.sliccy.ai`, `os/tray.js` makes seven a **tray leader** on SLICC's tray hub (`slicc-tray-hub`), through the sliccy-ai worker's same-origin proxy for `POST /tray`, `/controller/<token>` and the leader WebSocket, which drops cookies. The hub mints the join URL on `www.sliccy.ai`, and the panel shows `npx sliccy@next <join-url> follow` (`@next` until `sliccy`'s `latest` is the new CLI; `latest` is still six's). Followers then link the same way as a local CLI: seven offers, and the offer, answer and candidates go over the hub's bootstrap messages. TURN comes from the hub.
|
|
309
309
|
- **Who may link:** only followers with runtime `slicc-link/1` and full trust. A device **is its DTLS certificate**: its id is the SHA-256 fingerprint in its answer. The CLI keeps one certificate per hub and mints a new `controllerId` for every connection, which is only a routing id. A device that reconnects replaces its old link and gets its address back, and a new certificate is a new device. Known devices (name, label, address, what they offer) are kept in `localStorage` (`slicc-os.devices`), keyed by fingerprint, so a device that's away shows as reconnecting. The exit choice is keyed the same way.
|
|
@@ -335,7 +335,7 @@ A server running inside the kernel (vite, `python -m http.server`, impeccable `l
|
|
|
335
335
|
|
|
336
336
|
- **Testing on a tailnet of its own:** `test/integration/tailnet.mjs` starts one on this machine with no account, sign-in or key from anyone:
|
|
337
337
|
- [headscale](https://github.com/juanfont/headscale) with its embedded DERP over plain HTTP, behind a small CORS proxy for the browser's control requests;
|
|
338
|
-
- two userspace `tailscaled` nodes, `peer` (a web page
|
|
338
|
+
- two userspace `tailscaled` nodes, `peer` (a web page, a half-close echo, a `ws` echo server and a port that accepts and never answers) and `exit` (an approved exit node).
|
|
339
339
|
|
|
340
340
|
The stage B integration test then joins seven with a headscale pre-auth key (`hskey-…`, handed over in `globalThis.sliccTailscaleAuthKey`). The stored setting `derpOverHttp` makes the wasm speak DERP over `ws://`, and is only for this. The test checks:
|
|
341
341
|
- curl over raw TCP to the peer, by address and by MagicDNS name;
|
|
@@ -345,6 +345,8 @@ A server running inside the kernel (vite, `python -m http.server`, impeccable `l
|
|
|
345
345
|
- no inbound connections on 80, 5710 or 9222;
|
|
346
346
|
- public addresses unreachable without an exit node, and reachable by address and by name with one.
|
|
347
347
|
|
|
348
|
+
A second test on its own tailnet sends kernel `bash`'s WebSocket through the proxy (`/dev/tcp/127.0.0.1/3128`) and the Tailscale exit to the peer's echo server (text, binary, the subprotocol and a close from each end), sees a handshake to the silent port answered `502` after a 5 s `handshakeMs`, and sees a vault placeholder in the URL or a header refused before anything reaches the peer.
|
|
349
|
+
|
|
348
350
|
To run it: `HEADSCALE=… TAILSCALED=… TAILSCALE=… TAILSCALE_E2E=1 npm test -- --test-name-pattern 'stage B'` (`TS_SHOTS=<dir>` also saves screenshots of the Network panel and the Install / Update row).
|
|
349
351
|
- **Plain `localhost` and `127.0.0.1` stay the real machine.** The service worker passes them through untouched. Inside the kernel it's the other way round: `localhost` is the kernel loopback, and `host.slicc.internal` reaches the machine. The kernel exports the page-visible name as `SLICC_PAGE_LOOPBACK`, so tools can print the right URL.
|
|
350
352
|
- **Why the port goes in the name.** `kernel.localhost:8400` would resolve to the machine's real port 8400 whenever the service worker doesn't answer, as for a navigation or a WebSocket, and reach the wrong server. `8400.kernel.localhost` lands on port 80, which is closed or answered by [slicc-node](https://github.com/ai-ecoverse/slicc-node)'s kernel tunnel, and it gives every kernel port its own origin. The service worker routes by the name alone and ignores the URL's port.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-bios",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Slim loader that boots SLICC into OPFS and serves the UI from there",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@ai-ecoverse/slicc": "1.40.0",
|
|
46
|
-
"@ai-ecoverse/slicc-browsers": "1.7.
|
|
46
|
+
"@ai-ecoverse/slicc-browsers": "1.7.1",
|
|
47
47
|
"@ai-ecoverse/slicc-node": "2.5.1",
|
|
48
|
-
"@ai-ecoverse/slicc-shared-web": "1.
|
|
48
|
+
"@ai-ecoverse/slicc-shared-web": "1.20.0",
|
|
49
49
|
"@biomejs/biome": "2.5.15",
|
|
50
50
|
"es-module-lexer": "3.0.3",
|
|
51
51
|
"fake-indexeddb": "6.2.5",
|
package/src/packages/certs.json
CHANGED
|
@@ -422,6 +422,10 @@
|
|
|
422
422
|
"integrity": "sha512-F8H2HVuDswpmFq2tfqabKDQlzZ8xmWpNUDinUIWplixiz+SCKwA0uE4YJIAHdw+lGJP1AEVdHHTFtcroGGv1Pw==",
|
|
423
423
|
"sha256": "23e2b35274385c6b82cfe0b6fb140af4a15207902f97b1ed280d1b9543bcc4e4"
|
|
424
424
|
},
|
|
425
|
+
"@ai-ecoverse/wasm-tailscale@1.104.1-9": {
|
|
426
|
+
"integrity": "sha512-ZweOFby3+PerJJGVGnPUnQDia44N5UJfn6d2/xxnotme++++Bmki/qYQqI1YWUF+pSrO4crSOsMwXk1FVcjtNw==",
|
|
427
|
+
"sha256": "b4f99e62bc0fd6187e3f9f22df9544d6181414a2447209dce504dee9a4b4ddfe"
|
|
428
|
+
},
|
|
425
429
|
"@ai-ecoverse/wasm-tar@1.35.0-4": {
|
|
426
430
|
"integrity": "sha512-eCZROUZ8crzsEbdL1i5lm8I8Fgup3afj3ZW9gvN9GGV5pnFae8KZhmSlszwSFPMNgA4UTBu46mfTEF0pPmkOQA==",
|
|
427
431
|
"sha256": "311d1eb6b8db7b482f3650f6ee7f4efa9af6d3f4297738e9c5cd0dc2f9630d54"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"": {
|
|
7
7
|
"name": "slicc-bios-packages",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@ai-ecoverse/slicc-browsers": "1.7.
|
|
9
|
+
"@ai-ecoverse/slicc-browsers": "1.7.1",
|
|
10
10
|
"@ai-ecoverse/slicc-cdp": "1.11.2",
|
|
11
11
|
"@ai-ecoverse/slicc-clipboard": "1.0.2",
|
|
12
12
|
"@ai-ecoverse/slicc-kernel": "1.81.0",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
"node_modules/@ai-ecoverse/slicc-browsers": {
|
|
40
|
-
"version": "1.7.
|
|
41
|
-
"resolved": "https://registry.npmjs.org/@ai-ecoverse/slicc-browsers/-/slicc-browsers-1.7.
|
|
42
|
-
"integrity": "sha512-
|
|
40
|
+
"version": "1.7.1",
|
|
41
|
+
"resolved": "https://registry.npmjs.org/@ai-ecoverse/slicc-browsers/-/slicc-browsers-1.7.1.tgz",
|
|
42
|
+
"integrity": "sha512-35vGFMbZrl33ZQ9/VgjwZDBXp0RP87mKY0hnIG31tW5Nt5hh+icdp+3Y/t1kypoENAre+jnVEDa9W/H+yK6zyA==",
|
|
43
43
|
"license": "Apache-2.0",
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=24"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"description": "Packages the BIOS installs from npm into OPFS, pinned by package-lock.json and pnpm-lock.yaml",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@ai-ecoverse/slicc-browsers": "1.7.
|
|
6
|
+
"@ai-ecoverse/slicc-browsers": "1.7.1",
|
|
7
7
|
"@ai-ecoverse/slicc-cdp": "1.11.2",
|
|
8
8
|
"@ai-ecoverse/slicc-clipboard": "1.0.2",
|
|
9
9
|
"@ai-ecoverse/slicc-kernel": "1.81.0",
|
|
@@ -9,8 +9,8 @@ importers:
|
|
|
9
9
|
.:
|
|
10
10
|
dependencies:
|
|
11
11
|
'@ai-ecoverse/slicc-browsers':
|
|
12
|
-
specifier: 1.7.
|
|
13
|
-
version: 1.7.
|
|
12
|
+
specifier: 1.7.1
|
|
13
|
+
version: 1.7.1
|
|
14
14
|
'@ai-ecoverse/slicc-cdp':
|
|
15
15
|
specifier: 1.11.2
|
|
16
16
|
version: 1.11.2
|
|
@@ -95,8 +95,8 @@ importers:
|
|
|
95
95
|
|
|
96
96
|
packages:
|
|
97
97
|
|
|
98
|
-
'@ai-ecoverse/slicc-browsers@1.7.
|
|
99
|
-
resolution: {integrity: sha512-
|
|
98
|
+
'@ai-ecoverse/slicc-browsers@1.7.1':
|
|
99
|
+
resolution: {integrity: sha512-35vGFMbZrl33ZQ9/VgjwZDBXp0RP87mKY0hnIG31tW5Nt5hh+icdp+3Y/t1kypoENAre+jnVEDa9W/H+yK6zyA==}
|
|
100
100
|
engines: {node: '>=24'}
|
|
101
101
|
|
|
102
102
|
'@ai-ecoverse/slicc-cdp@1.11.2':
|
|
@@ -199,7 +199,7 @@ packages:
|
|
|
199
199
|
|
|
200
200
|
snapshots:
|
|
201
201
|
|
|
202
|
-
'@ai-ecoverse/slicc-browsers@1.7.
|
|
202
|
+
'@ai-ecoverse/slicc-browsers@1.7.1': {}
|
|
203
203
|
|
|
204
204
|
'@ai-ecoverse/slicc-cdp@1.11.2': {}
|
|
205
205
|
|