127 lines
5.4 KiB
HTML
127 lines
5.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Wednesday, 16 July 2026 — Roject</title>
|
|
<link rel="stylesheet" href="../../../../_assets_/styles.css">
|
|
<link rel="stylesheet" href="../../../../_assets_/nav.css">
|
|
</head>
|
|
<body>
|
|
<div class="page">
|
|
|
|
<header>
|
|
<p class="date">Wednesday, 16 July 2026</p>
|
|
<h1>Session History</h1>
|
|
<p class="subtitle">Brainstormed and scaffolded rokojori-tunnel — user-based local tunneling service.</p>
|
|
</header>
|
|
|
|
<section>
|
|
<h2>What we built</h2>
|
|
|
|
<div class="card">
|
|
<h3>rokojori-tunnel — Phase 1 (relay server)</h3>
|
|
<p>
|
|
New standalone service at <code>C:\rokojori\projects\web-projects\tunnel</code>,
|
|
same stack as the rest of the ecosystem (Node.js, Express, ts-node, JSON file
|
|
storage, shared JWT verification).
|
|
</p>
|
|
<ul style="line-height:1.9;margin-top:0.75rem">
|
|
<li><code>source/server/db.ts</code> — JSON file storage for tunnel configs
|
|
(<code>build/data/db/tunnels.json</code>)</li>
|
|
<li><code>source/server/middleware/requireAuth.ts</code> — JWT verification,
|
|
same pattern as rokojori-auth</li>
|
|
<li><code>source/server/relay/TunnelRegistry.ts</code> — in-memory
|
|
<code>Map<tunnelId, WebSocket></code> of active agent connections</li>
|
|
<li><code>source/server/relay/pending.ts</code> — pending request callbacks
|
|
keyed by <code>reqId</code> for matching responses to waiting HTTP connections</li>
|
|
<li><code>source/server/routes/tunnels.ts</code> — full CRUD plus
|
|
<code>GET /api/tunnels/available?purpose=</code> discovery endpoint</li>
|
|
<li><code>source/server/routes/agent.ts</code> — WebSocket upgrade handler;
|
|
verifies JWT, confirms ownership, registers socket in registry</li>
|
|
<li><code>source/server/routes/proxy.ts</code> — <code>ALL /t/:tunnelId/*</code>
|
|
relay; soft auth check for access mode, raw body forwarding, 30s timeout</li>
|
|
<li><code>source/server/index.ts</code> — Express + HTTP server with manual
|
|
WebSocket upgrade routing; JSON middleware applied only to <code>/api</code>
|
|
routes so proxy receives raw body streams</li>
|
|
<li><code>scripts/test-agent.ts</code> — standalone Node.js agent for
|
|
testing before the Electron app exists; connects via WebSocket and forwards
|
|
inbound relay requests to a local port</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>End-to-end test — local LLM over tunnel</h3>
|
|
<p>
|
|
Registered a tunnel via <code>POST /api/tunnels</code>, started the test agent
|
|
forwarding to port 8900 (<code>gemma4-coding-Q4_K_M.gguf</code> running locally),
|
|
and sent an OpenAI-compatible <code>/v1/chat/completions</code> request through
|
|
the relay. Full round-trip succeeded — request forwarded, response relayed back,
|
|
streaming token count confirmed in the response.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>tunneling.html outline document</h3>
|
|
<p>
|
|
New plan document at <code>workspace/outline/tunneling.html</code> covering:
|
|
the relay mechanic (three-leg model, minimal inspection, raw byte forwarding),
|
|
access modes (private / public / password-protected), tunnel metadata shape and
|
|
purpose tags, all three components (relay server, Electron agent app, rokojori-auth
|
|
permission), API endpoint reference, Roject LLM provider integration example,
|
|
file structure, and phased implementation plan.
|
|
</p>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Key decisions</h2>
|
|
|
|
<div class="card">
|
|
<p>
|
|
<strong>Separate service, not part of rokojori-auth.</strong>
|
|
Auth stays focused on identity. tunnel.rokojori.com is its own Express process
|
|
that verifies the shared JWT but owns all relay logic independently.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<p>
|
|
<strong>HTTP-only for Phase 1.</strong> Stable Diffusion (AUTOMATIC1111) and
|
|
OpenAI-compatible LLMs (Ollama, LM Studio) all speak HTTP with SSE streaming —
|
|
no WebSocket from the app side needed. Language servers use raw TCP and are out
|
|
of scope for now.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<p>
|
|
<strong>JSON envelope protocol for Phase 1.</strong> Requests and responses are
|
|
wrapped as <code>{ reqId, method, path, headers, body (base64) }</code> JSON
|
|
messages over the agent WebSocket. No raw byte framing needed at this stage;
|
|
the JSON envelope is simple enough and sidesteps binary WebSocket complexity.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<p>
|
|
<strong>No express.json() on proxy routes.</strong> The JSON middleware is applied
|
|
only to <code>/api/tunnels</code> so the proxy handler always receives a raw
|
|
readable body stream, regardless of content type.
|
|
</p>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
<footer>
|
|
Roject — session history
|
|
</footer>
|
|
|
|
</div>
|
|
<script>var NAV_ROOT = '../../../../';</script>
|
|
<script src="../../../../_assets_/nav-data.js"></script>
|
|
<script src="../../../../_assets_/nav.js"></script>
|
|
</body>
|
|
</html>
|