Organized by phase and week. Check items off as they complete.
Split files (to stay under 300 lines per file):
- This file: Week 0 (done) + Weeks 1-2 (done)
- tasks-weeks-3-8.md: Auth, Servers, Messaging — COMPLETE
- tasks-weeks-9-12.md: Voice, Desktop Client — COMPLETE
- tasks-future.md: Phase 4+ (Tauri, E2EE, Bridge)
Status: COMPLETE
- Create ReadMeFirst.md
- Create project-map.yaml
- Create decisions.md
- Create all INDEX.md files (in each crate/module as created)
- Create tasks.md
- Create quality-gate.md
- Create CLAUDE.md, .cursorrules, .github/copilot-instructions.md
- Create Cargo.toml workspace
- Create docker-compose.yml
- Create .env.example
- Create LICENSE (AGPL-3.0)
- Create .gitignore
- Initialize git repository
- Verify
cargo check --workspacepasses - Verify
cargo clippy --workspace -- -D warningspasses - Verify
cargo fmt --checkpasses
Status: COMPLETE
Objective: Core types, ID generation, permission system, database schema.
- Implement Snowflake ID generator (snowflake.rs — 270 lines)
- Custom epoch 2024-01-01, 64-bit [42:timestamp][5:worker][5:process][12:sequence]
- SnowflakeGenerator, Snowflake newtype, Display/FromStr, serde as string
- 11 unit tests (monotonicity, uniqueness, conversions, timestamp extraction)
- Implement permission bitfield system (permissions.rs + permission_compute.rs)
- 27 permission flags via bitflags crate (general, text, voice, admin)
- PermissionOverwrite, OverwriteType, compute_permissions()
- Admin bypass, role/member overwrite precedence
- 9 unit tests
- Create core model types (8 model files in models/)
- user.rs, server.rs, channel.rs, message.rs, member.rs, role.rs, invite.rs, voice_state.rs
- All Serialize/Deserialize/Debug/Clone, comprehensive tests
- 41 model tests
- Create shared event types (gateway.rs + events.rs)
- 19 GatewayEvent variants (lifecycle, messages, typing, presence, voice, servers, channels, members)
- Serde tagged JSON: {"type": "EventName", "data": {...}}
- 8 serialization tests
- Create database connection pool (lib.rs — create_pool, run_migrations, health_check)
- Create 9 SQL migrations
- 001_users, 002_servers, 003_channels, 004_messages, 005_roles
- 006_server_members + member_roles, 007_invites, 008_voice_states, 009_files
- Proper indexes, foreign keys, cascading deletes
- Implement user repository (user_repo.rs — full CRUD)
- Implement server repository (server_repo.rs — full CRUD + list_by_user)
- Implement channel repository (channel_repo.rs — full CRUD + list_by_server)
- Implement message repository (message_repo.rs — full CRUD + cursor pagination)
- Implement member repository (member_repo.rs — membership + role assignment)
- Create Axum skeleton with health check (GET /api/v1/health)
- main.rs: tokio::main, config loading, DB pool, migration, router
- Add request ID middleware (UUID per request via MakeRequestUuid)
- Add CORS middleware (strict prod, permissive dev)
- Create error handling (ApiError enum + IntoResponse, 7 variants)
- Set up tracing (JSON in prod, pretty in dev, env filter from RUST_LOG)
- Config from env vars with defaults and secret masking
-
cargo check --workspace— passes -
cargo clippy --workspace -- -D warnings— 0 warnings -
cargo fmt --check— passes -
cargo test --workspace— 99 tests passing (76 core + 8 db + 15 api) - No file exceeds 300 lines (max: 275)
- All files have structured doc headers
- INDEX.md files updated
- Verify docker-compose services start on dev server
- Install Rust toolchain on dev server (192.168.140.140)
- Install pnpm on dev server
All 26/26 browser tests passing. 30 migrations applied. Full feature set deployed at opencorde.com. See ReadMeFirst.md for complete feature list.
- Fix validation test bug (validate_channel_type type 3 = Stage is valid; test type 4)
- Implement lettre SMTP in email.rs (replaces logging stub)
- Add SMTP vars to .env.example
- Update ReadMeFirst.md, tasks.md, tasks-future.md
- 0a: Permission enforcement — wired compute_effective_permissions() into all routes (messages, channels, reactions, threads, members, invites, roles, webhooks)
- 0b: Per-endpoint rate limiting — axum-governor with per-IP token buckets; 5/min login, 3/min register, 5/sec messages, 10/min uploads, 60/min global
- 0c: JWT refresh token rotation — JTI (UUID v4) stored in refresh_tokens table; single-use; stolen token detection revokes all sessions; migration 041
- 0d: File upload security — MIME allowlist, per-category size limits (image 8MB, video 100MB, other 25MB), magic-byte verification, EXIF stripping (img-parts, lossless)
- 0e: XSS audit — custom markdown renderer already safe (escapeHtml() + code-block extraction); no DOMPurify needed
- 0f: HTTP security headers middleware — X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Content-Security-Policy
- 0g: SQL injection audit — zero format!() in repos; all queries use .bind() parameterization
- 0h: 2FA (TOTP) — migration 042, POST /auth/2fa/enable + verify + DELETE /auth/2fa, login requires totp_code when enabled; client: TwoFactorSetup.svelte, login TOTP step, settings section
- 0i: Password security audit — Argon2id confirmed, 8-char minimum enforced, delete account requires current password
- 0j: Audit log completeness — added member.kick, member.role_assign, member.role_remove; existing: ban, unban, timeout, timeout_removed
- 1a: ws/dispatch.rs — add
member_server_ids: HashSet<i64>param; route ChannelCreate/Update/Delete, RoleCreate/Update/Delete, MemberUpdate, ServerUpdate to server-member filter - 1b: ws/handler/main_loop.rs — thread
member_server_idsthrough toshould_dispatch - 1c: ws/handler/lifecycle.rs — load user's server IDs via
server_repo::list_by_user; pass torun_main_loop - 1d: ws/events.rs — add 7 new event builders (channel_update, channel_delete, role_create, role_update, role_delete, member_update, server_update)
- 1e: channels/handlers.rs — broadcast ChannelCreate/Update/Delete after successful CRUD
- 1f: servers/handlers/crud.rs — broadcast ServerUpdate after successful PATCH
- 1g: roles.rs — broadcast RoleCreate/Update/Delete + MemberUpdate (assign/unassign)
- 1h: client stores — initChannelListeners, initRoleListeners, initMemberListeners, initServerListeners; called from +layout.svelte
- 2a: messages store — added editMessage(messageId, content) and deleteMessage(messageId) API calls
- 2b: MessageContextMenu.svelte (new) — extracted context menu buttons + added Edit (✏) and Delete (🗑) for own messages only
- 2c: MessageList.svelte — added onEdit/onDelete/currentUserId props, inline edit mode (textarea, Enter saves, Esc cancels), uses MessageContextMenu component
- 2d: channel +page.svelte — wired handleEditMessage, handleDeleteMessage, passes currentUserId to MessageList
- Each task must pass quality-gate.md before completion
- Update ReadMeFirst.md with progress
- Blockers noted inline with
BLOCKED: reason
- 3a: migration 043_slowmode.sql — ALTER TABLE channels ADD COLUMN slowmode_delay INT NOT NULL DEFAULT 0
- 3b: channel_repo.rs — add slowmode_delay to ChannelRow; update_channel accepts slowmode_delay param
- 3c: channels/types.rs — add slowmode_delay to ChannelResponse and UpdateChannelRequest
- 3d: channels/handlers.rs — map slowmode_delay in channel_row_to_response; clamp 0–21600; pass to update_channel
- 3e: send_list.rs — after channel fetch, if slowmode_delay > 0 query user's last message; return RateLimited{retry_after} if too soon
- 3f: types.ts — add slowmode_delay to Channel interface
- 3g: ChannelSettingsModal.svelte — add slowmodeDelay state, number input (0–21600), include in PATCH body
- 3h: channel +page.svelte — pass channelSlowmode to modal, update store on save
- 4a: role_repo.rs — add list_by_member(user_id, server_id) → Vec via JOIN on member_roles
- 4b: roles.rs — GET /servers/{server_id}/members/{user_id}/roles returns Vec
- 4c: UserProfilePopover.svelte (new) — avatar, status dot, bio, role chips, Send Message DM button
- 4d: MessageList.svelte — avatar div and author name wrapped in clickable buttons, open popover on click
- 4e: channel +page.svelte — pass serverId prop to MessageList
Last updated: 2026-03-25