@mmmbuto/nexuscrew 0.9.16 → 0.9.18

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 +157 -3
  2. package/docs/ALIBABA_TOKEN_PLAN.md +70 -0
  3. package/docs/AUDIO_SHARE.md +119 -0
  4. package/docs/CELL_PANEL.md +156 -0
  5. package/docs/CONFIGURATION.md +122 -0
  6. package/docs/FLEET.md +222 -0
  7. package/docs/INSTALLATION.md +128 -0
  8. package/docs/LIVE_PROMPT.md +110 -0
  9. package/docs/MCP.md +151 -0
  10. package/docs/NODES.md +214 -0
  11. package/docs/NOTIFICATIONS.md +101 -0
  12. package/docs/OPERATIONS.md +114 -0
  13. package/docs/README.md +55 -0
  14. package/docs/SECURITY.md +185 -0
  15. package/docs/VL_MICRO_NODES.md +107 -0
  16. package/docs/img/fleet-deck-desktop.png +0 -0
  17. package/docs/img/session-mobile.png +0 -0
  18. package/docs/live-prompt-templates/LIVE_PROMPT.en.md +67 -0
  19. package/docs/live-prompt-templates/LIVE_PROMPT.es.md +70 -0
  20. package/docs/live-prompt-templates/LIVE_PROMPT.it.md +67 -0
  21. package/frontend/dist/assets/{index-CwsGpQwK.js → index-DmpQoCsc.js} +1 -1
  22. package/frontend/dist/index.html +1 -1
  23. package/frontend/dist/version.json +1 -1
  24. package/lib/auth/middleware.js +1 -1
  25. package/lib/cells/scope-guard.js +2 -2
  26. package/lib/cli/commands.js +7 -7
  27. package/lib/cli/fleet-service.js +2 -2
  28. package/lib/cli/pidfile.js +15 -15
  29. package/lib/cli/service.js +2 -2
  30. package/lib/cli/stable-alias.js +1 -1
  31. package/lib/files/routes.js +2 -2
  32. package/lib/fleet/builtin.js +6 -6
  33. package/lib/fleet/catalogs/zai-responses.json +82 -0
  34. package/lib/fleet/cell-exec.js +4 -4
  35. package/lib/fleet/cell-lease-server.js +17 -17
  36. package/lib/fleet/definitions.js +28 -17
  37. package/lib/fleet/launch-broker.js +3 -3
  38. package/lib/fleet/lease-client.js +1 -1
  39. package/lib/fleet/managed.js +86 -26
  40. package/lib/fleet/prompt-delivery.js +1 -1
  41. package/lib/fleet/runtime.js +3 -3
  42. package/lib/live-host/bridge.js +3 -3
  43. package/lib/live-host/routes.js +1 -1
  44. package/lib/mcp/server.js +7 -8
  45. package/lib/mcp/tools.js +1 -1
  46. package/lib/nodes/commands.js +2 -2
  47. package/lib/nodes/health.js +1 -1
  48. package/lib/nodes/identity.js +10 -13
  49. package/lib/nodes/store.js +2 -2
  50. package/lib/nodes/tunnel-supervisor.js +7 -7
  51. package/lib/nodes/tunnel.js +10 -11
  52. package/lib/notify/asks.js +4 -4
  53. package/lib/notify/persist.js +1 -1
  54. package/lib/notify/push.js +4 -4
  55. package/lib/notify/routes.js +12 -12
  56. package/lib/proxy/node-proxy.js +4 -4
  57. package/lib/proxy/panel-proxy.js +2 -2
  58. package/lib/pty/attach.js +1 -1
  59. package/lib/server.js +3 -3
  60. package/lib/settings/pairing-coordinator.js +1 -1
  61. package/lib/settings/routes.js +4 -4
  62. package/lib/tmux/lifecycle.js +4 -4
  63. package/lib/tmux/preview.js +1 -1
  64. package/lib/update/runner.js +9 -9
  65. package/lib/vl-nodes/broker.js +1 -1
  66. package/lib/ws/bridge.js +1 -1
  67. package/package.json +20 -2
  68. package/frontend/index.html +0 -20
package/docs/NODES.md ADDED
@@ -0,0 +1,214 @@
1
+ # Connect nodes
2
+
3
+ [← Documentation index](README.md)
4
+
5
+ Every NexusCrew installation starts as a local node. Connected nodes use
6
+ supervised OpenSSH links; NexusCrew does not replace SSH, generate SSH keys or
7
+ edit `authorized_keys`.
8
+
9
+ ## Pair a node
10
+
11
+ 1. On the installation that will host the new node, open
12
+ **Settings → Nodes → Invite a node**.
13
+ 2. Provide the OpenSSH target the joining device can use, such as `user@host`
14
+ or a local SSH config alias.
15
+ 3. On the joining device, paste the complete pairing link in
16
+ **Settings → Nodes** and choose **Test and connect**.
17
+ 4. If the portable address cannot select the correct key, open
18
+ **Advanced / edit** and use the SSH alias that already works on that device.
19
+
20
+ An invite belongs to the installation that will host the node, so it is issued
21
+ there and nowhere else. Minting one is not a federated operation: a node you are
22
+ connected to cannot be asked to admit a further node on your behalf. If you are
23
+ on a client and the interface refuses, open NexusCrew on the hub itself.
24
+
25
+ The pairing payload is not a browser URL. It contains a short-lived, one-time
26
+ invite and routing fields, but no SSH private key, provider key or PWA token.
27
+
28
+ For headless installations, use:
29
+
30
+ ```bash
31
+ nexuscrew nodes invite --ssh user@host
32
+ nexuscrew nodes pair
33
+ nexuscrew nodes join
34
+ ```
35
+
36
+ Pair/join reads the one-time payload from stdin so it does not need to appear
37
+ in process arguments.
38
+
39
+ ## Where a node's controls live
40
+
41
+ **Settings → Nodes** lists nodes one per row: name, how it is reached, and what
42
+ it sees of the network. Selecting a row opens that node's sheet — a panel at the
43
+ side on a wide screen, a sheet rising from the bottom on a narrow one — and
44
+ every per-node control lives there: reachability and transport, visibility and
45
+ its grants, edit, test, connect or disconnect, remove.
46
+
47
+ Nothing in the list itself mutates a node. Publishing *this* device, and
48
+ inviting a new one, stay outside the list because they are not properties of a
49
+ node you are looking at.
50
+
51
+ ## Labels and routes
52
+
53
+ The human-readable display label is separate from the local route handle. A
54
+ route defaults to a readable slug plus a stable node-ID suffix, so multiple
55
+ devices reporting `localhost` still receive distinct identities.
56
+
57
+ Rename an owned node from Settings or a roster; the same canonical label then
58
+ appears everywhere without changing route, identity, credentials, Share state
59
+ or deck identity.
60
+
61
+ For a routed node the current installation does not own, Settings offers a
62
+ local alias. That alias stays private to the viewing installation and follows
63
+ the stable instance identity.
64
+
65
+ ## Private and shared state
66
+
67
+ Newly joined devices are private by default. Private pairing is administrative
68
+ inventory, not operational publication: a private client can remain listed in
69
+ Settings while staying absent from routable topology, deck bars and MCP
70
+ discovery.
71
+
72
+ Enabling **Share this device through the selected hub** adds a verified reverse
73
+ channel to the existing SSH process. The hub decides whether authorized peers
74
+ see the whole network, only the hub or an explicit subset.
75
+
76
+ Share is stored as desired state:
77
+
78
+ - Failed activation rolls back to private.
79
+ - Deactivation saves private intent first.
80
+ - The hub must acknowledge withdrawal before the reverse channel is removed.
81
+ - If acknowledgement fails, bounded boot retries reconcile the saved state
82
+ without claiming remote removal completed.
83
+
84
+ Temporary loss of reachability does not revoke consent. An authorized node
85
+ remains visible as stale/offline until an authoritative refresh restores it or
86
+ confirms withdrawal.
87
+
88
+ ## Which cells a node may see
89
+
90
+ Sharing decides whether a node is reachable. A **cell scope** decides how much
91
+ of this installation it reaches once it is:
92
+
93
+ ```bash
94
+ nexuscrew nodes cells <name|nodeId> all # default
95
+ nexuscrew nodes cells <name|nodeId> none
96
+ nexuscrew nodes cells <name|nodeId> Research,Dev
97
+ ```
98
+
99
+ The scope is set here, on the installation that owns the cells — never by the
100
+ node being scoped, and never over the federation. It applies to what that node
101
+ lists (cells, fleet status, sessions) and to what it can act on, including the
102
+ terminal attach; a session belonging to no cell is outside every scope.
103
+ Switching away from the explicit list clears it, so a cell revoked today does
104
+ not come back the next time a list is set.
105
+
106
+ Within its scope a node keeps full authority. Read the cell scope section of
107
+ [Security model](SECURITY.md) before using it as a boundary against anything
108
+ you do not trust.
109
+
110
+ ## Tunnel behavior
111
+
112
+ NexusCrew creates one supervised private `ssh -L` process for a hub connection
113
+ and proves the forwarded TCP endpoint before reporting success. It does not use
114
+ `autossh` as a hidden second supervisor. A shared peer with a verified
115
+ rotatable pool may additionally run a short-lived, per-slot reverse supervisor;
116
+ each one has a distinct local target so the hub can prove the exact reverse
117
+ slot it reached.
118
+
119
+ Reverse ports are reserved across active and pending pairings, probed before
120
+ use and protected by a persistent uniqueness check. A stale same-name peer or
121
+ late collision returns an actionable conflict instead of silently consuming
122
+ the invitation.
123
+
124
+ OpenSSH restrictions still apply. A shared client may need its accepted hub key
125
+ to allow the negotiated reverse listener:
126
+
127
+ ```text
128
+ permitlisten="127.0.0.1:44002"
129
+ ```
130
+
131
+ Use the exact port printed by the tunnel diagnostic.
132
+
133
+ ## Rotatable reverse-port pools
134
+
135
+ A new shared peer receives a three-port pool with the same base and offsets
136
+ `+100` and `+200`. Read the base this installation actually assigned — do not
137
+ copy the one below, it is a placeholder — and grant exactly those three ports
138
+ **on the hub**, in the `~/.ssh/authorized_keys` entry that the hub accepts from
139
+ that device:
140
+
141
+ ```text
142
+ permitlisten="127.0.0.1:<BASE>",permitlisten="127.0.0.1:<BASE+100>",permitlisten="127.0.0.1:<BASE+200>"
143
+ ```
144
+
145
+ Add those options when you install or update the key. NexusCrew never writes,
146
+ widens, removes, or otherwise edits `authorized_keys`. A legacy peer with only
147
+ one `permitlisten` remains usable, but is reported as not rotatable until the
148
+ operator installs its full pool.
149
+
150
+ The direction is worth stating plainly, because getting it backwards sends you
151
+ looking where nothing is wrong: **the device asks for the reverse bind, the
152
+ hub's sshd grants or refuses it.** Nothing needs changing on the device — the
153
+ grant lives on the hub, in the entry carrying that device's key.
154
+
155
+ **Re-pairing a device changes its base.** Pool bases are monotonic and a
156
+ removed peer retires its own, so a device paired again is assigned a new one
157
+ while its key still carries the previous grant. Nothing warns you: the pairing
158
+ succeeds, the private `-L` works, the device looks connected — and Share alone
159
+ fails, because it is the only operation that needs the reverse channel. The
160
+ symptom is `share-channel-not-ready` with HTTP 409, and no amount of re-pairing
161
+ fixes it, because pairing is not what is broken.
162
+
163
+ The hub records every refusal it issues as a `SHARE_CHANNEL_REFUSED`
164
+ diagnostic, naming the peer, the typed failure code and **the port it probed**.
165
+ Read it in the Diagnostics view of the PWA. Confirm the cause on the hub with
166
+ `journalctl -u ssh` or `auth.log`:
167
+
168
+ ```text
169
+ Received request ... to remote forward to host 127.0.0.1 port <BASE>, but the request was denied
170
+ ```
171
+
172
+ That line names the exact port SSH policy is refusing. Grant that port on that
173
+ key, and remove any grant pointing at a base that now belongs to another peer:
174
+ one key must never hold a listen right on another peer's pool.
175
+
176
+ Rotation is deliberately narrow:
177
+
178
+ - The peer proposes a pre-authorized slot over its existing private `-L`; the
179
+ hub alone assigns the lease and generation.
180
+ - Every slot is proven over a slot-specific local endpoint before automatic
181
+ rotation is allowed. A generic SSH failure or a missing grant stops the
182
+ episode after one candidate instead of consuming the pool.
183
+ - A verified collision can switch to one ready slot at most once every ten
184
+ minutes. The old slot drains briefly; an unproven or unattributable listener
185
+ is quarantined and reported, never terminated automatically.
186
+ - Pool bases are monotonic. Removing a peer retires its base so a key that
187
+ still has old `permitlisten` rights cannot claim a later peer's port.
188
+
189
+ If the pool is exhausted, degraded, unverified, or not configured, NexusCrew
190
+ does not guess a new port or change SSH policy. Correct the displayed
191
+ `permitlisten` line, then retry Share from the device.
192
+
193
+ ## CLI
194
+
195
+ ```text
196
+ nexuscrew nodes list [--json]
197
+ nexuscrew nodes inspect <name|nodeId>
198
+ nexuscrew nodes edit <name|nodeId> ...
199
+ nexuscrew nodes test # every peer, plus what is only claimed
200
+ nexuscrew nodes up|down|connect|disconnect <name|nodeId>
201
+ nexuscrew nodes restart|reconnect <name|nodeId>
202
+ nexuscrew nodes share <name|nodeId> on|off
203
+ nexuscrew nodes cells <name|nodeId> all|none|Cell1,Cell2
204
+ nexuscrew nodes remove <name|nodeId> --yes
205
+ ```
206
+
207
+ Node and deck identities remain owner-qualified across the network. Routed
208
+ HTTP and WebSocket requests recheck authorization, hop count and cycle rules.
209
+
210
+ ## Related guides
211
+
212
+ - [Installation](INSTALLATION.md)
213
+ - [Fleet and terminals](FLEET.md)
214
+ - [Security](SECURITY.md)
@@ -0,0 +1,101 @@
1
+ # Notifications
2
+
3
+ [← Documentation index](README.md)
4
+
5
+ NexusCrew can deliver live cell notifications through three independent
6
+ surfaces:
7
+
8
+ 1. In-app toasts over the live event stream.
9
+ 2. Best-effort Web Push when the browser and operating system support it.
10
+ 3. Optional on-device spoken alerts while the PWA is visible and focused.
11
+
12
+ ## In-app and push delivery
13
+
14
+ Toasts are the primary live UI signal. Web Push requires browser permission;
15
+ on iOS it also requires an installed PWA.
16
+
17
+ The service worker cannot synthesize speech. A hidden or closed PWA therefore
18
+ uses only its normal system-notification path.
19
+
20
+ Depending on the operating system, a visible toast, Web Push notification and
21
+ spoken alert may coexist.
22
+
23
+ ## Spoken alerts
24
+
25
+ Enable **Settings → System → Read notifications aloud**.
26
+
27
+ Spoken alerts are:
28
+
29
+ - off by default
30
+ - stored as a browser-local, per-device preference
31
+ - available even when server settings are read-only
32
+ - synthesized by the device's browser speech engine
33
+ - spoken in the notification's declared content language when available
34
+ - never sent to a remote speech service
35
+
36
+ For backend-delivered speech on a shared Mac, Linux host, or Termux device,
37
+ use [Audio Share and native TTS](AUDIO_SHARE.md). It has a separate local
38
+ consent gate and does not depend on the browser remaining foregrounded.
39
+
40
+ `nc_notify` accepts an optional `lang` value for Italian, English or Spanish,
41
+ using either a base code (`it`) or an equivalent BCP-47 locale (`it-IT`). The
42
+ live PWA selects a matching installed voice when the browser exposes one.
43
+ Browsers commonly report an empty voice list during startup, so NexusCrew
44
+ refreshes its cache on `voiceschanged` and lets the browser choose until a
45
+ matching voice is available.
46
+
47
+ Notifications from older clients have no language field. For compatibility,
48
+ NexusCrew uses a language detected from the body only when the signal is
49
+ strong; short or mixed technical text falls back to the current UI language.
50
+ Unknown languages leave the utterance language empty so the browser uses its
51
+ own default rather than being forced to English.
52
+
53
+ Enabling speech runs an audible preview. NexusCrew reports success only after
54
+ the browser emits both speech start and speech end. A silent, missing or failed
55
+ native voice reports **voice unavailable** rather than a false success.
56
+
57
+ The preview must succeed once per page session, including after a reload.
58
+
59
+ ## Focus and privacy
60
+
61
+ Speech runs only while the document is visible and has operating-system focus.
62
+ Blur, background, opt-out and unmount cancel the current utterance and clear
63
+ pending work. Skipped notifications are not replayed when focus returns.
64
+
65
+ Focus provides one speaker among NexusCrew windows on the same device. Two
66
+ separately opted-in devices may both speak when each PWA is visible and
67
+ focused; this is intentional.
68
+
69
+ Credential-shaped values and private home paths are redacted before speech.
70
+ Spoken alerts do not replace the toast's accessibility status, so screen-reader
71
+ users can keep the feature disabled if their assistive technology already
72
+ announces notifications.
73
+
74
+ ## Queue behavior
75
+
76
+ - Duplicate live frames are suppressed for 60 seconds.
77
+ - At most two normal alerts remain pending.
78
+ - A high-urgency alert interrupts the current queue.
79
+ - A 30-second watchdog cancels a stuck browser utterance and advances.
80
+ - Failures are isolated from the event stream and visual toast delivery.
81
+
82
+ Only new live notification frames are spoken. Persisted questions and previous
83
+ events are not replayed.
84
+
85
+ ## Platform notes
86
+
87
+ | Platform | Expected behavior |
88
+ |---|---|
89
+ | Chrome / Edge desktop | System voice while the focused PWA is visible |
90
+ | Firefox desktop | System voice where Web Speech synthesis is available |
91
+ | Safari desktop | System voice while the page is focused |
92
+ | Android Chrome | Visible/focused PWA can speak; background uses normal push |
93
+ | iOS installed PWA | Visible/focused PWA can speak; lock screen relies on normal push |
94
+
95
+ Browser and operating-system voice availability remains device-dependent.
96
+
97
+ ## Related guides
98
+
99
+ - [Configuration](CONFIGURATION.md)
100
+ - [Audio Share and native TTS](AUDIO_SHARE.md)
101
+ - [Security](SECURITY.md)
@@ -0,0 +1,114 @@
1
+ # Operations
2
+
3
+ [← Documentation index](README.md)
4
+
5
+ ## CLI
6
+
7
+ | Command | Purpose |
8
+ |---|---|
9
+ | `nexuscrew` | Start or reuse the background runtime and print status |
10
+ | `nexuscrew show` | Open the authenticated PWA |
11
+ | `nexuscrew show token` | Print the authenticated link |
12
+ | `nexuscrew status` | Show service, port, role and node status |
13
+ | `nexuscrew stop` | Stop NexusCrew and managed tunnels, preserving tmux |
14
+ | `nexuscrew restart` | Restart NexusCrew and autostart links, preserving tmux |
15
+ | `nexuscrew boot` | Enable startup persistence |
16
+ | `nexuscrew boot off` | Disable persistence without stopping the runtime |
17
+ | `nexuscrew doctor` | Check Node, PTY, tmux, SSH, service and platform integration |
18
+ | `nexuscrew nodes ...` | Inspect and manage connected nodes |
19
+ | `nexuscrew help` | Show public command help |
20
+ | `nexuscrew version` | Show the installed version |
21
+
22
+ `init`, `serve`, `fleet-boot` and `mcp` are internal entry points rather than
23
+ the normal interactive workflow.
24
+
25
+ ## Boot integration
26
+
27
+ | Platform | Integration |
28
+ |---|---|
29
+ | Linux | systemd user service |
30
+ | macOS | per-user LaunchAgent |
31
+ | Android / Termux | detached runtime and optional Termux:Boot script |
32
+
33
+ Boot starts only cells marked `boot:true`. It is startup persistence, not a
34
+ watchdog for the tmux server itself.
35
+
36
+ Linux services use `KillMode=process` so restarting NexusCrew does not stop the
37
+ shared tmux server. Lifecycle commands fail closed when this protection cannot
38
+ be verified.
39
+
40
+ Termux pidfiles include process identity data. If Android reuses a PID under
41
+ another app UID, NexusCrew removes only the stale pidfile, never signals the
42
+ foreign process, and restarts only the configured OpenSSH supervisor.
43
+
44
+ ## Backup and restore
45
+
46
+ **Settings → Fleet** can export and restore selected cells, system prompts and
47
+ reusable engines.
48
+
49
+ Restore:
50
+
51
+ - previews conflicts
52
+ - supports per-item selection
53
+ - reports active cells requiring restart
54
+ - stores working directories relative to the target user's home
55
+ - requires explicit repair for legacy or foreign absolute paths
56
+
57
+ Archives contain credential variable names, never credential values, browser
58
+ tokens or live tmux state.
59
+
60
+ ## Updates
61
+
62
+ Global npm installs can follow stable `latest` automatically. NexusCrew:
63
+
64
+ 1. Serializes update attempts.
65
+ 2. Installs the selected stable version.
66
+ 3. Verifies the CLI and same-port runtime.
67
+ 4. Rolls back once to the exact previous version if health checks fail.
68
+
69
+ It never installs prereleases from `latest` or silently downgrades. Use
70
+ `NEXUSCREW_AUTO_UPDATE=0` to disable the scheduler.
71
+
72
+ ## Diagnostics
73
+
74
+ **Settings → System → Diagnostics** shows a bounded in-memory event buffer for the
75
+ local installation or an authorized routed node.
76
+
77
+ Verbose collection is explicit and expires after 5, 15, 30 or 60 minutes.
78
+ Operational warnings and errors remain available while verbose mode is off.
79
+ The view supports filtering, pause, autoscroll, copy, JSON export and clear.
80
+
81
+ Records are structured and redacted before storage. Raw terminal content,
82
+ prompts, command lines, environment values, credentials and filesystem paths
83
+ are not accepted as diagnostic metadata. Fleet launch failures expose closed
84
+ `code` and `phase` values rather than raw stderr.
85
+
86
+ ## Troubleshooting
87
+
88
+ - **Node older than 18** — upgrade Node before initialization.
89
+ - **tmux missing** — install tmux and rerun `nexuscrew`.
90
+ - **OpenSSH missing** — install `ssh`; `autossh` remains optional.
91
+ - **systemd user service unavailable** — consider
92
+ `loginctl enable-linger "$USER"`.
93
+ - **LaunchAgent failure** — inspect `nexuscrew doctor` and the user
94
+ `~/Library/LaunchAgents` permissions.
95
+ - **Termux:Boot does not start** — install and open the Android app once.
96
+ - **Voice unavailable** — distinguish browser synthesis from optional server
97
+ speech-to-text; see [Notifications](NOTIFICATIONS.md).
98
+
99
+ ## Development
100
+
101
+ ```bash
102
+ npm test
103
+ npm run build
104
+ node bin/nexuscrew.js serve
105
+ ```
106
+
107
+ Tests that exercise tmux use private sockets and must never attach to or stop
108
+ the operator's tmux server.
109
+
110
+ ## Related guides
111
+
112
+ - [Installation](INSTALLATION.md)
113
+ - [Configuration](CONFIGURATION.md)
114
+ - [Security](SECURITY.md)
package/docs/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # NexusCrew documentation
2
+
3
+ [← Main README](../README.md)
4
+
5
+ NexusCrew is designed around a small public entry point and focused technical
6
+ guides. Start with installation, then open only the area you need.
7
+
8
+ | Guide | Use it for |
9
+ |---|---|
10
+ | [Installation](INSTALLATION.md) | Platform prerequisites, first run, verification and upgrades |
11
+ | [Fleet and terminals](FLEET.md) | Cells, engines, providers, decks, terminal input and mobile behavior |
12
+ | [The Live per-cell prompt](LIVE_PROMPT.md) | Where `LIVE_PROMPT.md` goes, what the bridge does with it, and the ready-to-copy IT/EN/ES templates |
13
+ | [The cell panel](CELL_PANEL.md) | A web interface next to the terminal: configuration, the loopback rule, how the frame authenticates, and granting it across nodes |
14
+ | [Connect nodes](NODES.md) | Pairing, SSH routes, private/share state and routed aliases |
15
+ | [Notifications](NOTIFICATIONS.md) | In-app toasts, Web Push and on-device spoken alerts |
16
+ | [Audio Share and native TTS](AUDIO_SHARE.md) | Node-native TTS, consent, exact targets, groups and MCP commands |
17
+ | [MCP bridge](MCP.md) | Operator tools, client configuration and cell-to-cell delivery |
18
+ | [VL micro-device nodes](VL_MICRO_NODES.md) | Pairing, exact health, bounded management and ACK semantics |
19
+ | [Configuration](CONFIGURATION.md) | Local paths, environment overrides and browser-local settings |
20
+ | [Operations](OPERATIONS.md) | CLI, boot, backups, updates, diagnostics and development |
21
+ | [Security](SECURITY.md) | Network, token, credential, file and federation boundaries |
22
+ | [Alibaba Token Plan](ALIBABA_TOKEN_PLAN.md) | Managed Claude Code, Codex-VL and Pi profiles |
23
+
24
+ Additional public references:
25
+
26
+ - [MCP companions](../MCP_COMPANIONS.md)
27
+ - [Machine-readable companion catalog](../mcp-companions.json)
28
+ - [Changelog](../CHANGELOG.md)
29
+ - [License](../LICENSE)
30
+
31
+ ## Fast paths
32
+
33
+ ### I want to use NexusCrew on this machine
34
+
35
+ Read [Installation](INSTALLATION.md), run `nexuscrew doctor`, then open the PWA
36
+ with `nexuscrew show`.
37
+
38
+ ### I want to reach another machine
39
+
40
+ Read [Connect nodes](NODES.md). NexusCrew supervises OpenSSH; it does not
41
+ replace your SSH configuration or create SSH keys.
42
+
43
+ ### I want to run AI CLI workers
44
+
45
+ Read [Fleet and terminals](FLEET.md), then configure cells and provider
46
+ presence in **Settings → Fleet**.
47
+
48
+ ### I want an AI session to contact the operator
49
+
50
+ Read [MCP bridge](MCP.md) and register `nexuscrew mcp` in the client.
51
+
52
+ ### I want to expose NexusCrew publicly
53
+
54
+ Do not. Read [Security](SECURITY.md): the supported model is loopback plus an
55
+ SSH tunnel or VPN you control.
@@ -0,0 +1,185 @@
1
+ # Security model
2
+
3
+ [← Documentation index](README.md)
4
+
5
+ NexusCrew is local-first and fail-closed. It has no hosted control plane,
6
+ required account or supported public-listener mode.
7
+
8
+ ## Network boundary
9
+
10
+ NexusCrew binds only to `127.0.0.1`. Non-loopback binds are rejected.
11
+
12
+ To reach a remote installation, carry its loopback port through SSH or a VPN
13
+ you control:
14
+
15
+ ```bash
16
+ ssh -L 41820:127.0.0.1:41820 user@your-host
17
+ ```
18
+
19
+ Direct public exposure through a reverse proxy, public port forward or network
20
+ bind is not supported.
21
+
22
+ ## Browser authentication
23
+
24
+ Every API and WebSocket connection requires the local bearer token. The token
25
+ is stored in a user-only file and passed to the browser in the URL fragment:
26
+
27
+ ```text
28
+ http://127.0.0.1:41820/#token=...
29
+ ```
30
+
31
+ Fragments are not sent in the initial HTTP request or written to server access
32
+ logs. Treat the complete link as a credential and do not open it on a shared
33
+ device.
34
+
35
+ ## Session and transport authority
36
+
37
+ - tmux remains the session authority.
38
+ - OpenSSH remains the network and identity authority.
39
+ - NexusCrew supervises SSH but does not create keys or edit
40
+ `authorized_keys`.
41
+ - Node and deck identities remain owner-qualified.
42
+ - Routed HTTP and WebSocket requests recheck ACL, hop count and cycle rules.
43
+
44
+ ### Reverse-port recovery
45
+
46
+ For a shared peer, a reverse-port pool is a fixed SSH policy boundary, not a
47
+ permission NexusCrew can expand. The application never modifies
48
+ `authorized_keys`; a hub operator explicitly grants the three loopback
49
+ `permitlisten` entries for a rotatable peer.
50
+
51
+ Before accepting a pool slot, the hub sends a fresh HMAC challenge to the
52
+ specific loopback listener and verifies its instance, generation and port. The
53
+ probe never sends the peer bearer credential to an unknown listener. A failed
54
+ or unknown listener is quarantined for diagnosis: NexusCrew does not kill SSH
55
+ processes or release ports it cannot prove it owns. Removed pools remain
56
+ retired, preventing an old SSH key from binding a port reassigned to another
57
+ peer.
58
+
59
+ ## Provider credentials
60
+
61
+ Provider keys are resolved only on the node launching the process. NexusCrew
62
+ can use its service environment, compatible user-owned provider files or an
63
+ optional node-local write-only store.
64
+
65
+ Credential values are excluded from:
66
+
67
+ - Fleet cell and engine definitions
68
+ - backups
69
+ - API and status responses
70
+ - tmux state
71
+ - process arguments
72
+ - temporary files
73
+ - diagnostics and logs
74
+
75
+ The PWA reports only whether a required variable is configured. Replacement
76
+ values are transient in the browser and are written only to the selected
77
+ node's credential store.
78
+
79
+ ## Files
80
+
81
+ Per-session file exchange is scoped under `~/NexusFiles/<session>`. Upload and
82
+ download operations reject traversal and symlink escapes.
83
+
84
+ Clipboard images and dropped files are stored in the selected session inbox;
85
+ their path is inserted into the terminal without automatically pressing Enter.
86
+
87
+ ## Pairing and sharing
88
+
89
+ **Pairing grants owner-equivalent authority. Pair only devices you own.**
90
+
91
+ A paired node can create tmux sessions — including a shell — attach to them over
92
+ the federated WebSocket and write into them as the user running NexusCrew. It
93
+ can define engines and cells, and a custom engine may name an existing
94
+ executable with arguments of its choosing: the trust gate checks the binary's
95
+ ownership, mode and path, not the intent of its arguments.
96
+
97
+ Admission is the exception. Minting a pairing invite is not federated: an invite
98
+ belongs to the installation that will host the new node, so it is issued there,
99
+ locally. A paired node cannot admit a third party on your behalf.
100
+
101
+ This is a deliberate boundary, not a defect list. NexusCrew was designed to put
102
+ one person's machines on one control plane, and it treats a paired node the way
103
+ it treats you.
104
+
105
+ ### Cell scope
106
+
107
+ A per-node cell scope narrows what one paired node sees and can act on. It is
108
+ set locally, on the installation that owns the cells:
109
+
110
+ ```bash
111
+ nexuscrew nodes cells <node> all # default: every cell
112
+ nexuscrew nodes cells <node> none # no cell at all
113
+ nexuscrew nodes cells <node> Research,Dev # exactly these
114
+ ```
115
+
116
+ The scope is enforced on the federated request, in one place in front of the
117
+ API router, so a listing and an action pass the same predicate. Route targets
118
+ are declared in an explicit table rather than inferred, and a federated request
119
+ that names a cell or a session on a route missing from that table is refused
120
+ rather than allowed — a route added later starts closed, not open. Defining,
121
+ editing or importing a cell is refused outright for a scoped node: it must not
122
+ be able to create the cell it was not granted. The terminal attach is gated
123
+ with the same predicate, and a tmux session that maps to no cell is outside
124
+ every scope.
125
+
126
+ What the scope does **not** change: the pairing itself is still
127
+ owner-equivalent for everything it governs, and the granted cells are granted
128
+ fully — inside its scope a node still creates sessions, attaches to them and
129
+ writes into them as the user running NexusCrew. It attenuates reach, not the
130
+ authority held within that reach. A capability model with proof of possession —
131
+ authority granted, attenuated and revoked as a token rather than as
132
+ configuration — remains on the roadmap and is not implemented.
133
+
134
+ So: do not accept a pairing invite from an installation you do not own, and do
135
+ not treat a cell scope as a sandbox for code you do not trust. It is a
136
+ reduction of surface between machines that are already yours.
137
+
138
+ Pairing links contain a short-lived one-time invite and routing data, but no
139
+ SSH private key, provider key or PWA token.
140
+
141
+ Newly paired nodes are private by default. Sharing is explicit desired state
142
+ and uses a verified reverse channel in the supervised SSH process. Revocation
143
+ is saved locally before the hub is asked to withdraw the node; the UI does not
144
+ claim remote removal until acknowledgement.
145
+
146
+ ## Diagnostics and speech
147
+
148
+ Diagnostics accept structured, bounded metadata and reject raw terminal
149
+ content, prompts, command lines, environment values, credentials and private
150
+ paths.
151
+
152
+ Optional spoken notifications use the browser's device-local speech engine.
153
+ Credential-shaped values and private home paths are redacted before speech;
154
+ notification text is not sent to a speech service.
155
+
156
+ Audio Share is a separate backend path for a node with a real speaker. Its
157
+ local consent defaults to off and is independent of node Share and visibility;
158
+ neither routing control grants permission to make sound. Calls use an
159
+ HMAC-proven active Fleet origin, exact node IDs, target-side ACL and rate
160
+ limits. Audio groups are local delivery preferences, never an authorization.
161
+ See [Audio Share and native TTS](AUDIO_SHARE.md).
162
+
163
+ ## Updates and process safety
164
+
165
+ Stable updates verify the new CLI and same-port runtime, and roll back once to
166
+ the exact previous version on failure. tmux sessions remain outside the
167
+ service process group.
168
+
169
+ Termux process handling verifies process identity before sending a signal.
170
+ Android PID reuse under another app UID is treated as a stale pidfile, never as
171
+ permission to signal the foreign process.
172
+
173
+ ## Report a security issue
174
+
175
+ Do not include live tokens, credentials, private keys or complete authenticated
176
+ links in a public issue. Use the repository's private security-reporting
177
+ channel when available.
178
+
179
+ ## Related guides
180
+
181
+ - [Connect nodes](NODES.md)
182
+ - [Configuration](CONFIGURATION.md)
183
+ - [Notifications](NOTIFICATIONS.md)
184
+ - [Audio Share and native TTS](AUDIO_SHARE.md)
185
+ - [Operations](OPERATIONS.md)