SameSite=None requires Secure=true or modern browsers silently reject
the Set-Cookie header. Since the site is served over HTTPS via Caddy,
this was causing login to succeed (200) but the session cookie to be
dropped, making the subsequent /auth/me call fail with 401.
SameSite=Lax is the correct setting for same-origin session cookies.
- fetchMe() no longer surfaces 401 as a user-facing error (it just
means 'no session', not a failure)
- API client auto-clears auth state on 401 mid-session so the user
gets redirected to login instead of seeing 'ERR: Request failed: 401'
- Session middleware now accepts Authorization: Bearer <token> header
as fallback when no cookie is present (for Tauri/native clients)
- Login, register, and WebAuthn endpoints expose X-Session-Token header
so non-browser clients can capture the token
- usePermissions ORs current user roles + @everyone only (not all server roles)
- cache myRolesByServer; load on active server; refresh after self role edit
- gate/notify @everyone and @channel; plain @username push; special mention UI
- refresh FEATURE_PARITY (DMs exist; drop stale critical gaps)
- README production deploy notes dumpster.service
- unit tests for permission bits and broadcast mention tokens
Members API appends server bots (is_bot). Sidebar groups ONLINE / OFFLINE / BOTS.
Bots get green BOT badge, no kick menu or profile. Mentions and DMs skip bots.
Root cause of "not anonymous":
1. Confess deleted via raw SQL with no MESSAGE_DELETE broadcast
2. Frontend extractIds only accepted message_id, but deletes send id
so live clients never removed deleted messages without refresh
Fix:
- Intercept /confess at message create: never store or broadcast the
original; post only the anonymous bot message
- Accept both id and message_id on MESSAGE_DELETE in the WS store
- Include both fields on delete broadcasts
Root cause: makeSender requires bot_servers membership, but create
flow never auto-added bots when users only picked a channel.
- Start() resolves config.channel_id → server and upserts bot_servers
- Confess cursor uses (created_at,id) so deletes don't stall polling
Moved thread and forum-tag routes from nested /servers/{sid}/channels/ to
top-level /channels/{id}/... to match frontend API calls. Forum posts were
getting SPA HTML fallback instead of JSON.
Added orange notification dots to DM and server buttons in ServerBar.
- Remove Topic header (causes issues with Apple push service)
- Log push response status codes for debugging
- Log endpoint prefix on errors for identification
- Auto-cleanup 410 (gone) subscriptions on success path too
- DM handler now sends push notifications to other conversation members
- Cleared stale push subscriptions (old broken VAPID keys)
- Auto-subscribe will re-create them on next page load
- Auto-subscribe to push on login, register, and session restore
- Guard against nil db in SendChannelNotification
- Log push send count per channel message
- Bump SW cache to v5
Old implementation just copied random bytes as the public key (not real EC math).
New cmd/keygen calls the library's proper P-256 key generation.
Removed broken local GenerateVAPIDKeys from push/handlers.go.
- [FEATURES] tab next to PINNED in channel header
- Create, vote/unvote, filter by status (open/planned/done/rejected)
- Sort by vote count (most voted first)
- Status selector for moderation
- DB: feature_requests + feature_request_votes tables
- Backend: full CRUD + vote endpoints under /servers/{id}/feature-requests
The backend was broadcasting TYPING_START with only channel_id.
Frontend required user_id + username to show who is typing.
Added Username to Client struct, fetched on WS connect via JOIN.
Typing events now enriched with sender identity before broadcast.
Query was ORDER BY ASC LIMIT 50 — returning the 50 oldest messages.
On page refresh users saw ancient messages instead of recent ones.
Changed to ORDER BY DESC LIMIT then reverse in Go so the API still
returns chronological order but from the bottom of the history.
Channel Update handler required MANAGE_CHANNELS role permission.
Server owner was getting 'forbidden' because their roles didn't
include that bit. Added owner bypass (same pattern as Delete).
The 'at least one user_id is required' check ran before the creator
was added to memberSet, so {user_ids: []} always 400'd. Now empty
user_ids is allowed — the creator is always in memberSet.
ContextMenu: reusable hook-based component that positions a menu at
the cursor and auto-closes on outside click or Escape.
ChannelList:
- Right-click channel -> Edit Name, Permissions, Delete Channel
- Right-click group header -> Edit Name, Create Channel Here, Delete Group
- Inline rename: replacing name text with an input, commits on Enter/blur
- CreateChannelModal now accepts defaultGroupId to pre-select a group
ServerBar:
- Right-click server icon -> Server Settings, Invite People, Leave Server
- Leave calls DELETE /servers/{id}/members/me
Backend:
- Added LeaveServer handler (DELETE /servers/{serverID}/members/me)
- Server owner cannot leave; must transfer or delete
The token column was dropped in a previous migration step
(ALTER TABLE webhooks DROP COLUMN IF EXISTS token), but the
CREATE INDEX referencing it still ran, causing migration failure
on databases where the column was already removed.
Phase 7.1 — Per-channel notification settings
- New notification_settings table (user_id, channel_id, level)
- GET/PUT/DELETE handlers under /channels/{channelID}/notifications
- Push dispatch and @mention loops filtered by notification level
- Frontend: bell icon per channel cycling all/mentions/none
Phase 7.4 — Read receipts
- New read_states table (user_id, channel_id, last_read_message_id)
- PUT /channels/{channelID}/read and GET /users/me/read-states
- Auto-mark-read when messages load in ChatArea
- Unread dot for channels never opened
Also: favicon/icon refresh in index.html
- New VideoGrid component: live camera feeds (local + remote) in VoicePanel
- Camera toggle in VoiceControls: enable/disable video per participant
- Noise suppression toggle in VoiceControls + voice store
- VOICE_WHISPER WS event: backend routes whisper to target user only
- Whisper UI: per-participant whisper button, notification toasts with dismiss
- Fix circular import between voice.ts and ws.ts (use lazy accessor)