@cordfuse/crosstalk 7.0.0-alpha.9 → 7.0.0-beta.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.
- package/GUIDE-CLI.md +322 -0
- package/GUIDE-PROMPTS.md +118 -0
- package/LICENSE +21 -0
- package/README.md +402 -61
- package/bin/crosstalk.js +88 -54
- package/commands/agent.js +69 -0
- package/commands/auth.js +273 -0
- package/commands/channel.js +54 -44
- package/commands/chat.js +107 -71
- package/commands/daemon.js +120 -0
- package/commands/logs.js +108 -19
- package/commands/message.js +125 -0
- package/commands/server.js +153 -0
- package/commands/settings.js +49 -0
- package/commands/status.js +37 -13
- package/commands/token.js +136 -0
- package/commands/transport.js +270 -0
- package/commands/version.js +3 -3
- package/commands/workflow.js +234 -0
- package/deploy/crosstalk@.service +62 -0
- package/deploy/install.sh +82 -0
- package/lib/api-client.js +77 -22
- package/lib/credentials.js +207 -0
- package/lib/nativeServer.js +173 -0
- package/lib/resolve.js +101 -34
- package/package.json +27 -4
- package/src/activation.ts +104 -0
- package/src/api.ts +1716 -0
- package/src/auth/enforce.ts +68 -0
- package/src/auth/handlers.ts +266 -0
- package/src/auth/middleware.ts +132 -0
- package/src/auth/setup.ts +263 -0
- package/src/auth/tokens.ts +285 -0
- package/src/auth/users.ts +267 -0
- package/src/dispatch.ts +492 -0
- package/src/dispatchers.ts +91 -0
- package/src/filenames.ts +28 -0
- package/src/frontmatter.ts +26 -0
- package/src/init.ts +116 -0
- package/src/invoke.ts +201 -0
- package/src/log-buffer.ts +67 -0
- package/src/models.ts +283 -0
- package/src/resolve.ts +100 -0
- package/src/state.ts +190 -0
- package/src/stop.ts +37 -0
- package/src/transport.ts +243 -0
- package/src/web/auth-pages.ts +160 -0
- package/src/web/channels.ts +395 -0
- package/src/web/chat-page.ts +636 -0
- package/src/web/chat-pty.ts +254 -0
- package/src/web/dashboard.ts +129 -0
- package/src/web/layout.ts +237 -0
- package/src/web/stubs.ts +510 -0
- package/src/web/workflows.ts +490 -0
- package/src/workflow.ts +470 -0
- package/template/CLAUDE.md +10 -0
- package/template/CROSSTALK-VERSION +1 -0
- package/template/CROSSTALK.md +262 -0
- package/template/PROTOCOL.md +70 -0
- package/template/README.md +64 -0
- package/template/auth/.gitkeep +0 -0
- package/template/auth/README.md +224 -0
- package/template/data/crosstalk.yaml +196 -0
- package/template/gitignore +4 -0
- package/commands/down.js +0 -40
- package/commands/init.js +0 -243
- package/commands/pull.js +0 -22
- package/commands/replies.js +0 -40
- package/commands/restart.js +0 -29
- package/commands/rm.js +0 -109
- package/commands/run.js +0 -115
- package/commands/up.js +0 -135
package/README.md
CHANGED
|
@@ -1,97 +1,438 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Crosstalk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A shared message bus for humans and AI agents, built on git.**
|
|
4
4
|
|
|
5
|
-
> **
|
|
5
|
+
> **alpha.18 status.** Crosstalk is now 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`. Operators on v7 (Docker) or alpha.17 should follow the upgrade notes at the bottom of this file.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
> ### Six words that come up everywhere on this page
|
|
10
|
+
>
|
|
11
|
+
> - **Transport** — a git repo, scaffolded by `crosstalk transport init`. It IS the message 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, committed 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 git repo is the message bus.**
|
|
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 commits.** Every send is a markdown file with YAML frontmatter, committed and pushed. The whole conversation is the git history: nothing to lose, nothing hidden, nothing to back up separately.
|
|
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." Crosstalk inherits that.
|
|
41
|
+
- **Self-coordinating.** Collision-free filenames + rebase-retry on push mean the bus works correctly even with no central coordinator and concurrent writers.
|
|
42
|
+
|
|
43
|
+
If you can `git push`, you can participate. No infrastructure to provision, no central server holding your conversation.
|
|
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
|
|
13
102
|
```
|
|
14
103
|
|
|
15
|
-
|
|
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.
|
|
16
107
|
|
|
17
108
|
---
|
|
18
109
|
|
|
19
|
-
##
|
|
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).
|
|
20
113
|
|
|
21
114
|
```sh
|
|
22
|
-
# 1.
|
|
23
|
-
crosstalk
|
|
24
|
-
|
|
25
|
-
# 2.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
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>
|
|
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
|
|
44
134
|
```
|
|
45
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
|
+
|
|
46
140
|
---
|
|
47
141
|
|
|
48
|
-
##
|
|
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:**
|
|
49
218
|
|
|
50
|
-
|
|
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).
|
|
51
220
|
|
|
52
|
-
|
|
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
|
+
## Adding a second machine
|
|
239
|
+
|
|
240
|
+
Multi-machine adds exactly one new idea: **routing by alias.**
|
|
241
|
+
|
|
242
|
+
```sh
|
|
243
|
+
# On the second machine — user mode example
|
|
244
|
+
export CROSSTALK_USER_MODE=1
|
|
245
|
+
crosstalk transport init --remote <your-transport-url>
|
|
246
|
+
crosstalk server start
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
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.
|
|
250
|
+
|
|
251
|
+
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.
|
|
252
|
+
|
|
253
|
+
Full protocol: **[transport/CROSSTALK.md](transport/CROSSTALK.md)**.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Remote operation
|
|
258
|
+
|
|
259
|
+
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.
|
|
260
|
+
|
|
261
|
+
### Defaults
|
|
262
|
+
|
|
263
|
+
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.
|
|
264
|
+
|
|
265
|
+
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).
|
|
266
|
+
|
|
267
|
+
### Tailscale-fronted (recommended)
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# On the host running the engine — leave the engine bound to loopback,
|
|
271
|
+
# let Tailscale terminate TLS on the tailnet edge.
|
|
272
|
+
tailscale serve --bg --https=3445 http://localhost:7000
|
|
273
|
+
tailscale serve --bg --http=3082 http://localhost:7000
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
(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`.)
|
|
277
|
+
|
|
278
|
+
The host is now reachable as `https://<host>.tailnet.ts.net:3445` from any tailnet device.
|
|
279
|
+
|
|
280
|
+
**Cordfuse port conventions** (each app fronted on its own pair so multiple tools coexist on one tailnet host):
|
|
281
|
+
|
|
282
|
+
| App | HTTP front | HTTPS front |
|
|
53
283
|
|---|---|---|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
284
|
+
| llmux | `3080` | `3443` |
|
|
285
|
+
| vyzr | `3081` | `3444` |
|
|
286
|
+
| **crosstalk** | **`3082`** | **`3445`** |
|
|
287
|
+
|
|
288
|
+
### Logging into a remote engine
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Add the remote engine as a new profile alongside the local default.
|
|
292
|
+
crosstalk auth login --server https://crosstalk.example.com:3445 --profile prod
|
|
293
|
+
# Prompts for username + passphrase. Bearer token stored at
|
|
294
|
+
# ~/.config/crosstalk/credentials.json (mode 0600). The local 'default'
|
|
295
|
+
# profile is untouched.
|
|
296
|
+
|
|
297
|
+
crosstalk auth list
|
|
298
|
+
# PROFILE USERNAME SERVER
|
|
299
|
+
# * default steve http://127.0.0.1:7000
|
|
300
|
+
# prod ops https://crosstalk.example.com:3445
|
|
301
|
+
```
|
|
69
302
|
|
|
70
|
-
|
|
303
|
+
### Switching engines
|
|
304
|
+
|
|
305
|
+
Three ways, in priority order:
|
|
306
|
+
|
|
307
|
+
- **Per command:** `crosstalk --profile prod message send --to sonnet "..."` or `crosstalk --server <url> ...`
|
|
308
|
+
- **Switch the active profile:** `crosstalk auth use prod` — every subsequent command targets `prod` until you switch back
|
|
309
|
+
- **Default profile:** the active profile from `credentials.json`
|
|
310
|
+
|
|
311
|
+
### What stays local
|
|
312
|
+
|
|
313
|
+
A few things still need to be *on* the engine's host:
|
|
314
|
+
|
|
315
|
+
- `crosstalk server start|stop|restart` — these operate the local engine process (the systemd / pidfile lifecycle). They have no remote mode.
|
|
316
|
+
- `crosstalk chat <agent>` — spawns a foreground agent CLI in your terminal. The engine doesn't proxy it.
|
|
317
|
+
- 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.
|
|
318
|
+
|
|
319
|
+
Everything in the [CLI guide](GUIDE-CLI.md) under `channel`, `message`, `workflow`, `agent`, `logs`, `settings`, `status`, `token` works against any profile.
|
|
71
320
|
|
|
72
321
|
---
|
|
73
322
|
|
|
74
|
-
##
|
|
323
|
+
## Daemon internals
|
|
324
|
+
|
|
325
|
+
`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.
|
|
326
|
+
|
|
327
|
+
### Engine subcommands
|
|
328
|
+
|
|
329
|
+
| Subcommand | Purpose |
|
|
330
|
+
|---|---|
|
|
331
|
+
| `crosstalk daemon dispatch --alias <name>` | The dispatch loop. Started by `crosstalk server start` or the systemd unit. |
|
|
332
|
+
| `crosstalk daemon init <dir>` | Scaffold a transport template into `<dir>`. Used internally by `crosstalk transport init`. |
|
|
333
|
+
| `crosstalk daemon stop` | Signal SIGTERM to the running dispatcher via pidfile. |
|
|
334
|
+
|
|
335
|
+
### Auth model
|
|
336
|
+
|
|
337
|
+
Every operational HTTP endpoint requires bearer-token auth:
|
|
338
|
+
|
|
339
|
+
- `/healthz`, `/version` — open (monitoring probes).
|
|
340
|
+
- Everything else (`/channels`, `/messages`, `/status`, `/replies`, `/agents/installed`, all `/api/*`) — requires `Authorization: Bearer <token>` (or the equivalent session cookie for the web UI).
|
|
341
|
+
|
|
342
|
+
**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:
|
|
343
|
+
|
|
344
|
+
- The web UI uses cookie auth set on login.
|
|
345
|
+
- 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.
|
|
346
|
+
|
|
347
|
+
401 responses include a hint pointing at `crosstalk auth login` so operators discover the auth flow without reading docs.
|
|
348
|
+
|
|
349
|
+
User/token records live under `<base>/<name>/crosstalk-state/auth/` (`users.json`, `tokens.json`, mode 0600; never pushed to git).
|
|
350
|
+
|
|
351
|
+
### Web UI
|
|
75
352
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
353
|
+
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:
|
|
354
|
+
|
|
355
|
+
| Route | Purpose |
|
|
356
|
+
|---|---|
|
|
357
|
+
| `/` | Dashboard — engine heartbeat, claimed models, channels, pending work, SSE live updates |
|
|
358
|
+
| `/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 |
|
|
359
|
+
| `/c`, `/c/<handle>` | Channel list + threaded message view; send / rename / delete inline |
|
|
360
|
+
| `/w` | Workflow list + **compose form** (natural-language → `POST /api/workflows/compose` → editable YAML preview → `POST /api/workflows/submit`) |
|
|
361
|
+
| `/w/<childUuid>` | Live workflow detail with phase badge (compile / fanout / synthesize / complete / failed), per-dispatch + per-reply rows, completion banner, opt-in browser notification |
|
|
362
|
+
| `/agents` | Installed CLIs + yaml-referenced + currently-claimed catalog |
|
|
363
|
+
| `/tokens` | Mint + revoke API tokens; plaintext shown once at mint |
|
|
364
|
+
| `/account` | Sign out + change passphrase |
|
|
365
|
+
| `/users` | Admin only — create / delete / toggle-admin (self-protected) |
|
|
366
|
+
| `/logs` | Live SSE tail of the engine's structured event stream (`/api/logs/stream`) with filter + pause |
|
|
367
|
+
| `/settings`, `/about` | Read-only engine snapshot + product info |
|
|
368
|
+
|
|
369
|
+
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.
|
|
370
|
+
|
|
371
|
+
### Dependencies
|
|
372
|
+
|
|
373
|
+
- [`tsx`](https://www.npmjs.com/package/tsx) — run TypeScript directly without a build step (engine source lives under `src/`).
|
|
374
|
+
- [`yaml`](https://www.npmjs.com/package/yaml) — frontmatter and `data/crosstalk.yaml` parsing.
|
|
375
|
+
- [`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.
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Repository layout
|
|
380
|
+
|
|
381
|
+
alpha.18 collapsed the v7 `client/` + `engine/` split into one package:
|
|
382
|
+
|
|
383
|
+
- **`bin/crosstalk.js`** — the noun-verb CLI dispatcher (operator-facing entry).
|
|
384
|
+
- **`commands/`** — per-noun handlers (`auth`, `token`, `server`, `transport`, `channel`, `message`, `workflow`, `chat`, `agent`, `logs`, `settings`, `daemon`, `version`).
|
|
385
|
+
- **`lib/`** — shared client-side helpers (api client, credentials store, argv parser, error reporter, native engine lifecycle).
|
|
386
|
+
- **`src/`** — TypeScript engine code, executed via `tsx` at run time.
|
|
387
|
+
- **`template/`** — seed transport template that `crosstalk transport init` copies into a new transport (protocol spec, model registry, agent orientation prompts).
|
|
388
|
+
- **`deploy/`** — `install.sh` + `crosstalk@.service` systemd template for system-mode deployments.
|
|
389
|
+
- **`transport/`** — protocol artifacts (CROSSTALK.md, PROTOCOL.md, …) shared between client + engine.
|
|
390
|
+
|
|
391
|
+
Operator guides at the repo root:
|
|
392
|
+
|
|
393
|
+
- **[GUIDE-CLI.md](GUIDE-CLI.md)** — full noun-verb reference for every `crosstalk` subcommand.
|
|
394
|
+
- **[GUIDE-PROMPTS.md](GUIDE-PROMPTS.md)** — drive Crosstalk in plain language by `cd`-ing into a transport and running an agent CLI directly.
|
|
395
|
+
|
|
396
|
+
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
397
|
|
|
85
398
|
---
|
|
86
399
|
|
|
87
|
-
##
|
|
400
|
+
## Upgrading from alpha.17 / v7
|
|
401
|
+
|
|
402
|
+
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:
|
|
403
|
+
|
|
404
|
+
| Old | New |
|
|
405
|
+
|---|---|
|
|
406
|
+
| `crosstalk init` | `crosstalk transport init` |
|
|
407
|
+
| `crosstalk rm` | `crosstalk transport rm` |
|
|
408
|
+
| `crosstalk channel <name>` (bare) | `crosstalk channel create <name>` |
|
|
409
|
+
| `crosstalk channel <h> --rename <new>` | `crosstalk channel rename <h> <new>` |
|
|
410
|
+
| `crosstalk channel <h> --delete` | `crosstalk channel delete <h>` |
|
|
411
|
+
| `crosstalk run --type primitive --to <m> <body>` | `crosstalk message send --to <m> <body>` |
|
|
412
|
+
| `crosstalk run --type workflow <file>` | `crosstalk workflow run <file>` |
|
|
413
|
+
| `crosstalk replies <relPath>...` | `crosstalk message replies <relPath>...` |
|
|
414
|
+
| `crosstalk token mint <name>` | `crosstalk token create <name>` |
|
|
415
|
+
| `crosstalk chat --agent <name>` | `crosstalk chat <name>` (positional) |
|
|
416
|
+
| `crosstalk up / down / restart / pull / logs` (v7 shims) | gone — use `crosstalk server start / stop / restart` and `crosstalk server logs` |
|
|
417
|
+
| `crosstalkd` bin | gone — use `crosstalk daemon` |
|
|
418
|
+
| `@cordfuse/crosstalkd` package | deprecated — `@cordfuse/crosstalk` is now the only package |
|
|
419
|
+
| `~/.config/crosstalk/cli-token` | auto-migrated to `~/.config/crosstalk/credentials.json` on first read; old file deleted |
|
|
420
|
+
| systemd unit `crosstalkd@<name>` | `crosstalk@<name>` (rerun `deploy/install.sh`) |
|
|
421
|
+
|
|
422
|
+
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.
|
|
423
|
+
|
|
424
|
+
If you have a v7 Docker install:
|
|
88
425
|
|
|
89
|
-
|
|
426
|
+
1. `docker compose -f <your-compose-file> down` first.
|
|
427
|
+
2. `npm install -g @cordfuse/crosstalk@latest` — pulls alpha.18.
|
|
428
|
+
3. If you previously installed `@cordfuse/crosstalkd`: `npm uninstall -g @cordfuse/crosstalkd` (deprecated, no longer published from alpha.18).
|
|
429
|
+
4. In user mode: `export CROSSTALK_USER_MODE=1 && crosstalk server start --containername <name>`.
|
|
430
|
+
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
431
|
|
|
91
|
-
|
|
432
|
+
The v7 GHCR image `ghcr.io/cordfuse/crosstalk-server:*` is no longer being updated.
|
|
92
433
|
|
|
93
434
|
---
|
|
94
435
|
|
|
95
|
-
##
|
|
436
|
+
## Status
|
|
96
437
|
|
|
97
|
-
|
|
438
|
+
Crosstalk alpha.18 — single-package monorepo + strict noun-verb CLI. The protocol version lives at `CROSSTALK-VERSION` (single integer) at the transport root. Published as [`@cordfuse/crosstalk`](https://www.npmjs.com/package/@cordfuse/crosstalk).
|