Skip to content

feat: CodeForge Web (workspace.crazymine.de) behind Cloudflare Access - #2

Open
Pikaswelt wants to merge 4 commits into
mainfrom
feat/codeforge-web
Open

Pikaswelt wants to merge 4 commits into
mainfrom
feat/codeforge-web

Conversation

@Pikaswelt

Copy link
Copy Markdown
Owner

What changed

  • web/codeforge-web-server.mjs: serves the built UI and implements the IPC calls the terminal workspace needs (shells, agy accounts, system status, git info). Desktop-only features answer with empty data.
  • Terminals = tmux sessions (cf-w-<tab>), shared with the codeforge CLI. A dropped connection or reload only detaches; on reconnect the browser re-attaches (never recreates a closed session) and skips the start command. Closing a tab ends the session.
  • Accounts come from ~/.codeforge/agy-accounts (same as CLI); account tabs run codeforge __run <id> (agy with --dangerously-skip-permissions).
  • UI: web links open in a new browser tab; account list syncs with the server in web mode; start command only runs for new sessions.
  • web/install.sh + hardened codeforge-web.service (KillMode=process keeps tmux alive across restarts).

Security

  • Listens on 127.0.0.1 only; public access only through the Cloudflare Tunnel.
  • Cloudflare Access app (one-time PIN, single e-mail allowlist).
  • Server verifies the Access JWT itself on every HTTP request and WebSocket upgrade: RS256 signature against the team certs, issuer, audience, exp/nbf, e-mail allowlist. Fails closed; refuses to start without config.
  • WebSocket origin check, 1 MB message limit, input validation for terminal/account IDs, CSP/HSTS/frame-ancestors none, GET/HEAD only, no SPA fallback for missing assets, forced cert refresh rate-limited.

Testing

  • tsc + vite build pass.
  • Local E2E with a test key: 403 for no token, bad signature, foreign e-mail, expired, wrong aud/iss, alg none, unknown kid; 200 with valid header/cookie; WS 403 for missing token / foreign origin.
  • WS flow: create → output → disconnect (session lives) → reconnect (reattached, old screen back) → kill (gone).
  • Headless Chromium: UI loads, "Antigravity starten (5)" opens 5 logged-in agy panes in a 3×2 grid.
  • Live: workspace.crazymine.de redirects to Access login (HTTP and WS); service unreachable on the public IP.
  • Not tested: a full Access login in a real browser (needs the e-mail PIN).

🤖 Generated with Claude Code

root and others added 4 commits October 5, 2026 23:55
web/codeforge-web-server.mjs serves the UI and runs terminals on the
server. It listens on 127.0.0.1 only and accepts HTTP and WebSocket
requests only with a valid Cloudflare Access JWT (signature, issuer,
audience, expiry, e-mail allowlist) and, for WebSockets, the public
origin. Without that configuration it refuses to start.

Terminals are tmux sessions shared with the codeforge CLI: they survive
disconnects and reloads, the browser re-attaches without retyping the
start command, and they end only when closed. Antigravity accounts come
from ~/.codeforge/agy-accounts, same as the CLI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
News and welcome popups no longer show on start, the setup modal is gone,
and the update window only opens when the user starts a check. "Was ist neu"
in the account menu now opens the news window directly.
The top bar gets a sidebar toggle. Below 1024px the sidebar is a drawer
that starts closed and closes after navigating; on desktop the collapsed
state is remembered. Grid terminals stack in one scrollable column there.
xterm.js has no touch scrolling, so vertical swipes are turned into wheel
events, which tmux (mouse mode) uses to scroll the session history.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant