Commit graph

28 commits

Author SHA1 Message Date
Stephen Dennis
4e94bb75aa docs: the browser client connects DIRECTLY to netmux — correct #2215's README
The README merged in #2216 claimed browsers cannot reach netmux and
that both transports require Hydra.  Wrong on the central point:
netmux serves WebSocket natively on its ordinary game ports
(mux/src/websocket.cpp, RFC 6455) with first-byte protocol detection
sharing each port between telnet and WebSocket (#1074/#2193,
proto_detect_window), and the handshake accepts both /wsclient and /
— which is exactly the path js/connection.js dials.  The minimal
browser deployment is netmux plus static files, no extra process;
Hydra is the OPTIONAL layer for session resume, multi-game links,
stored credentials, and gRPC-Web.

The error came from concluding absence out of a truncated grep: the
file listing was piped through head and mux/proxy's matches filled the
window before mux/src/websocket.cpp appeared.  A truncated listing is
not a complete listing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 22:12:06 -06:00
Stephen Dennis
6e6dc6d611 docs(#2215): README for the browser client
client/web was a complete HTML5 client with nothing self-describing in
it — and players have started asking publicly whether TinyMUX has
browser support, with no page to point at.  Covers what it is, why
both transports go through Hydra (browsers cannot open raw TCP), which
transport is proxy-agnostic, deployment (static hosting + hydra.conf,
the mixed-content and cors_origin rules), the test_web.js harness and
its keep-modules-dependency-free convention, and the 2.13-vs-2.14
status.

Facts checked against the sources: transport split per
connection.js/hydra_connection.js (including the /h-command list and
gRPC-Web fallback), listener types and CORS default from
hydra.conf.example, hydra's standalone make from mux/proxy/Makefile,
localStorage persistence from settings.js, and `node test_web.js`
run green before writing (all PASS).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 22:05:50 -06:00
Stephen Dennis
17f74f1a94 fix(web/mcp): cap multiline reassembly pending size (#1889)
McpParser stored every unterminated #$#* tag without bound. Cap pending
messages (32), per-message bytes (256 KiB), and total pending (1 MiB);
evict oldest or drop the overflowing tag with a diagnostic. Regression
covers flood of unique tags, fat continuations, and happy-path reassembly.
2026-07-31 09:27:38 -06:00
Stephen Dennis
123563522e test(web): pin oversized telnet SB discard (#1788)
Rebase onto master after #1789; add node unit for 5k GMCP SB then ok line.
2026-07-29 13:21:35 -06:00
Stephen Dennis
d0cdcce123 fix(client): extend #1788 buffer caps to web, tf, android, win32gui
Same unbounded line/SB/Hydra reassembly as console/iOS. Cap text at 64 KiB
and telnet SB at 4 KiB; cap web grpc-web frame reassembly. Stamp J1–J6 on
the audit map.
2026-07-29 13:21:35 -06:00
Stephen Dennis
6288c4da37 Retire ISSUES.md trackers; migrate open items to GitHub issues
The 22 per-tracker ISSUES.md files carried both their open items and a
full FIXED / FALSE ALARM / NOT A BUG audit history. The 57 still-open
items have been migrated to GitHub issues #706-#762 with a 2.14-aligned
label taxonomy (area:* / type:* / priority:* / topic:*), so open work
now lives in the issue tracker instead of in-tree Markdown.

The closed/audit history of every tracker is preserved here in git
history (this commit's parent); nothing is lost.

Open items migrated by tracker:
  mux/src/         (1)   -> #706
  mux/lib/         (10)  -> #707-#716
  mux/modules/engine/ (18) -> #717-#734
  mux/ganl/        (10)  -> #735-#744
  mux/modules/sqlslave/ (9) -> #745-#753
  testcases/       (4)   -> #754-#757
  client/tf/ + client/ (5) -> #758-#762

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:14:00 -05:00
Stephen Dennis
b4f8114709 Add structured Hydra vitals hooks across clients 2026-04-10 19:53:43 -06:00
Stephen Dennis
06e9b1772e Add web regression harness and Win32 project validator 2026-04-10 19:46:20 -06:00
Stephen Dennis
1f2782fa72 Move web world passwords out of localStorage 2026-04-10 19:40:56 -06:00
Stephen Dennis
c1e63deee3 Harden credential storage across console and Win32 GUI clients
Console (Unix): worlds.txt saved with mode 0600, warns on load if
permissions are loose. Password zeroed from memory via secure_zero()
after Hydra authentication succeeds.

Win32 GUI: Hydra passwords moved from plaintext worlds.json to
Windows Credential Manager (CredWriteW/CredReadW). Transparent
migration from existing JSON on first load; passwords stripped from
JSON on next save. Username and non-secret fields remain in JSON.

iOS (Keychain) and Android (EncryptedSharedPreferences) were already
using platform credential stores — no changes needed.

Web client localStorage exposure deferred as lower priority (different
threat model, not the primary deployment target).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:56:47 -06:00
Stephen Dennis
176e50a591 Bounds-check ANSI 256/truecolor parser in web client
renderAnsiLine() in client/web/js/terminal.js parsed `\e[38;5;Nm`
(256-color) and `\e[38;2;R;G;Bm` (truecolor) by directly reading
codes[j+2..j+4] with no check that the codes array was that long.
Malformed short sequences from the server leaked `undefined` into
`XTERM_COLORS[undefined]` and into `rgb(r,g,b)` CSS strings, producing
wrong fallback colors or literal `NaN` in styles.

Add an `isByte(v)` guard that requires a finite integer in [0, 255]
for every palette index and RGB component on both the fg (38) and bg
(48) paths. Short or out-of-range sequences now fall through to plain
text instead of emitting broken CSS.

Verified with `node --check` and a 15-case harness (well-formed,
short, out-of-range, bg variants, bold mix, plain, empty).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:56:37 -06:00
Stephen Dennis
74415d7e9b Update issue trackers 2026-04-04 15:58:31 -06:00
Stephen Dennis
1409352744 Fix Hydra web client integration drift 2026-03-29 10:27:47 -06:00
Stephen Dennis
05637224ca Fix review regressions in platform JIT and Hydra client 2026-03-25 02:07:41 -06:00
Stephen Dennis
55e6bb8324 Add WebSocket GameSession transport to HTML5 client
Step 6/7 of WebSocket GameSession transport.

Primary transport is now a WebSocket with hydra-gamesession subprotocol
carrying binary protobuf ClientMessage/ServerMessage frames. Falls back
to grpc-web unary SendInput when the WebSocket is not connected.

Changes:
- Proto.encodeMessage() for nested message encoding in ClientMessage
- ClientMessage/ServerMessage field maps for bidi protobuf codec
- _startGameSession(): opens WS, sends first-message auth via
  SetPreferences.session_id, decodes ServerMessage (game_output,
  gmcp, pong, system_notice)
- _sendInput() prefers WS when open, falls back to grpc-web
- sendNaws() sends preferences update over WS
- Keepalive sends PingMessage over WS instead of unary RPC
- Reconnect uses _startGameSession() instead of _startSubscribe()
- _startSubscribe() preserved as legacy fallback (no longer called)
- All unary control RPCs (/hconnect, /hlinks, etc.) unchanged on
  grpc-web Fetch path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:40:53 -06:00
Stephen Dennis
3d496b97f5 Fix TF plaintext gRPC, send_naws color tracking, HTML5 color format
Three client ISSUES fixes:

1. TF TLS: HydraConnection now accepts use_tls parameter and creates
   SslCredentials channel when the world has SSL flag set.  Worlds
   with 'x' flag get TLS gRPC; without it, plaintext (for local dev).

2. TF send_naws: tracks currentColorFormat_ from initial SetPreferences
   and uses it on resize, instead of always hardcoding ANSI_TRUECOLOR.
   Prevents preference resets when window resizes.

3. HTML5 Subscribe: explicitly sends color_format=1 (ANSI_TRUECOLOR)
   matching the new proto enum where 0=COLOR_UNSPECIFIED.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:08:46 -06:00
Stephen Dennis
9033f3dac0 Add terminal size to Subscribe for legacy/grpc-web clients
SessionRequest now carries terminal_width and terminal_height fields
(proto fields 3-4).  When a Subscribe RPC provides non-zero values,
the server forwards NAWS to the active game back-door link so the
game renders at the correct width.

Implemented in both native gRPC (grpc_server.cpp) and grpc-web
(session_manager.cpp handleGrpcWebRequest) Subscribe handlers.
HTML5 client sends approximate terminal dimensions based on window
size in _startSubscribe().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 18:15:48 -06:00
Stephen Dennis
bcdb9f8081 Move browser session token from localStorage to sessionStorage
localStorage persists across browser sessions and is accessible from
any tab on the same origin, making long-lived Hydra session tokens
vulnerable to XSS exfiltration and shared-browser leakage.

sessionStorage is scoped to the current tab and cleared on tab close.
Session resume still works across page reloads within the same tab,
but tokens no longer persist after the tab is closed or leak to
other tabs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 17:48:28 -06:00
Stephen Dennis
e07685b8f2 Add /hdetach command, complete RPC coverage in TF and HTML5
Both clients now expose 16 Hydra commands covering 23 of 24 proto
RPCs (only SubscribeGmcp is uncovered — it's a dedicated streaming
RPC for GMCP-only consumers, not suited to a terminal command).

New: /hdetach marks the Hydra session as detached (back-door links
stay alive server-side for later resume).

Full command list: /hconnect /hswitch /hdisconnect /hlinks /hgames
/hscroll /haddcred /hdelcred /hcreds /hstart /hstop /hrestart
/hstatus /hdetach /hhelp (plus automatic auth, keepalive, reconnect).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:14:02 -06:00
Stephen Dennis
6cf89d3018 Add process management commands to TF and HTML5
/hstart, /hstop, /hrestart, /hstatus commands dispatch to StartGame,
StopGame, RestartGame, GetGameStatus gRPC RPCs.  Users can manage
local game processes from any Hydra-connected client.  Both TF and
HTML5 clients now expose the full set of 15 Hydra commands via /h*.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:12:03 -06:00
Stephen Dennis
9f349b7bb4 Add keepalive pings and credential management to TF and HTML5
Both clients now have:

1. Keepalive pings: TF sends ClientMessage.ping every 60s via the
   bidi stream (checked in read_lines poll cycle).  HTML5 sends
   unary Ping RPC every 60s via setInterval.  Prevents silent
   connection drops through NAT.

2. Credential management: /haddcred, /hdelcred, /hcreds commands
   dispatch to AddCredential, DeleteCredential, ListCredentials
   RPCs.  Users can now manage stored game login credentials for
   Hydra auto-login without using the telnet interface.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:08:36 -06:00
Stephen Dennis
f5bb4d77c6 Live grpc-web Subscribe streaming and HTML5 session persistence
Two fixes:

1. grpc-web Subscribe now keeps the HTTP connection open as a live
   chunked stream instead of flushing queued output and closing.
   The subscriber fd is registered in the session's frontDoors list
   so onBackDoorData() sends game output as chunked grpc-web data
   frames in real-time.  sendToClient() also handles grpc-web
   subscribers for system messages.  This makes the HTML5 Hydra
   transport fully functional for interactive play.

2. HTML5 session persistence: sessionId is stored in localStorage
   keyed by world name.  On connect, the client first tries to
   resume the saved session via GetSession RPC.  If valid, it skips
   authentication and immediately starts the Subscribe stream.
   Intentional disconnect clears the stored session.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:00:35 -06:00
Stephen Dennis
bfffb07126 Fix Tier 1 gaps in TitanFugue and HTML5 Hydra transports
Three critical fixes across both clients:

1. Command interceptor: /hconnect, /hswitch, /hdisconnect, /hlinks,
   /hgames, /hscroll, /hhelp commands are now intercepted in
   send_line() and dispatched to the corresponding gRPC RPCs instead
   of being sent as game input.

2. Auto-reconnect: when the bidi stream (TF) or Subscribe stream
   (HTML5) breaks, the client retries up to 5 times with 3s delay
   using the same session_id.  Hydra sessions persist server-side
   so reconnect resumes seamlessly.

3. HTML5 protobuf encoder fix: no longer skips fields with value 0
   or false.  Only undefined/null are skipped.  This fixes encoding
   of enums (e.g. ColorFormat=0 is ANSI_TRUECOLOR) and explicit
   zero values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 15:32:41 -06:00
Stephen Dennis
cd8b3cc9c2 Add gRPC-Web/Hydra transport to HTML5 client
HydraConnection class implements the same interface as Connection
(WebSocket) using grpc-web protocol via fetch() API.  No build tools
or npm dependencies — includes a minimal hand-coded protobuf
encoder/decoder for the subset of Hydra messages the browser needs.

World edit dialog adds Transport dropdown (WebSocket vs Hydra),
password field, and game name field.  Hydra worlds authenticate via
gRPC-Web, auto-connect to the configured game, and receive output
via server-streaming Subscribe with chunked response parsing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 15:09:04 -06:00
Stephen Dennis
03b2cea53d Bring Web client to feature parity with Android/iOS Titan
New JS modules:
  timers.js — setInterval-based repeating timers
  hooks.js — CONNECT/DISCONNECT/ACTIVITY event hooks
  spawns.js — pattern-based output routing to spawn views
  variables.js — $var.name namespace (world, event, regexp, datetime, temp)
  mcp.js — MCP 2.1 protocol with simpleedit support

Trigger system upgraded:
  hilite, substitution (find/replace), line classification, TTS,
  enabled flag, capture group substitution ($0, $1, $2)

New slash commands (23 total):
  /repeat, /killtimer, /timers — timer management
  /hook, /unhook, /hooks — event hooks
  /spawn add|remove|list|focus — output routing
  /log — toggle logging (downloads as file)
  /speak — text-to-speech via Web Speech API
  /set, /unset, /vars — variable management

Integration:
  MCP parser per connection, intercepts #$# lines
  MCP editor dialog (textarea) for remote editing
  Hooks fire on connect/disconnect with auto-login
  Timers auto-cancel on disconnect
  Spawn matching routes to per-tab line buffers
  Log downloads as timestamped .log file via Blob URL
  TTS via SpeechSynthesis API
  Reconnect path wired with all new features

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:32:02 -06:00
Stephen Dennis
1fa5282d54 Web client: triggers, auto-complete, reconnect, /commands
Trigger system: regex matching on incoming lines with gag, priority,
shot count. Trigger Manager dialog (Add/Edit/Delete) accessible from
toolbar button. Triggers persist to localStorage. /def, /undef, /list
commands work from the input line.

Incoming lines run through TriggerDB.check() before display — gagged
lines are suppressed, trigger actions execute as commands.

Input auto-complete: Tab key completes /commands from a built-in list.
Multiple matches shown in output, single match completes with space.

Reconnect button on disconnected tabs — italic styling with a green
recycle arrow that reconnects with the same host/port/ssl.

Slash commands: /connect, /disconnect, /worlds, /triggers, /find,
/clear, /def, /undef, /list, /help. Unknown /commands pass through
to the server (some MUDs use them).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:34:35 -06:00
Stephen Dennis
a0b201ea96 Add toolbar buttons to Titan web client
Browser eats Ctrl+N and Ctrl+F. Added a visible toolbar with
Connect, Worlds, Disconnect, Find, and Clear buttons. Keyboard
shortcuts changed to Ctrl+Shift+N/F to avoid browser conflicts.

Welcome message updated to reference the toolbar buttons.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:27:33 -06:00
Stephen Dennis
8fe83beb07 Add Titan web client — HTML5/WebSocket MU* client
Zero frameworks, zero build step. Pure HTML + CSS + vanilla JS.
Opens index.html in any modern browser.

Architecture:
- WebSocket connects directly to TinyMUX's existing port (server
  detects WebSocket upgrade in the initial negotiation phase)
- Telnet parser in JS (~200 lines): NAWS, TTYPE, CHARSET, GMCP, MSSP
- ANSI SGR renderer: 16-color, 256-color, truecolor (24-bit RGB)
  parsed from escape sequences into inline CSS spans
- CSS flexbox layout: tab bar, output pane, input line, status bar

Features:
- Tabbed multi-world with activity indicators
- Click or middle-click to switch/close tabs, Ctrl+Tab to cycle
- Command history (Up/Down), PgUp/PgDn scrollback
- Connect dialog (Ctrl+N), World Manager with add/edit/delete
- Find in scrollback (Ctrl+F) with highlight
- Settings in localStorage (worlds, font, scrollback)
- Dark theme, monospace font, responsive layout

Files: index.html, css/titan.css, js/main.js, js/connection.js,
js/telnet.js, js/terminal.js, js/settings.js

No xterm.js dependency — custom ANSI renderer using DOM spans.
No npm, no webpack, no node_modules. Just open the HTML file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:24:59 -06:00