@mmmbuto/nexuscrew 0.8.58 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +197 -2
  2. package/README.md +23 -4
  3. package/frontend/dist/assets/index-0vuhL1YP.css +32 -0
  4. package/frontend/dist/assets/index-Bu_-2-Uu.js +93 -0
  5. package/frontend/dist/index.html +2 -2
  6. package/frontend/dist/version.json +1 -1
  7. package/lib/audio/adapters.js +50 -6
  8. package/lib/cli/commands.js +54 -2
  9. package/lib/cli/doctor.js +95 -12
  10. package/lib/cli/init.js +25 -3
  11. package/lib/cli/path.js +43 -10
  12. package/lib/cli/pidfile.js +23 -2
  13. package/lib/config.js +24 -0
  14. package/lib/fleet/builtin.js +161 -19
  15. package/lib/fleet/catalogs/opencode-go.json +50 -4
  16. package/lib/fleet/cell-exec.js +87 -9
  17. package/lib/fleet/cell-lease-server.js +719 -0
  18. package/lib/fleet/cell-lease.js +112 -0
  19. package/lib/fleet/definitions.js +101 -7
  20. package/lib/fleet/launch-broker.js +115 -3
  21. package/lib/fleet/lease-client.js +191 -0
  22. package/lib/fleet/lease-routes.js +92 -0
  23. package/lib/fleet/lease-verifier.js +230 -0
  24. package/lib/fleet/managed.js +366 -48
  25. package/lib/fleet/prompt-delivery.js +50 -2
  26. package/lib/fleet/provider.js +1 -1
  27. package/lib/fleet/runtime.js +53 -6
  28. package/lib/live-host/bridge.js +369 -0
  29. package/lib/live-host/routes.js +190 -0
  30. package/lib/live-host/store.js +96 -0
  31. package/lib/mcp/tools.js +51 -0
  32. package/lib/nodes/commands.js +21 -3
  33. package/lib/nodes/store.js +57 -0
  34. package/lib/nodes/tunnel.js +23 -1
  35. package/lib/proxy/federation.js +130 -9
  36. package/lib/proxy/node-proxy.js +33 -0
  37. package/lib/proxy/panel-auth.js +337 -0
  38. package/lib/proxy/panel-proxy.js +336 -0
  39. package/lib/server.js +252 -7
  40. package/lib/settings/pairing-coordinator.js +32 -0
  41. package/lib/settings/public-peering-routes.js +13 -1
  42. package/package.json +1 -1
  43. package/skills/aidesktop/SKILL.md +201 -0
  44. package/skills/aidesktop/docker/Dockerfile +21 -0
  45. package/skills/aidesktop/docker/custom-cont-init.d/10-cdp-relay.sh +20 -0
  46. package/skills/aidesktop/docker/docker-compose.example.yml +75 -0
  47. package/skills/crew/SKILL.md +15 -0
  48. package/skills/live/SKILL.md +90 -0
  49. package/skills/mail-assistant/SKILL.md +15 -0
  50. package/skills/memory/SKILL.md +15 -0
  51. package/skills/nexuscrew/SKILL.md +113 -0
  52. package/skills/nexuscrew-agent/SKILL.md +18 -0
  53. package/skills/vl-msa/SKILL.md +15 -0
  54. package/frontend/dist/assets/index-BEGNtmx2.js +0 -93
  55. package/frontend/dist/assets/index-CYi_lhCg.css +0 -32
  56. package/skills/alibaba-token-media/SKILL.md +0 -133
  57. package/skills/alibaba-token-media/agents/openai.yaml +0 -4
  58. package/skills/alibaba-token-media/references/api-contract.md +0 -97
  59. package/skills/alibaba-token-media/scripts/alibaba_token_media.py +0 -550
  60. package/skills/fill-forms/SKILL.md +0 -154
  61. package/skills/fill-forms/agents/openai.yaml +0 -4
  62. package/skills/fill-forms/references/overlay-technique.md +0 -99
  63. package/skills/fill-forms/requirements.txt +0 -4
  64. package/skills/fill-forms/scripts/dump_docx.py +0 -70
  65. package/skills/fill-forms/scripts/fill_docx.py +0 -207
  66. package/skills/fill-forms/scripts/fill_pdf.py +0 -424
  67. package/skills/fill-forms/scripts/inspect_pdf.py +0 -188
  68. package/skills/fill-forms/scripts/prepare_signature.py +0 -171
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: aidesktop
3
+ description: Use when a user wants to give a cell a web panel via panelUrl, asks why a panel URL or panel request was rejected, wants to build the AI Desktop container recipe for an isolated browser and desktop, or needs to connect a Playwright MCP client to that desktop's Chromium through its CDP relay.
4
+ ---
5
+
6
+ # AI Desktop
7
+
8
+ A desktop you can watch and a browser you can drive are two faces of the same
9
+ container: watching it is the **panel**, driving it is the **CDP relay** a
10
+ Playwright MCP client attaches to. This skill covers both together, plus the
11
+ recipe that builds the container itself.
12
+
13
+ ## The panel
14
+
15
+ `panelUrl` is a **property of the cell** (or its engine, as a default for
16
+ every cell using it) — a sibling of `engine` and `cwd`, not a command and not
17
+ something the cell runs. The app's cell/engine editor has a `panelUrl` field
18
+ for this; do not write the URL into the command field, which makes the cell
19
+ try to execute it as a program and fails to start, while no panel button
20
+ appears either, since `panelUrl` was never actually set.
21
+
22
+ The value must be an `http:`/`https:` URL pointing at **loopback** —
23
+ `127.0.0.1`, `[::1]`, or `localhost`. Anything else is rejected when the
24
+ definition is read, and the cell keeps working, just without a panel button.
25
+ **A container's own address is not loopback** — publish its port to
26
+ `127.0.0.1` on the host that runs the node, and point `panelUrl` there. This
27
+ is not a nitpick: the forwarder resolves *which cell*, never *where to
28
+ connect*, so allowing a non-loopback destination would turn it into a way to
29
+ reach any address the node can reach. See
30
+ [The cell panel](../../docs/CELL_PANEL.md) for the full explanation and the
31
+ worked example.
32
+
33
+ ### Origin separation (0.9.1)
34
+
35
+ The panel used to be served from the same origin as the control plane, which
36
+ meant a compromised or hostile page mounted in the panel iframe could, in
37
+ principle, read the operator's token straight out of the app's own
38
+ `localStorage`. As of 0.9.1, the panel is served from a **second loopback
39
+ port** — the browser's Same-Origin Policy treats a different port as a
40
+ different origin, so a document in that frame has nothing of the control
41
+ plane's to read or write. The app also sends
42
+ `Content-Security-Policy: frame-ancestors` on its own page, which is a
43
+ separate, orthogonal protection: it stops the app itself from being embedded
44
+ as someone else's iframe.
45
+
46
+ This covers **remote** cells too, not just local ones. Two paired nodes
47
+ negotiate a second port pair the same way they already negotiate the control
48
+ plane's own tunnel: when the hub side has a panel server running, it
49
+ announces its panel port at join time, the joining side reserves a local port
50
+ for it, and the supervisor forwards both destinations over the same SSH
51
+ connection — one `-L` for control, one for the panel, never sharing a port.
52
+ `GET /api/config` exposes the result as `nodePanelPorts` (node name → the
53
+ locally forwarded port), and the app resolves the right one per cell from its
54
+ route before ever opening the frame.
55
+
56
+ A node with no port pair on record — paired **before** this existed, or
57
+ paired since but with the local port reservation having failed at the time
58
+ (pairing still succeeds; the port pair is treated as an extension, never a
59
+ reason to fail the bond) — is not treated as an error either way: its cells
60
+ fall back to the old federated path (same origin as the control plane)
61
+ exactly as before, and a cell never borrows another node's port to paper over
62
+ the gap — the absence stays visible as the old behavior, not a silent, wrong
63
+ origin. The pair is only established at pairing time, not by reconnecting an
64
+ existing tunnel; re-pair the node to pick one up.
65
+
66
+ None of this changes how a request actually gets into the frame: an
67
+ authenticated call asks for a one-use ticket bound to one cell, the frame's
68
+ first request spends it and receives a viewing cookie scoped to that cell's
69
+ panel path, and the app's own token never reaches the frame. See
70
+ [How the browser gets in](../../docs/CELL_PANEL.md#how-the-browser-gets-in)
71
+ for the full mechanism — it is unchanged by the port move, only relocated.
72
+
73
+ ### Panels on another node
74
+
75
+ Opening a panel that lives on a paired node needs that node's permission,
76
+ **granted there, never by the requester**:
77
+
78
+ ```bash
79
+ nexuscrew nodes panel <node> on
80
+ ```
81
+
82
+ Denied by default. Without it, the rejection names itself —
83
+ `panel-not-granted` — instead of leaving a blank rectangle to debug.
84
+
85
+ ## The Docker recipe
86
+
87
+ **This repository ships the recipe, not an image.** The base,
88
+ `lscr.io/linuxserver/webtop`, is GPL-3.0; a `FROM` line pointing at its public
89
+ source is a recipe, not a distributed derivative, and the only thing this
90
+ project actually owns is the two added lines below and their reasons. It also
91
+ means no registry to host, nothing to keep patched on your behalf, and a
92
+ recipe you can read before you run it.
93
+
94
+ Build it yourself from [`docker/`](docker/):
95
+
96
+ ```bash
97
+ cd <skill-dir>/docker
98
+ cp docker-compose.example.yml docker-compose.yml
99
+ # create ./.gui_password yourself — see the comments in the compose file
100
+ docker compose up -d --build
101
+ ```
102
+
103
+ First start creates an **empty** browser profile: no logins, no history,
104
+ nothing carried over. The example compose ships no branding and no
105
+ pre-populated `/config` — you get a bare webtop desktop plus the two
106
+ additions below.
107
+
108
+ ### The two additions, and why each exists
109
+
110
+ - **`socat`** — modern Chrome ignores `--remote-debugging-address` and binds
111
+ its DevTools/CDP port to `127.0.0.1` *inside* the container, so a Docker
112
+ port mapping alone never reaches it. `socat` relays
113
+ `0.0.0.0:9223 -> 127.0.0.1:9222` inside the container's own network
114
+ namespace; the published host port still only opens on `127.0.0.1`, so the
115
+ loopback-only rule is unchanged — nothing new is exposed, only bridged.
116
+ - **the init script** also clears `Singleton{Lock,Cookie,Socket}` files an
117
+ unclean shutdown leaves behind, which otherwise stop Chromium from starting
118
+ again with no visible error.
119
+
120
+ Notably absent: anything that tries to sandbox the browser. That is the next
121
+ section, and it is the part worth reading before you adapt this recipe.
122
+
123
+ ### Where the boundary is
124
+
125
+ **The browser in this container runs with `--no-sandbox`, and this recipe
126
+ keeps it that way.** That is a trade, made deliberately, and you should
127
+ understand it before deciding whether it fits your situation.
128
+
129
+ The base image launches Chromium through `/usr/local/bin/wrapped-chromium`,
130
+ which hardcodes the flag:
131
+
132
+ ```
133
+ ${BIN} --password-store=basic --no-sandbox --test-type "$@"
134
+ ```
135
+
136
+ Installing Chromium's setuid helper does not change this, and overriding the
137
+ wrapper to drop the flag does not produce a sandbox either. Measured inside a
138
+ running container, launching the browser without it aborts:
139
+
140
+ ```
141
+ The setuid sandbox is not running as root. Common causes:
142
+ * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)
143
+ Failed to move to new namespace: PID namespaces supported,
144
+ Network namespace supported, but failed: errno = Operation not permitted
145
+ ```
146
+
147
+ Both routes are closed, and for opposite reasons. The **setuid** sandbox needs
148
+ its helper to elevate, which `no-new-privileges` exists to prevent. The
149
+ **namespace** sandbox needs unprivileged user namespaces, which the container
150
+ denies. Opening either one means handing the container `CAP_SYS_ADMIN` or
151
+ `seccomp=unconfined`.
152
+
153
+ That is the trade: **you would weaken the boundary that is actually holding in
154
+ order to build one inside it.** For a desktop reachable only over loopback,
155
+ the container is the stronger of the two — so the recipe keeps the container
156
+ hard and accepts the browser soft. `no-new-privileges` stays on.
157
+
158
+ **What this costs you, stated plainly:** a renderer exploit lands in the
159
+ container. Everything the browser can reach is in the blast radius — its
160
+ profile, its logged-in sessions, and anything you mount. Mount as little as
161
+ possible, keep the published ports on loopback, and do not treat this desktop
162
+ as isolation between *sites*: it is isolation between the desktop and the
163
+ host.
164
+
165
+ **If your situation differs** — an untrusted desktop, or a host where the
166
+ container boundary matters less than the browser one — the inverse trade is
167
+ legitimate: grant `seccomp=unconfined`, override the wrapper, and verify you
168
+ actually got a sandbox rather than assuming it. Check the running process, not
169
+ the launch log:
170
+
171
+ - open `chrome://sandbox` inside the desktop and read what it reports, or
172
+ - confirm the zygote no longer carries `--no-sandbox` (`ps -eo args | grep zygote`).
173
+
174
+ Until one of those confirms it, assume the browser is unsandboxed — which,
175
+ with this recipe as shipped, it is.
176
+ ### Commanding the browser
177
+
178
+ The relay's published port, `127.0.0.1:9222` on the host, is the CDP
179
+ endpoint a Playwright MCP client attaches to — for example:
180
+
181
+ ```bash
182
+ playwright-mcp --cdp-endpoint http://127.0.0.1:9222
183
+ ```
184
+
185
+ Register that command as its own MCP server in the AI client's configuration;
186
+ NexusCrew does not bundle browser automation itself, it only gives the
187
+ container a loopback CDP port to attach one to. It is the same Chromium the
188
+ panel shows over the desktop, so it carries the same logins — treat driving
189
+ it with the same care as watching it.
190
+
191
+ ## Dependencies
192
+
193
+ **Bundled:** the `docker/` recipe (`Dockerfile`, `docker-compose.example.yml`,
194
+ `custom-cont-init.d/10-cdp-relay.sh`) ships with this skill.
195
+
196
+ **External (you must provide):**
197
+
198
+ | Need | Install | Probe / failure mode |
199
+ |---|---|---|
200
+ | Docker with Compose v2 | your platform's Docker install | `docker compose version` fails → nothing in `docker/` builds or runs |
201
+ | A Playwright-capable MCP client, to drive the browser | install separately, point it at the CDP port | if no such client is registered, no `browser_*`-style tools exist in that session — the desktop and its panel still work without it |
@@ -0,0 +1,21 @@
1
+ # AI Desktop = a webtop base plus the one thing the base is missing: a way to
2
+ # reach Chrome DevTools Protocol from outside the container.
3
+ #
4
+ # WHAT THIS IMAGE DELIBERATELY DOES NOT DO
5
+ # It does not try to give the browser its own sandbox. That is a decision, not
6
+ # an omission, and it was made after measuring — see ../SKILL.md, "Where the
7
+ # boundary is". In short: inside this container the browser's own sandbox
8
+ # cannot start (setuid helper blocked by no-new-privileges, unprivileged user
9
+ # namespaces denied), and the changes that would let it start are exactly the
10
+ # ones that weaken the container — the boundary that actually holds here.
11
+ FROM lscr.io/linuxserver/webtop:debian-xfce
12
+
13
+ RUN apt-get update \
14
+ && apt-get install -y --no-install-recommends socat \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # socat: modern Chrome IGNORES --remote-debugging-address and binds CDP to
18
+ # 127.0.0.1 inside the container, so a Docker port mapping alone never
19
+ # reaches it. The relay 0.0.0.0:9223 -> 127.0.0.1:9222 is the only bridge,
20
+ # and it still only leaves the container through a port you publish to the
21
+ # host's own loopback (see docker-compose.example.yml).
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+ # Runs at every container start, as root, BEFORE the desktop session starts.
3
+ #
4
+ # 1) Orphaned profile locks: if the container dies while Chromium is still
5
+ # running, Singleton{Lock,Cookie,Socket} files are left behind pointing at
6
+ # a dead pid, and Chromium then REFUSES to start on the next boot.
7
+ # Observed symptom: autostart present, zero processes, no visible error.
8
+ # It is safe to remove them here: the desktop session has not started yet,
9
+ # so no browser can possibly be alive.
10
+ rm -f /config/browser-profile/Singleton* 2>/dev/null || true
11
+
12
+ # 2) CDP relay: modern Chrome ignores --remote-debugging-address and binds
13
+ # DevTools to 127.0.0.1 inside the container, unreachable through a Docker
14
+ # port mapping. socat bridges it. What leaves the container through this
15
+ # port still only reaches the host's own loopback — see the port mapping
16
+ # in docker-compose.example.yml.
17
+ pkill -f "TCP-LISTEN:9223" 2>/dev/null || true
18
+ nohup socat TCP-LISTEN:9223,fork,reuseaddr TCP:127.0.0.1:9222 >/dev/null 2>&1 &
19
+
20
+ echo "[cdp-relay] stale profile locks cleared; socat 9223 -> 9222 started"
@@ -0,0 +1,75 @@
1
+ # Example compose for the AI Desktop recipe. Copy this next to the Dockerfile
2
+ # and custom-cont-init.d/, adjust the marked values, then:
3
+ #
4
+ # docker compose up -d --build
5
+ #
6
+ # First start creates an EMPTY browser profile under ./config — no logins,
7
+ # no history, nothing carried over from anywhere else. Expect a self-signed
8
+ # certificate warning on the HTTPS port on first connect: normal for a
9
+ # loopback-only service, and safe to trust locally.
10
+ services:
11
+ ai-desktop:
12
+ build: .
13
+ image: ai-desktop:local
14
+ container_name: ai-desktop
15
+ restart: unless-stopped
16
+
17
+ environment:
18
+ - PUID=1000
19
+ - PGID=1000
20
+ - TZ=Etc/UTC # set your own timezone
21
+ - TITLE=AI Desktop
22
+ # GUI login: without a password, anyone who reaches the port gets a
23
+ # terminal with passwordless sudo INSIDE the container. Create your own
24
+ # ./.gui_password file (mode 600, one line, no trailing newline needed)
25
+ # before the first start — do not put the password in this file.
26
+ - CUSTOM_USER=admin # set your own login name
27
+ - FILE__PASSWORD=/run/gui_password
28
+
29
+ volumes:
30
+ - ./config:/config
31
+ - ./.gui_password:/run/gui_password:ro
32
+ - ./custom-cont-init.d:/custom-cont-init.d:ro
33
+
34
+ # Loopback only, like everything else in NexusCrew: reach this from
35
+ # elsewhere through an SSH tunnel or VPN you control, never by publishing
36
+ # these ports beyond 127.0.0.1.
37
+ # 6900 = http, 6901 = https. The https port is the one worth using day to
38
+ # day — full desktop features (clipboard, audio/video codecs) need it.
39
+ ports:
40
+ - "127.0.0.1:6900:3000"
41
+ - "127.0.0.1:6901:3001"
42
+ # CDP of the Chromium running on the desktop: the channel a Playwright
43
+ # MCP client attaches to (see SKILL.md). Same browser you see and use
44
+ # over the desktop, so same logins.
45
+ - "127.0.0.1:9222:9223"
46
+
47
+ # Chromium crashes on tab open without adequate /dev/shm.
48
+ shm_size: "1gb"
49
+ mem_limit: 4g # tune to your machine
50
+ mem_reservation: 1g
51
+ cpus: 2.0
52
+
53
+ # KEEP THIS. The container is the boundary that holds here, and this line
54
+ # is part of it. The browser inside runs with --no-sandbox — that is not
55
+ # an accident, it is the trade this image makes: giving Chromium its own
56
+ # sandbox would require handing the container CAP_SYS_ADMIN or
57
+ # seccomp=unconfined, weakening the one boundary that is actually doing
58
+ # the work in order to add one inside it.
59
+ #
60
+ # Measured, not assumed: with this line set, launching the browser without
61
+ # --no-sandbox aborts — the setuid helper cannot elevate (PR_SET_NO_NEW_PRIVS)
62
+ # and the namespace sandbox is denied ("failed to move to new namespace:
63
+ # Operation not permitted"). See ../SKILL.md, "Where the boundary is".
64
+ #
65
+ # The consequence is real and you should plan for it: a renderer exploit
66
+ # lands in this container. Treat what the browser can reach — its profile,
67
+ # its sessions, anything you mount — as being inside the blast radius.
68
+ security_opt:
69
+ - no-new-privileges:true
70
+
71
+ logging:
72
+ driver: json-file
73
+ options:
74
+ max-size: "10m"
75
+ max-file: "3"
@@ -87,3 +87,18 @@ report.
87
87
 
88
88
  Transport receipts do not prove task acceptance or completion. Report only
89
89
  results that have been verified.
90
+
91
+ ## Dependencies
92
+
93
+ **Bundled:** nothing — this skill is documentation only.
94
+
95
+ **External (you must provide):**
96
+
97
+ | Need | Install | Probe / failure mode |
98
+ |---|---|---|
99
+ | A Crew MCP fabric (companion) | optional companion `mcp-crewd-rs`: <https://github.com/DioNanos/mcp-crewd-rs> — the repository's Install section is authoritative and may change; this skill deliberately does not duplicate its commands (**not verified here**) | if the companion is not registered, the client exposes no `cell_*` tools; ask the client for its tool list |
100
+ | Client MCP registration | register the companion's stdio command in the AI client's MCP config | same failure mode: absent tools, not a runtime error |
101
+ | A `crew` token issued by the fabric | `crew token issue` on the host that runs the fabric (see the companion's docs) | spawns are rejected as unauthorized — the rejection names the missing authorization |
102
+
103
+ Without the companion there is no worker delegation in that session: say so
104
+ and suggest the companion once instead of emulating cells with raw tmux.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: live
3
+ description: Use when a user wants to know or change which cell is the Live host on a node, asks why a Live host designation was rejected, or needs to grant or revoke another node's permission to designate a Live host cell on this one (liveHostAccess, via nexuscrew nodes live-host).
4
+ ---
5
+
6
+ # Live host cell
7
+
8
+ Live is the phone-facing quick-open flow: a single **host cell**, designated
9
+ per node, is what a Live session lands on. See
10
+ [Fleet and terminals](../../docs/FLEET.md) for what Live is; this skill covers
11
+ designating it, clearing it, and permitting it across nodes.
12
+
13
+ ## One host cell per node, guarded by a revision
14
+
15
+ Each node keeps exactly one `hostCell` (or none). Changing it is a
16
+ compare-and-swap on a `revision` number, never an unconditional write:
17
+
18
+ 1. `GET .../live-host` returns `{ hostCell, revision, eligible, host: { lease } }`.
19
+ 2. `POST .../live-host/designate { cellId, expectedRevision }` — `expectedRevision`
20
+ must be the value just read. A stale revision (someone else changed the
21
+ designation meanwhile) is rejected with 409, not silently overwritten.
22
+ 3. `POST .../live-host/clear { expectedRevision }` — same rule, removes the
23
+ designation.
24
+
25
+ Always re-read the revision immediately before writing; never reuse one from
26
+ an earlier render. This is what keeps two people (or two tabs) racing to star
27
+ a cell from leaving the store in a mixed state — one write wins, the other
28
+ gets a 409 and re-reads.
29
+
30
+ The designation survives the cell going inactive; it is never dropped just
31
+ because tmux is not attached right now. `eligible` is computed fresh on every
32
+ read from the roster and the designated cell's lease state
33
+ (`live`/`grace`/`expired`/`none`/`unavailable`) — a designated-but-not-eligible
34
+ host is a distinct, readable state, not an error to explain away.
35
+
36
+ ## Command the node that owns the cell, not the one serving the page
37
+
38
+ This is the point of the feature: **the request must reach the node whose
39
+ roster contains the cell**, not whichever node happens to be rendering the
40
+ current page. Route it exactly like a federated deck — an empty route for a
41
+ local cell, that node's route array for a remote one:
42
+
43
+ ```js
44
+ getLiveHost(token, route) // route: [] or [...hops]
45
+ designateHostCell(token, cellId, expectedRevision, route)
46
+ clearHostCell(token, expectedRevision, route)
47
+ ```
48
+
49
+ Before 0.9.1 these calls took no `route`, so starring a cell shown from a
50
+ remote node either did nothing or silently changed the wrong node's
51
+ designation. That mismatch — not a missing capability — was the defect: a
52
+ parameter nobody passed does not exist.
53
+
54
+ ## Federated permission: liveHostAccess
55
+
56
+ A peer may designate or read another node's Live host only if that node has
57
+ granted it. The permission is per peer, **denied by default**, and granted by
58
+ the node that **owns** the cell — never by the one asking:
59
+
60
+ ```bash
61
+ nexuscrew nodes live-host <node> on
62
+ nexuscrew nodes live-host <node> off
63
+ ```
64
+
65
+ Without it, the request gets a **named** rejection, not a timeout or a star
66
+ that quietly does nothing: HTTP 403 with `reason: "live-host-not-granted"`.
67
+ Surface this distinctly from a generic failure — the fix is a grant on the
68
+ owning node, not a retry from the requester.
69
+
70
+ ## Common mistakes
71
+
72
+ - **Reusing a stale `revision`.** `GET` immediately before every
73
+ `designate`/`clear`; a revision read earlier in the session is stale by
74
+ definition once anything else has changed the designation.
75
+ - **Designating a cell that belongs to a different node than the route
76
+ targets.** The route selects which node's roster is searched; a `cellId`
77
+ absent from that node's local roster is rejected with 404, never forwarded
78
+ further to guess where it might live.
79
+ - **Reading a `live-host-not-granted` rejection as a bug.** It means exactly
80
+ what it says: run `nexuscrew nodes live-host <node> on` on the node that
81
+ owns the cell, not on the node making the request.
82
+ - **Assuming Live host state is global.** It is one value **per node**; a node
83
+ with nothing designated still answers `GET` with `hostCell: null` — a valid
84
+ state, distinct from "not permitted to ask".
85
+
86
+ ## Dependencies
87
+
88
+ **Bundled:** this is a NexusCrew core feature. No external MCP companion or
89
+ separate service is required — only a running NexusCrew node on each side of
90
+ the designation.
@@ -69,3 +69,18 @@ client-native scheduler or loop mechanism instead of manual polling, avoid
69
69
  duplicate jobs, respect quiet hours, and remain silent on unchanged ticks when
70
70
  the host workflow supports silent monitoring. A recurring authorization does
71
71
  not automatically authorize sending or permanent deletion.
72
+
73
+ ## Dependencies
74
+
75
+ **Bundled:** nothing — this skill is documentation only.
76
+
77
+ **External (you must provide):**
78
+
79
+ | Need | Install | Probe / failure mode |
80
+ |---|---|---|
81
+ | A mail MCP connector already exposed by the current client (any Gmail/IMAP-capable MCP server; e.g. the optional companion `mcp-email-rs` <https://github.com/DioNanos/mcp-email-rs> — its Install section is authoritative, **not verified here**) | register the connector in the AI client's MCP config, following that connector's own instructions | if no mail tools are exposed, mailbox discovery returns nothing to call — ask the client for its tool list; do not assume provider folders or account layout |
82
+ | Mailbox credentials | provided by the connector's own auth flow | auth failures surface from the connector, not from this skill |
83
+
84
+ This skill never sends or deletes mail on its own initiative; a missing
85
+ connector means the task cannot start, which must be reported, not worked
86
+ around with direct IMAP scripting.
@@ -79,3 +79,18 @@ document store, not in a bounded log.
79
79
  If no Memory MCP tool is available and this skill is packaged with NexusCrew,
80
80
  the optional companion is documented in `../../MCP_COMPANIONS.md`. Explain the
81
81
  missing capability and ask before installing or configuring anything.
82
+
83
+ ## Dependencies
84
+
85
+ **Bundled:** nothing — this skill is documentation only.
86
+
87
+ **External (you must provide):**
88
+
89
+ | Need | Install | Probe / failure mode |
90
+ |---|---|---|
91
+ | A Memory MCP server (companion) | optional companion `mcp-memory-rs`: <https://github.com/DioNanos/mcp-memory-rs> — the repository's Install section is authoritative and may change; this skill deliberately does not duplicate its commands (**not verified here**) | if the companion is not registered, the client exposes no `memory_*` tools; ask the client for its tool list rather than guessing |
92
+ | Client MCP registration | register the companion's stdio command in the AI client's MCP config | same failure mode as above: absent tools, not a runtime error |
93
+
94
+ Without the companion this skill is not usable in that session: say so and
95
+ suggest the companion once (see the packaged `mcp-companions.json` catalog)
96
+ instead of approximating with file reads.
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: nexuscrew
3
+ description: Read this first when working with NexusCrew for any reason — understanding what it is, what an AI agent can do through it, how cells, nodes, decks, engines, panels and Live fit together, which companion skills cover which capability, and where the trust boundaries are. This is the entry point: every other NexusCrew skill assumes what is written here.
4
+ ---
5
+
6
+ # NexusCrew — what it is and what you can do through it
7
+
8
+ Read this before the other NexusCrew skills. They describe individual
9
+ capabilities; this one describes the machine they belong to, and the rules that
10
+ hold across all of them.
11
+
12
+ If the running build and this document disagree, **the build wins**. Check with
13
+ `nexuscrew status`, `nexuscrew doctor`, and the MCP tool list actually exposed
14
+ in your session.
15
+
16
+ ## The shape of the system
17
+
18
+ NexusCrew turns live tmux sessions, AI CLI workers and connected machines into
19
+ one local-first control plane, reachable from a browser.
20
+
21
+ Five nouns carry almost everything:
22
+
23
+ - **Node** — one installation on one machine. Its identity is an opaque
24
+ `instanceId`; the human-readable name is a **label**, and two nodes may
25
+ legitimately carry the same one. **Address things by id, never by name.**
26
+ - **Cell** — one stable working identity (`Dev`, `Research`, …) bound to one
27
+ tmux session and one engine. A cell is not a process: it survives restarts of
28
+ the service, and stopping it does not end the work it was doing.
29
+ - **Engine** — what a cell runs: an AI CLI, a plain shell, a command in a
30
+ container. Some are *managed* (the service knows how to describe and
31
+ configure them), some are not.
32
+ - **Deck** — a relationship between cells that work together. Not a duplicate
33
+ cell, not a group chat: an arrangement.
34
+ - **Panel** — an optional web interface a cell can carry, served next to its
35
+ terminal. A remote desktop, a notebook, a dashboard.
36
+
37
+ Two machines that have paired are **peers**. Federation is the normal case, not
38
+ the exception: assume the human is driving from one node toward another.
39
+
40
+ ## What an AI agent can actually do
41
+
42
+ Through the MCP bridge, when the tools are exposed in your session:
43
+
44
+ | You want to | Use | Covered by |
45
+ |---|---|---|
46
+ | tell the human something, or ask | `nc_notify`, `nc_ask` | `nexuscrew-agent` |
47
+ | read runtime state, identity, decks | `nc_status`, `nc_identity`, `nc_deck` | `nexuscrew-agent` |
48
+ | find and message another cell | `nc_cells` then `nc_send_cell` | `nexuscrew-agent` |
49
+ | speak on a node or audio group | `nc_speak`, `nc_speak_group` | `nexuscrew-agent` |
50
+ | hand a file to the human | `nc_send_file`, `nc_inbox` | `nexuscrew-agent` |
51
+ | find out why a cell will not start | `nc_cell_diagnostics` | `nexuscrew-agent` |
52
+ | keep state across sessions | Memory MCP | `memory` |
53
+ | index and retrieve documents | MSA MCP | `vl-msa` |
54
+ | delegate bounded work to workers | Crew MCP | `crew` |
55
+ | read, search and triage mail | Mail MCP | `mail-assistant` |
56
+
57
+ The last four are **optional companions**, listed in
58
+ [`mcp-companions.json`](../../mcp-companions.json). They are separate servers:
59
+ absent unless installed, and never installed silently.
60
+
61
+ ## Four rules that will save you a wasted hour
62
+
63
+ **A receipt is not an outcome.** `submitted` from `nc_send_cell` means the text
64
+ was pasted and Enter was pressed. It does not mean the message was understood,
65
+ accepted, or acted on. If completion matters, ask for an explicit answer.
66
+
67
+ **There is no offline queue.** A cell that is not active cannot receive. A
68
+ `canReceive: false` peer is not "queued", it is unreachable.
69
+
70
+ **Discovery is not authorization.** Seeing a cell or node in a listing does not
71
+ mean you may act on it. Panels in particular are denied by default and granted
72
+ per peer, by the node that owns the cell.
73
+
74
+ **A cell that looks idle may not be.** A TUI queues incoming messages while it
75
+ works, which is healthy. Do not conclude a cell is stuck from a quiet pane.
76
+
77
+ ## Trust boundaries
78
+
79
+ - Everything binds to **loopback** by default. Reaching a node from elsewhere
80
+ means a tunnel or a pairing, deliberately.
81
+ - A **panel** must point at loopback on the machine that runs the node. This is
82
+ not a port-forward: the caller picks *which cell*, never *where to connect*.
83
+ Without that rule, opening "a panel" would be a way to reach anything the
84
+ node can reach.
85
+ - Never put tokens, keys, cookies or pairing material into MCP payloads,
86
+ messages to cells, or documents. Do not read credential files to work around
87
+ a missing tool.
88
+ - Mutations are honoured or refused, never faked: `NEXUSCREW_READONLY=1` and
89
+ routed-peer inspect-only limits are real, and a refusal is information.
90
+
91
+ ## Where to look next
92
+
93
+ | Topic | Document |
94
+ |---|---|
95
+ | Cells, engines, decks | [Fleet](../../docs/FLEET.md) |
96
+ | Peers, pairing, visibility | [Nodes](../../docs/NODES.md) |
97
+ | The MCP bridge and client setup | [MCP](../../docs/MCP.md) |
98
+ | A cell's web panel | [Cell panel](../../docs/CELL_PANEL.md) |
99
+ | Files, environment, settings | [Configuration](../../docs/CONFIGURATION.md) |
100
+ | CLI, boot, backup, diagnostics | [Operations](../../docs/OPERATIONS.md) |
101
+ | Trust boundaries in depth | [Security](../../docs/SECURITY.md) |
102
+
103
+ ## When something does not work
104
+
105
+ In this order, because each step rules out the one before:
106
+
107
+ 1. `nexuscrew status` — is the service running, on which port, in which roles?
108
+ 2. `nexuscrew doctor` — local diagnostics.
109
+ 3. Is the tool you need **actually exposed** in this session? Do not emulate a
110
+ missing tool by reading state files; say it is missing and degrade openly.
111
+ 4. For a cell that will not start: `nc_cell_diagnostics` before anything else —
112
+ it returns the redacted command and the last startup failure.
113
+ 5. Only then look at files, and say that you are doing so and why.
@@ -169,3 +169,21 @@ work. The setting is also applied to windows created later in that session.
169
169
  - **Assuming a node listens on the port you know** → NexusCrew selects a free port per installation, and a peer's remote port is not the port that node listens on locally. Read it from `nexuscrew status` on that node; a health check aimed at the wrong port reports a dead service that is perfectly alive.
170
170
  - **Writing a reply into a local inbox directory** → the inbox is per-installation and is not synchronised between nodes. Answering a remote caller by dropping a file in your own inbox reaches nobody; reply through the tool that addressed you.
171
171
  - **Treating a dead scroll gesture as a web-terminal bug** → the pane is in the alternate buffer. Check whether it predates the NexusCrew setting or opted out with `alternateScreen:true`; never send raw page keys to a TUI to work around it.
172
+
173
+ ## Dependencies
174
+
175
+ **Bundled (installed with the package):** the `nexuscrew` CLI, `lib/`, these
176
+ skills, and the `bin/nc-send` / `bin/nc-deliver` helpers arrive with
177
+ `npm install -g @mmmbuto/nexuscrew` (Node.js >= 18 required by `engines`).
178
+
179
+ **External (you must provide):**
180
+
181
+ | Need | Install | Probe / failure mode |
182
+ |---|---|---|
183
+ | Node.js >= 18 | Debian/Ubuntu `apt install nodejs` (nodesource for 18+), Fedora `dnf install nodejs`, macOS `brew install node`, Termux `pkg install nodejs-lts` | `node -v` prints >= 18; below that `npm install` refuses per `engines` |
184
+ | tmux | Debian/Ubuntu `apt install tmux`, Fedora `dnf install tmux`, macOS `brew install tmux`, Termux `pkg install tmux` | `nexuscrew doctor` reports tmux missing by name; `nc-send` exits 127 with `nc-send: tmux not found on PATH (set TMUX_BIN)` — the failure names itself |
185
+ | An AI client that can register the MCP server | register the stdio command `nexuscrew mcp` in the client's MCP config | if the `nc_*` tools are not exposed, no `nc_` tool exists in the session — see "MCP bridge" above |
186
+ | A running NexusCrew service (for most tools) | `nexuscrew serve` (foreground) or your platform service manager | tools fail to reach the bridge; Termux has no systemd — run `nexuscrew serve` inside a tmux session or your own keep-alive |
187
+
188
+ If tmux is missing, MCP inspection (`nc_status`, `nc_identity`) still works;
189
+ anything that targets a session (including the `nc-send` fallback) does not.
@@ -66,3 +66,18 @@ round. Avoid a single unbounded search.
66
66
  If no VL-MSA tool is available and this skill is packaged with NexusCrew, the
67
67
  optional companion is documented in `../../MCP_COMPANIONS.md`. Explain the
68
68
  missing capability and ask before installing or configuring anything.
69
+
70
+ ## Dependencies
71
+
72
+ **Bundled:** nothing — this skill is documentation only.
73
+
74
+ **External (you must provide):**
75
+
76
+ | Need | Install | Probe / failure mode |
77
+ |---|---|---|
78
+ | A VL-MSA MCP server (companion) | optional companion `mcp-vl-msa-rs`: <https://github.com/DioNanos/mcp-vl-msa-rs> — the repository's Install section is authoritative and may change; this skill deliberately does not duplicate its commands (**not verified here**) | if the companion is not registered, the client exposes no `msa_*` tools; ask the client for its tool list |
79
+ | Client MCP registration | register the companion's stdio command in the AI client's MCP config | same failure mode: absent tools, not a runtime error |
80
+
81
+ Without the companion, durable retrieval is simply unavailable in that
82
+ session: say so and suggest the companion once instead of reading the
83
+ server's on-disk collections directly (which this skill forbids).