Commit Graph
28 Commits
Author SHA1 Message Date
jevb 4d1a1676c7 feat: TOFU cert pinning, settings cache refactor, ban enforcement, and 80%+ test coverage
- Implement TOFU certificate pinning in Rust WS proxy with accept_cert_fingerprint command
- Refactor settings cache from package-level globals to Hub methods (eliminates global state)
- Add runtime ban check on WS message handling (kicks banned users mid-session)
- Sanitize reaction error messages to prevent IDOR information leaks
- Add slog error logging to REST handlers (channel, invite, search)
- Handle channel_delete for active channel in client dispatcher
- Add certMismatchBlock to prevent auto-reconnect on TOFU mismatch
- Consolidate root-level spec docs into docs/brain/06-Specs/ vault
- Add 80%+ test coverage for ws (80.9%) and admin (81.7%) packages
- Delete completed TODOS.md (all items resolved)
2026-03-17 11:05:52 +01:00
jevb ce4326766a fix: resolve all golangci-lint issues blocking CI server build
Fix 70+ errcheck violations by adding explicit error discards (`_ =`)
for unchecked return values across test helpers and deferred Close()
calls. Remove unused `senderID` field from broadcastMsg and unused
`defaultCleanupMaxWindow` const. Apply De Morgan's law, remove empty
branch, and simplify redundant type declaration per staticcheck.
2026-03-17 08:09:52 +01:00
jevb 9c1d99683c fix: address PR review findings (issues #9-#14)
- Fix capacity over-allocation and use strings.Builder in getReactionsBatch (#9)
- Replace `any` types and cache Tauri invoke in window-state.ts (#10)
- Remove custom `contains` helper, fix NilHub tests to pass nil (#11)
- Add nil guards before hub method calls in admin handlers (#12)
- Run golangci-lint v2: modernize interface{}/any, range-over-int loops,
  remove dead code, fix errcheck, add .golangci.yml config (#13)
- Add 23 client unit test suites (694 tests), exclude Tauri-coupled
  files from coverage, achieve 80%+ threshold (#14)

Closes #9, closes #10, closes #11, closes #12, closes #13, closes #14
2026-03-17 04:11:04 +01:00
jevb 1b596367c4 fix: address PR review findings (issues #3-#8)
- Fix double-close panic in Hub.Stop/GracefulStop using sync.Once (#3)
- Bump golangci-lint action to v9 with v2.11.3 for Go 1.25 support (#4)
- Add input validation guards to SearchMessages (#5)
- Handle promise rejections in InviteManager with error toasts (#6)
- Add missing reply_to and edited_at columns to admin test schema (#7)
- Add ClientCount to HubBroadcaster interface and wire into stats endpoint (#8)
2026-03-17 03:20:37 +01:00
jevb 8f4349ba42 feat: server enhancements, client test selectors, and UI polish
Server:
- Add message search and pinned messages support
- Add admin hub integration and live connection stats
- Update admin test mocks for hub interface

Client:
- Add data-testid attributes to components for E2E testing
- Add window management capabilities (position, size, maximize)
- Add prod E2E test config and script
- Fix CSS imports (use vite bundling instead of HTML link tags)
- Add inline styles to InviteManager overlay for reliability
- Update CHATSERVER.md references from WPF to Tauri

Docs:
- Update quick-start guide
2026-03-17 02:56:19 +01:00
jevb 79ea3ab42b refactor: split oversized files + add store notification batching
- Split Server/admin/api.go (788→281 lines) into handlers_users.go,
  handlers_channels.go, handlers_settings.go, handlers_backup.go
- Split Client SettingsOverlay.ts (~685→173 lines) into 7 per-tab
  modules under components/settings/
- Add queueMicrotask-based notification batching to createStore with
  flush() for synchronous test assertions
- Update 8 test files with flush() calls for batched store updates

Addresses TODOS.md #9 (split oversized files) for 2 of 3 targets.
2026-03-17 02:17:26 +01:00
jevb 4bdc83a368 fix: resolve 15 post-review issues across server and client
Server fixes:
- Move ATTACH_FILES permission check before CreateMessage to prevent
  orphaned messages on permission denial
- Fix hardcoded /api/files/ URL to /api/v1/files/ per spec
- Add error logging for GetAttachmentsByMessageIDs failure
- Set 1MB WebSocket read limit to match client-side limit
- Extract requireChannelPerm helper, replacing 8 repeated patterns

Client fixes:
- Wire onUnauthorized callback to clear auth on 401 responses
- Store auth token in authStore before WS connect
- Reset WS state to disconnected when Tauri APIs unavailable
- Add connectivity guard and 200ms send debounce on message send
- Add toast container to MainPage with error feedback on 5 API failures
- Clear voice currentChannelId on server-driven voice_leave for current user
- Apply stored theme/font/compact preferences at app startup
- Fix infinite scroll throttle to use store subscription instead of fixed timer

Tests:
- Add TestChatSend_AttachmentsDeniedNoMessageCreated
- Add attachments table to handler test schema
2026-03-17 01:59:34 +01:00
jevb 95c5b2d3b9 test: add authorization and contract tests for channel access
- REST authorization: 6 tests verifying READ_MESSAGES enforcement
  on GET /channels, GET /channels/{id}/messages, and GET /search
  with channel override deny and admin bypass
- WS authorization: 4 tests verifying channel_focus and chat_send
  permission checks with deny overrides and admin bypass
- Contract tests: 3 tests asserting response shapes match API.md
  (message fields, user object, attachments, reactions with me flag,
  search result fields)

Closes test gaps identified in CODE_REVIEW.md.
2026-03-16 17:17:00 +01:00
jevb 53d78feef6 feat: add attachment persistence and link on chat_send (High #3)
- Add attachment_queries.go with GetAttachmentByID, LinkAttachmentsToMessage,
  and GetAttachmentsByMessageIDs
- Wire attachment linking in handleChatSend with ATTACH_FILES permission check
- Include linked attachments in chat_message WS broadcast payload
- Wire attachment batch-fetch into GetMessagesForAPI for REST responses
- Add attachments table to channel handler test schema
2026-03-16 17:00:09 +01:00
jevb b2bfe5593c feat: align REST responses with API.md spec (High #1)
- Add MessageAPIResponse, UserPublic, AttachmentInfo, ReactionInfo types
- Add GetMessagesForAPI query with user object, reactions (with me flag),
  and attachments array matching API.md shape
- Update SearchMessages to return user object {id, username, avatar}
  instead of flat username field
- Update GET /messages handler to use new API-shaped query
- Batch-fetch reactions for all messages in a single query for performance
2026-03-16 16:57:01 +01:00
jevb 54221e8c07 fix: resolve 4 Critical + 2 High + 1 Medium server protocol violations
- Critical #1: Add READ_MESSAGES permission checks to channel_focus,
  GET /channels, GET /messages, and GET /search
- Critical #2: Send type "auth_error" instead of "error" with AUTH_ERROR
  code, preventing infinite client reconnect loops
- Critical #3: Replace role_id (number) with role (string name) in
  member_join, auth_ok, and ready payloads via JOIN on roles table
- Critical #4: Always include attachments field (empty array) in
  chat_message broadcasts to prevent client crash
- High #2: Add /api/v1/health endpoint alongside /health
- Medium #1: Handle ping WS messages with pong response
2026-03-16 16:54:56 +01:00
jevb b7d63dd443 chore: update .gitignore to exclude local tooling, build artifacts, and internal docs
Remove Claude Code configs, skills, publish artifacts, HTML mockups,
and internal planning docs from git tracking. Files remain local.
2026-03-15 16:54:55 +01:00
jevb 049b58c183 fix: prevent stale "online" status for users not connected via WebSocket
- Remove premature UpdateUserStatus("online") from REST login handler;
  the WebSocket serve.go already sets "online" on actual WS connect
- Add ResetAllUserStatuses() called at server startup to clear stale
  statuses from previous runs or crashes (alongside ClearAllVoiceStates)
2026-03-15 12:21:10 +01:00
jevb 6f564c7d2f fix: add JSON tags to Role/VoiceState, fix WebSocket error surfacing
Root cause: server's db.Role and db.VoiceState structs had no JSON tags,
causing Go to serialize field names as PascalCase while the C# client
expected snake_case. Every role deserialized with Id=0, crashing
ToDictionary with "duplicate key: 0".

- Add json tags to Role and VoiceState in Server/db/models.go
- Change Disconnected event to carry reason string for diagnostics
- Wire ErrorReceived in MainViewModel to show server-side WS errors
- Fix MainWindow to surface WebSocket errors on MainPage (not ConnectPage)
- Use _reconnectCts.Token for receive loop instead of caller's token
- Make ToDictionary calls safe with TryAdd to prevent future crashes
2026-03-15 12:18:19 +01:00
jevb c1c25ed26c feat: implement full client UI from mockup — 10 phases, 331 tests
Client UI:
- Design system: Colors, Typography, Controls resource dictionaries
- Message actions: reply compose bar, hover edit/delete/reply buttons
- Rich content: code blocks, attachments, system messages, content parser
- Server strip: 72px sidebar with server icons, home button, add server
- Status picker: popup for changing online/idle/dnd/invisible status
- ConnectPage: server health check dots with auto-refresh
- User popup: profile card with banner, avatar, roles, member since
- Emoji picker: 6 categories, search, grid of Unicode emojis
- Settings overlay: full-screen with sidebar navigation
- Friends/DM view: sidebar + friends list with tabs (online/all/pending)
- Toast notifications: auto-dismiss after 3s with fade animation

Models & services:
- Attachment model added to Message, ApiMessage, ChatMessagePayload
- EditMessageAsync, DeleteMessageAsync, SendStatusChangeAsync APIs
- MessageContentParser (code blocks, inline code, bold, italic)
- EmojiData, ToastService, HealthStatusToBrushConverter

Server (from prior session):
- Voice room management, SFU, speaker detection
- ACME/TLS support, config improvements
- Protocol and schema updates

Tests: 331 passing (61 converter + 24 voice service + 34 voice VM +
41 parser + 9 edit/delete + existing)
2026-03-15 11:42:25 +01:00
jevb 6eba999233 feat: add Let's Encrypt ACME support, fix security issues, improve server UX
Server:
- Add Let's Encrypt (ACME) TLS mode with autocert, HTTP-01 challenges on :80,
  and automatic certificate renewal (tls.mode: "acme" in config.yaml)
- Add ASCII art startup banner with server info and endpoint URLs
- Fix CSP blocking admin panel inline styles/scripts (per-route override)
- Suppress TLS handshake error noise in console output
- Fix TOCTOU race in invite consumption (atomic UPDATE with row-count check)
- Fix sendMsg mutex race condition (hold lock for entire send)
- Fix permission override formula (deny-first, allow-wins)
- Fix voice join parsing channelID before permission check
- Add session expiry check at WebSocket auth and periodic revalidation
- Add message length limit (4000 chars) and emoji length validation (32 bytes)
- Add file size enforcement in storage after io.Copy
- Add checksum URL validation in updater
- Add backup path traversal protection (BackupToSafe)
- Add self-modification guard in admin handlePatchUser
- Fix admin ownerOnlyMiddleware to use context user instead of re-auth
- Remove redundant startup log lines (banner shows same info)
- Add periodic expired session cleanup (15-min ticker)
- Add permissions package with bitfield constants and EffectivePerms
- Add rate limiter cleanup goroutine to prevent unbounded growth
- Add auth helpers (IsEffectivelyBanned, IsSessionExpired)
- Add WebSocket origin validation

Client:
- Add TOFU certificate trust service
- Add receive loop error handling
- Fix redundant else-if in OnChatMessage
2026-03-15 07:07:59 +01:00
jevb 25449eb204 feat: redesign login UI, add save-password, fix permissions, add audit logging, member_join broadcast
- Redesign ConnectPage with modern dark theme, profile cards with delete buttons, login/register toggle
- Add DPAPI-encrypted password saving with "Remember my password" checkbox
- Fix permission bit constants to match SCHEMA.md (Member role 0x663)
- Add migration 004 to fix existing Member role permissions
- Add comprehensive audit logging across all server packages (auth, admin, ws, setup)
- Add member_join WebSocket broadcast so new users appear in members list in real-time
- Add host URL normalization (strip scheme prefix) for reverse proxy compatibility
- Add REST API client, ChatService orchestrator, WebSocket service with reconnection
- Add model types (WsEnvelope payloads, API responses), converters, tests
2026-03-15 00:31:39 +01:00
jevb d425dc5553 feat: add setup wizard for initial owner account creation
When no users exist, the admin panel shows a setup wizard instead of the
login form. Creates the first Owner account with a session token and
generates an unlimited invite code for onboarding other users. The setup
endpoint is locked out after the first user is created.

Also fixes the admin panel 404 by serving index.html directly for the
root path instead of delegating to http.FileServer.
2026-03-14 22:36:35 +01:00
jevb 80ceabc78b fix: set default TLS cert/key paths to data/cert.pem and data/key.pem 2026-03-14 22:17:47 +01:00
jevb 69b76ada12 feat: add update notification banner to admin dashboard 2026-03-14 22:06:54 +01:00
jevb 73a621ef0f feat: implement server auto-update API endpoints with download, verify, and restart 2026-03-14 22:05:13 +01:00
jevb 27b7c000da feat: add updater package with GitHub Release checking and checksum verification 2026-03-14 21:59:58 +01:00
jevb 8ab2c93f1e feat: add server_restart WebSocket message type for update notifications 2026-03-14 21:58:18 +01:00
jevb 5aa216d991 fix: correct embed path (static not admin/static) and simplify audit_log migration 2026-03-14 21:37:47 +01:00
jevb ab389764b5 feat: implement Phase 5 (voice/WebRTC signaling) and Phase 6 (admin panel)
Phase 5 — Voice:
- migrations/002_voice_states.sql: voice_states table with FK + index
- db/voice_queries: JoinVoiceChannel, LeaveVoiceChannel, GetVoiceState,
  GetChannelVoiceStates, UpdateVoiceMute, UpdateVoiceDeafen, ClearVoiceState
- ws/voice_handlers: handleVoiceJoin (perm check, DB, broadcast existing
  states), handleVoiceLeave, handleVoiceMute, handleVoiceDeafen,
  handleVoiceSignal (rate-limited relay, SDP never logged),
  handleSoundboard (rate-limited, USE_SOUNDBOARD perm check)
- ws/handlers: dispatch voice_join/leave/mute/deafen/offer/answer/ice/soundboard
- ws/serve: call handleVoiceLeave on disconnect; include voice states in ready payload
- ws/messages: buildVoiceState, buildVoiceLeave, buildVoiceSignalRelay
- api/voice_handler: GET /api/v1/voice/credentials — HMAC-SHA1 TURN creds
- config: VoiceConfig (TURNSecret, STUNPort, TURNPort, TURNEnabled)

Phase 6 — Admin Panel:
- migrations/003_audit_log.sql: audit_log table with indexes
- db/admin_queries: GetServerStats, ListAllUsers, UpdateUserRole,
  ForceLogoutUser, AdminCreate/Update/DeleteChannel, LogAudit,
  GetAuditLog, GetSetting, SetSetting, GetAllSettings, BackupTo
- admin/api: full REST API — stats, users, channels, audit log, settings,
  backup; adminAuthMiddleware (ADMINISTRATOR bit), ownerOnlyMiddleware
- admin/static/index.html: single-page admin panel (dark theme, vanilla JS,
  no CDN) — dashboard, users, channels, audit log, settings sections
- admin/admin.go: NewHandler wiring go:embed static files + API

Fixes: Channel struct json tags (was serializing as "ID" not "id"),
duplicate getWithToken helper renamed in voice_handler_test.go

Test coverage: admin 59.1%, api 78.2%, auth 90.9%, db 82.0%, ws 37.9%
2026-03-14 21:31:03 +01:00
jevb 36640e3051 feat: implement Phase 4 real-time chat (WebSocket hub + message REST)
- db: channel_queries (ListChannels, GetChannel, CRUD, permissions),
  message_queries (CreateMessage, GetMessage, GetMessages paginated,
  EditMessage, DeleteMessage soft, AddReaction, RemoveReaction,
  GetReactions, SearchMessages FTS5, UpdateReadState)
- db: fix in-memory DB isolation — SetMaxOpenConns(1) for :memory: path
- ws/hub: replace stub with full Hub (register/unregister, broadcast to
  channel/all, send to user, thread-safe, buffered broadcast channel)
- ws/client: Client with send channel, NewTestClient helpers for tests
- ws/handlers: dispatch chat_send/edit/delete, reaction_add/remove,
  typing_start, presence_update — all with rate limiting and permission checks
- ws/messages: JSON builder helpers for all server→client message types
- ws/serve: ServeWS HTTP handler, WS auth handshake (10s timeout),
  ready payload, writePump/readPump goroutines, graceful disconnect
- api: channel_handler — GET /channels, GET /channels/{id}/messages,
  GET /search; fixed double-mount of /api/v1 route group
- api/router: mount channel routes, start hub, register /api/v1/ws

Test coverage: api 77.6%, auth 90.9%, db 84.2%, ws 26.7% (serve.go
requires live WS connection; hub/handlers/messages fully covered)
2026-03-14 21:17:09 +01:00
jevb b7dd6eabe9 feat: implement Phase 2 auth & security with TDD
- auth/session: 256-bit crypto-random tokens, SHA-256 hashing for storage
- auth/password: bcrypt cost 12, strength validation (8-72 chars)
- auth/ratelimit: sliding-window RateLimiter with lockout, thread-safe
- db/models: User, Session, Invite, Role types
- db/auth_queries: full user/session/invite CRUD with in-memory test coverage
- api/middleware: AuthMiddleware (Bearer token), RequirePermission (bitfield),
  RateLimitMiddleware (X-Real-IP, Retry-After header)
- api/auth_handler: POST register/login, POST logout, GET me
  - Generic errors — username existence never revealed
  - Rate limits: 3/min register, 5/min login, lockout after 10 failures
- api/invite_handler: create/list/revoke behind MANAGE_INVITES permission
- bluemonday sanitization on all user-supplied string fields

Test coverage: auth 90.9%, db 84.4%, api 80.9%
2026-03-14 20:52:11 +01:00
jevb a1434ad07f feat: implement Phase 1 server skeleton with TDD
- Scaffold Go module (github.com/owncord/server) with all package dirs
- config: koanf-based YAML loader with env var overrides, default generation
- db: pure-Go SQLite (modernc, no CGO), WAL mode, FK enforcement, full
  15-table schema from SCHEMA.md including FTS5 and idempotent migrations
- auth/tls: ECDSA P-256 self-signed cert generation, LoadOrGenerate for
  all 4 TLS modes (self_signed, acme, manual, off)
- api: chi router with request ID middleware, /health and /api/v1/info
- main: graceful shutdown (30s timeout), structured slog JSON logging
- Stubs for ws, storage, admin packages ready for Phase 2+

Test coverage: api 100%, auth 85.7%, db 82.4%, config 80.6%
Binary: chatserver.exe 12MB, GOOS=windows GOARCH=amd64
2026-03-14 20:34:37 +01:00