@cordfuse/crosstalk 7.0.0-alpha.9 → 8.0.0-alpha.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 (72) hide show
  1. package/GUIDE-CLI.md +322 -0
  2. package/GUIDE-PROMPTS.md +118 -0
  3. package/LICENSE +21 -0
  4. package/README.md +422 -61
  5. package/bin/crosstalk.js +88 -54
  6. package/commands/agent.js +69 -0
  7. package/commands/auth.js +273 -0
  8. package/commands/channel.js +54 -44
  9. package/commands/chat.js +107 -71
  10. package/commands/daemon.js +120 -0
  11. package/commands/logs.js +108 -19
  12. package/commands/message.js +125 -0
  13. package/commands/server.js +185 -0
  14. package/commands/settings.js +49 -0
  15. package/commands/status.js +37 -13
  16. package/commands/token.js +136 -0
  17. package/commands/transport.js +299 -0
  18. package/commands/version.js +3 -3
  19. package/commands/workflow.js +234 -0
  20. package/deploy/crosstalk@.service +62 -0
  21. package/deploy/install.sh +82 -0
  22. package/lib/api-client.js +77 -22
  23. package/lib/credentials.js +207 -0
  24. package/lib/nativeServer.js +182 -0
  25. package/lib/resolve.js +106 -37
  26. package/package.json +27 -4
  27. package/src/activation.ts +104 -0
  28. package/src/api.ts +1717 -0
  29. package/src/auth/enforce.ts +68 -0
  30. package/src/auth/handlers.ts +266 -0
  31. package/src/auth/middleware.ts +132 -0
  32. package/src/auth/setup.ts +263 -0
  33. package/src/auth/tokens.ts +285 -0
  34. package/src/auth/users.ts +267 -0
  35. package/src/dispatch.ts +530 -0
  36. package/src/dispatchers.ts +91 -0
  37. package/src/filenames.ts +28 -0
  38. package/src/frontmatter.ts +26 -0
  39. package/src/init.ts +116 -0
  40. package/src/invoke.ts +201 -0
  41. package/src/log-buffer.ts +67 -0
  42. package/src/models.ts +300 -0
  43. package/src/resolve.ts +100 -0
  44. package/src/state.ts +191 -0
  45. package/src/stop.ts +37 -0
  46. package/src/transport.ts +353 -0
  47. package/src/web/auth-pages.ts +160 -0
  48. package/src/web/channels.ts +395 -0
  49. package/src/web/chat-page.ts +636 -0
  50. package/src/web/chat-pty.ts +254 -0
  51. package/src/web/dashboard.ts +130 -0
  52. package/src/web/layout.ts +237 -0
  53. package/src/web/stubs.ts +510 -0
  54. package/src/web/workflows.ts +490 -0
  55. package/src/workflow.ts +470 -0
  56. package/template/CLAUDE.md +10 -0
  57. package/template/CROSSTALK-VERSION +1 -0
  58. package/template/CROSSTALK.md +262 -0
  59. package/template/PROTOCOL.md +70 -0
  60. package/template/README.md +64 -0
  61. package/template/auth/.gitkeep +0 -0
  62. package/template/auth/README.md +224 -0
  63. package/template/data/crosstalk.yaml +196 -0
  64. package/template/gitignore +4 -0
  65. package/commands/down.js +0 -40
  66. package/commands/init.js +0 -243
  67. package/commands/pull.js +0 -22
  68. package/commands/replies.js +0 -40
  69. package/commands/restart.js +0 -29
  70. package/commands/rm.js +0 -109
  71. package/commands/run.js +0 -115
  72. package/commands/up.js +0 -135
package/README.md CHANGED
@@ -1,97 +1,458 @@
1
- # @cordfuse/crosstalk — host client
1
+ # Crosstalk
2
2
 
3
- The host-side CLI for the Crosstalk protocol. Pairs with [`@cordfuse/crosstalkd`](https://www.npmjs.com/package/@cordfuse/crosstalkd) the daemon running inside the [`crosstalk-server`](https://github.com/cordfuse/crosstalk/tree/main/server) container and the [transport template](https://github.com/cordfuse/crosstalk/tree/main/transport) it scaffolds.
3
+ **A shared message bus for humans and AI agents. The bus is a directory carried by git, or a plain/shared filesystem.**
4
4
 
5
- > **What Crosstalk is.** An agent-agnostic swarm communication protocol built on git. A git repo is the message bus. Full background: **[github.com/cordfuse/crosstalk](https://github.com/cordfuse/crosstalk#why-crosstalk-exists)**.
5
+ > **Status (8.x).** Crosstalk is a single npm package `@cordfuse/crosstalk` (the separate `@cordfuse/crosstalkd` daemon package is deprecated; the daemon lives as `crosstalk daemon dispatch` inside this same package). CLI surface is strict noun-verb, mirroring `@cordfuse/llmux`. **As of 8.x the bus is transport-pluggable:** git (distributed, versioned, auditable) is the default; a local or shared filesystem — `transport: local`, including a mounted SMB/NFS share — needs no git and no remote. Select per transport with the `transport:` key in `data/crosstalk.yaml`.
6
6
 
7
7
  ---
8
8
 
9
- ## Install
9
+ > ### Six words that come up everywhere on this page
10
+ >
11
+ > - **Transport** — the message bus, scaffolded by `crosstalk transport init`. A directory: a git repo (default — distributed, versioned) or a plain/shared filesystem (`transport: local`). It IS the bus.
12
+ > - **Machine** — a host running one crosstalk engine. Identifies itself via the dispatcher `--alias` (defaulting to the transport name).
13
+ > - **Message** — a markdown file with YAML frontmatter, written to a channel. The unit of work.
14
+ > - **Model** — a named agent invocation declared in `data/crosstalk.yaml` (e.g. `sonnet`, `codex-o3`).
15
+ > - **Actor** — an optional persona file (`local/actors/<name>.md`) that prepends to a model's prompt as system context.
16
+ > - **Channel** — a UUID directory under `data/channels/`. A conversation thread. Optionally parented.
17
+ >
18
+ > If you remember those six, the rest of this README will read smoothly.
19
+
20
+ ---
21
+
22
+ ## The problem
23
+
24
+ AI coding agents are powerful individually. The moment you want two of them to coordinate, you hit the orchestration question — and every existing answer forces a trade.
25
+
26
+ - **SDK-based frameworks** (LangChain, CrewAI, AutoGen, "agent OS" tools) call vendor APIs directly. They're built around a specific runtime and compose with vendor SDKs, not the agent CLIs you already run. Vendor-coupled by design.
27
+ - **Single-vendor CLI wrappers** (Anthropic's Claude Agent SDK, GitHub Actions integrations like `claude-code-action` or OpenAI's Codex action) spawn one vendor's CLI very well. They don't mix vendors, and they don't model agent-to-agent peer messaging.
28
+ - **Workflow orchestrators** (Temporal, Airflow, n8n) run anything you give them, but require their own server and scheduler. Not built for peer messaging between long-lived agents.
29
+ - **Custom shell glue** wiring `claude --print` into `codex exec` works for a one-shot demo. Breaks at the second machine. No audit trail, no retry semantics, no durability.
30
+
31
+ The missing piece: a way to make **any** agent CLI talk to **any** other agent CLI, **across machines**, with **no broker**, where the conversation is **durable and auditable** for free.
32
+
33
+ ## The solution
34
+
35
+ Crosstalk is that missing layer. The whole protocol is one idea — **a directory is the message bus.** Carry that directory with git (distributed, versioned, auditable) or a plain/shared filesystem (local, LAN, a mounted share) — same protocol either way.
36
+
37
+ - **Any CLI participates.** If an agent's CLI runs one prompt non-interactively and prints a reply, it's a valid model. Mix Claude Code, Codex, Gemini CLI, Qwen Code, opencode, Antigravity — or any future CLI that follows the same shape — in one transport.
38
+ - **Messages are files.** Every send is a markdown file with YAML frontmatter dropped into a channel. On the git transport each is a commit, so the whole conversation is git history — nothing to lose, nothing hidden, nothing to back up separately. On the filesystem transport it's just the files on disk.
39
+ - **Peer-to-peer.** Each machine runs its own crosstalk engine. No broker, no central runtime, no registry.
40
+ - **Multi-machine for free.** Git already solves "synchronize this across hosts"; a shared mount (SMB/NFS) does the same on a LAN. Crosstalk inherits whichever you point it at.
41
+ - **Self-coordinating.** Collision-free filenames mean concurrent writers never clash; the git transport adds rebase-retry on push, the filesystem transport relies on the unique filenames alone. Either way the bus works correctly with no central coordinator.
42
+
43
+ No infrastructure to provision, no central server holding your conversation — just a directory, on git or a filesystem.
44
+
45
+ ---
46
+
47
+ ## The one contract
48
+
49
+ A Crosstalk **model** is any CLI that:
50
+
51
+ 1. accepts a prompt (the dispatcher appends it as the CLI's last argument), and
52
+ 2. prints its reply to **stdout**, then exits `0`.
53
+
54
+ That's it. No SDK, no plugin, no adapter. See [GUIDE-CLI.md](GUIDE-CLI.md#models) for ready-made entries per agent CLI.
55
+
56
+ ---
57
+
58
+ ## Install + try it locally (user mode)
59
+
60
+ User mode is the right choice for solo dev or any setup where the operator IS the only participant on this machine. The daemon runs as your own user; auth tokens land in the regular `~/.claude`, `~/.codex` etc. spots; no sudo needed.
10
61
 
11
62
  ```sh
63
+ # 1. Install the CLI. One package now — daemon is a subcommand.
12
64
  npm install -g @cordfuse/crosstalk
65
+ # (Requires Node 20+ and git on PATH.)
66
+
67
+ # 2. Opt into user mode for this shell session (system mode is the default).
68
+ export CROSSTALK_USER_MODE=1
69
+
70
+ # 3. Scaffold a transport. Default name is 'crosstalk'; pass
71
+ # --containername <name> for a second/named one. Storage lives under
72
+ # ~/.local/share/crosstalk/<name>/ (or the per-OS XDG equivalent).
73
+ crosstalk transport init
74
+
75
+ # 4. First-run setup. The engine prints a /setup?token=... URL at boot —
76
+ # visit it once to create the admin user. After that, CLI auth lives at
77
+ # ~/.config/crosstalk/credentials.json (multi-profile; see `auth list`).
78
+ crosstalk server start
79
+ # (Open the printed URL, finish the wizard, then return here.)
80
+ crosstalk auth login
81
+
82
+ # 5. Edit data/crosstalk.yaml — uncomment a provider block (e.g.
83
+ # anthropic-personal) and drop your env vars into auth/<provider>.env
84
+ # per transport/auth/README.md. Install the agent CLI in your shell:
85
+ npm install -g @anthropic-ai/claude-code
86
+
87
+ # 6. Create a channel.
88
+ crosstalk channel create general
89
+
90
+ # 7. Send a primitive. The engine picks it up, invokes Claude,
91
+ # and commits the reply back into this same git repo.
92
+ crosstalk message send --to sonnet "What is the capital of France?"
93
+
94
+ # 8. Wait a few seconds, then check for replies:
95
+ crosstalk message replies <relPath printed by step 7>
96
+
97
+ # Lifecycle: stop / status / tail logs / restart
98
+ crosstalk server stop
99
+ crosstalk server status --probe
100
+ crosstalk server logs -f
101
+ crosstalk server restart
102
+ ```
103
+
104
+ That's the entire surface area, single-machine. The engine reads `data/crosstalk.yaml`, checks PATH for the first token of each entry, and claims the models whose CLI is installed.
105
+
106
+ To set up a remote, either pass `--remote <url>` to `crosstalk transport init` at scaffold time, or `cd` into `~/.local/share/crosstalk/<name>/transport/` and run raw git.
107
+
108
+ ---
109
+
110
+ ## Install + run as a service (system mode)
111
+
112
+ System mode is the right choice for a headless host, a shared workstation, or any deploy where you want the engine to start on boot, run as a non-login service user, and be controlled by systemd. This is the default mode (no env var to set).
113
+
114
+ ```sh
115
+ # 1. Install the CLI globally.
116
+ sudo npm install -g @cordfuse/crosstalk
117
+
118
+ # 2. Run the installer — creates the 'crosstalk' system user, /etc and
119
+ # /var/lib directories, and the systemd template unit.
120
+ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/cordfuse/crosstalk/main/deploy/install.sh)"
121
+ # Or, if you cloned the repo: sudo ./deploy/install.sh
122
+
123
+ # 3. Initialize a transport AS the service user.
124
+ sudo -u crosstalk crosstalk transport init --containername main
125
+
126
+ # 4. Enable + start the systemd instance for it.
127
+ sudo systemctl enable --now crosstalk@main
128
+ sudo systemctl status crosstalk@main
129
+ sudo journalctl -u crosstalk@main -f # or: tail -f /var/log/crosstalk/main.log
130
+
131
+ # Additional named transports: repeat with a different --containername.
132
+ sudo -u crosstalk crosstalk transport init --containername staging
133
+ sudo systemctl enable --now crosstalk@staging
134
+ ```
135
+
136
+ Storage lives at `/var/lib/crosstalk/<name>/`, owned by the `crosstalk` user. Agent CLI auth tokens land under `/var/lib/crosstalk/.claude/`, `/var/lib/crosstalk/.codex/`, etc. — the service user's home. The daemon never touches `/home/*`.
137
+
138
+ In system mode, `crosstalk server start|stop|restart` invocations from the CLI print a hint redirecting the operator to `sudo systemctl <verb> crosstalk@<name>` — the right lifecycle when systemd owns the process.
139
+
140
+ ---
141
+
142
+ ## Pick your agent
143
+
144
+ Models are declared in `data/crosstalk.yaml` — nested under providers. Each provider may declare an `env_file:` pointing at a per-provider dotenv file under `auth/`. The engine reads it at agent spawn and merges into the subprocess env. Addressing is `<provider>/<model>` (bare names also work when unambiguous).
145
+
146
+ ```yaml
147
+ # data/crosstalk.yaml
148
+ providers:
149
+ anthropic-personal:
150
+ env_file: auth/anthropic-personal.env # CLAUDE_CODE_OAUTH_TOKEN=...
151
+ models:
152
+ sonnet: claude --print --dangerously-skip-permissions --model sonnet
153
+ haiku: claude --print --dangerously-skip-permissions --model haiku
154
+
155
+ google-personal:
156
+ env_file: auth/google-personal.env # GEMINI_API_KEY=...
157
+ models:
158
+ gemini-pro: gemini --skip-trust --yolo -p --model gemini-1.5-pro
159
+ agy: agy --print
160
+
161
+ openrouter:
162
+ env_file: auth/openrouter.env # OPENAI_API_KEY=sk-or-... + OPENAI_BASE_URL=...
163
+ models:
164
+ qwen3-coder: qwen --auth-type openai --yolo --model qwen/qwen3-coder
165
+ ```
166
+
167
+ Per-agent install commands + the env vars each agent reads are documented in [transport/auth/README.md](transport/auth/README.md). For the auth schema design rationale, see [V8-AUTH-DESIGN.md](V8-AUTH-DESIGN.md).
168
+
169
+ ---
170
+
171
+ ## Workflows
172
+
173
+ For multi-step work you have two paths:
174
+
175
+ ```sh
176
+ # Interactive — describe what you want in plain language; engine compiles
177
+ # it into a YAML plan, you review/edit, then submit:
178
+ crosstalk workflow compose "Fan out 3 sonnet drafts and have opus synthesize"
179
+
180
+ # Or hand-author the YAML/markdown and submit directly:
181
+ crosstalk workflow run workflows/review-and-synthesize.md
182
+ ```
183
+
184
+ Example `workflows/review-and-synthesize.md`:
185
+
186
+ ```markdown
187
+ ---
188
+ type: workflow
189
+ to: opus@cachy
190
+ as: orchestrator
191
+ ---
192
+
193
+ 1. Fan out 3 junior developers running sonnet@laptop. Each drafts the
194
+ proposal in their own voice.
195
+ 2. Send the 3 drafts to a reviewer running opus@cachy to synthesize
196
+ into a single coherent proposal.
197
+ 3. Return the synthesis to the original requester.
198
+ ```
199
+
200
+ The runtime auto-creates a child channel and takes over deterministically: one LLM call compiles the prose body into a `{fanout, synthesize}` JSON plan, `plan.fanout.count` sub-primitives fire into the child channel (scoped via the dispatcher registry to avoid duplicate work across hosts), then once their replies are all in, one synthesis sub-primitive aggregates them, and the synthesis reply gets routed back to you in the parent channel.
201
+
202
+ `crosstalk workflow status` lists in-flight workflows + their phase (compile / fanout / synthesize / complete / failed).
203
+
204
+ ---
205
+
206
+ ## Two ways to use it
207
+
208
+ - **[GUIDE-CLI.md](GUIDE-CLI.md)** — drive Crosstalk with the `crosstalk` command. For scripts and operators who like a terminal.
209
+ - **[GUIDE-PROMPTS.md](GUIDE-PROMPTS.md)** — drive Crosstalk in plain language. `crosstalk chat <agent>` spawns an interactive agent CLI on the host that reads `PROTOCOL.md` from the transport and runs `crosstalk` commands on your behalf. The agent process is a foreground child — when you exit, it's gone (no persistent live agents; crosstalk is async-by-design).
210
+
211
+ ---
212
+
213
+ ## Putting the transport on a git remote
214
+
215
+ The local-only quickstart left your transport as a plain local git repo — no `origin`, no `push`. That's fine for solo experimentation. To collaborate or run a second machine, push the transport to any git host that takes ssh:// or https://.
216
+
217
+ **Example with GitHub:**
218
+
219
+ 1. Create an empty repo on GitHub. Don't check the "initialize with README" box — the transport already has the right contents, and any pre-seeded files will conflict on first push. A private repo is the common choice (a transport is your conversation history; treat it like a project repo, not a public one).
220
+
221
+ 2. From inside your local transport directory:
222
+
223
+ ```sh
224
+ git remote add origin git@github.com:<you>/<your-transport>.git
225
+ git push -u origin main
226
+ ```
227
+
228
+ Use SSH (`git@github.com:…`) rather than HTTPS — the dispatcher commits replies back continuously, and SSH avoids the credential prompts an HTTPS remote would otherwise require.
229
+
230
+ 3. The push will include `CROSSTALK-VERSION`, `PROTOCOL.md`, `CROSSTALK.md`, `data/crosstalk.yaml`, `local/actors/orchestrator.md`, and any channels you've created. No state, no secrets — everything in the repo is meant to be shared with the other participants.
231
+
232
+ Same flow works for self-hosted Gitea or GitLab. Crosstalk doesn't care; it only talks git.
233
+
234
+ > **Heads up: SSH access.** Every machine that runs the engine needs to be able to `git push` to the transport repo, because the engine commits replies and pushes them. In user mode the daemon uses your operator `~/.ssh/`; in system mode it uses `/var/lib/crosstalk/.ssh/` (drop a deploy key there as the `crosstalk` user).
235
+
236
+ ---
237
+
238
+ ## Local filesystem transport (no git)
239
+
240
+ Don't want a git remote? Point Crosstalk at a plain directory instead — a local folder for a single host, or a **mounted SMB/NFS share** for several hosts on a LAN. No git, no remote, no infrastructure.
241
+
242
+ ```sh
243
+ # Scaffold a local-fs transport straight onto the directory (e.g. a NAS mount):
244
+ crosstalk transport init --transport local --path /mnt/share/crosstalk
245
+
246
+ # Run the engine on it — the bus lives on the share, state stays machine-local:
247
+ crosstalk server start --path /mnt/share/crosstalk
13
248
  ```
14
249
 
15
- Requires: Node.js 20 + Docker on PATH + git on PATH. Works on Linux, macOS, Windows (Docker Desktop).
250
+ `--transport local` writes `transport: local` into `data/crosstalk.yaml` (no git init), and the dispatcher self-selects the filesystem transport: it reads/writes message files directly and picks up new ones via an mtime cursor — event-driven `fs.watch` on a true local FS, polling over a network mount. Each host that mounts the share runs its own engine and keeps its own machine-local cursor/heartbeat, so concurrent writers never clash (collision-free filenames).
251
+
252
+ Tradeoffs vs. git: no version history, no audit trail, no conflict resolution beyond the unique filenames — but zero setup and no SSH/credentials. Reach for git when *what was said* matters; reach for the filesystem transport for quick local/LAN swarms.
253
+
254
+ > **SFTP?** Mount it with `sshfs` and use `transport: local` — Crosstalk just sees a directory.
16
255
 
17
256
  ---
18
257
 
19
- ## Quickstart
258
+ ## Adding a second machine
259
+
260
+ Multi-machine adds exactly one new idea: **routing by alias.**
20
261
 
21
262
  ```sh
22
- # 1. Scaffold a transport. Runtime-owned at <base>/crosstalk/transport/.
23
- crosstalk init
24
-
25
- # 2. Bring up the engine container.
26
- crosstalk up
27
- # Generates <base>/crosstalk/docker-compose.yml and runs `docker
28
- # compose up -d` for ghcr.io/cordfuse/crosstalk-server:<latest>.
29
- # No cwd magic — works from any directory.
30
-
31
- # 3. Install an agent CLI inside the container, then authenticate.
32
- crosstalk chat --shell
33
- npm install -g @anthropic-ai/claude-code
34
- exit
35
- crosstalk chat --agent claude --login
36
- # Auth URL opens in your default browser via xdg-open / open / start.
37
- # Token saved in the /crosstalk-root volume so subsequent dispatched
38
- # workers reuse it.
39
-
40
- # 4. Send your first message.
41
- crosstalk channel general
42
- crosstalk run --type primitive --to sonnet "What is the capital of France?"
43
- crosstalk replies <relPath printed by step above>
263
+ # On the second machine user mode example
264
+ export CROSSTALK_USER_MODE=1
265
+ crosstalk transport init --remote <your-transport-url>
266
+ crosstalk server start
44
267
  ```
45
268
 
269
+ Now any send addressed to `--to sonnet@server` lands on the server (alias defaults to the transport name; override at start time). Bare `--to sonnet` (no `@machine`) reaches whichever dispatcher claiming `sonnet` picks it up first.
270
+
271
+ There are no host files to author. There is no per-machine declaration committed to the transport. A machine's identity is just its dispatcher alias. The model registry (`data/crosstalk.yaml`) is shared by everyone; each dispatcher claims the entries whose CLI is installed on that machine.
272
+
273
+ Full protocol: **[transport/CROSSTALK.md](transport/CROSSTALK.md)**.
274
+
46
275
  ---
47
276
 
48
- ## Subcommand surface
277
+ ## Remote operation
278
+
279
+ The CLI can target **any reachable engine** — local loopback, another machine on your LAN, a teammate's box over Tailscale. Multi-profile auth lets one CLI hold credentials for several engines and switch between them per command.
49
280
 
50
- Every name-resolved verb takes an optional `--containername <name>` (or `-c <name>`) flag. Bare verb = default container (literally named `crosstalk`).
281
+ ### Defaults
51
282
 
52
- | Group | Subcommand | What it does |
283
+ The engine binds **`127.0.0.1` only** out of the box. The bearer-token gate is enforced on every operational endpoint regardless of bind address — even local CLI calls authenticate the same way a remote one would. Loopback is defense-in-depth, not the security boundary.
284
+
285
+ To accept connections from off-host, set `CROSSTALK_API_BIND=0.0.0.0` (or a specific interface) at engine start. **Don't expose bare HTTP on a public IP** — auth alone doesn't replace TLS. Front the daemon with something that terminates TLS and validates source (Tailscale serve recommended; nginx / Caddy also fine).
286
+
287
+ ### Tailscale-fronted (recommended)
288
+
289
+ ```bash
290
+ # On the host running the engine — leave the engine bound to loopback,
291
+ # let Tailscale terminate TLS on the tailnet edge.
292
+ tailscale serve --bg --https=3445 http://localhost:7000
293
+ tailscale serve --bg --http=3082 http://localhost:7000
294
+ ```
295
+
296
+ (Substitute the engine's actual port — `7000` for the default transport, `7001+` for named ones. Find it at `<base>/<name>/api-port` or in `crosstalk server status`.)
297
+
298
+ The host is now reachable as `https://<host>.tailnet.ts.net:3445` from any tailnet device.
299
+
300
+ **Cordfuse port conventions** (each app fronted on its own pair so multiple tools coexist on one tailnet host):
301
+
302
+ | App | HTTP front | HTTPS front |
53
303
  |---|---|---|
54
- | Meta | `crosstalk version` | Print client + engine versions (warns on skew) |
55
- | Protocol | `crosstalk init [-c <name>] [--remote <url>]` | Scaffold a transport at `<base>/<name>/transport/` |
56
- | Protocol | `crosstalk run --type primitive\|workflow ...` | Dispatch a primitive or workflow |
57
- | Protocol | `crosstalk replies <relPath>...` | Poll reply status |
58
- | Protocol | `crosstalk status` | Container name, transport host path, engine state, channels |
59
- | Protocol | `crosstalk channel <name> [--rename\|--delete]` | Channel operations |
60
- | Lifecycle | `crosstalk up` | Generate docker-compose, start the engine container |
61
- | Lifecycle | `crosstalk down` | Stop the engine container (keep storage) |
62
- | Lifecycle | `crosstalk rm [--force]` | Remove container AND wipe storage (cannot undo) |
63
- | Lifecycle | `crosstalk restart` | Down + up |
64
- | Lifecycle | `crosstalk pull` | Refresh the engine image |
65
- | Lifecycle | `crosstalk logs [-f]` | Stream the engine container's logs |
66
- | Interactive | `crosstalk chat --agent <name>` | Interactive session with an agent CLI inside the container |
67
- | Interactive | `crosstalk chat --agent <name> --login` | OAuth flow with browser-open URL handling |
68
- | Interactive | `crosstalk chat --shell` | Drop into bash inside the container (debug, install CLIs) |
304
+ | llmux | `3080` | `3443` |
305
+ | vyzr | `3081` | `3444` |
306
+ | **crosstalk** | **`3082`** | **`3445`** |
307
+
308
+ ### Logging into a remote engine
69
309
 
70
- `--agent` is required for any non-shell chat — containers can have multiple installed CLIs, and the operator must pick explicitly. The error message names which ones are actually installed (queried from the engine's `/agents/installed` endpoint).
310
+ ```bash
311
+ # Add the remote engine as a new profile alongside the local default.
312
+ crosstalk auth login --server https://crosstalk.example.com:3445 --profile prod
313
+ # Prompts for username + passphrase. Bearer token stored at
314
+ # ~/.config/crosstalk/credentials.json (mode 0600). The local 'default'
315
+ # profile is untouched.
316
+
317
+ crosstalk auth list
318
+ # PROFILE USERNAME SERVER
319
+ # * default steve http://127.0.0.1:7000
320
+ # prod ops https://crosstalk.example.com:3445
321
+ ```
322
+
323
+ ### Switching engines
324
+
325
+ Three ways, in priority order:
326
+
327
+ - **Per command:** `crosstalk --profile prod message send --to sonnet "..."` or `crosstalk --server <url> ...`
328
+ - **Switch the active profile:** `crosstalk auth use prod` — every subsequent command targets `prod` until you switch back
329
+ - **Default profile:** the active profile from `credentials.json`
330
+
331
+ ### What stays local
332
+
333
+ A few things still need to be *on* the engine's host:
334
+
335
+ - `crosstalk server start|stop|restart` — these operate the local engine process (the systemd / pidfile lifecycle). They have no remote mode.
336
+ - `crosstalk chat <agent>` — spawns a foreground agent CLI in your terminal. The engine doesn't proxy it.
337
+ - The transport's git remote (separate from the engine's HTTP API) — each engine still needs `git push` access to the bare origin to commit replies. The "Putting the transport on a git remote" + "Adding a second machine" sections above cover that.
338
+
339
+ Everything in the [CLI guide](GUIDE-CLI.md) under `channel`, `message`, `workflow`, `agent`, `logs`, `settings`, `status`, `token` works against any profile.
340
+
341
+ ---
342
+
343
+ ## Daemon internals
344
+
345
+ `crosstalk daemon dispatch` is what runs under the hood — `crosstalk server start` spawns it as a detached host process (user mode), and the systemd unit invokes it as `ExecStart=` (system mode). Operators don't typically run it directly; the wrappers exist so the lifecycle is the same in both modes.
346
+
347
+ ### Engine subcommands
348
+
349
+ | Subcommand | Purpose |
350
+ |---|---|
351
+ | `crosstalk daemon dispatch --alias <name>` | The dispatch loop. Started by `crosstalk server start` or the systemd unit. |
352
+ | `crosstalk daemon init <dir>` | Scaffold a transport template into `<dir>`. Used internally by `crosstalk transport init`. |
353
+ | `crosstalk daemon stop` | Signal SIGTERM to the running dispatcher via pidfile. |
354
+
355
+ ### Auth model
356
+
357
+ Every operational HTTP endpoint requires bearer-token auth:
358
+
359
+ - `/healthz`, `/version` — open (monitoring probes).
360
+ - Everything else (`/channels`, `/messages`, `/status`, `/replies`, `/agents/installed`, all `/api/*`) — requires `Authorization: Bearer <token>` (or the equivalent session cookie for the web UI).
361
+
362
+ **First-run setup** is a one-time web wizard. The engine boot prints a `/setup?token=…` URL (logged as `auth_first_run_setup_token`); the operator visits it to create the first admin user. After that:
363
+
364
+ - The web UI uses cookie auth set on login.
365
+ - The CLI stores credentials at `~/.config/crosstalk/credentials.json` (XDG-aware, mode 0600), obtained via `crosstalk auth login`. The file is multi-profile (one CLI, multiple engines); see `crosstalk auth list`. Each call sends `Authorization: Bearer …` from the active profile automatically.
366
+
367
+ 401 responses include a hint pointing at `crosstalk auth login` so operators discover the auth flow without reading docs.
368
+
369
+ User/token records live under `<base>/<name>/crosstalk-state/auth/` (`users.json`, `tokens.json`, mode 0600; never pushed to git).
370
+
371
+ ### Web UI
372
+
373
+ The engine serves a complete web UI on the same loopback port. After `crosstalk server start`, visit `http://127.0.0.1:<port>/` in a browser. Routes:
374
+
375
+ | Route | Purpose |
376
+ |---|---|
377
+ | `/` | Dashboard — engine heartbeat, claimed models, channels, pending work, SSE live updates |
378
+ | `/chat` | Interactive chat panel — pick an agent CLI + cwd, attach via WebSocket (`/ws/chat`) to a fresh pty. `/exit` or the kill button SIGTERMs/SIGKILLs the agent. Async-by-design: no persistent sessions |
379
+ | `/c`, `/c/<handle>` | Channel list + threaded message view; send / rename / delete inline |
380
+ | `/w` | Workflow list + **compose form** (natural-language → `POST /api/workflows/compose` → editable YAML preview → `POST /api/workflows/submit`) |
381
+ | `/w/<childUuid>` | Live workflow detail with phase badge (compile / fanout / synthesize / complete / failed), per-dispatch + per-reply rows, completion banner, opt-in browser notification |
382
+ | `/agents` | Installed CLIs + yaml-referenced + currently-claimed catalog |
383
+ | `/tokens` | Mint + revoke API tokens; plaintext shown once at mint |
384
+ | `/account` | Sign out + change passphrase |
385
+ | `/users` | Admin only — create / delete / toggle-admin (self-protected) |
386
+ | `/logs` | Live SSE tail of the engine's structured event stream (`/api/logs/stream`) with filter + pause |
387
+ | `/settings`, `/about` | Read-only engine snapshot + product info |
388
+
389
+ Off-host access (e.g., from a phone): the engine binds 127.0.0.1 only — front it with `tailscale serve` or similar TLS-terminating proxy.
390
+
391
+ ### Dependencies
392
+
393
+ - [`tsx`](https://www.npmjs.com/package/tsx) — run TypeScript directly without a build step (engine source lives under `src/`).
394
+ - [`yaml`](https://www.npmjs.com/package/yaml) — frontmatter and `data/crosstalk.yaml` parsing.
395
+ - [`ws`](https://www.npmjs.com/package/ws) + [`node-pty`](https://www.npmjs.com/package/node-pty) — WebSocket bridge for the `/chat` web panel (browser xterm.js ↔ host pty). Native build via `node-pty`'s prebuilds; falls back to source build on uncommon platforms.
71
396
 
72
397
  ---
73
398
 
74
- ## What lives where
399
+ ## Repository layout
400
+
401
+ alpha.18 collapsed the v7 `client/` + `engine/` split into one package:
402
+
403
+ - **`bin/crosstalk.js`** — the noun-verb CLI dispatcher (operator-facing entry).
404
+ - **`commands/`** — per-noun handlers (`auth`, `token`, `server`, `transport`, `channel`, `message`, `workflow`, `chat`, `agent`, `logs`, `settings`, `daemon`, `version`).
405
+ - **`lib/`** — shared client-side helpers (api client, credentials store, argv parser, error reporter, native engine lifecycle).
406
+ - **`src/`** — TypeScript engine code, executed via `tsx` at run time.
407
+ - **`template/`** — seed transport template that `crosstalk transport init` copies into a new transport (protocol spec, model registry, agent orientation prompts).
408
+ - **`deploy/`** — `install.sh` + `crosstalk@.service` systemd template for system-mode deployments.
409
+ - **`transport/`** — protocol artifacts (CROSSTALK.md, PROTOCOL.md, …) shared between client + engine.
410
+
411
+ Operator guides at the repo root:
412
+
413
+ - **[GUIDE-CLI.md](GUIDE-CLI.md)** — full noun-verb reference for every `crosstalk` subcommand.
414
+ - **[GUIDE-PROMPTS.md](GUIDE-PROMPTS.md)** — drive Crosstalk in plain language by `cd`-ing into a transport and running an agent CLI directly.
75
415
 
76
- - **Host** `crosstalk` binary on PATH. Per-transport storage at `<base>/<name>/` (one tree per container).
77
- - **Container** (literally named whatever the operator picked at `init`, default `crosstalk`; image `ghcr.io/cordfuse/crosstalk-server`) — `crosstalkd` daemon, bind-mounted from:
78
- - `<base>/<name>/transport/` → `/var/lib/crosstalk-transport` (runtime-owned git repo)
79
- - `<base>/<name>/crosstalk-root/` → `/crosstalk-root` (operator CLIs + auth)
80
- - `<base>/<name>/crosstalk-state/` → `/var/lib/crosstalk-state` (cursor + heartbeat)
81
- - **`<base>`** is per-OS user-mode: `$XDG_DATA_HOME/crosstalk` on Linux, `~/Library/Application Support/crosstalk` on macOS, `%LOCALAPPDATA%\crosstalk` on Windows. `CROSSTALK_STORAGE_MODE=system` switches to `/var/lib`-style paths.
82
- - **Engine HTTP API** — `127.0.0.1:<port>`. Default container = port 7000; named containers allocate at init from 7001 upward, recorded in `<base>/<name>/api-port`.
83
- - **Container identification** — every crosstalkd container gets the Docker label `crosstalk.transport=true`. The client uses the label for inventory queries, not name-pattern matching.
416
+ Design docs: **[V7-SPEC.md](V7-SPEC.md)** (the v7 protocol spec, still authoritative), **[V8-AUTH-DESIGN.md](V8-AUTH-DESIGN.md)** (v8 auth schema), **[V8-CLI-ALIGN.md](V8-CLI-ALIGN.md)** (the alpha.18 noun-verb plan + migration table).
84
417
 
85
418
  ---
86
419
 
87
- ## Architecture
420
+ ## Upgrading from alpha.17 / v7
421
+
422
+ alpha.18 is a clean break — every old verb form is gone, no shims. The full mapping lives in [V8-CLI-ALIGN.md](V8-CLI-ALIGN.md); the highlights:
423
+
424
+ | Old | New |
425
+ |---|---|
426
+ | `crosstalk init` | `crosstalk transport init` |
427
+ | `crosstalk rm` | `crosstalk transport rm` |
428
+ | `crosstalk channel <name>` (bare) | `crosstalk channel create <name>` |
429
+ | `crosstalk channel <h> --rename <new>` | `crosstalk channel rename <h> <new>` |
430
+ | `crosstalk channel <h> --delete` | `crosstalk channel delete <h>` |
431
+ | `crosstalk run --type primitive --to <m> <body>` | `crosstalk message send --to <m> <body>` |
432
+ | `crosstalk run --type workflow <file>` | `crosstalk workflow run <file>` |
433
+ | `crosstalk replies <relPath>...` | `crosstalk message replies <relPath>...` |
434
+ | `crosstalk token mint <name>` | `crosstalk token create <name>` |
435
+ | `crosstalk chat --agent <name>` | `crosstalk chat <name>` (positional) |
436
+ | `crosstalk up / down / restart / pull / logs` (v7 shims) | gone — use `crosstalk server start / stop / restart` and `crosstalk server logs` |
437
+ | `crosstalkd` bin | gone — use `crosstalk daemon` |
438
+ | `@cordfuse/crosstalkd` package | deprecated — `@cordfuse/crosstalk` is now the only package |
439
+ | `~/.config/crosstalk/cli-token` | auto-migrated to `~/.config/crosstalk/credentials.json` on first read; old file deleted |
440
+ | systemd unit `crosstalkd@<name>` | `crosstalk@<name>` (rerun `deploy/install.sh`) |
441
+
442
+ Transport git repos are portable — the storage path is unchanged (`~/.local/share/crosstalk/<name>/transport/` in user mode; `/var/lib/crosstalk/<name>/transport/` in system mode). You don't need to re-init.
443
+
444
+ If you have a v7 Docker install:
88
445
 
89
- Mirrors `docker` (host CLI) / `dockerd` (daemon). The split keeps the protocol-layer logic in one place (the engine) and exposes a clean operator surface on the host without forcing operators to manage Node/agent CLIs/OAuth tokens on their bare host.
446
+ 1. `docker compose -f <your-compose-file> down` first.
447
+ 2. `npm install -g @cordfuse/crosstalk@latest` — pulls the latest 8.x.
448
+ 3. If you previously installed `@cordfuse/crosstalkd`: `npm uninstall -g @cordfuse/crosstalkd` (deprecated, no longer published since alpha.18).
449
+ 4. In user mode: `export CROSSTALK_USER_MODE=1 && crosstalk server start --containername <name>`.
450
+ 5. In system mode: rerun `sudo ./deploy/install.sh` to lay down the new `crosstalk@.service` unit, then `sudo systemctl daemon-reload && sudo systemctl enable --now crosstalk@<name>`. The old `crosstalkd@<name>.service` files can be removed.
90
451
 
91
- Client makes plain HTTP requests to the engine over loopback. No auth (same model as ollama, postgres-on-localhost, dockerd's local socket; the loopback bind is what gates exposure). Version skew detected via `X-Crosstalk-Engine-Version` response header — client warns once per process when the operator forgot to `crosstalk pull && crosstalk restart` after upgrading the client (or vice versa).
452
+ The v7 GHCR image `ghcr.io/cordfuse/crosstalk-server:*` is no longer being updated.
92
453
 
93
454
  ---
94
455
 
95
- ## License
456
+ ## Status
96
457
 
97
- MIT.
458
+ Crosstalk 8.x — single-package monorepo + strict noun-verb CLI, transport-pluggable (git or a local/shared filesystem). The protocol version lives at `CROSSTALK-VERSION` (single integer) at the transport root. Published as [`@cordfuse/crosstalk`](https://www.npmjs.com/package/@cordfuse/crosstalk).