# MNW Server — Todo
**Last updated:** 2026-05-31 late evening (post Run #9 — launch-eve pass).
## Status
All 5 axes at A- after Run #9 fixes. **0 CRITICAL open · 1 SERIOUS open (deferred) · 3 HIGH open (deferred) · 7 MED open (deferred).** Launchplan §1.5 A- bar holds. See `docs/audit_review.md` Run #9 section for full triage.
## Run #9 — fixed this session (2026-05-31)
- **UX-CRITICAL** Signup TOCTOU 23505 → 500 + form loss. `join_wizard.rs`: catch 23505 with constraint-name routing, surface as `return_error`. Follow-up: preserve typed form fields on error swap (Phase 4).
- **Sec-SERIOUS** `delete_all_sessions_for_user` non-atomic JWT bump → wrapped in `pool.begin()` / `tx.commit()` (`db/sessions.rs:247`).
- **Sec-SERIOUS** 2FA login-email IP spoofable via bare `x-forwarded-for` → swapped to `crate::helpers::extract_client_ip` (`routes/pages/public/two_factor.rs:308`).
- **Pay-SERIOUS** Webhook dual-failure 503 short-circuited on Stripe retry → call `unmark_event_processed` before returning 503 (`routes/stripe/webhook/mod.rs:81`).
`cargo check --tests` clean; targeted unit tests (sessions/webhook/two_factor/join_wizard) 33/33 green. Full DB-integration suite needs astra postgres.
## Run #9 — deferred with rationale (Phase 4)
- [ ] **Pay-SERIOUS** Subscription webhook out-of-order events resurrect `active`. Needs `created`-timestamp re-extraction from `UntypedEvent` + `WHERE last_event_at <= $created` guards across Fan+/creator-tier/synckit subscription writes. Cross-cutting; worst case is minutes-window of restored access until next webhook.
- [ ] **Sto-HIGH** Migration 129 dead-letter table never written (`cleanup.rs:453`). Operational visibility, not runtime; one-INSERT fix.
- [ ] **Perf-HIGH** Per-request `reqwest::Client::new()` in 5 hot paths (dashboard/main, public/landing, api/internal/cli_features, api/domains, auth.rs). Hoist to OnceLock or AppState pooled client.
- [ ] **Perf-HIGH** Unbounded `tokio::spawn` in `cleanup.rs:215-220` `spawn_expired_account_cleanups`. Lift existing `CLEANUP_PARALLELISM=4` JoinSet pattern from `cleanup_sandbox_accounts` 100 lines above.
- [ ] **Pay-MED** `pricing.rs::parse_dollars_to_cents` strips European decimal comma; `1,23` → 12300¢.
- [ ] **Pay-MED** SyncKit app-sub checkout silently defaults `storage_limit_bytes` to 0 if metadata missing.
- [ ] **Pay-MED** Guest checkout email sentinel `"unknown@guest"` collision risk.
- [ ] **Sto-MED** `is_s3_key_live` 7 EXISTS subqueries on unindexed s3_key columns — sequential scans per retry. Add partial indexes WHERE NOT NULL.
- [ ] **Sto-MED** `is_s3_key_live` LIKE suffix `'%' || s3_key` false-positives on neighboring keys → S3 object leaks. Anchor with `/`.
- [ ] **UX-MED** `purchase.html:145` `?return_to=` dead-wired; login handler always redirects `/dashboard`.
- [ ] **UX-MED** Admin user filter buttons (`admin-users.html:35-44`) use `class="primary"` instead of `btn-primary` — renders unstyled.
## Run #9 — LOW/NOTE (carry forward)
- [ ] **UX-LOW** Pagination links in `git/issues.html:72,76` don't URL-encode `search` param.
- [ ] **UX-LOW** 5 sites use `.render().unwrap_or_default()` on Askama templates — blank UI on render failure, no log line.
- [ ] **UX-LOW** `slugify` (`formatting.rs:85`) produces `"post"` for any non-ASCII title.
- [ ] **Sec-MINOR** `csrf.rs:176-185` `validate_token_consuming` doesn't actually consume — rename or rotate.
- [ ] **Sec-MINOR** `routes/oauth.rs:101-111` `is_localhost_redirect` allows any port regardless of registered URI.
- [ ] **Sec-MINOR** `scanning/archive.rs:124` path-traversal check misses lone `..` segment (no trailing `/`).
- [ ] **Perf-LOW** `db/page_views.rs` `pending` HashMap has no max-cardinality cap.
- [ ] **Perf-LOW** `build_runner.rs:441` artifact tmpfile leaks if process crashes between SCP and `remove_file`.
Live state: working tree has 104+ Run #8 files plus 4 Run #9 files (`join_wizard.rs`, `sessions.rs`, `two_factor.rs`, `webhook/mod.rs`, `docs/audit_review.md`, `todo.md`).
## Open before launch (Monday 2026-06-01)
### Platform-as-product audits (skill-driven, code-review scope; fresh context recommended)
- [ ] `/creator-fuzz` — would a working creator trust this with their livelihood?
- [ ] `/use-fuzz` — discoverability, learnability, first-five-minutes
- [ ] `/business-fuzz` — pricing copy, fee surfacing, refund-policy wording vs actual platform behaviour
### Per-project hygiene (manual, my call when ready)
- [ ] README first-screen audit — what is this / who is it for / where to get it / what does it cost. No headliner paragraphs.
- [ ] `Cargo.toml` version bump for the launch deploy (pick the number; I do the edit if needed)
- [ ] CHANGELOG entry for the launch version
### Monday browser/prod testing (saved for Monday per current direction)
- [ ] §1.1 Walk every public page: footer present, OG/Twitter meta render correctly in Facebook + Twitter debuggers, error pages render via forced 404/403/500
- [ ] §1.2 First-run creator flow end-to-end in production: signup → Stripe Connect → first item upload
- [ ] §1.3 Each seeded creator's `/{handle}` page renders without empty sections; sample item per medium (audio/video/text/download) reachable from `/discover`
- [ ] §1.4 Production deploy of post-fuzz build + version recorded via `record_deploy`; scheduled jobs running on prod (cleanup, scan jobs retention, build reaper, broadcast fan-out); Stripe webhook reachable from dashboard ping; backup snapshot taken pre-launch + restoration path documented in `_private/docs/mnw/server-docs/`; `/health` green
- [ ] §5 launch-day sequence: final deploy, smoke-test logged-out from non-dev machine, update bios/link-in-bio/handles, confirm `maxj.phd` resolves, tag launch commit (`git tag launch-2026-06-01`)
## Open question for the user (action before Monday)
- [ ] **Confirm all role-based email addresses route to real mailboxes**: `info@`, `security@`, `dmca@`, `privacy@`, `dpo@`, `legal@`, `billing@`, `policy@`, `reports@`, `community@`, `appeals@`, `press@`, `noreply@`. Legal pages (terms, privacy, copyright, appeals) and several role-routed flows reference them. If any are aspirational, that's a launch risk for the legal pages and an inbound-mail blackhole. Verify with Postmark/forwarding setup.
## Deferred with rationale (no action; documented)
- [ ] `build_runner.rs:151` serial-target loop. LOW; builds run rarely; refactor touches denominator + error aggregation + log order. Post-launch.
- [ ] `scheduler/mod.rs:92-279` advisory-lock per-tier granularity. Multi-replica concern; defer until multi-replica is real.
- [ ] Drop unused `completion_effects` table (migration cleanup, schema-only).
- [ ] Templatize founder + standard annual prices in `tiers.md` and `pricing.md` (e.g. `$86/yr`, `$130/yr`, `$194/yr`, `$324/yr`; standard `$173/$259/$389/$648`). docengine substitutions don't support arithmetic; would require adding derived `tiers.founding.basic_annual` etc keys in `shared/docengine/src/assumptions.rs`. Not blocking.
- [ ] `_head_assets.html` apple-touch-icon + manifest link wiring. `static/manifest.json` exists but the `` was reverted; bring back if/when desired.
- [ ] Migrate footer's `What's new` and `Shortcuts` `` to `data-*` attributes following the `data-copy-link` pattern. UX MED, not blocking.
## What's done this session (compact summary, full details below)
- **Ultra Fuzz Run #8** — all 5 axes A-. SERIOUS webhook unbounded spawn closed via new `src/background.rs` (bounded mpsc + semaphore-bounded concurrent execution). `spawn_email!` macro migrated; 17 callers + 5 manual webhook spawns + 5 same-disease per-request email spawns now route through bg queue. Run #8 5 new MEDs all closed (cart `min_price_cents`, cart-all chain-break, item-wizard `pricing_model`, inline-JS templates, cart free-claim N+1). Previously-deferred Payments H2 `claim_free_project` race closed.
- **7-wave backlog sweep** — 24 of 26 carried items across auth/security/scanning/db/storage/UX/perf/payments. New schema migration `133_items_duration_seconds_nonnegative.sql`. New `commit_rescan` helper extends chronic-disease seal to admin paths. Two LOW items deferred above.
- **4 cross-cutting sweeps** — `info@makenot.work` email pin (8 files), localhost/TODO/emoji/secret scans all clean.
- **§1.1 public-surface code work** — OG + Twitter card meta in `base.html` (per-page overridable blocks), `static/manifest.json` created with brand colours, `error.html` drops broken back button + adds contact link, `Contact` link added to footer (mailto:info@), new `routes/pages/public/sitemap.rs` (with in-memory 10-min cache + LIKE-wildcard escape from the security review).
- **Doc-fuzz** — `content-scanning.md` restructured (Malware checks + Authenticity checks sections, added URLhaus/MetaDefender/signing layers), `policy.html` See-also block linking 6 legal pages, `tiers.md` prose prices templatized via `{{ tiers.standard.* | int }}`.
- **Exorcise** — 9 AI-tell removals across compare.md, content-scanning.md, appeals.md, faq.md.
- **Nitpick** — 2 polish edits (dead `let _ = scan_status` removed, unused tuple-name destructure tidied).
- **Security review** — 2 MEDs fixed inline: sitemap.xml in-memory cache to absorb crawler/attacker hammering; LIKE-wildcard escape on `is_s3_key_live` to prevent `_` in s3_keys from false-positive matching.
---
## Ultra Fuzz 2026-05-31 (Run #8 — final re-grade)
### Above-MED items to address before launch (or defer with rationale)
- [x] **Perf SERIOUS — Webhook hot-path unbounded `tokio::spawn`.** Fixed 2026-05-31. New `src/background.rs` with `BackgroundTx` + bounded mpsc (1024) + semaphore-bounded concurrency (8 workers vs 25 pool conns). `spawn_email!` macro refactored to use the bg queue (covers 17 callers). 5 manual webhook spawns migrated (`checkout_helpers.rs:58, 96, 124, 290` + `checkout.rs:618`). Same-disease per-request email spawns also migrated: postmark issue replies (×2), guest-claim email, join-wizard signup (×2). `cargo test --lib` 1654 / 0. Deferred (different shapes): import pipeline (long-running), MT community create (HTTP not pool), departure/status broadcast (broadcast-class), idempotency store (trivial).
### New MED-tier findings (all closed 2026-05-31)
- [x] **Payments MED — Cart `min_price_cents` bypass.** Fixed. Both cart paths (`process_seller_checkout` and `create_cart_checkout`) now check `pc.min_price_cents` for non-platform Discount codes before applying. Skips the ineligible item (others may still qualify) rather than rejecting the whole cart — matches the existing scope-skip pattern.
- [x] **Payments MED — Cart-all chain-break on all-free first seller.** Fixed. `process_seller_checkout` signature changed `Result` → `Result