max / makenotwork
2 files changed,
+142 insertions,
-303 deletions
| @@ -1,139 +1,50 @@ | |||
| 1 | 1 | # Makenotwork TODO | |
| 2 | 2 | ||
| 3 | 3 | ## Status | |
| 4 | - | Done: All pre-beta phases. Active: Creator setup (Stripe), manual testing. Next: Soft launch. | |
| 4 | + | Done: All pre-beta phases, UX audit remediation. Active: Creator setup (Stripe), manual testing. Next: Soft launch. | |
| 5 | 5 | ||
| 6 | 6 | v0.4.8. Audit grade A (Run 18, 2026-05-01). ~1,230 unit + ~684 integration = ~1,914 tests. Mutation kill rate 99.4%. Property-based testing active (proptest). `cargo test --features fast-tests` for fast runs. | |
| 7 | 7 | ||
| 8 | 8 | Human tasks (manual testing, outreach, legal, infrastructure) moved to `human_todo.md`. | |
| 9 | + | Completed items moved to `todo_done.md`. | |
| 9 | 10 | ||
| 10 | 11 | --- | |
| 11 | 12 | ||
| 12 | - | ## Audit Run 19 (2026-05-02) | |
| 13 | + | ## Remaining Audit Items | |
| 13 | 14 | ||
| 14 | - | ### Code | |
| 15 | - | - [x] **[MEDIUM]** Add timeout to `payments/connect.rs:198` raw reqwest call (Stripe resume-subscription) | |
| 16 | - | - [x] **[MEDIUM]** Add `ModerationActionId` newtype and `ModerationActionType` enum to `db/moderation.rs` (already existed in id_types.rs + enums.rs) | |
| 15 | + | ### Code (Run 19) | |
| 17 | 16 | - [ ] **[LOW]** Add README.md to server/ | |
| 18 | - | - [x] **[LOW]** Bump dependency pins: tokio 1.49->1.50, uuid 1.20->1.22, chrono 0.4.43->0.4.44, yara-x 1.13->1.14, anyhow 1.0.101->1.0.102 | |
| 19 | - | - [x] Fix auth rate limit for lockout test (`constants.rs` fast-tests override, burst 5->20) | |
| 20 | 17 | ||
| 21 | - | ### Deferred | |
| 18 | + | ### Deferred Code Quality | |
| 22 | 19 | - [ ] Extract inline SQL from route handlers to db/ (4 locations) | |
| 23 | 20 | - [ ] Remove `async-trait` in favor of Rust 2024 native async traits | |
| 24 | 21 | - [ ] Migrate inline `onclick` handlers to `addEventListener` for strict CSP | |
| 22 | + | - [ ] Monitor scheduler.rs (1249), git/mod.rs (624), license_keys.rs (684) for growth | |
| 25 | 23 | ||
| 26 | - | --- | |
| 27 | - | ||
| 28 | - | ## UX Audit Remediation (2026-05-02) | |
| 29 | - | ||
| 30 | - | Usability audit grade: B. Complexity C+, Completeness B+, Learnability B, Discoverability B-. | |
| 31 | - | ||
| 32 | - | ### Critical (broken or high-dropout flows) | |
| 33 | - | ||
| 34 | - | - [x] **[HIGH]** Add "Add to collection" UI — "Save to collection" dropdown on item pages + "Add to collection" in library purchase context menus. Checkbox toggle, inline create, click-outside-to-close | |
| 35 | - | - [x] **[HIGH]** Consolidate item creation wizard from 8 steps to 6 — merged Details+Appearance into "Basics" step, removed Distribution step (already in dashboard Pricing tab) | |
| 24 | + | ### UX — Remaining | |
| 36 | 25 | - [ ] **[HIGH]** Add global search to site header — search only exists on /discover page. Add input to `site_header.html` with Cmd+K shortcut | |
| 37 | 26 | ||
| 38 | - | ### High (significant friction reduction) | |
| 39 | - | ||
| 40 | - | - [x] **[MEDIUM]** Restructure user dashboard tabs — 4 core tabs (Account, Projects, Payments, Support) always visible, Analytics/Creator/SyncKit/Media/SSH Keys/Forums in "More" dropdown | |
| 41 | - | - [x] **[MEDIUM]** Fix price input to use dollars, not cents — PWYW min, subscription tier, promo code fixed discount, and inline item edit all now accept dollars with auto-conversion to cents | |
| 42 | - | - [x] **[MEDIUM]** Standardize pricing terminology — item wizard now says "One-Time Purchase" to match project wizard, paywall, landing page, and docs | |
| 43 | - | - [x] **[MEDIUM]** Add self-service refund UI for creators — new "Sales" tab on item dashboard with per-transaction refund buttons, Stripe refund API integration | |
| 44 | - | ||
| 45 | - | ### Medium (discoverability and learnability) | |
| 46 | - | ||
| 47 | - | - [x] **[MEDIUM]** Surface embed feature — added "Edit" and "Embed" links on public item pages for creators (via `is_owner` flag) | |
| 48 | - | - [x] **[MEDIUM]** Link data export from dashboard — promoted "Your Data" section from collapsed `<details>` to always-visible in Account tab | |
| 49 | - | - [x] **[MEDIUM]** Explain creator application — pitch.html already says "Most applications are approved within a few days" | |
| 50 | - | - [x] **[LOW]** Add one-line descriptions to project feature checkboxes — already implemented via `ProjectFeature::description()` + `type-card-desc` in template | |
| 51 | - | - [x] **[LOW]** Improve empty states — library purchases has "Discover Content" CTA, project content has "Add First Item" CTA with explanatory text | |
| 52 | - | - [x] **[LOW]** Make promo code visible at checkout — replaced `<details>` with visible inline form | |
| 53 | - | ||
| 54 | - | ### Low (power-user improvements) | |
| 55 | - | ||
| 56 | - | - [x] **[LOW]** Add bulk operations for item management — already implemented (publish/unpublish/delete in project Content tab with multi-select) | |
| 57 | - | - [x] **[LOW]** Add keyboard shortcuts — `?` help overlay with shortcut list, `Esc` closes modals, `Cmd+S` saves forms (Cmd+K deferred until global search) | |
| 58 | - | - [x] **[LOW]** Add soft delete with 7-day recovery — items now soft-deleted (deleted_at column), auto-purged after 7 days by scheduler, restore endpoint at POST /api/items/{id}/restore | |
| 59 | - | - [x] **[LOW]** Add wishlist/bookmark for fans — "Wishlist" toggle on item pages, wishlists table, toggle API at POST /api/wishlists/{item_id} | |
| 60 | - | - [x] **[LOW]** Add changelog or "What's New" — `/changelog` page with entry history, linked from site footer | |
| 61 | - | ||
| 62 | - | ### Deferred (post-beta table stakes) | |
| 63 | - | ||
| 64 | - | - [ ] Reviews/ratings system for items (all three competitors have this) | |
| 27 | + | ### UX — Deferred (post-beta table stakes) | |
| 28 | + | - [ ] Reviews/ratings system for items | |
| 65 | 29 | - [ ] Gift purchases at checkout | |
| 66 | 30 | - [ ] HTML rich email for creator broadcasts (currently plain text only) | |
| 67 | - | - [ ] "Edit" link on public project/item pages for logged-in creators (currently must navigate to dashboard) | |
| 68 | 31 | - [ ] RSS feed link on all project pages (currently only shown if blog posts exist) | |
| 69 | 32 | ||
| 70 | 33 | --- | |
| 71 | 34 | ||
| 72 | - | ## Integration Test Fixes (completed 2026-05-02) | |
| 73 | - | ||
| 74 | - | All 34 previously-failing tests resolved (10 root causes). Additional 3 sandbox test failures fixed. Key changes: | |
| 75 | - | ||
| 76 | - | - Auth rate limiter: moved from `route_layer` to per-handler `.layer()` — GET /login no longer rate-limited (production fix) | |
| 77 | - | - Advisory lock deadlock: `pg_advisory_lock`/`pg_advisory_unlock` used different pool connections, leaving locks permanently held. Replaced with `check_sandbox_cap` that pins lock+count+unlock to a single connection (production fix) | |
| 78 | - | - Test harness: unique IP per TestClient via atomic counter, preventing cross-test rate limit interference | |
| 79 | - | - `fast-tests` feature: Argon2 reduced to 8MiB/1iter (~10ms vs ~600ms), sandbox rate limit relaxed. Run tests with `cargo test --features fast-tests`. Sandbox suite: 2.3s (was 40+ min) | |
| 80 | - | - Consolidated 4 sandbox "blocks" tests into 1, fixed UUID type in subscription test | |
| 81 | - | ||
| 82 | - | --- | |
| 83 | - | ||
| 84 | - | ## Audit Run 18 (2026-05-01) | |
| 85 | - | ||
| 86 | - | ### Testing | |
| 87 | - | - [x] **[LOW]** Add unit tests to `wam_client.rs` — 5 tests (URL construction, serialization, fire-and-forget error handling) | |
| 88 | - | - [x] **[LOW]** Add unit tests to `git_ssh.rs` for `parse_ssh_command` and `parse_repo_path` — 14 tests (all operations, quoting, traversal, edge cases) | |
| 89 | - | ||
| 90 | - | ### Performance | |
| 91 | - | - [x] **[LOW]** `scanning/hash_lookup.rs` creates new `reqwest::Client` per call — fixed: static `LazyLock<reqwest::Client>` | |
| 92 | - | - [x] **[LOW]** `routes/ota.rs` `delete_release_handler` — replaced `list_releases` (O(N)) + `list_artifacts` with `get_release_artifact_keys` (O(1) ownership check + S3 key fetch) | |
| 93 | - | ||
| 94 | - | ### Data Integrity | |
| 95 | - | - [x] **[LOW]** CSV import `parse_amount_cents` — removed ambiguous heuristic. Whole numbers are always cents. Decimal values (e.g. "12.50") are dollars.cents. No format option needed. | |
| 96 | - | ||
| 97 | - | ### Deferred | |
| 98 | - | - [x] Split `helpers.rs` (~1,268 lines) into `formatting.rs`, `crypto.rs`, `rate_limit.rs` — re-exported from helpers for backward compat | |
| 99 | - | - [x] Reduce `analytics.rs` query duplication — extracted `Scope` enum with `where_clause`/`table_prefix`/`bind_scope`, 623→468 lines (155 LOC removed) | |
| 100 | - | - [x] `discover.rs` — already deduplicated via `append_item_discover_filters` + `bind_item_discover_filters!` macro. Remaining 3 SELECT variants differ in `match_score` expression (trigram/constant/NULL) — genuinely distinct SQL | |
| 101 | - | - [x] `routes/admin/` performance: `admin_users` called `count_users` 3× — replaced with single `count_users_summary` using `COUNT(*) FILTER` | |
| 102 | - | ||
| 103 | - | --- | |
| 104 | - | ||
| 105 | - | ## Pre-Beta Code Tasks | |
| 35 | + | ## Pre-Beta — Remaining | |
| 106 | 36 | ||
| 107 | 37 | ### SyncKit | |
| 108 | - | - [x] Sync log compaction — cursor-based: tracks `last_pulled_seq` per device (migration 084), deletes entries all devices have pulled (7-day safety margin). Runs in monitor maintenance loop alongside existing 90-day age-based prune. | |
| 109 | - | - [ ] Add key rotation mechanism (requires server-side re-encryption of all sync_log entries) — deferred post-beta | |
| 110 | - | ||
| 111 | - | ### Git Access Provisioning | |
| 112 | - | - [x] Dashboard page for SSH key management — promoted from collapsed `<details>` in Account tab to dedicated "SSH Keys" dashboard tab. Tab conditionally shown when `git_enabled`. Reuses existing API endpoints and HTMX partials. | |
| 113 | - | - [x] Per-repo collaborator access — `repo_collaborators` table (migration 087) with per-user `can_push` flag. SSH auth (`git_ssh.rs`) checks collaborator table for push and private repo read access. API: `POST/GET/DELETE /api/repos/{id}/collaborators`. Dashboard Code tab shows collaborators per linked repo with add/remove UI. | |
| 114 | - | - [x] Replace manual `setup-ssh-keys.sh` — added `mnw-admin setup-git` subcommand that creates `/opt/git/.ssh`, sets permissions, installs sudoers rule, and verifies syntax. Shell script superseded. | |
| 115 | - | ||
| 116 | - | ### Moderation | |
| 117 | - | - [x] Admin "send warning" action: `POST /api/admin/users/{id}/warn` sends policy-violation email without suspending. Records in moderation_actions. | |
| 118 | - | - [x] `moderation_actions` table (migration 085): id, user_id, admin_id, action_type, reason, content_ref, resolved_at, created_at. Indexed for active-actions lookup and user history. | |
| 119 | - | - [x] Admin handlers record actions: suspend→creates "suspension" action, terminate→creates "termination" action, remove_item→creates "content_removal" action (with item_id as content_ref). | |
| 120 | - | - [x] Actions resolved automatically: unsuspend→resolves suspension actions, restore_item→resolves content_removal by content_ref. | |
| 121 | - | - [x] User-facing "Account Status" section on Settings page: shows active moderation actions (danger-bg boxes with type, date, reason) or "Your account is in good standing." Links to appeals and support. Added to `user_details.html` after the Account section. | |
| 122 | - | - [x] Moderation history (collapsed `<details>`): shows resolved past actions with resolution date. Only visible when history exists. | |
| 38 | + | - [ ] Add key rotation mechanism (requires server-side re-encryption of all sync_log entries) | |
| 123 | 39 | ||
| 124 | 40 | ### Incident Notification System | |
| 125 | - | - [ ] Let creators opt into status alerts (email or webhook) when platform status changes. Implementation: subscribe endpoint on /health, store preferences in DB, trigger email on status transition (Operational -> Degraded/Error and recovery). Reuse existing email infrastructure (Postmark). | |
| 41 | + | - [ ] Let creators opt into status alerts (email or webhook) when platform status changes | |
| 126 | 42 | ||
| 127 | 43 | ### Frontend | |
| 128 | 44 | - [ ] Git browser integration: add discover/follow integration (post-beta) | |
| 129 | 45 | ||
| 130 | 46 | --- | |
| 131 | 47 | ||
| 132 | - | ## Code Review Remediation — Deferred | |
| 133 | - | - [ ] Monitor scheduler.rs (1249), git/mod.rs (624), license_keys.rs (684) for growth | |
| 134 | - | ||
| 135 | - | --- | |
| 136 | - | ||
| 137 | 48 | ## File Scanning — Future Improvements | |
| 138 | 49 | ||
| 139 | 50 | ### Background scan queue (next) | |
| @@ -141,118 +52,57 @@ All 34 previously-failing tests resolved (10 root causes). Additional 3 sandbox | |||
| 141 | 52 | - [ ] Enqueue oversized files from `scan_and_classify` instead of blanket HeldForReview | |
| 142 | 53 | - [ ] Scheduler picks up queued scans, streams from S3 to temp file, scans from disk | |
| 143 | 54 | - [ ] Update entity scan status + notify creator on completion | |
| 144 | - | - [ ] ClamAV already supports chunked `INSTREAM` — use it for streaming scans | |
| 145 | - | - [ ] SHA-256 is naturally streaming — hash in chunks during download | |
| 146 | - | - [ ] YARA requires full buffer — memory-map the temp file or skip YARA for large files | |
| 147 | 55 | ||
| 148 | 56 | ### Separate scanning service (later, when traffic justifies) | |
| 149 | 57 | - [ ] Extract scan worker into standalone binary (same crate, different bin target) | |
| 150 | - | - [ ] Worker polls scan_queue, runs on dedicated machine with more RAM | |
| 151 | - | - [ ] Allows horizontal scaling independently of request serving | |
| 152 | 58 | ||
| 153 | 59 | --- | |
| 154 | 60 | ||
| 155 | 61 | ## Competitive Comparison Remediation (2026-04-29) | |
| 156 | 62 | ||
| 157 | 63 | ### i18n — Grade C | |
| 158 | - | - [ ] Evaluate `fluent-rs` vs `rust-i18n` for Rust i18n (Fluent is Mozilla's, used by Firefox; rust-i18n is macro-based) | |
| 159 | - | - [ ] Extract all user-facing strings from templates into message catalog (server/templates/) | |
| 160 | - | - [ ] Add locale negotiation middleware (Accept-Language header + user preference) | |
| 161 | - | - [ ] i18n error messages (Liberapay's `LazyResponse` pattern — render error text in user's locale) | |
| 64 | + | - [ ] Evaluate `fluent-rs` vs `rust-i18n` | |
| 65 | + | - [ ] Extract user-facing strings into message catalog | |
| 66 | + | - [ ] Add locale negotiation middleware | |
| 67 | + | - [ ] i18n error messages | |
| 162 | 68 | ||
| 163 | 69 | ### OpenAPI Spec — Grade A- | |
| 164 | - | Infrastructure done: `utoipa` integrated, spec at `/api/openapi.json`. 19 endpoints documented (6 license key, 13 SyncKit), 29 schemas. | |
| 165 | - | ||
| 166 | - | - [ ] Annotate remaining public endpoints (public projects, guest checkout, email signup) | |
| 167 | - | - [ ] Auto-generate API reference docs from spec (integrate with DocEngine or separate page) | |
| 70 | + | - [ ] Annotate remaining public endpoints | |
| 71 | + | - [ ] Auto-generate API reference docs from spec | |
| 168 | 72 | ||
| 169 | 73 | ### CI/CD Formalization — Grade B+ | |
| 170 | 74 | - [ ] Add `cargo clippy` + `cargo test` as git pre-push hook or CI gate | |
| 171 | - | - [ ] Add migration integrity check to CI (Ghost has `check-migration-integrity.js`) | |
| 172 | - | - [ ] Add test timing report to CI output (flag tests >5s as slow) | |
| 173 | - | - [ ] Consider sourcehut builds.sr.ht manifest as lightweight hosted CI (no GitHub Actions needed) | |
| 174 | - | ||
| 175 | - | ### unwrap() Audit — Grade A | |
| 176 | - | - [x] Audited all `unwrap()` in production code paths (routes/, db/, auth, payments, storage, scanning, scheduler) | |
| 177 | - | - [x] Only 1 dangerous unwrap found: `promo_codes.rs:212` (`and_hms_opt(23,59,59).unwrap()`). Fixed with `.ok_or_else()`. | |
| 178 | - | - [x] All others are safe: tests, static regex in LazyLock, infallible Response::builder(), guarded by preceding None checks. | |
| 179 | - | - Periodic sweep sufficient — no clippy deny needed | |
| 75 | + | - [ ] Add migration integrity check to CI | |
| 76 | + | - [ ] Add test timing report to CI output | |
| 77 | + | - [ ] Consider sourcehut builds.sr.ht manifest | |
| 180 | 78 | ||
| 181 | 79 | --- | |
| 182 | 80 | ||
| 183 | 81 | ## Post-Beta | |
| 184 | 82 | ||
| 185 | 83 | ### Earn-Back Credit Program | |
| 186 | - | - [ ] Schema: `earn_back_credits` table (user_id, period_start, period_end, subscription_paid_cents, revenue_earned_cents, credit_months, applied, created_at) | |
| 187 | - | - [ ] Annual calculation job in scheduler: on each creator's account anniversary, compare 12-month subscription fees paid vs gross sales revenue | |
| 188 | - | - [ ] If fees > revenue: credit = ceil((fees - revenue) / monthly_tier_price) months, capped at 12 | |
| 189 | - | - [ ] Apply credits: skip Stripe billing for credited months (pause subscription or issue Stripe credit notes) | |
| 190 | - | - [ ] Dashboard display: show earn-back status (months until anniversary, current earnings vs fees paid, projected credit) | |
| 191 | - | - [ ] Email notification on credit issuance (congratulatory if earned back, supportive if credited) | |
| 192 | - | - [ ] Update economics.md and how-we-work.md to reflect implementation status (remove "Planned" qualifier) | |
| 193 | - | - [ ] Counter on public pricing page: "X creators have earned back their subscription fees" | |
| 84 | + | - [ ] Schema, annual calculation job, apply credits, dashboard display, email notification | |
| 194 | 85 | ||
| 195 | 86 | ### Churn Monitoring and Creator Health | |
| 196 | - | - [ ] `creator_health` materialized view or scheduled query: monthly revenue, upload frequency, login frequency, follower count, subscription age per creator | |
| 197 | - | - [ ] Churn risk scoring: flag creators with declining activity (no uploads in 30 days, no logins in 14 days, revenue drop >50% month-over-month) | |
| 198 | - | - [ ] Admin dashboard widget: churn risk list, retention cohort chart (by signup month), tier distribution over time | |
| 199 | - | - [ ] Revenue concentration alert: warn if any single creator represents >10% of total subscription revenue | |
| 200 | - | - [ ] Monthly retention metrics email to admin: new creators, churned creators, net change, MRR, average revenue per creator | |
| 87 | + | - [ ] creator_health materialized view, churn risk scoring, admin dashboard widget | |
| 201 | 88 | - [ ] Trigger: implement before reaching 100 creators | |
| 202 | 89 | ||
| 203 | 90 | ### Phase 11B: Promotions | |
| 204 | - | - [ ] Affiliate/referral program (per-product opt-in, configurable commission %, 30-day cookie) | |
| 91 | + | - [ ] Affiliate/referral program | |
| 205 | 92 | ||
| 206 | 93 | ### Phase 13D: Creator Platform Import System — Remaining | |
| 207 | - | ||
| 208 | - | #### Phase B | |
| 209 | - | - [ ] Substack ZIP importer (posts.json + subscribers.csv inside ZIP archive) | |
| 210 | - | - [ ] Ghost JSON importer (Ghost export format → ImportPayload) | |
| 211 | - | ||
| 212 | - | #### Phase C | |
| 213 | - | - [ ] Gumroad CSV/API importer (sales CSV + product API) | |
| 214 | - | - [ ] Bandcamp sales CSV preset (column mapping preset for Bandcamp export format) | |
| 215 | - | - [ ] Ko-fi members CSV preset (column mapping preset) | |
| 216 | - | - [ ] Lemon Squeezy REST API importer | |
| 217 | - | - [ ] Patreon OAuth API importer (OAuth flow + paginated member/post API) | |
| 94 | + | - [ ] Substack ZIP importer, Ghost JSON importer | |
| 95 | + | - [ ] Gumroad CSV/API, Bandcamp CSV preset, Ko-fi CSV preset, Lemon Squeezy, Patreon OAuth | |
| 218 | 96 | ||
| 219 | 97 | ### Phase 14E: Media Transcoding Pipeline | |
| 220 | - | ||
| 221 | - | #### Phase 14E-1: Probe + Detect Infrastructure | |
| 222 | - | - [ ] Probe uploaded files at confirm time: extract codec, bitrate, sample rate, duration, dimensions | |
| 223 | - | - [ ] Classify uploads as lossless (WAV/AIFF/FLAC/ALAC/ProRes) or lossy (MP3/AAC/OGG/Opus/H.264/VP9) | |
| 224 | - | - [ ] Auto-populate `duration_seconds`, `video_width`, `video_height` from probe data | |
| 225 | - | - [ ] Store detected codec + source classification in DB (new columns or metadata JSON) | |
| 226 | - | ||
| 227 | - | #### Phase 14E-2: Audio Transcoding (SmallFiles tier) | |
| 228 | - | - [ ] Background job: lossless audio uploads -> Opus 128 kbps (SmallFiles) or FLAC (BigFiles+) | |
| 229 | - | - [ ] WAV/AIFF -> FLAC saves ~50%; WAV/AIFF -> Opus 128 saves ~90% | |
| 230 | - | - [ ] FLAC/ALAC -> Opus 128 saves ~83% (SmallFiles only) | |
| 231 | - | - [ ] Lossy uploads (MP3/AAC/OGG/Opus): store as-is, no conversion | |
| 232 | - | - [ ] Track `original_s3_key` + `delivery_s3_key` per file (originals kept for BigFiles+) | |
| 233 | - | ||
| 234 | - | #### Phase 14E-3: Fan Download Format Choice | |
| 235 | - | - [ ] Fan chooses download format at purchase time (original lossless, FLAC, MP3 320, AAC 256) | |
| 236 | - | - [ ] Pre-generate common formats on first request, cache in S3 | |
| 237 | - | - [ ] SmallFiles: delivery format only (transparent Opus/AAC). BigFiles+: original + delivery formats | |
| 238 | - | ||
| 239 | - | #### Phase 14E-4: Video Transcoding | |
| 240 | - | - [ ] ProRes/uncompressed MOV -> H.264 CRF 18 MP4 (universal playback) | |
| 241 | - | - [ ] Optionally generate VP9 for web streaming (40-50% smaller than H.264) | |
| 242 | - | - [ ] Lossy video (H.264/VP9/H.265): store as-is, no re-encode | |
| 243 | - | - [ ] Remux where possible (H.264 in MOV -> H.264 in MP4, zero quality loss) | |
| 244 | - | - [ ] Auto-generated thumbnails (ffmpeg frame extraction at configurable timestamp) | |
| 245 | - | ||
| 246 | - | #### Phase 14E-5: Everything Tier Features | |
| 247 | - | - [ ] Adaptive bitrate streaming (HLS/DASH) — multiple quality levels per video | |
| 248 | - | - [ ] Audio: FLAC + Opus 128 + Opus 64 quality ladder | |
| 249 | - | - [ ] Video: original + 1080p + 720p + 480p quality ladder (VP9 or AV1) | |
| 250 | - | - [ ] Bandwidth metering + per-tier bandwidth caps | |
| 98 | + | - [ ] Probe + detect infrastructure | |
| 99 | + | - [ ] Audio transcoding (SmallFiles tier) | |
| 100 | + | - [ ] Fan download format choice | |
| 101 | + | - [ ] Video transcoding | |
| 102 | + | - [ ] Everything tier: adaptive bitrate streaming, quality ladders | |
| 251 | 103 | ||
| 252 | 104 | ### Phase 14B: Embeddable Widgets | |
| 253 | - | - [ ] Embed endpoint /embed/i/{uuid} — embeddable buy button, audio player, 30-sec preview | |
| 254 | - | - [ ] Overlay widget (JS snippet for external sites, checkout popup) | |
| 255 | - | - [ ] Inline embed (iframe-based product card) | |
| 105 | + | - [ ] Embed endpoint, overlay widget, inline embed | |
| 256 | 106 | ||
| 257 | 107 | ### Phase 16: Performance | |
| 258 | 108 | - [ ] Response caching, query optimization, CDN, metrics endpoint | |
| @@ -261,179 +111,89 @@ Infrastructure done: `utoipa` integrated, spec at `/api/openapi.json`. 19 endpoi | |||
| 261 | 111 | - [ ] Evaluate Cloudflare `/crawl` endpoint, per-creator crawl preference toggle | |
| 262 | 112 | ||
| 263 | 113 | ### Phase 17B: Content Newsletters — Remaining | |
| 264 | - | - [ ] Delivery metrics (sent, delivered, opened, clicked) | |
| 265 | - | - [ ] Section-level email preferences (subscribers opt in/out per project) | |
| 114 | + | - [ ] Delivery metrics, section-level email preferences | |
| 266 | 115 | ||
| 267 | 116 | ### Phase 17C: Comments — Remaining | |
| 268 | - | - [ ] Creator moderation via MT moderation tools | |
| 269 | - | - [ ] Restrict commenting to buyers/subscribers (MT community membership gating) | |
| 117 | + | - [ ] Creator moderation via MT tools, restrict commenting to buyers/subscribers | |
| 270 | 118 | ||
| 271 | 119 | ### Phase 20: OSS Creator Tools | |
| 272 | - | - [ ] G7: Git-backed wikis | |
| 273 | - | - [ ] G7B: Compare view, tags/releases, code search, repo creation UI, activity feed | |
| 274 | - | - [ ] G8: Platform-wide mailing lists via platform integration I3 (needs subscription management UI + devlog subscribe button) | |
| 275 | - | - [ ] OSS-specific: sponsor tiers, license display, FUNDING.yml, build artifacts | |
| 276 | - | - [ ] CI/Build enhancements: status badges, artifact signing, log streaming | |
| 277 | - | - [ ] Distribution: package mirrors, reproducible builds, changelog rendering | |
| 278 | - | - [ ] Sustainability: fund-this-dependency widget, aggregate goals | |
| 120 | + | - [ ] Git-backed wikis, compare view, tags/releases, code search | |
| 121 | + | - [ ] Platform-wide mailing lists, sponsor tiers, CI/build enhancements | |
| 279 | 122 | ||
| 280 | 123 | ### Phase 20B: Mobile Apps (Consumption) | |
| 281 | - | - [ ] iOS + Android: library view, download purchased content, offline, audio player, reader, push notifications | |
| 124 | + | - [ ] iOS + Android: library, downloads, offline, audio player, reader, push notifications | |
| 282 | 125 | ||
| 283 | 126 | ### Phase 20C: Physical Product Listings | |
| 284 | - | - [ ] Physical product listing type (self-fulfilled, no MNW fulfillment) | |
| 285 | - | - [ ] Shipping address collection at checkout | |
| 286 | - | - [ ] Order management in creator dashboard (mark shipped, tracking number) | |
| 127 | + | - [ ] Physical product type, shipping address, order management | |
| 287 | 128 | ||
| 288 | 129 | ### Ko-fi Comparison Gaps | |
| 289 | - | - [ ] Fundraising goals — display campaign target + progress bar on project page | |
| 290 | - | - [ ] Commissions — listing with portfolio, slot limits, client messaging, upfront payment | |
| 291 | - | - [ ] Social integrations — Discord webhook or Zapier integration post-beta | |
| 130 | + | - [ ] Fundraising goals, commissions, social integrations | |
| 292 | 131 | ||
| 293 | 132 | ### Phase 20D: Automated Revenue Split Payouts | |
| 294 | - | - [ ] Automated Stripe Transfers for revenue splits (currently recorded as obligations; owners settle directly) | |
| 295 | - | - [ ] Requires switching split-enabled projects from direct charges to destination charges | |
| 296 | - | - [ ] Dashboard: mark splits as settled (manual confirmation while automated transfers not available) | |
| 297 | - | - [ ] Trigger: stable split recording for 3+ months, legal review complete | |
| 133 | + | - [ ] Automated Stripe Transfers, multi-processor support | |
| 298 | 134 | ||
| 299 | 135 | ### Phase 21: Scheduled Content — Remaining | |
| 300 | 136 | - [ ] Pre-save + pre-order, countdown display, calendar view | |
| 301 | 137 | ||
| 302 | 138 | ### Phase 22: Live Streaming (Everything tier) | |
| 303 | - | ||
| 304 | - | Trigger: implement when first Everything tier creator subscribes (or when demand signals justify). | |
| 305 | - | Full spec preserved in git history (commit before this refactor). Summary: | |
| 306 | - | ||
| 307 | - | - Phase 22A: DB schema (streams, stream_sessions, stream_donations) + CRUD | |
| 308 | - | - Phase 22B: Internal API endpoints (auth webhook, started/ended/viewers hooks, HMAC) | |
| 309 | - | - Phase 22C: Public API (status, viewer-token, live page, donate, SSE events) | |
| 310 | - | - Phase 22D: Dashboard UI (stream setup, status, usage meter, settings, VOD archive, donations) | |
| 311 | - | - Phase 22E: MediaMTX deployment (see `human_todo.md`) | |
| 312 | - | - Phase 22F: VOD archival pipeline (ffmpeg concat, thumbnails, S3 upload, auto-publish) | |
| 313 | - | - Phase 22G: Stream donations (0% fee, overlay, leaderboard) | |
| 314 | - | - Phase 22H: Live page + HLS player (hls.js, access control, chat, live indicators) | |
| 315 | - | - Phase 22I: Billing + overage (usage tracking, Stripe metered billing, grace period) | |
| 316 | - | - Phase 22J: Testing (unit + integration + manual checklist) | |
| 317 | - | - Phase 22K: Documentation updates | |
| 318 | - | ||
| 319 | - | Phase ordering: 22A → 22B → 22E → 22C → 22H → 22D → 22I → 22G → 22F → 22J → 22K | |
| 320 | - | MVP: 22A + 22B + 22E + 22C + 22H (stream, fans watch, no donations/VOD/billing) | |
| 321 | - | ||
| 322 | - | Cost model: MediaMTX zero cost (MIT), $0.03-0.04/stream-hour marginal, 20hr included per $60/mo creator. Break-even: $0.60-0.80/mo per creator. Dedicated VPS trigger: >3 concurrent or >50% CPU. | |
| 139 | + | - [ ] Full spec in git history. MVP: 22A + 22B + 22E + 22C + 22H | |
| 140 | + | - [ ] Trigger: first Everything tier creator subscribes | |
| 323 | 141 | ||
| 324 | 142 | ### Phase 24: Payment Independence | |
| 325 | - | ||
| 326 | - | #### Stablecoin checkout (low effort) | |
| 327 | - | - [ ] Add `crypto` to `payment_method_types` in checkout session creation (or enable via Stripe Dashboard) | |
| 328 | - | - [ ] Handle refunds (Stripe returns stablecoins to fan's original wallet automatically) | |
| 329 | - | - [ ] Test end-to-end: fan pays with USDC, creator sees USD in Stripe balance | |
| 330 | - | - [ ] Update payments.md and payouts.md | |
| 331 | - | ||
| 332 | - | #### Phase 24A: Reduce creator-side fees (trigger: creator feedback) | |
| 333 | - | - [ ] Evaluate Stripe's Link for lower repeat-purchase friction | |
| 334 | - | - [ ] Research bundling/minimum-purchase features to reduce fixed fee impact | |
| 335 | - | - [ ] Document which Stripe features could reduce creator processing costs | |
| 336 | - | ||
| 337 | - | #### Phase 24B: Stripe dependency mitigation (trigger: >200 creators or policy change) | |
| 338 | - | - [ ] Evaluate Stripe Treasury for embedded financial accounts | |
| 339 | - | - [ ] Document Stripe suspension recovery plan | |
| 340 | - | - [ ] Identify one backup processor (Adyen, Square) with Connect-like direct charges | |
| 341 | - | - [ ] Architecture assessment: map code paths for multi-processor support | |
| 342 | - | ||
| 343 | - | #### Phase 24C: International expansion (trigger: rejected applications from unsupported countries) | |
| 344 | - | - [ ] Track which countries applicants come from | |
| 345 | - | - [ ] Evaluate licensed payout partners (Tipalti, Hyperwallet/PayPal) | |
| 346 | - | - [ ] Evaluate BaaS providers (Unit, Sila Money) for ACH payouts | |
| 347 | - | - [ ] Research Stripe country coverage expansion | |
| 143 | + | - [ ] Stablecoin checkout, reduce creator-side fees, Stripe dependency mitigation, international expansion | |
| 348 | 144 | ||
| 349 | 145 | ### License Key Self-Management | |
| 350 | - | - [ ] Let buyers manage activations (view devices, deactivate) without a MNW account | |
| 351 | - | - [ ] Configurable offline grace period per item (default 7 days, creator can set 14/30) | |
| 352 | - | - [ ] License transfer: let a buyer reassign a key to another user (creator toggle per item) | |
| 353 | - | ||
| 354 | - | ### SyncKit S5: SDK & Multi-Tenant | |
| 355 | - | - [ ] Swift SDK, multi-tenant isolation, rate limiting per tier | |
| 356 | - | - [ ] Developer dashboard as MNW dashboard tab | |
| 357 | - | ||
| 358 | - | ### SyncKit S8: Advanced Features | |
| 359 | - | - [ ] WebSocket realtime sync (shared gateway with MT real-time threads) | |
| 360 | - | - [ ] CRDT conflict resolution, selective sync, schema migration, pull filtering | |
| 361 | - | ||
| 362 | - | ### SyncKit S9: Productize | |
| 363 | - | - [ ] Add SyncKit usage dashboard tab (current weight, burst consumed, per-user breakdown) | |
| 364 | - | - [ ] Write developer-facing landing page | |
| 365 | - | - [ ] Write quickstart guide: integrate SyncKit into a Tauri app in 30 minutes | |
| 366 | - | - [ ] Publish example app (minimal Tauri + SyncKit template) | |
| 367 | - | - [ ] Health dashboard for SyncKit service | |
| 368 | - | - [ ] Stripe billing integration for SyncKit add-on (weight + burst metering) | |
| 369 | - | - [ ] Implement weight + burst quota enforcement | |
| 370 | - | - [ ] Per-user seat tracking | |
| 371 | - | ||
| 372 | - | ### Developer Services (Future) | |
| 373 | - | - [ ] DS2: Crash reporting → MT threads | |
| 374 | - | - [ ] DS3: Feedback collection → MT threads | |
| 375 | - | - [ ] Dashboard aggregate endpoint (pulls crash/feedback counts from MT internal API) | |
| 146 | + | - [ ] Buyer activation management, offline grace period, license transfer | |
| 147 | + | ||
| 148 | + | ### SyncKit S5/S8/S9 | |
| 149 | + | - [ ] Swift SDK, multi-tenant, rate limiting, WebSocket realtime, CRDT, productize | |
| 150 | + | ||
| 151 | + | ### Developer Services | |
| 152 | + | - [ ] Crash reporting, feedback collection, dashboard aggregate endpoint | |
| 376 | 153 | ||
| 377 | 154 | ### Fan+ Accounts | |
| 378 | - | - [ ] Stripe integration: create Fan+ product/price ($8/mo), webhook handlers | |
| 379 | - | - [ ] Monthly $5 credit: generate single-use promo code on renewal, email to subscriber | |
| 380 | - | - [ ] `+` badge on username display | |
| 381 | - | - [ ] `is_fan_plus` flag in session data for feature gating | |
| 382 | - | - [ ] Dev community as private MT community (Fan+ only) | |
| 383 | - | - [ ] Advisory polls infrastructure (simple yes/no polls, Fan+ only) | |
| 384 | - | - [ ] Guarantees/economics page updates for Fan+ | |
| 155 | + | - [ ] Stripe integration, monthly credit, badge, feature gating, dev community | |
| 385 | 156 | ||
| 386 | 157 | ### DocEngine — Remaining | |
| 387 | - | - [ ] Full-text search index (build at load time, JSON endpoint for client-side search) | |
| 388 | - | - [ ] Versioned docs (directory per version, version switcher) | |
| 158 | + | - [ ] Full-text search index, versioned docs | |
| 389 | 159 | ||
| 390 | 160 | ### Notification Service | |
| 391 | - | - [ ] `notifications` table (user_id, type, source, title, body, link, read, created_at) | |
| 392 | - | - [ ] Notification creation on key events (new comment, new follower, purchase, mention, flag) | |
| 393 | - | - [ ] API endpoints: GET /api/notifications (paginated), POST /api/notifications/read | |
| 394 | - | - [ ] MT consumes MNW notifications API | |
| 395 | - | - [ ] Client apps (GO/BB/AF) poll notifications for sync/OTA events | |
| 396 | - | - [ ] Digest preferences (immediate, daily, weekly) | |
| 397 | - | - [ ] In-app notification center (MNW dashboard) | |
| 161 | + | - [ ] notifications table, event triggers, API endpoints, digest preferences | |
| 398 | 162 | ||
| 399 | 163 | ### Search Infrastructure | |
| 400 | - | - [ ] MNW full-text search (tsvector on items, blog posts, projects) | |
| 401 | - | - [ ] Unified search API: `/api/search?q=term&scope=items,threads,posts,projects` | |
| 402 | - | - [ ] Cross-project search results (MNW items + MT threads in one response) | |
| 164 | + | - [ ] Full-text search (tsvector), unified search API, cross-project results | |
| 403 | 165 | ||
| 404 | 166 | ### Image Upload Pipeline | |
| 405 | - | - [ ] Shared image processing: thumbnail generation, format validation, size limits | |
| 167 | + | - [ ] Thumbnail generation, format validation, size limits | |
| 406 | 168 | ||
| 407 | 169 | ### Link Preview Extraction | |
| 408 | - | - [ ] Extract MT `link_preview.rs` into shared crate or copy pattern to MNW | |
| 409 | - | - [ ] MNW: OG metadata for item/project pages (social sharing cards) | |
| 410 | - | - [ ] MNW blog posts: auto-preview linked URLs | |
| 170 | + | - [ ] Extract to shared crate, OG metadata, blog post URL previews | |
| 411 | 171 | ||
| 412 | 172 | --- | |
| 413 | 173 | ||
| 414 | 174 | ## Dependencies (blocked on upstream) | |
| 415 | - | - [ ] Monitor yara-x for wasmtime >=42.0.2 (11 CVEs including 2 critical — RUSTSEC-2026-0095, -0096) | |
| 175 | + | - [ ] Monitor yara-x for wasmtime >=42.0.2 (RUSTSEC-2026-0095, -0096) | |
| 416 | 176 | - [ ] Monitor aws-sdk-s3 for lru fix (RUSTSEC-2026-0002) | |
| 417 | 177 | - [ ] Monitor async-stripe for instant fix (RUSTSEC-2024-0384) | |
| 418 | 178 | - [ ] rsa (RUSTSEC-2023-0071) via sqlx-mysql + yara-x — no fix available | |
| 419 | 179 | ||
| 420 | 180 | ## Deferred | |
| 421 | 181 | - [ ] Team/organization accounts | |
| 422 | - | - [ ] Creator @makenot.work email addresses (forwarding-only, Migadu) | |
| 182 | + | - [ ] Creator @makenot.work email addresses (Migadu) | |
| 423 | 183 | - [ ] Tax-year revenue summary, invoice generation, 1099 guidance | |
| 424 | - | - [ ] Merchant of Record (MoR) — handle VAT/GST/sales tax globally | |
| 184 | + | - [ ] Merchant of Record (MoR) | |
| 425 | 185 | - [ ] Email drip workflows | |
| 426 | 186 | - [ ] Music metadata fields (BPM, key, genre) | |
| 427 | - | - [ ] Podcast private RSS feeds (per-subscriber unique RSS, Spotify integration) | |
| 187 | + | - [ ] Podcast private RSS feeds | |
| 428 | 188 | - [ ] OG image generation | |
| 429 | 189 | - [ ] UTM parameter tracking | |
| 430 | 190 | - [ ] Blog post revision history | |
| 431 | 191 | - [ ] Series/serial ordering, reading progress | |
| 432 | 192 | - [ ] Traffic/referrer tracking | |
| 433 | - | - [ ] Revisit admin system (currently config-based ADMIN_USER_ID) | |
| 193 | + | - [ ] Revisit admin system (currently config-based) | |
| 434 | 194 | - [ ] S3 bucket versioning | |
| 435 | - | - [ ] PDF stamping (watermark with buyer email/name) | |
| 436 | - | - [ ] CONCURRENTLY index strategy — plan before tables grow large | |
| 195 | + | - [ ] PDF stamping | |
| 196 | + | - [ ] CONCURRENTLY index strategy | |
| 437 | 197 | ||
| 438 | 198 | ## Key Paths | |
| 439 | 199 | ``` | |
| @@ -445,7 +205,7 @@ MNW/server/src/ | |||
| 445 | 205 | import/ (CSV converter, pipeline, intermediate format) |
Lines truncated
| @@ -121,3 +121,82 @@ Systematic creator-perspective audit of docs, legal, code, and competitive posit | |||
| 121 | 121 | - [x] Expanded analytics.md "We don't track" list — explicitly lists page views, referrals, UTM, geo, conversion funnels as absent | |
| 122 | 122 | - [x] Renamed Streaming tier to Everything — enum, DB migration 079, all templates, all docs, CLAUDE.md, env vars | |
| 123 | 123 | - [x] Removed "video coming soon" labels from how-we-work.md, items.md — video uploads are implemented | |
| 124 | + | ||
| 125 | + | --- | |
| 126 | + | ||
| 127 | + | ## Audit Run 19 (2026-05-02) | |
| 128 | + | ||
| 129 | + | - [x] Add timeout to `payments/connect.rs:198` raw reqwest call (Stripe resume-subscription) | |
| 130 | + | - [x] Add `ModerationActionId` newtype and `ModerationActionType` enum (already existed in id_types.rs + enums.rs) | |
| 131 | + | - [x] Bump dependency pins: tokio 1.50, uuid 1.22, chrono 0.4.44, yara-x 1.14, anyhow 1.0.102 | |
| 132 | + | - [x] Fix auth rate limit for lockout test (constants.rs fast-tests override, burst 5->20) | |
| 133 | + | ||
| 134 | + | --- | |
| 135 | + | ||
| 136 | + | ## Audit Run 18 (2026-05-01) | |
| 137 | + | ||
| 138 | + | - [x] Add unit tests to `wam_client.rs` — 5 tests | |
| 139 | + | - [x] Add unit tests to `git_ssh.rs` — 14 tests | |
| 140 | + | - [x] `scanning/hash_lookup.rs` — static `LazyLock<reqwest::Client>` | |
| 141 | + | - [x] `routes/ota.rs` `delete_release_handler` — O(1) ownership check | |
| 142 | + | - [x] CSV import `parse_amount_cents` — removed ambiguous heuristic | |
| 143 | + | - [x] Split `helpers.rs` into `formatting.rs`, `crypto.rs`, `rate_limit.rs` | |
| 144 | + | - [x] Reduce `analytics.rs` query duplication — `Scope` enum, 623→468 lines | |
| 145 | + | - [x] `discover.rs` — already deduplicated via macro | |
| 146 | + | - [x] `routes/admin/` — single `count_users_summary` using `COUNT(*) FILTER` | |
| 147 | + | ||
| 148 | + | --- | |
| 149 | + | ||
| 150 | + | ## UX Audit Remediation (2026-05-02) — All Complete | |
| 151 | + | ||
| 152 | + | - [x] **[HIGH]** Add "Add to collection" UI — dropdown on item pages + library context menus | |
| 153 | + | - [x] **[HIGH]** Consolidate item wizard from 8 to 6 steps — merged Details+Appearance, removed Distribution | |
| 154 | + | - [x] **[MEDIUM]** Restructure dashboard tabs — 4 core + "More" dropdown | |
| 155 | + | - [x] **[MEDIUM]** Fix price input to use dollars — PWYW min, tier, promo, inline edit | |
| 156 | + | - [x] **[MEDIUM]** Standardize pricing terminology — "One-Time Purchase" everywhere | |
| 157 | + | - [x] **[MEDIUM]** Self-service refund UI — Sales tab on item dashboard + Stripe API | |
| 158 | + | - [x] **[MEDIUM]** Surface embed feature — Edit/Embed links on public item pages | |
| 159 | + | - [x] **[MEDIUM]** Link data export — promoted from collapsed details to visible | |
| 160 | + | - [x] **[MEDIUM]** Explain creator application — already present in pitch.html | |
| 161 | + | - [x] **[LOW]** Feature checkbox descriptions — already implemented via `ProjectFeature::description()` | |
| 162 | + | - [x] **[LOW]** Improve empty states — CTAs already present | |
| 163 | + | - [x] **[LOW]** Make promo code visible at checkout — inline form | |
| 164 | + | - [x] **[LOW]** Bulk operations — already implemented (publish/unpublish/delete) | |
| 165 | + | - [x] **[LOW]** Keyboard shortcuts — `?` help overlay, Esc, Cmd+S | |
| 166 | + | - [x] **[LOW]** Soft delete with 7-day recovery — deleted_at column, scheduler purge, restore endpoint | |
| 167 | + | - [x] **[LOW]** Wishlist/bookmark — toggle on item pages, wishlists table | |
| 168 | + | - [x] **[LOW]** Changelog — /changelog page linked from footer | |
| 169 | + | ||
| 170 | + | --- | |
| 171 | + | ||
| 172 | + | ## unwrap() Audit — Grade A | |
| 173 | + | ||
| 174 | + | - [x] Audited all `unwrap()` in production code paths | |
| 175 | + | - [x] Only 1 dangerous unwrap found: `promo_codes.rs:212`. Fixed with `.ok_or_else()`. | |
| 176 | + | - [x] All others are safe: tests, static regex in LazyLock, infallible Response::builder() | |
| 177 | + | ||
| 178 | + | --- | |
| 179 | + | ||
| 180 | + | ## Pre-Beta Code Tasks — Completed | |
| 181 | + | ||
| 182 | + | ### SyncKit | |
| 183 | + | - [x] Sync log compaction — cursor-based, runs in monitor maintenance loop | |
| 184 | + | ||
| 185 | + | ### Git Access Provisioning | |
| 186 | + | - [x] Dashboard page for SSH key management | |
| 187 | + | - [x] Per-repo collaborator access — migration 087 | |
| 188 | + | - [x] Replace manual `setup-ssh-keys.sh` — `mnw-admin setup-git` | |
| 189 | + | ||
| 190 | + | ### Moderation | |
| 191 | + | - [x] Admin "send warning" action | |
| 192 | + | - [x] `moderation_actions` table (migration 085) | |
| 193 | + | - [x] Admin handlers record actions | |
| 194 | + | - [x] Actions resolved automatically | |
| 195 | + | - [x] User-facing "Account Status" section | |
| 196 | + | - [x] Moderation history | |
| 197 | + | ||
| 198 | + | --- | |
| 199 | + | ||
| 200 | + | ## Integration Test Fixes (2026-05-02) | |
| 201 | + | ||
| 202 | + | All 34 previously-failing tests resolved. Key changes: auth rate limiter per-handler, advisory lock deadlock fix, unique IP per TestClient, fast-tests feature flag, sandbox test consolidation. |