Commit Graph

35 Commits

Author SHA1 Message Date
Vantz Stockwell
80463235b0 fix(rdp): VERSION echo + guacd host networking for overlay reach
- Echo VERSION_X_Y_Z args back to guacd in CONNECT handshake
- Set guacd to network_mode: host so it can reach RDP targets on
  NetBird/Tailscale overlay networks (100.64.x.x)
- App container uses host.docker.internal to reach guacd on host
- Add diagnostic logging for guacd→browser instruction relay

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 05:23:02 -04:00
Vantz Stockwell
9d3a93bea9 fix(rdp): parse guacd args response and send matching positional connect values 2026-03-14 05:05:00 -04:00
Vantz Stockwell
e3a978b639 fix(sftp): cache SFTP channel per session to prevent channel exhaustion 2026-03-14 04:18:29 -04:00
Vantz Stockwell
3b1c1aeda1 feat(sftp): add download save-to-disk + upload support, remove debug banner 2026-03-14 04:11:45 -04:00
Vantz Stockwell
f124d4b7d2 fix(rdp): convert to manual ws.Server, fix URL path, fix double session 2026-03-14 02:40:37 -04:00
Vantz Stockwell
e39d8fbdda debug: comprehensive SFTP channel + readdir diagnostic logging 2026-03-14 02:22:42 -04:00
Vantz Stockwell
46e2cb6e2f debug: add SFTP message logging to diagnose empty directory listing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 02:01:33 -04:00
Vantz Stockwell
4ccf138744 fix: seed script checks for any existing user, not just admin@wraith.local
Previously the seed only checked for admin@wraith.local by email,
so it would create a duplicate if the admin had changed their email.
Now skips seeding entirely if any user exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 01:48:19 -04:00
Vantz Stockwell
6262ab6e7e debug: verify ssh2 key parsing and log derived public key
Uses ssh2 utils.parseKey() to check if the key decrypts and
parses correctly, logs the key type and public key fingerprint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 01:13:51 -04:00
Vantz Stockwell
36c8527c28 debug: add SSH auth diagnostic logging
Logs key format, length, auth method selection, and ssh2 debug
output for auth/key events to diagnose why key auth is rejected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 01:08:54 -04:00
Vantz Stockwell
11e1705110 fix: detect orphaned SSH key references and missing auth methods
When a credential's sshKeyId points to a deleted/missing SSH key row,
the connection attempt silently had zero auth methods. Now throws a
clear error explaining the SSH key is missing. Also catches the case
where a credential has neither password nor SSH key configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 01:04:41 -04:00
Vantz Stockwell
639ac329a8 fix: TDZ crash on SSH connect failure — sessionId referenced before assignment
When SSH timed out, the onClose callback referenced `const sessionId`
before connect() resolved, causing a Temporal Dead Zone ReferenceError
that killed the process. Changed to `let` with try/catch so connection
failures send an error message to the client instead of crashing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:50:35 -04:00
Vantz Stockwell
8d95fc5652 fix: add crash handlers to catch process-killing exceptions
Process-level uncaughtException/unhandledRejection handlers plus
try/catch around upgrade and connection handlers. This will log
whatever is crashing the server on browser WebSocket connections
before the process dies, instead of silently restarting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:36:44 -04:00
Vantz Stockwell
8207b78b36 fix: hijack upgrade event before WsAdapter can consume the socket
WsAdapter registered its upgrade handler first and destroyed sockets
for non-matching paths. Now we remove all existing upgrade listeners,
install ours first, and forward non-terminal/sftp upgrades to the
original WsAdapter handlers for RDP.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:28:54 -04:00
Vantz Stockwell
55b944bfc5 fix: restore WsAdapter for RdpModule gateway, manual handler coexists
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:21:48 -04:00
Vantz Stockwell
734ab5633e fix: bypass NestJS WsAdapter — manual WebSocket upgrade handling
The NestJS WsAdapter silently swallowed WebSocket connections through
NPM despite 101 responses in the access log. Replaced with manual
ws.Server instances using noServer mode and explicit HTTP upgrade
event handling. Gateways are now plain @Injectable services, not
@WebSocketGateway decorators.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:12:01 -04:00
Vantz Stockwell
10f3537b01 fix: move WebSocket paths under /api/ prefix to work through NPM proxy
NPM forwards /api/* correctly but silently drops WebSocket upgrades on
/ws/* despite toggle being enabled and custom nginx config. Moving
gateways to /api/ws/terminal and /api/ws/sftp so they ride the same
proxy rules that already work for REST endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:13:45 -04:00
Vantz Stockwell
9dc5938fa6 debug: add HTTP upgrade event listener to diagnose WebSocket routing
handleConnection never fires despite browser getting open event.
Adding server-level upgrade listener to see if upgrades reach NestJS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:34:19 -04:00
Vantz Stockwell
77a76262f5 debug: add verbose WebSocket logging to terminal gateway
Need to see if handleConnection is even being called.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:27:43 -04:00
Vantz Stockwell
c4d7ad1833 fix: WebSocket auth always fails — client object has no URL property
With the NestJS ws adapter, the JWT token URL is on the HTTP upgrade
request (second arg to handleConnection), not on the WebSocket client
object. client.url was undefined, new URL(undefined) threw, catch
returned null, and every connection got 4001 Unauthorized.

Fix: Pass the IncomingMessage req to validateClient and prefer req.url.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:16:03 -04:00
Vantz Stockwell
f778213c32 fix: inline modals in index.vue, proper DTO for profile update
Dialogs: bypassed component-based dialogs entirely — inlined modals
directly in index.vue with inline style fallbacks for z-index/colors.
If button clicks work, we see the modal. Period.

Profile 500: created UpdateProfileDto with class-validator decorators
so ValidationPipe processes it correctly. Added error logging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:09:05 -04:00
Vantz Stockwell
13111ae007 feat: nav bar (Home link), profile management, TOTP 2FA
- Add Home/Profile links to nav bar alongside Vault/Settings/Logout
- Profile page: change email, display name, password
- TOTP 2FA: setup with QR code, verify, disable with password
- Login flow: two-step TOTP challenge when 2FA is enabled
- Backend: new endpoints PUT /profile, POST /totp/setup|verify|disable
- Migration: add totp_secret and totp_enabled columns to users

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 08:36:03 -04:00
Vantz Stockwell
f1e3892572 fix: correct dist path (dist/src/main.js), static path, explicit schema for migrate 2026-03-13 08:23:52 -04:00
Vantz Stockwell
bced9728d3 fix: add initial Prisma migration so migrate deploy creates tables 2026-03-13 08:22:27 -04:00
Vantz Stockwell
5c82bbbb79 fix: auto-run Prisma migrate + seed on container startup 2026-03-13 08:19:46 -04:00
Vantz Stockwell
5d75869bb4 feat: RDP backend — Guacamole TCP tunnel to guacd over WebSocket
- guacamole.service.ts: raw TCP client to guacd on GUACD_HOST:GUACD_PORT.
  Performs SELECT rdp → CONNECT handshake with full RDP parameter set.
  Provides encode/decode helpers for length-prefixed Guacamole wire format.
- rdp.gateway.ts: WebSocket gateway at /ws/rdp. JWT auth via WsAuthGuard.
  Handles connect (host lookup, credential decrypt, guacd tunnel open) and
  guac (bidirectional instruction forwarding). Updates lastConnectedAt and
  creates ConnectionLog on connect (same pattern as SSH gateway).
- rdp.module.ts: imports VaultModule, ConnectionsModule, AuthModule.
- app.module.ts: registers RdpModule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 17:27:09 -04:00
Vantz Stockwell
56be3fc102 feat: SFTP gateway — file operations over WebSocket
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:17:18 -04:00
Vantz Stockwell
60d7b6b024 feat: SSH terminal gateway — ssh2 proxy over WebSocket
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:17:12 -04:00
Vantz Stockwell
fd916fa4ef fix: add @types/jest, fix WsAuthGuard TS error 2026-03-12 17:13:50 -04:00
Vantz Stockwell
6fa8f6c5d8 feat: settings — key/value store with CRUD API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:09:08 -04:00
Vantz Stockwell
5762eb706e feat: vault — encrypted credentials + SSH key management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:08:53 -04:00
Vantz Stockwell
41411b0cb8 feat: connection manager — hosts + groups CRUD with search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:08:00 -04:00
Vantz Stockwell
3b9a0118b5 feat: AES-256-GCM encryption service + auth module (JWT, guards, seed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:07:14 -04:00
Vantz Stockwell
5a6c376821 feat: Prisma schema (7 models) + NestJS bootstrap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:06:09 -04:00
Vantz Stockwell
88dbb99f9d feat: project scaffold — Docker, NestJS, Nuxt 3, Prisma config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:05:37 -04:00