eleven / ocho Public

docs: keep the Node sidecar as a deliberate reference implementation (#326)

The Go port is done and is the default, so the docs still saying 'planned Go
port' / 'planned default' are stale. Update agent/README.md, docs/bots.md, and
CLAUDE.md to state the intent explicitly: agent/ is kept ON PURPOSE as a
supported, runnable Node reference — a worked example of running a bot in Node,
and the clearest illustration of the E2EE wire format (the Go tests pin
themselves to these JS sources, so the reference actively guards the port
against drift). Not deprecated, not scheduled for removal, don't let it rot.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 2c3e54312340cd614f94adde3b70af930e689a46 parent 0e47152
3 files changed, +44 −21
  • CLAUDE.md +10 −6
  • agent/README.md +12 −4
  • docs/bots.md +22 −11
diff --git a/CLAUDE.md b/CLAUDE.md
index ce2c8f1..572d85b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -51,8 +51,10 @@ holds a usable key. Two invariants follow: **bot code never runs inside the
server process** (compromising the server must not yield bot keys or the
Anthropic key), and the sidecar stays a **pure outbound client** (no inbound
ports), so operators can move it to a better-defended machine — off-box, behind
-NAT, or in a no-inbound private network. Trust model + the planned Go port (a
-`lchat bot` subcommand; `agent/` remains the Node reference): `docs/bots.md`.
+NAT, or in a no-inbound private network. Trust model + the Go sidecar (the
+default now — `lchat bot run`, a subcommand of the single binary, no Node;
+`agent/` is kept **on purpose** as the runnable Node reference, not deprecated):
+`docs/bots.md`.
Native iOS push goes through a central APNs relay (`push.elevenmessenger.com`,
`push/`) so the Apple `.p8` signing key lives in exactly one place instead of on
@@ -83,11 +85,13 @@ in, so deploy is one file (no cgo).
**Authoring guide: `web/static/apps/README.md`** — read it before writing or
changing a mini-app.
- `push.go` — Web Push (self-hosted VAPID, `SherClockHolmes/webpush-go`).
-- `agent/` — the bot sidecar (Claude / Eleven / Announcer): a separate headless
- Node process that is just another E2EE member; holds bot private keys + the
+- The **bot sidecar** (Claude / Eleven / Announcer): a separate headless process
+ that is just another E2EE member; holds bot private keys + the
`ANTHROPIC_API_KEY`, outbound-only, runs on any machine (see `docs/bots.md`).
- A Go port (`lchat bot` subcommand) is the planned default; the Node version
- stays as the reference implementation.
+ The default is **Go** — `lchat bot run`, a subcommand of the single binary
+ (`botcmd.go`, `internal/bot/`), no Node runtime. `agent/` is the **Node
+ reference implementation, kept on purpose** (a runnable example + the wire-format
+ illustration the Go tests pin against) — don't delete it or let it rot.
- `unfurl.go` — OpenGraph link previews (SSRF-guarded fetch + cache).
- `controld.go` / `cloud.go` — self-serve space creation on a router box: a
root-only local control daemon that executes `add-instance` (with a capacity
diff --git a/agent/README.md b/agent/README.md
index 713bab0..d0af4a8 100644
--- a/agent/README.md
+++ b/agent/README.md
@@ -6,10 +6,18 @@ ever sees ciphertext** — private keys and your `ANTHROPIC_API_KEY` live only o
this host. It shares `web/static/crypto.js` verbatim (imported directly), so
its wire format is guaranteed to match the app.
-> This Node sidecar is the **reference implementation**. A Go port (a `lchat
-> bot` subcommand — one static binary, no Node runtime) is the planned default;
-> see `docs/bots.md § Direction: a Go sidecar`. The Node version stays supported
-> as a working example for self-hosters.
+> **The default sidecar is now Go** — `lchat bot run`, a subcommand of the same
+> single static `lchat` binary, no Node runtime (`internal/bot/`, see
+> `docs/bots.md`). It's a full drop-in: same wire format, and session/state/
+> recipe files are byte-compatible, so it can take over a Node bot's identity in
+> place.
+>
+> **This Node sidecar is kept on purpose** as a runnable **reference
+> implementation** — a worked example of how to run a bot in Node if you'd
+> rather (it imports `web/static/crypto.js` verbatim, so it's also the clearest
+> illustration of the exact E2EE wire format a client speaks). It is not
+> deprecated and not scheduled for removal; keep it working alongside the Go
+> port.
| agent | what it does |
|---|---|
diff --git a/docs/bots.md b/docs/bots.md
index 83f199b..7f11a6e 100644
--- a/docs/bots.md
+++ b/docs/bots.md
@@ -1,8 +1,11 @@
# 🤖 Bots (agents) — design
-Status: **the sidecar exists and runs** (`agent/`, deployed to the `test` instance);
-everything past "What exists today" is proposed direction captured from design
-discussion, not built. This doc is the single place for where we landed.
+Status: **the sidecar exists and runs.** The default is the **Go** sidecar
+(`lchat bot run`, part of the single binary — see *Direction: a Go sidecar*
+below, now shipped); the **Node** sidecar (`agent/`) is kept as the runnable
+reference. Beyond the "Direction" and "What exists today" sections, the rest is
+proposed direction captured from design discussion, not built. This doc is the
+single place for where we landed.
## What a bot is
@@ -73,14 +76,22 @@ is a server**, and adding one to a thread extends that thread's E2E envelope to
the machine the operator chose. Where that machine lives is the single biggest
security decision the operator makes about bots.
-## Direction: a Go sidecar (decided 2026-07-06)
-
-The sidecar will be ported to **Go** — ideally a subcommand of the single
-binary (`lchat bot`), still run as its **own process** with its own env (never
-inside the server process: the bot holds private keys + the Anthropic key, and
-folding it into the server would collapse the trust boundary above). The Node
-sidecar (`agent/`) stays in-repo as the **functional reference implementation**
-for self-hosters who prefer it.
+## Direction: a Go sidecar (decided 2026-07-06, shipped 2026-07-07)
+
+The sidecar was ported to **Go** — a subcommand of the single binary (`lchat
+bot run`), run as its **own process** with its own env (never inside the server
+process: the bot holds private keys + the Anthropic key, and folding it into
+the server would collapse the trust boundary above). It is now the **default**;
+the full status is below.
+
+**The Node sidecar (`agent/`) is kept in-repo on purpose** — a supported,
+runnable **reference implementation**, not deprecated and not scheduled for
+removal. It's the worked example of how to run a bot in Node if you'd rather,
+and (because it imports `web/static/crypto.js` verbatim) the clearest
+illustration of the exact E2EE wire format a client speaks. Keep it working
+alongside the Go port; the Go tests pin themselves to these JS sources
+(`bot-commands.js`, `actions.mjs`, `eleven.mjs`, the shared `knowledge.md`), so
+the reference actively guards the port against drift.
Why: