diff --git a/source/components/rojo-chat-panel/rojo-chat-panel.ts b/source/components/rojo-chat-panel/rojo-chat-panel.ts
index 93dd59f..f979c44 100644
--- a/source/components/rojo-chat-panel/rojo-chat-panel.ts
+++ b/source/components/rojo-chat-panel/rojo-chat-panel.ts
@@ -3,6 +3,22 @@ import { ContextMenuDirectory, ContextMenuReadOnlyEntry } from '../context-menu/
declare const markdownit: ( options?: Record Wednesday, 16 July 2026 Brainstormed and scaffolded rokojori-tunnel — user-based local tunneling service. Brainstormed and scaffolded rokojori-tunnel; Phase 1 relay built; Electron agent app, production deployment, streaming protocol, Roject integration, and client-side chunk animation added in a second session.
+ Full Electron desktop app at
+ Deployed to the same server as roject.rokojori.com. Key config:
+
+ Replaced the single-shot
+ Two integration points added in Roject:
+
+ TCP batching through the relay delivers larger chunks than direct local streaming,
+ making the chat feel like it buffers instead of streams. Fixed client-side in
+ Session History
- Session 2 — Electron agent, deployment, streaming, Roject integration
+
+ Electron Tunnel Agent app
+ tunnel/electron-agent/ — system tray icon,
+ login window (email + password → account.rokojori.com), persistent token storage in
+ userData/tokens.json, and a main window with a tunnel list. Each tunnel
+ row shows its status (green dot when agent connected) and Start / Stop / Delete
+ buttons. A modal handles creating new tunnels. The agent process runs in Electron's
+ main process via the existing TunnelAgent class (WebSocket, auto-reconnect,
+ exponential back-off). Build: npm run electron:dev.
+ Production deployment — tunnel.rokojori.com
+
+
+ deploy/nginx-tunnel.conf — HTTP proxy with WebSocket upgrade for
+ /api/agent/ (proxy_read_timeout 3600s), TLS via Let's Encryptdeploy/tunnel-rokojori.service — systemd unit; EnvironmentFile
+ points to /opt/tunnel-rokojori/.env; PORT=3003
+ (3002 was already taken by styles.rokojori.com)ts-node moved from devDependencies → dependencies
+ so npm install --omit=dev still installs it on the serverExecStart uses the local node_modules/.bin/ts-node
+ to avoid version mismatches with any globally installed npx wrapperStreaming relay protocol
+ RelayResponse (collect all chunks, send one JSON blob)
+ with a three-message streaming protocol over the agent WebSocket:
+ res_start (status + headers), res_data (base64 chunk),
+ res_end. The proxy route calls res.flushHeaders() on the first
+ res_start, streams each chunk with res.write(), and ends with
+ res.end(). The 30 s timeout was extended to 120 s and cancelled the moment
+ res_start arrives. Legacy single-shot RelayResponse messages
+ still work via a backward-compat path in pending.ts.
+ Roject integration — tunnel browser & chat
+
+
+ rojo-settings-panel — calls
+ GET /api/rojos/tunnels/browse (Roject server proxies to
+ tunnel.rokojori.com/api/tunnels/available), renders a picker list;
+ clicking an entry fills the Tunnel ID field automatically.baseURL to append
+ /v1 (/t/:tunnelId/v1); fixed auth token forwarding
+ (cookie OR Authorization Bearer header — dual-source pattern) so the Electron app
+ can use tunnels without a cookie.TUNNEL_SERVER_URL env var used throughout; defaults to
+ https://tunnel.rokojori.com in production.Client-side chunk animation
+ rojo-chat-panel.ts: a typeText() function splits any
+ chunk longer than 6 characters into 3-character pieces and awaits 18 ms between
+ each piece (~166 chars/sec). Chunks of ≤ 6 chars are displayed instantly, so
+ true single-token responses from a local direct LLM are unaffected.
+ Key decisions
@@ -112,6 +193,32 @@
+ ts-node in production dependencies. Moving ts-node (and typescript)
+ from devDependencies to dependencies is the simplest way to ensure they survive
+ npm install --omit=dev on the server without a separate build step.
+
+ Dual-source token extraction. The Roject server extracts the JWT
+ from the accessToken cookie first, then falls back to the
+ Authorization: Bearer header. This lets both browser-based (cookie)
+ and Electron-based (Bearer) clients use the tunnel chat without separate code paths.
+
+ Client-side fake streaming instead of server changes. + Splitting large relay chunks in the browser is zero-risk and zero-latency overhead — + no server changes, no protocol changes. The animation runs entirely in the UI + and is transparent to the LLM backend. +
+