Files
gotermix/CLAUDE.md
T
2026-05-24 07:18:54 +00:00

4.1 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

# Build (static — no glibc dep, runs on NixOS / Alpine / any Linux)
CGO_ENABLED=0 go build .

# Build with injected encryption key (production)
CGO_ENABLED=0 go build -ldflags "-X gotermix/internals.fileEncKeyHex=$(openssl rand -hex 32)" .

# Build with env-var key
export GOTERMINAL_ENC="your64hexchars"
CGO_ENABLED=0 go build -ldflags "-X gotermix/internals.fileEncKeyHex=${GOTERMINAL_ENC}" .

# Cross-compile for Linux amd64 from any OS
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build .

# Run (dev)
./gotermix
./gotermix -addr 0.0.0.0:8443 -nopw

# Change credentials (app must restart to pick up)
./gotermix -setlogin newuser newpassword

# Store custom TLS cert (validates before storing, exits after)
./gotermix -cert /etc/ssl/my.crt -certkey /etc/ssl/my.key
./gotermix -certreset

# Tests — none exist yet
go vet ./...

Architecture

Entry point: main.go (5 lines). All logic in internals/ package. Web assets in internals/web/ (embedded via //go:embed).

Session model

/ always creates a new PTY-backed shell session (hex ID 32 chars). /s/<id> reconnects to existing session. Multiple browser tabs can share one session — all see the same PTY output via broadcast. The session ID is embedded in the served HTML via strings.NewReplacer replacing [[SESSION_ID]] and [[AUTHED]] literals in the shellPageHTML const. Sessions are reaped after 24h idle (10-min ticker). Rolling 1MB replay buffer lets new tab connections catch up on history.

Transport

HTTPS only. Self-signed cert auto-generated in memory on startup unless custom cert paths are stored (encrypted) in gws-creds.json. TLS handshake errors from browsers rejecting self-signed certs are suppressed via tlsHandshakeFilter. WebSocket (/ws/<id>) carries raw PTY bytes as binary frames; resize events as JSON text frames.

Auth flow

  1. Browser POSTs credentials to /auth
  2. Server checks against storedCreds (SHA-256 × 50,000 rounds + salt, constant-time compare)
  3. On success: sets gws_auth cookie (HttpOnly, Secure, SameSite=Lax, 12h) containing HMAC-SHA256 timestamp token
  4. All subsequent routes (/ws/, /upload, /download) call isAuthed() which validates the token
  5. -nopw flag bypasses all auth checks (nopwMode = true)

Credential & key storage

  • gws-creds.json (next to binary): AES-256-GCM encrypted JSON with username, salt, hash, optional cert paths
  • Encryption key priority: (1) build-time -ldflags "-X gotermix/internals.fileEncKeyHex=...", (2) gws.key file next to binary, (3) auto-generated and written to gws.key
  • Default creds if file missing or unreadable: user ivor / Silv3rSw0rd!

File transfer

  • Upload: browser POSTs multipart to /upload with sid field. Server writes to OS temp file, then injects mv <tmp> <dest> directly into the PTY shell — file lands with the shell's effective user permissions.
  • Download: GET /download?path=...http.ServeFile after filepath.Clean. Absolute paths used directly; relative paths anchored to initialCwd (cwd at startup).

Frontend

UI lives in internals/web/shell.html (~670 lines, embedded at build time). Favicon in internals/web/favicon.svg. Uses xterm.js + FitAddon from CDN. Keyboard handling: capture-phase listener blocks all Ctrl+key browser shortcuts; xterm's attachCustomKeyEventHandler handles Ctrl+Shift+C (copy), Ctrl+V (paste via Clipboard API). [[SESSION_ID]] and [[AUTHED]] placeholders replaced by serveTerminalPage via strings.NewReplacer.

External dependencies

  • github.com/creack/pty — PTY allocation and resize (pty.Start, pty.Setsize)
  • github.com/gorilla/websocket — WebSocket upgrader and framing

Routes

Path Handler
/ New session, serve terminal page
/s/<32-hex-id> Reconnect to specific session
/ws/<32-hex-id> WebSocket: PTY I/O + resize
/auth POST: credential check, set cookie
/upload POST multipart: inject mv into PTY
/download GET ?path=: ServeFile
/favicon.svg Inline SVG terminal icon