max / makenotwork
105 files changed,
+2430 insertions,
-5883 deletions
| @@ -97,6 +97,24 @@ upload_binary() { | |||
| 97 | 97 | echo "[upload] Done" | |
| 98 | 98 | } | |
| 99 | 99 | ||
| 100 | + | send_restart_warning() { | |
| 101 | + | echo "[warning] Sending 30s restart warning to users..." | |
| 102 | + | local token | |
| 103 | + | token=$(ssh $SERVER "grep '^CLI_SERVICE_TOKEN=' $REMOTE_DIR/.env 2>/dev/null | cut -d= -f2-" | tr -d '\r\n') | |
| 104 | + | if [ -z "$token" ]; then | |
| 105 | + | echo "[warning] CLI_SERVICE_TOKEN not found in .env, skipping warning" | |
| 106 | + | return 0 | |
| 107 | + | fi | |
| 108 | + | local status | |
| 109 | + | status=$(ssh $SERVER "curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:3000/api/internal/restart-warning -H 'Authorization: Bearer $token' -H 'Content-Type: application/json' -d '{\"seconds\": 30}'") | |
| 110 | + | if [ "$status" = "204" ]; then | |
| 111 | + | echo "[warning] Restart warning sent, waiting 30s..." | |
| 112 | + | sleep 30 | |
| 113 | + | else | |
| 114 | + | echo "[warning] Warning request returned HTTP $status, continuing without delay" | |
| 115 | + | fi | |
| 116 | + | } | |
| 117 | + | ||
| 100 | 118 | restart_app() { | |
| 101 | 119 | echo "[restart] Restarting makenotwork..." | |
| 102 | 120 | ssh $SERVER "systemctl restart makenotwork" | |
| @@ -112,6 +130,7 @@ case "${1:-full}" in | |||
| 112 | 130 | --quick) | |
| 113 | 131 | echo "=== Quick Deploy ===" | |
| 114 | 132 | build_binary | |
| 133 | + | send_restart_warning | |
| 115 | 134 | upload_binary | |
| 116 | 135 | restart_app | |
| 117 | 136 | ;; | |
| @@ -123,6 +142,7 @@ case "${1:-full}" in | |||
| 123 | 142 | echo "=== Full Deploy ===" | |
| 124 | 143 | build_binary | |
| 125 | 144 | upload_config | |
| 145 | + | send_restart_warning | |
| 126 | 146 | upload_binary | |
| 127 | 147 | restart_app | |
| 128 | 148 | ;; |
| @@ -1,77 +1,307 @@ | |||
| 1 | - | # Audit History | |
| 2 | - | ||
| 3 | - | Condensed record of cross-project audit rounds. Full grades and commentary live in each project's `docs/<short-name>/audit_review.md`. Per-run details archived below the current state. | |
| 4 | - | ||
| 5 | - | ## Current Grades (Run 12 — 2026-03-28) | |
| 6 | - | ||
| 7 | - | | Project | Code | Arch | Test | Security | Perf | Docs | Deps | Frontend | Overall | | |
| 8 | - | |---------|:----:|:----:|:----:|:--------:|:----:|:----:|:----:|:--------:|:-------:| | |
| 9 | - | | MNW | A | A | A | A+ | A | A | A | A | A | | |
| 10 | - | | GO | A | A+ | A | A | A | A | A | A | A | | |
| 11 | - | | BB | A | A | A | A | A | A | A- | A | A | | |
| 12 | - | | AF | A | A | A | A | A | A | A | A | A | | |
| 13 | - | | PoM | A | A | A | A | A | A- | A | - | A | | |
| 14 | - | | SK SDK | A | A | A+ | A- | A | A | A | - | A | | |
| 15 | - | | MT | A | A | A | A+ | A | A- | A | A- | A | | |
| 16 | - | | TagTree | A+ | A | A+ | A | A | A | A+ | - | A | | |
| 17 | - | ||
| 18 | - | ## Audit Counts | |
| 19 | - | ||
| 20 | - | | Project | Total Audits | First Audit | Current Grade | | |
| 21 | - | |---------|:------------:|-------------|:-------------:| | |
| 22 | - | | MNW | 33 | Feb 2026 | A | | |
| 23 | - | | GO | 11 | Mar 2026 | A | | |
| 24 | - | | BB | 11 | Mar 2026 | A | | |
| 25 | - | | AF | 16 | Mar 2026 | A | | |
| 26 | - | | PoM | 10 | Mar 2026 | A | | |
| 27 | - | | SK SDK | 7 | Mar 2026 | A | | |
| 28 | - | | MT | 7 | Mar 2026 | A | | |
| 29 | - | | TagTree | 2 | Mar 2026 | A | | |
| 30 | - | ||
| 31 | - | ## Test Counts | |
| 32 | - | ||
| 33 | - | MNW 1,174 (584 unit + 545 integration + 17 admin + 28 health, **2 failures**) | GO ~734 (686 Rust + 48 JS) | BB 602 (547 Rust + 55 JS) | AF 611 | PoM 359 (222 unit + 8 cli + 129 integration) | SK SDK 297 (197 unit + 99 integration + 1 doctest) | MT 225+ (35 unit + 190 integration) | TagTree 99 | **Total: ~4,101** | |
| 34 | - | ||
| 35 | - | ## Cross-Project Patterns | |
| 36 | - | ||
| 37 | - | **Shared strengths:** All 8 projects use parameterized SQL (zero injection risk), typed error handling, clean module boundaries. Zero production `unsafe` code outside platform FFI (AF drag_out/ has 17 justified FFI unsafe blocks for macOS objc2 + Windows COM). All compile and test clean (MNW has 2 integration test failures — filed as action items). All use `#[instrument(skip_all)]` on public async functions. Zero dead code across all 8 projects (clippy `dead_code`/`unused` clean). TagTree integrated into 5 consumers (AF, GO, BB, MT, MNW) with per-app TagConfig. | |
| 38 | - | ||
| 39 | - | **Remaining A- grades (at ceiling for pre-beta):** | |
| 40 | - | - BB Deps A-: 26 deps, all justified for Tauri + crypto + DB + XML + theming | |
| 41 | - | - SK SDK Security A-: No key rotation mechanism — deferred post-beta | |
| 42 | - | ||
| 43 | - | **Upstream-blocked deps:** | |
| 44 | - | - rsa (RUSTSEC-2023-0071) via sqlx-mysql — non-issue (MNW uses Postgres) | |
| 45 | - | - lru (RUSTSEC-2026-0002) via aws-sdk-s3 | |
| 46 | - | - time DoS (RUSTSEC-2026-0009) in GO | |
| 47 | - | ||
| 48 | - | **New advisories (Run 12):** | |
| 49 | - | - aws-lc-sys 0.38.0 (RUSTSEC-2026-0044 + -0048, HIGH 7.4) — PoM, MT. Fix: `cargo update -p aws-lc-sys` | |
| 50 | - | - rustls-webpki 0.103.9 (RUSTSEC-2026-0049) — BB, AF, PoM, SK, MT. Fix: `cargo update -p rustls-webpki` | |
| 51 | - | - tar 0.4.44 (RUSTSEC-2026-0067 + -0068, medium 5.1) — BB. Fix: `cargo update -p tar` | |
| 52 | - | - async-std unmaintained (RUSTSEC-2025-0052, warning) — GO via async-imap | |
| 53 | - | - bincode unmaintained (RUSTSEC-2025-0141, warning) — MNW via syntect/yara-x | |
| 54 | - | ||
| 55 | - | ## Adversarial Testing (2026-03-17) | |
| 56 | - | ||
| 57 | - | 21 attack surfaces audited across all 7 projects. Full results in `docs/adv_testing.md`. All 16 actionable items resolved. 1 MEDIUM remaining (CSP `unsafe-inline` — deferred, required by HTMX). 11 LOWs remaining (all cosmetic or by-design). | |
| 58 | - | ||
| 59 | - | ## Run History (brief) | |
| 60 | - | ||
| 61 | - | | Run | Date | Scope | Key Events | | |
| 62 | - | |-----|------|-------|------------| | |
| 63 | - | | Archived 1-10 | Feb-Mar 2026 | MNW only | 20 audits, stabilized at A | | |
| 64 | - | | Run 1-2 | Mar 6-8 | MNW | Fresh start, 23 findings all fixed | | |
| 65 | - | | Run 3 | Mar 10 | MNW + PoM (first) | PoM B+ baseline | | |
| 66 | - | | Run 4 | Mar 11 | All 6 | First full cross-project. JS audit (33/33 fixed). SK SDK first audit (B+) | | |
| 67 | - | | Run 5 | Mar 13 | All 6 | Skeptical pre-launch lens. 24 items all fixed. GO A→A-→A, SK B+→A-, PoM A-→A | | |
| 68 | - | | Security Deep Dive | Mar 13 | All 6 | 12 fail-closed fixes. AF Security A-→A, PoM Security A-→A | | |
| 69 | - | | Adversarial | Mar 14 | All 6 + MT | 1 HIGH + 7 MEDIUM + 8 LOW, all fixed. MT first formal audit (B+→A) | | |
| 70 | - | | Run 6 | Mar 16 | All 7 | Zero critical/high. 9 action items, 5 resolved same-day | | |
| 71 | - | | Run 7 | Mar 17 | All 7 | 12/12 items resolved (incl. 4 carried from Run 6). PoM cli/ + SK client/ split | | |
| 72 | - | | Run 8 | Mar 17 | All 7 | 1 LOW finding (MNW ILIKE escaping — resolved). Fourth consecutive zero grade changes | | |
| 73 | - | | Adversarial Testing | Mar 17 | All 7 | 21 surfaces, 16 fixes across 3 sessions. All resolved | | |
| 74 | - | | Run 9 | Mar 18 | All 7 | MNW Sentry removed (tracing-only). AF clippy fixes. Release builds signed+notarized. Fifth consecutive zero grade changes | | |
| 75 | - | | Run 10 | Mar 22 | All 8 | TagTree first audit (A-). MNW Phase 25-26 (creation + join wizards, +8.6K LOC). Zero dead code across all 8 projects. Sixth consecutive zero grade changes (existing 7). Git hygiene: GO/BB/AF/PoM/MT/SK dirty, TagTree no repo | | |
| 76 | - | | Run 11 | Mar 22 | MNW | Platform integration I3+I4 (mailing lists + content newsletter). 1,013 tests (+17). 3 dep vulns fixed (aws-lc-sys, rustls-webpki). 2 N+1 patterns fixed (admin backfill + onboarding drip → batch queries). Seventh consecutive zero grade changes | | |
| 77 | - | | Run 12 | Mar 28 | All 8 | MNW +161 tests (1,174 total, 2 failures). 5 new dep advisories (aws-lc-sys HIGH in PoM+MT, rustls-webpki in 5 projects, tar in BB). MNW bundles + batch upload + ProjectFeature trait. Total tests: ~4,101. Eighth consecutive run with zero grade changes | | |
| 1 | + | # MNW Server -- Audit History | |
| 2 | + | ||
| 3 | + | Full chronological audit log. See [audit_review.md](./audit_review.md) for current state. | |
| 4 | + | ||
| 5 | + | ## Changes Since Last Audit | |
| 6 | + | ||
| 7 | + | ### Thirty-third audit (2026-03-28, Run 12 cross-project) | |
| 8 | + | - **Test count:** 1,174 (584 unit + 545 integration + 17 admin + 28 health). 2 FAILURES. 0 clippy warnings. | |
| 9 | + | - **Grade:** A (maintained). v0.3.13. | |
| 10 | + | - **Migrations:** 045 -> 048 (+3: bundles, batch uploads, project features). | |
| 11 | + | - **Test failures (2):** | |
| 12 | + | - `workflows::htmx::delete_item_returns_toast` — panics at htmx.rs:345: HX-Trigger header missing on item delete response. Filed as MEDIUM action item. | |
| 13 | + | - `workflows::wizards::item_wizard_license_keys` — panics at wizards.rs:579: RowNotFound after license key wizard step. Filed as MEDIUM action item. | |
| 14 | + | - **Bundles + batch upload:** New ProjectFeature trait for feature-gated functionality. Bundle items (multiple files per item). Batch file upload support. Well-implemented type safety with A+ grade on new types. | |
| 15 | + | - **Email-first issue tracker:** (shipped since Run 11) Web write UI removed, labels removed. Issues via inbound email, replies via HMAC-signed Reply-To. Commit-message reopens added. Migration 045. | |
| 16 | + | - **I5 (Git Patch Inbound):** (shipped since Run 11) `postmark_inbound` handler, `{slug}@patches.makenot.work`, MT patches category, message-ID threading. 9 integration tests. | |
| 17 | + | - **Dependency advisory:** bincode unmaintained (RUSTSEC-2025-0141, warning only) — upstream via syntect/yara-x. | |
| 18 | + | - **Mandatory surprise:** The `ProjectFeature` trait is a clean, minimal abstraction for feature-gating. Each feature is a zero-sized type implementing the trait, with `is_enabled()` checking feature flags on the project. No feature flag infrastructure bloat — just a trait and an enum. Verdict: **Well-designed.** | |
| 19 | + | - **Previous items verified:** All 30 resolved items confirmed intact. 3 upstream-blocked deps unchanged. | |
| 20 | + | ||
| 21 | + | ### Previously unaudited (2026-03-25, email-first issue tracker + I5 git patch inbound) | |
| 22 | + | - **Test count:** 1,060 (517 unit + 544 integration + 28 health + 4 load). 0 failures. | |
| 23 | + | - **Migrations:** 043 -> 045 (+2: patch_message_ids, issue_message_ids). | |
| 24 | + | - **Email-first issue tracker:** Web write UI completely removed (create, edit, close/reopen, comment forms, labels). Issues created exclusively via inbound email (`{owner}+{repo}@issues.makenot.work`). Comments via email reply (HMAC-signed Reply-To addresses). Issue labels removed entirely. Close/reopen via commit messages only (`fixes #N`, `closes #N`, `reopens #N`). Notification emails include Reply-To, Message-ID, In-Reply-To headers for proper threading. `issue_message_ids` table for email threading (parallels `patch_message_ids`). | |
| 25 | + | - **I5 (Git Patch Inbound):** Verified code-complete. `postmark_inbound` handler processes `git send-email` patches to `{slug}@patches.makenot.work`, creates MT threads in `patches` category (auto-created on demand), multi-part series threaded via Message-ID/In-Reply-To/References. `db/patches.rs` + migration 044. 9 integration tests. Operational: DNS MX for `patches.makenot.work` + Postmark inbound domain config needed at deploy time. | |
| 26 | + | - **Dead code cleanup:** Removed 7 unused issue label DB functions, 2 unused comment functions (`delete_comment`, `get_comment`), `labels.rs` route module, 3 template structs, 3 HTML template files. | |
| 27 | + | - **New code:** `postmark_inbound_issues` handler (new issue + reply paths), `extract_issue_address`, `extract_reply_local`, `strip_quoted_text` helpers, `Reopen` variant in push_refs, 4 new DB functions (`get_issue_by_id`, `get_issue_participants`, `insert_issue_message_id`, `get_issue_id_by_any_message_id`), `send_email_with_headers_and_unsub` on EmailClient. 15 unit tests + 23 integration tests. | |
| 28 | + | - **CSRF fix:** `/postmark/webhook` broadened to `/postmark/` in exempt paths — fixes 3 previously-failing patches tests (403 on inbound webhook). | |
| 29 | + | ||
| 30 | + | ### Thirty-second audit (2026-03-22, Run 11 MNW-focused) | |
| 31 | + | - **Test count:** 1,013 (465 unit + 516 integration + 28 health + 4 load). 0 clippy warnings. Zero dead code. | |
| 32 | + | - **Grade:** A (maintained). v0.3.6. 153 source files. | |
| 33 | + | - **Migrations:** 038 -> 041 (+3: mailing_lists, backfill_mailing_list_subscribers, content_newsletter). | |
| 34 | + | - **Platform integration I3 (Mailing Lists):** New `db/mailing_lists.rs` with CRUD for per-project mailing lists + subscriber management. `mailing_lists` + `mailing_list_subscribers` tables. Auto-create content + devlog lists on project creation. Subscribe on follow/purchase, unsubscribe on unfollow. 6 integration tests. | |
| 35 | + | - **Platform integration I4 (Content Newsletter):** Refactored email delivery from follows-based to mailing-list-based. `send_release_announcements()` now queries content mailing list subscribers instead of direct followers. New `send_blog_post_announcements()` for blog post emails (previously blog posts only created MT threads, never sent emails). `web_only` toggle on items and blog posts to publish without emailing. `mark_blog_post_announced()` idempotent guard. Blog post announcement email template. 4 new integration tests. | |
| 36 | + | - **API polish:** `web_only` field added to `ItemResponse`, `BlogPostResponse`, and `BlogPostEditResponse`. | |
| 37 | + | - **Dependency fixes:** aws-lc-sys 0.38.0->0.39.0 (RUSTSEC-2026-0044 + 0048 resolved), rustls-webpki 0.103.9->0.103.10 (RUSTSEC-2026-0049 resolved for v0.103 chain). | |
| 38 | + | - **Mandatory surprise:** Mailing list delivery has zero duplication with follows-based delivery. Near-identical scheduler functions kept separate — correct per abstraction guidelines. Verdict: **Actually fine.** | |
| 39 | + | - **N+1 fix (admin.rs):** MT backfill handler was calling `get_user_by_id()` per project in a loop. Replaced with batch `get_users_by_ids()` (single `ANY($1)` query) + HashMap lookup. | |
| 40 | + | - **N+1 fix (scheduler.rs):** Onboarding drip was calling `advance_onboarding_step()` per skippable user. Replaced with `batch_advance_onboarding_step()` (single `ANY($1)` UPDATE) for skip sets, individual advance only for users that need emails. | |
| 41 | + | - **No new findings.** All previous items remain resolved. | |
| 42 | + | ||
| 43 | + | ### Thirty-first audit (2026-03-22, Run 10 cross-project) | |
| 44 | + | - **Test count:** 996 (was 1,000 — net -4 from old join/modal tests removed, +7 join wizard, +10 creation wizard offsets). 0 clippy warnings. Zero dead code. | |
| 45 | + | - **Grade:** A (maintained). v0.3.5. 147 source files (was 142). | |
| 46 | + | - **Migrations:** 035 -> 038 (+3: creator tiers, fan_plus, tag paths). | |
| 47 | + | - **Phase 25 (Creation Wizards):** Multi-step HTMX wizards for project (5 steps) and item (6 steps) creation. New route module `routes/pages/dashboard/wizards/` (mod.rs, project.rs, item.rs). 15 templates. `wizard.css` + `wizard.js` infrastructure. Old modal forms removed. 10 integration tests. | |
| 48 | + | - **Phase 26 (Join Wizard):** HTMX multi-step signup replacing client-side JS wizard. New `routes/pages/public/join_wizard.rs`. 5 steps: account (creates user + logs in), profile, creator pitch, stripe, welcome. Optional steps skippable. 7 integration tests. Old `join_handler` removed from `auth.rs`. | |
| 49 | + | - **TagTree integration:** Migration 038 adds `path TEXT NOT NULL` to tags with recursive CTE backfill. `get_tag_ancestors()` rewritten from N+1 parent chain walk to 2-query path-based lookup. `validate_tag_slug()` uses tagtree (TagConfig: max_depth 5, max_length 100). | |
| 50 | + | - **DocEngine extraction:** Documentation rendering extracted to standalone crate. Source LOC reduced from ~50K to ~29K. | |
| 51 | + | - **Rust patterns audit:** Reduced triple clone of `tc.category` in discover filters via `is_some_and()` + move. | |
| 52 | + | - **Dead code:** Zero clippy `dead_code`/`unused` warnings across all targets. Verified with `cargo clippy --all-targets`. | |
| 53 | + | - **Mandatory surprise:** The join wizard's `step_account_create` handler reuses 100% of the existing auth infrastructure (hash_password, login_user, track_session, check_password_breach, email verification) without any duplication. The entire signup flow was factored into a new file by calling existing functions — zero copy-paste, zero new auth logic. Verdict: **Impressive.** | |
| 54 | + | - **No new findings.** All previous items remain resolved. | |
| 55 | + | ||
| 56 | + | ### Thirtieth audit (2026-03-18, Run 9 cross-project) | |
| 57 | + | - **Test count:** 1,000 (unchanged). 0 clippy warnings. | |
| 58 | + | - **Grade:** A (maintained). v0.3.2. | |
| 59 | + | - **Key change:** Sentry removed — tracing-only observability. Files changed: Cargo.toml (sentry dep removed), sentry_layer.rs (deleted), main.rs (Sentry init + tracing layer removed), lib.rs (sentry_layer middleware removed), error.rs (Sentry scope tagging removed), config.rs (sentry_dsn field removed), health endpoint (sentry status removed), test harness (sentry_dsn: None removed from 4 test configs). | |
| 60 | + | - **Observability compensation:** 301 `#[instrument(skip_all)]` annotations. TraceLayer with request ID propagation and status-based log levels. Structured JSON logging in production. Health monitor with DB snapshots + alert emails. | |
| 61 | + | - **Mandatory surprise:** The Sentry removal is surgically clean — zero stale references remain in source (only 1 harmless comment). Tracing setup is more sophisticated than typical Sentry usage: request-scoped tracing, task-level instrumentation, health snapshots persisted to DB, and alert escalation with cooldown. | |
| 62 | + | - **No new findings.** All previous items remain resolved. | |
| 63 | + | ||
| 64 | + | ### Twenty-ninth audit (2026-03-17, Run 8 cross-project) | |
| 65 | + | - **Test count:** 968 -> 1,000 (+32 integration tests). Milestone: four-digit test count. | |
| 66 | + | - **Grade:** A (maintained). v0.3.0. | |
| 67 | + | - **Clippy:** 0 warnings (was 7 — all resolved: collapsible_if, too_many_arguments, explicit_counter_loop). | |
| 68 | + | - **New finding:** ~~LOW: ILIKE wildcard characters (`%`, `_`) not escaped in user search input across db/issues.rs, db/discover.rs, db/categories.rs, db/tags.rs.~~ Resolved: SQL-side `replace()` chain escapes `\`, `%`, `_` in all 20 ILIKE clauses; Rust-side escaping in issues.rs `format!` pattern. | |
| 69 | + | - **Mandatory surprise:** 1,000 tests with zero production unwraps (265 unwrap/expect, all `#[cfg(test)]` only) — Impressive discipline at 50K LOC. | |
| 70 | + | - **Previous items verified:** All resolved. No carried items from Run 7. | |
| 71 | + | ||
| 72 | + | ### Twenty-seventh audit (2026-03-16, Run 6 cross-project) | |
| 73 | + | - **Test count:** 832 -> 968 (+136 tests from G6 implementation) | |
| 74 | + | - **Grade:** A (maintained). G6 features (issue email notifications + commit message references) fully integrated. | |
| 75 | + | - **Source LOC:** 49,940 (up from ~40K), 143 files (up from 186 — likely different counting method) | |
| 76 | + | - **Clippy:** 7 warnings (4 collapsible_if, 2 too_many_arguments on new email methods, 1 explicit_counter_loop). Previously 2. | |
| 77 | + | - **New findings:** LOW: parse_issue_refs regex recompilation per call (should use LazyLock, same pattern already fixed in docs.rs) | |
| 78 | + | - **Mandatory surprise:** parse_issue_refs regex — LOW, functional but inconsistent with existing LazyLock usage elsewhere | |
| 79 | + | - **Previous items verified:** All previous remediated items confirmed intact. 2 upstream-blocked deps unchanged. | |
| 80 | + | ||
| 81 | + | ### Twenty-sixth audit (2026-03-13, pre-launch skeptical lens) | |
| 82 | + | - **Test count:** 821 -> 824 (+3 tests) | |
| 83 | + | - **Grade:** A (maintained). Pre-launch skeptical audit found 2 medium + 1 low issue, all bounded in impact. | |
| 84 | + | - **New findings:** Rate limiting gap on /forgot-password, refund flow not transactional, v2 webhook non-constant-time comparison. | |
| 85 | + | - **Positive surprise:** Password reset token auto-invalidation via embedded password hash — confirms excellent security engineering. | |
| 86 | + | - **Previous items verified:** All 27 prior remediated items confirmed intact. email.rs split, DMARC upgrade, Postmark DKIM all done. | |
| 87 | + | ||
| 88 | + | **Post-audit remediation (2026-03-13):** | |
| 89 | + | - All 3 findings from twenty-sixth audit resolved: forgot-password rate limiting, refund transaction wrapping, constant-time webhook comparison | |
| 90 | + | - Additionally: from_trusted replaced with validated constructor in login/signup, LazyLock for docs.rs regex | |
| 91 | + | - Documentation upgraded to A: SyncKit route handlers documented (15 handlers), SyncKit model field docs added (32 fields), payments.rs and auth.rs module docs expanded, architecture not applicable (MNW docs already existed), README created. | |
| 92 | + | ||
| 93 | + | ### Twenty-fifth audit (2026-03-11, full re-audit) | |
| 94 | + | - **Growth:** 55,223 -> 57,172 total LOC (+3.5%), 765 -> 821 tests (+56), 187 -> 186 files | |
| 95 | + | - **Source LOC:** 40,873 (src/), 16,299 (tests/) | |
| 96 | + | - **Test density:** 14.4 tests/KLOC (up from 13.9) | |
| 97 | + | - **Clippy:** 2 cosmetic warnings (single_match in mnw-admin.rs and lib.rs), down from 0 -- new code introduced minor style nit | |
| 98 | + | - **Scheduler:** New `scheduler.rs` module (180 LOC) for scheduled publish and onboarding drip emails | |
| 99 | + | - **Migrations:** 24 (up from 21) | |
| 100 | + | - **Instrumentation:** 0 `#[instrument]` annotations; 199 tracing log calls across 36 files (manual structured tracing compensates for lack of #[instrument]) | |
| 101 | + | - **Production unwrap hygiene:** ~30 non-test unwrap/expect calls, all justified (startup, infallible HMAC, static regex, static redirect paths) | |
| 102 | + | - **Previous fixes verified:** All 24 remediated items confirmed intact | |
| 103 | + | - **New action items:** 3 (email.rs split, DMARC upgrade, Postmark DKIM verification) | |
| 104 | + | ||
| 105 | + | ### Twenty-fourth audit (2026-03-10, delta review) | |
| 106 | + | - **Growth:** 47,463 -> 55,223 LOC (+16%), 168 -> 187 files, 684 -> 765 tests | |
| 107 | + | - **Trust tiers:** Upload trust system (migration 021) -- new uploads default to `held_for_review` until creator is trusted. Affects 6 scanning/storage tests that expect `clean` status on macOS (no ClamAV + no trust override in test harness). | |
| 108 | + | - **Git repos:** Linked to projects with bidirectional navigation + releases page (migration 020, `db/git_repos.rs`, `routes/git.rs`) | |
| 109 | + | - **Promo codes unification:** Discount codes + download codes merged into unified promo codes system (migration 019, `db/promo_codes.rs`, `routes/api/promo_codes.rs`). Old test files renamed. | |
| 110 | + | - **CI scripts:** `deploy/run-ci.sh` and `deploy/setup-git-ssh.sh` added for automated CI | |
| 111 | + | - **Previous fixes verified:** All 22 remediated items from twenty-second and twenty-third audits confirmed intact | |
| 112 | + | - **6 environment-dependent test failures (fixed 2026-03-10):** All 6 caused by untrusted test users. Fixed by adding `h.trust_user(user_id)` to test setup helpers. | |
| 113 | + | ||
| 114 | + | ### Improved (twenty-third audit -- 2026-03-09) | |
| 115 | + | - **Clippy clean-up:** 33 warnings fixed across 14 files | |
| 116 | + | - **Naming consistency:** `discount_code` -> `promo_code` in item checkout path | |
| 117 | + | - **Test coverage:** 8 new tests + 53 adversarial tests (597 -> 684 tests) | |
| 118 | + | - **Input validation hardened:** 100-char length cap for FreeAccess codes; trial_days capped at 365; `pwyw_min_cents` validated; discount value capped | |
| 119 | + | - **Security:** postmark_token redacted in EmailConfig Debug impl | |
| 120 | + | - **Concurrency:** subscription webhook promo code increment wrapped in DB transaction | |
| 121 | + | ||
| 122 | + | ### Improved (twenty-second audit -- 2026-03-08) | |
| 123 | + | - All 16 findings remediated. 6 dead code functions removed. 8 missing `#[instrument]` attributes added. | |
| 124 | + | - Contact revocation feature shipped. 9 new tests (588 -> 597). | |
| 125 | + | ||
| 126 | + | ### Regressed | |
| 127 | + | - Nothing. | |
| 128 | + | ||
| 129 | + | ### Grade changes (this audit) | |
| 130 | + | - No grade changes from previous audit. All module and project grades stable. | |
| 131 | + | ||
| 132 | + | ## Metrics Over Time | |
| 133 | + | ||
| 134 | + | | Audit Date | LOC | Rust Files | Tests | Tests/KLOC | Clippy Warnings | Cold Spots | Overall | | |
| 135 | + | |------------|-----|-----------|-------|-----------|----------------|------------|---------| | |
| 136 | + | | 2026-03-06 | 45K | 154 | 588 | 13.0 | 0 | 3 | A | | |
| 137 | + | | 2026-03-08 | 47K | 168 | 597 | 12.6 | 0 | 1 | A | | |
| 138 | + | | 2026-03-09 | 52K | 175 | 684 | 13.2 | 0 | 1 | A | | |
| 139 | + | | 2026-03-10 | 55K | 187 | 765 | 13.9 | 0 | 1 | A | | |
| 140 | + | | 2026-03-11 | 57K | 186 | 821 | 14.4 | 2 | 1 | A | | |
| 141 | + | | 2026-03-13 | 57K | 186 | 832 | 14.6 | 2 | 1 | A | | |
| 142 | + | | 2026-03-16 | 50K src | 143 | 968 | 19.4 | 7 | 0 | A | | |
| 143 | + | | 2026-03-17 | 50K src | 143 | 1,000 | 20.0 | 0 | 0 | A | | |
| 144 | + | | 2026-03-18 | 50K src | 142 | 1,000 | 20.0 | 0 | 0 | A | | |
| 145 | + | | 2026-03-22 | 29K src | 147 | 996 | 34.3 | 0 | 0 | A | | |
| 146 | + | | 2026-03-22 | 29K src | 153 | 1,013 | 34.9 | 0 | 0 | A | | |
| 147 | + | | 2026-03-28 | ~30K src | ~160 | 1,174 | ~39 | 0 | 0 | A | | |
| 148 | + | ||
| 149 | + | --- | |
| 150 | + | ||
| 151 | + | ## Documentation Review | |
| 152 | + | ||
| 153 | + | **Last reviewed:** 2026-03-04 (first doc audit, full scope including unpublished/internal) | |
| 154 | + | ||
| 155 | + | ### Overall Grade: A | |
| 156 | + | ||
| 157 | + | Large doc surface (80+ files across public, unpublished, internal). Public-facing docs had several inaccuracies fixed this audit. Unpublished docs had a systemic issue: many creator-facing docs described planned features as available (analytics, embedding, video hosting, streaming). Internal outreach docs had stale tier names, outdated TikTok deadline, and DEV.to member count inconsistency. competition.md had 30+ features listed as "Planned" that are now "Done". All identified issues fixed. | |
| 158 | + | ||
| 159 | + | ### Document Heatmap | |
| 160 | + | ||
| 161 | + | #### Public Docs (`docs/public/`) | |
| 162 | + | ||
| 163 | + | | Document | Status | Last Verified | Notes | | |
| 164 | + | |----------|:------:|:-------------:|-------| | |
| 165 | + | | about/roadmap.md | Fixed | 2026-03-04 | Passkeys moved to "What's Built", test count 349->386 | | |
| 166 | + | | about/how-we-work.md | Fixed | 2026-03-04 | Removed "forums" from Basic tier, "video/streams" from step 2 | | |
| 167 | + | | about/story.md | Current | 2026-03-04 | Founder story | | |
| 168 | + | | about/guarantees.md | Current | 2026-03-04 | SLA commitments | | |
| 169 | + | | support/faq.md | Fixed | 2026-03-04 | Removed 4 broken links to non-existent sub-pages | | |
| 170 | + | | support/contact.md | Current | 2026-03-04 | Contact info | | |
| 171 | + | | legal/privacy-policy.md | Current | 2026-03-04 | Privacy policy | | |
| 172 | + | | legal/terms-of-service.md | Current | 2026-03-04 | ToS | | |
| 173 | + | | legal/acceptable-use.md | Current | 2026-03-04 | AUP | | |
| 174 | + | ||
| 175 | + | #### CLAUDE.md (MNW Section) | |
| 176 | + | ||
| 177 | + | | Area | Status | Last Verified | Notes | | |
| 178 | + | |------|:------:|:-------------:|-------| | |
| 179 | + | | Source tree | Fixed | 2026-03-04 | Added lib.rs, constants.rs, docs.rs, helpers.rs, monitor.rs, sentry_layer.rs, scanning/, synckit_auth.rs, wordlist.rs | | |
| 180 | + | | Route tree | Fixed | 2026-03-04 | dashboard.rs->dashboard/, stripe.rs->stripe/, added oauth.rs + synckit.rs | | |
| 181 | + | | templates.rs | Fixed | 2026-03-04 | Changed to templates/ (directory module) | | |
| 182 | + | | types.rs | Fixed | 2026-03-04 | Changed to types/ (directory module) | | |
| 183 | + | | Migrations | Fixed | 2026-03-04 | 001-040 -> 001-009 | | |
| 184 | + | | Pricing tiers | Fixed | 2026-03-04 | Removed "forums" from Basic | | |
| 185 | + | ||
| 186 | + | #### Unpublished Docs (`docs/unpublished/`) | |
| 187 | + | ||
| 188 | + | | Area | Status | Last Verified | Notes | | |
| 189 | + | |------|:------:|:-------------:|-------| | |
| 190 | + | | strategy/competition.md | Fixed | 2026-03-04 | 30+ feature statuses updated Planned->Done, test count 28->386, SyncKit status corrected, feature matrix updated | | |
| 191 | + | | strategy/transition.md | Fixed | 2026-03-04 | Codebase stats updated (10->109 files, 3.8K->33K lines), file list modernized | | |
| 192 | + | | strategy/pitch.md | Fixed | 2026-03-04 | Removed "forums" from Basic tier | | |
| 193 | + | | community/forums.md | Fixed | 2026-03-04 | Platform forum marked as Planned (was written as if it exists) | | |
| 194 | + | | community/code-of-conduct.md | Fixed | 2026-03-04 | Forum reference softened to future tense | | |
| 195 | + | | getting-started/tier-basic.md | Fixed | 2026-03-04 | Added status note about analytics placeholder, forums, contacts dashboard | | |
| 196 | + | | getting-started/tier-big-files.md | Fixed | 2026-03-04 | Added status note: video hosting not yet implemented | | |
| 197 | + | | getting-started/tier-streaming.md | Fixed | 2026-03-04 | Added status note: live streaming not yet implemented | | |
| 198 | + | | creator/analytics.md | Fixed | 2026-03-04 | Added status note: analytics tab is placeholder | | |
| 199 | + | | creator/embedding.md | Fixed | 2026-03-04 | Added status note: embeds not yet implemented | | |
| 200 | + | | creator/dashboard.md | Fixed | 2026-03-04 | Added status note: detailed financial metrics not yet built | | |
| 201 | + | | creator/purchases.md | Fixed | 2026-03-04 | "multiple formats" -> "original uploaded format" | | |
| 202 | + | | tech/security.md | Fixed | 2026-03-04 | GitHub URL -> Sourcehut URL | | |
| 203 | + | | tech/open-source.md | Fixed | 2026-03-04 | GitHub URL -> Sourcehut URL | | |
| 204 | + | | tech/portability.md | Current | 2026-03-04 | Properly marks planned features, no issues | | |
| 205 | + | | creator/contact-sharing.md | Current | 2026-03-04 | Properly marks planned features, no issues | | |
| 206 | + | | creator/downloads.md | Current | 2026-03-04 | Accurate: original format only | | |
| 207 | + | | strategy/synckit-plan.md | Fixed | 2026-03-04 | Stripped calendar dates (sequencing only), marked Phase 1 as done, updated to reflect Rust SDK (not Swift), corrected DB schema/architecture to match implementation, updated technical roadmap | | |
| 208 | + | | All legal docs | Current | 2026-03-04 | No feature claims, stable content | | |
| 209 | + | | All business docs | Current | 2026-03-04 | No feature claims, stable content | | |
| 210 | + | ||
| 211 | + | #### Internal Docs (`docs/internal/`) | |
| 212 | + | ||
| 213 | + | | Area | Status | Last Verified | Notes | | |
| 214 | + | |------|:------:|:-------------:|-------| | |
| 215 | + | | outreach/outreach.md | Fixed | 2026-03-04 | Tier naming fixed, TikTok deadline updated | | |
| 216 | + | | outreach/creator_qol.md | Fixed | 2026-03-04 | Path references updated (docs/reference/ -> docs/unpublished/, tier-text.md -> tier-basic.md, transfer.md -> migration.md) | | |
| 217 | + | | outreach/creators/index.md | Fixed | 2026-03-04 | TikTok deadline updated | | |
| 218 | + | | outreach/creators/tech.md | Fixed | 2026-03-04 | DEV.to member count 3M+ -> 5M+ | | |
| 219 | + | | outreach/creators/niches.md | Fixed | 2026-03-04 | Course creator pitch tier $10 -> $20-30 | | |
| 220 | + | | outreach/creators/*.md (other niche files) | Not audited | -- | Creator stats are snapshots; refresh before outreach | | |
| 221 | + | | outreach/creator-investment-strategy.md | Not audited | -- | Consolidated 2026-03-04: removed duplicated sections from outreach.md, added cross-references | | |
| 222 | + | | outreach/creator-budget.md | Not audited | -- | External subscription prices may have changed | | |
| 223 | + | | outreach/creators/dashboard.md | Not audited | -- | Manually maintained counts may drift from niche files | | |
| 224 | + | ||
| 225 | + | #### Root Docs | |
| 226 | + | ||
| 227 | + | | Document | Status | Last Verified | Notes | | |
| 228 | + | |----------|:------:|:-------------:|-------| | |
| 229 | + | | docs/todo.md | Current | 2026-03-04 | Status line matches codebase | | |
| 230 | + | | docs/audit_review.md | Current | 2026-03-04 | Code audit history | | |
| 231 | + | | docs/docs-todo.md | Deleted | 2026-03-10 | Merged into docs/mnw/todo.md | | |
| 232 | + | | docs/notes-todo.md | Deleted | 2026-03-10 | Merged into docs/mnw/todo.md | | |
| 233 | + | | docs/importers.md | Not audited | -- | Phase 13D importer specs | | |
| 234 | + | ||
| 235 | + | ### Stale References Found (2026-03-04 Audit) | |
| 236 | + | ||
| 237 | + | #### Public Docs | |
| 238 | + | | Location | Issue | Resolution | | |
| 239 | + | |----------|-------|------------| | |
| 240 | + | | roadmap.md | Passkeys listed under "What's Next" -- already done (Phase 9.2) | Moved to "What's Built" | | |
| 241 | + | | roadmap.md | Test count says 349 -- actual is 386 | Updated | | |
| 242 | + | | how-we-work.md | "forums" in Basic tier -- not implemented | Removed | | |
| 243 | + | | how-we-work.md | "audio, video, text, or streams" -- video/streaming not built | Changed to "audio, text, or digital files" | | |
| 244 | + | | faq.md | Links to faq-billing.md, faq-content.md, faq-payouts.md, faq-technical.md -- none exist | Removed broken links | | |
| 245 | + | ||
| 246 | + | #### CLAUDE.md | |
| 247 | + | | Location | Issue | Resolution | | |
| 248 | + | |----------|-------|------------| | |
| 249 | + | | MNW section | 5 files listed as .rs that are directory modules | Fixed all 5 | | |
| 250 | + | | MNW section | Migration count 001-040, only 9 exist | Fixed to 001-009 | | |
| 251 | + | | MNW section | Route tree missing oauth.rs, synckit.rs | Added | | |
| 252 | + | | MNW section | Src tree missing ~10 modules | Added all | | |
| 253 | + | | MNW pricing | "forums" in Basic tier | Removed | | |
| 254 | + | ||
| 255 | + | #### Unpublished Docs | |
| 256 | + | | Location | Issue | Resolution | | |
| 257 | + | |----------|-------|------------| | |
| 258 | + | | competition.md | 30+ features listed as "Planned" that are now Done | Updated all statuses | | |
| 259 | + | | competition.md | Test count "Unit tests (15) + integration tests (13)" | Updated to "386 automated tests" | | |
| 260 | + | | competition.md | SyncKit listed as entirely Planned | Updated 11 items to Done | | |
| 261 | + | | competition.md | Feature matrix: discount codes "No", subscriptions "Planned" | Updated both to Yes/Done | | |
| 262 | + | | transition.md | "10 Rust source files", "~3,800 lines of Rust" | Updated to 109 files, ~33,000 lines | | |
| 263 | + | | transition.md | Old file structure (db.rs, routes/pages.rs, etc.) | Updated to current directory modules | | |
| 264 | + | | pitch.md | "forums" in Basic tier | Removed | | |
| 265 | + | | forums.md | Platform forum described as existing | Marked as Planned | | |
| 266 | + | | code-of-conduct.md | References "the main Makenot.work forum" | Updated to future tense | | |
| 267 | + | | tier-big-files.md | Video hosting described as available | Added "not yet implemented" note | | |
| 268 | + | | tier-streaming.md | Live streaming described as available | Added "not yet implemented" note | | |
| 269 | + | | creator/analytics.md | Analytics described as built | Added placeholder status note | | |
| 270 | + | | creator/embedding.md | Embeds described as built | Added "not yet implemented" note | | |
| 271 | + | | creator/dashboard.md | Detailed financial metrics described as built | Added placeholder status note | | |
| 272 | + | | creator/purchases.md | "Downloadable in multiple formats" | Changed to "original uploaded format" | | |
| 273 | + | | tech/security.md | GitHub URL (github.com/makecreative/makenot.work) | Changed to Sourcehut | | |
| 274 | + | | tech/open-source.md | Same GitHub URL | Changed to Sourcehut | | |
| 275 | + | ||
| 276 | + | #### Internal Docs | |
| 277 | + | | Location | Issue | Resolution | | |
| 278 | + | |----------|-------|------------| | |
| 279 | + | | outreach.md | Tier names "Text, Small Files, Audio, Big Files" | Changed to "Basic, Small Files, Big Files, Streaming" | | |
| 280 | + | | outreach.md | TikTok "full shutdown by March 2026" | Updated to "ongoing regulatory limbo" | | |
| 281 | + | | creators/index.md | Same TikTok deadline | Updated | | |
| 282 | + | | creator_qol.md | References docs/reference/ paths | Changed to docs/unpublished/ | | |
| 283 | + | | creator_qol.md | References tier-text.md | Changed to tier-basic.md | | |
| 284 | + | | creator_qol.md | References transfer.md | Changed to migration.md | | |
| 285 | + | | creators/tech.md | DEV.to "3M+ devs" | Updated to "5M+ reach" | | |
| 286 | + | | creators/niches.md | Course creator pitch says "$10/month" | Changed to "$20-30/month" with tier explanation | | |
| 287 | + | ||
| 288 | + | ### Cold Spots (Not Yet Audited) | |
| 289 | + | ||
| 290 | + | | Area | Size | Risk | Notes | | |
| 291 | + | |------|------|------|-------| | |
| 292 | + | | outreach/creators/*.md (niche files) | 9 files, ~321 creators | Medium | Patreon counts, subscriber numbers are snapshots. Refresh before actual outreach. | | |
| 293 | + | | outreach/creator-investment-strategy.md | 1 file | Done | Consolidated 2026-03-04: removed duplicated sections from outreach.md, added cross-references | | |
| 294 | + | | outreach/creator-budget.md | 1 file | Low | External subscription prices may have changed | | |
| 295 | + | | outreach/creators/dashboard.md | 1 file | Low | Manually maintained counts may drift from niche files | | |
| 296 | + | | strategy/synckit-plan.md | 1 file | Done | Audited 2026-03-04: dates stripped, implementation divergences noted, Phase 1 marked done | | |
| 297 | + | | docs-todo.md, notes-todo.md | Deleted | -- | Merged into docs/mnw/todo.md (2026-03-10) | | |
| 298 | + | | legal/copyright.md | 1 file | Low | DMCA agent address placeholder | | |
| 299 | + | | legal/liability.md | 1 file | Low | Multiple [STATE] and [Pending legal review] placeholders | | |
| 300 | + | ||
| 301 | + | ### Action Items | |
| 302 | + | ||
| 303 | + | - Keep roadmap.md test count in sync when test count changes | |
| 304 | + | - When forums are implemented, add back to Basic tier and update forums.md/code-of-conduct.md | |
| 305 | + | - When video hosting ships, remove status notes from tier-big-files.md and related creator docs | |
| 306 | + | - When streaming ships, remove status note from tier-streaming.md | |
| 307 | + | - Refresh creator stats in niche files before beginning actual outreach |
| @@ -362,306 +362,6 @@ All four focus areas completed. 53 tests across 4 files; no vulnerabilities foun | |||
| 362 | 362 | ||
| 363 | 363 | See `docs/mnw/adversarial.md` for the prompt template. | |
| 364 | 364 | ||
| 365 | - | ## Changes Since Last Audit | |
| 366 | - | ||
| 367 | - | ### Thirty-third audit (2026-03-28, Run 12 cross-project) | |
| 368 | - | - **Test count:** 1,174 (584 unit + 545 integration + 17 admin + 28 health). 2 FAILURES. 0 clippy warnings. | |
| 369 | - | - **Grade:** A (maintained). v0.3.13. | |
| 370 | - | - **Migrations:** 045 -> 048 (+3: bundles, batch uploads, project features). | |
| 371 | - | - **Test failures (2):** | |
| 372 | - | - `workflows::htmx::delete_item_returns_toast` — panics at htmx.rs:345: HX-Trigger header missing on item delete response. Filed as MEDIUM action item. | |
| 373 | - | - `workflows::wizards::item_wizard_license_keys` — panics at wizards.rs:579: RowNotFound after license key wizard step. Filed as MEDIUM action item. | |
| 374 | - | - **Bundles + batch upload:** New ProjectFeature trait for feature-gated functionality. Bundle items (multiple files per item). Batch file upload support. Well-implemented type safety with A+ grade on new types. | |
| 375 | - | - **Email-first issue tracker:** (shipped since Run 11) Web write UI removed, labels removed. Issues via inbound email, replies via HMAC-signed Reply-To. Commit-message reopens added. Migration 045. | |
| 376 | - | - **I5 (Git Patch Inbound):** (shipped since Run 11) `postmark_inbound` handler, `{slug}@patches.makenot.work`, MT patches category, message-ID threading. 9 integration tests. | |
| 377 | - | - **Dependency advisory:** bincode unmaintained (RUSTSEC-2025-0141, warning only) — upstream via syntect/yara-x. | |
| 378 | - | - **Mandatory surprise:** The `ProjectFeature` trait is a clean, minimal abstraction for feature-gating. Each feature is a zero-sized type implementing the trait, with `is_enabled()` checking feature flags on the project. No feature flag infrastructure bloat — just a trait and an enum. Verdict: **Well-designed.** | |
| 379 | - | - **Previous items verified:** All 30 resolved items confirmed intact. 3 upstream-blocked deps unchanged. | |
| 380 | - | ||
| 381 | - | ### Previously unaudited (2026-03-25, email-first issue tracker + I5 git patch inbound) | |
| 382 | - | - **Test count:** 1,060 (517 unit + 544 integration + 28 health + 4 load). 0 failures. | |
| 383 | - | - **Migrations:** 043 -> 045 (+2: patch_message_ids, issue_message_ids). | |
| 384 | - | - **Email-first issue tracker:** Web write UI completely removed (create, edit, close/reopen, comment forms, labels). Issues created exclusively via inbound email (`{owner}+{repo}@issues.makenot.work`). Comments via email reply (HMAC-signed Reply-To addresses). Issue labels removed entirely. Close/reopen via commit messages only (`fixes #N`, `closes #N`, `reopens #N`). Notification emails include Reply-To, Message-ID, In-Reply-To headers for proper threading. `issue_message_ids` table for email threading (parallels `patch_message_ids`). | |
| 385 | - | - **I5 (Git Patch Inbound):** Verified code-complete. `postmark_inbound` handler processes `git send-email` patches to `{slug}@patches.makenot.work`, creates MT threads in `patches` category (auto-created on demand), multi-part series threaded via Message-ID/In-Reply-To/References. `db/patches.rs` + migration 044. 9 integration tests. Operational: DNS MX for `patches.makenot.work` + Postmark inbound domain config needed at deploy time. | |
| 386 | - | - **Dead code cleanup:** Removed 7 unused issue label DB functions, 2 unused comment functions (`delete_comment`, `get_comment`), `labels.rs` route module, 3 template structs, 3 HTML template files. | |
| 387 | - | - **New code:** `postmark_inbound_issues` handler (new issue + reply paths), `extract_issue_address`, `extract_reply_local`, `strip_quoted_text` helpers, `Reopen` variant in push_refs, 4 new DB functions (`get_issue_by_id`, `get_issue_participants`, `insert_issue_message_id`, `get_issue_id_by_any_message_id`), `send_email_with_headers_and_unsub` on EmailClient. 15 unit tests + 23 integration tests. | |
| 388 | - | - **CSRF fix:** `/postmark/webhook` broadened to `/postmark/` in exempt paths — fixes 3 previously-failing patches tests (403 on inbound webhook). | |
| 389 | - | ||
| 390 | - | ### Thirty-second audit (2026-03-22, Run 11 MNW-focused) | |
| 391 | - | - **Test count:** 1,013 (465 unit + 516 integration + 28 health + 4 load). 0 clippy warnings. Zero dead code. | |
| 392 | - | - **Grade:** A (maintained). v0.3.6. 153 source files. | |
| 393 | - | - **Migrations:** 038 -> 041 (+3: mailing_lists, backfill_mailing_list_subscribers, content_newsletter). | |
| 394 | - | - **Platform integration I3 (Mailing Lists):** New `db/mailing_lists.rs` with CRUD for per-project mailing lists + subscriber management. `mailing_lists` + `mailing_list_subscribers` tables. Auto-create content + devlog lists on project creation. Subscribe on follow/purchase, unsubscribe on unfollow. 6 integration tests. | |
| 395 | - | - **Platform integration I4 (Content Newsletter):** Refactored email delivery from follows-based to mailing-list-based. `send_release_announcements()` now queries content mailing list subscribers instead of direct followers. New `send_blog_post_announcements()` for blog post emails (previously blog posts only created MT threads, never sent emails). `web_only` toggle on items and blog posts to publish without emailing. `mark_blog_post_announced()` idempotent guard. Blog post announcement email template. 4 new integration tests. | |
| 396 | - | - **API polish:** `web_only` field added to `ItemResponse`, `BlogPostResponse`, and `BlogPostEditResponse`. | |
| 397 | - | - **Dependency fixes:** aws-lc-sys 0.38.0→0.39.0 (RUSTSEC-2026-0044 + 0048 resolved), rustls-webpki 0.103.9→0.103.10 (RUSTSEC-2026-0049 resolved for v0.103 chain). | |
| 398 | - | - **Mandatory surprise:** Mailing list delivery has zero duplication with follows-based delivery. Near-identical scheduler functions kept separate — correct per abstraction guidelines. Verdict: **Actually fine.** | |
| 399 | - | - **N+1 fix (admin.rs):** MT backfill handler was calling `get_user_by_id()` per project in a loop. Replaced with batch `get_users_by_ids()` (single `ANY($1)` query) + HashMap lookup. | |
| 400 | - | - **N+1 fix (scheduler.rs):** Onboarding drip was calling `advance_onboarding_step()` per skippable user. Replaced with `batch_advance_onboarding_step()` (single `ANY($1)` UPDATE) for skip sets, individual advance only for users that need emails. | |
| 401 | - | - **No new findings.** All previous items remain resolved. | |
| 402 | - | ||
| 403 | - | ### Thirty-first audit (2026-03-22, Run 10 cross-project) | |
| 404 | - | - **Test count:** 996 (was 1,000 — net -4 from old join/modal tests removed, +7 join wizard, +10 creation wizard offsets). 0 clippy warnings. Zero dead code. | |
| 405 | - | - **Grade:** A (maintained). v0.3.5. 147 source files (was 142). | |
| 406 | - | - **Migrations:** 035 -> 038 (+3: creator tiers, fan_plus, tag paths). | |
| 407 | - | - **Phase 25 (Creation Wizards):** Multi-step HTMX wizards for project (5 steps) and item (6 steps) creation. New route module `routes/pages/dashboard/wizards/` (mod.rs, project.rs, item.rs). 15 templates. `wizard.css` + `wizard.js` infrastructure. Old modal forms removed. 10 integration tests. | |
| 408 | - | - **Phase 26 (Join Wizard):** HTMX multi-step signup replacing client-side JS wizard. New `routes/pages/public/join_wizard.rs`. 5 steps: account (creates user + logs in), profile, creator pitch, stripe, welcome. Optional steps skippable. 7 integration tests. Old `join_handler` removed from `auth.rs`. | |
| 409 | - | - **TagTree integration:** Migration 038 adds `path TEXT NOT NULL` to tags with recursive CTE backfill. `get_tag_ancestors()` rewritten from N+1 parent chain walk to 2-query path-based lookup. `validate_tag_slug()` uses tagtree (TagConfig: max_depth 5, max_length 100). | |
| 410 | - | - **DocEngine extraction:** Documentation rendering extracted to standalone crate. Source LOC reduced from ~50K to ~29K. | |
| 411 | - | - **Rust patterns audit:** Reduced triple clone of `tc.category` in discover filters via `is_some_and()` + move. | |
| 412 | - | - **Dead code:** Zero clippy `dead_code`/`unused` warnings across all targets. Verified with `cargo clippy --all-targets`. | |
| 413 | - | - **Mandatory surprise:** The join wizard's `step_account_create` handler reuses 100% of the existing auth infrastructure (hash_password, login_user, track_session, check_password_breach, email verification) without any duplication. The entire signup flow was factored into a new file by calling existing functions — zero copy-paste, zero new auth logic. Verdict: **Impressive.** | |
| 414 | - | - **No new findings.** All previous items remain resolved. | |
| 415 | - | ||
| 416 | - | ### Thirtieth audit (2026-03-18, Run 9 cross-project) | |
| 417 | - | - **Test count:** 1,000 (unchanged). 0 clippy warnings. | |
| 418 | - | - **Grade:** A (maintained). v0.3.2. | |
| 419 | - | - **Key change:** Sentry removed — tracing-only observability. Files changed: Cargo.toml (sentry dep removed), sentry_layer.rs (deleted), main.rs (Sentry init + tracing layer removed), lib.rs (sentry_layer middleware removed), error.rs (Sentry scope tagging removed), config.rs (sentry_dsn field removed), health endpoint (sentry status removed), test harness (sentry_dsn: None removed from 4 test configs). | |
| 420 | - | - **Observability compensation:** 301 `#[instrument(skip_all)]` annotations. TraceLayer with request ID propagation and status-based log levels. Structured JSON logging in production. Health monitor with DB snapshots + alert emails. | |
| 421 | - | - **Mandatory surprise:** The Sentry removal is surgically clean — zero stale references remain in source (only 1 harmless comment). Tracing setup is more sophisticated than typical Sentry usage: request-scoped tracing, task-level instrumentation, health snapshots persisted to DB, and alert escalation with cooldown. | |
| 422 | - | - **No new findings.** All previous items remain resolved. | |
| 423 | - | ||
| 424 | - | ### Twenty-ninth audit (2026-03-17, Run 8 cross-project) | |
| 425 | - | - **Test count:** 968 -> 1,000 (+32 integration tests). Milestone: four-digit test count. | |
| 426 | - | - **Grade:** A (maintained). v0.3.0. | |
| 427 | - | - **Clippy:** 0 warnings (was 7 — all resolved: collapsible_if, too_many_arguments, explicit_counter_loop). | |
| 428 | - | - **New finding:** ~~LOW: ILIKE wildcard characters (`%`, `_`) not escaped in user search input across db/issues.rs, db/discover.rs, db/categories.rs, db/tags.rs.~~ Resolved: SQL-side `replace()` chain escapes `\`, `%`, `_` in all 20 ILIKE clauses; Rust-side escaping in issues.rs `format!` pattern. | |
| 429 | - | - **Mandatory surprise:** 1,000 tests with zero production unwraps (265 unwrap/expect, all `#[cfg(test)]` only) — Impressive discipline at 50K LOC. | |
| 430 | - | - **Previous items verified:** All resolved. No carried items from Run 7. | |
| 431 | - | ||
| 432 | - | ### Twenty-seventh audit (2026-03-16, Run 6 cross-project) | |
| 433 | - | - **Test count:** 832 -> 968 (+136 tests from G6 implementation) | |
| 434 | - | - **Grade:** A (maintained). G6 features (issue email notifications + commit message references) fully integrated. | |
| 435 | - | - **Source LOC:** 49,940 (up from ~40K), 143 files (up from 186 — likely different counting method) | |
| 436 | - | - **Clippy:** 7 warnings (4 collapsible_if, 2 too_many_arguments on new email methods, 1 explicit_counter_loop). Previously 2. | |
| 437 | - | - **New findings:** LOW: parse_issue_refs regex recompilation per call (should use LazyLock, same pattern already fixed in docs.rs) | |
| 438 | - | - **Mandatory surprise:** parse_issue_refs regex — LOW, functional but inconsistent with existing LazyLock usage elsewhere | |
| 439 | - | - **Previous items verified:** All previous remediated items confirmed intact. 2 upstream-blocked deps unchanged. | |
| 440 | - | ||
| 441 | - | ### Twenty-sixth audit (2026-03-13, pre-launch skeptical lens) | |
| 442 | - | - **Test count:** 821 -> 824 (+3 tests) | |
| 443 | - | - **Grade:** A (maintained). Pre-launch skeptical audit found 2 medium + 1 low issue, all bounded in impact. | |
| 444 | - | - **New findings:** Rate limiting gap on /forgot-password, refund flow not transactional, v2 webhook non-constant-time comparison. | |
| 445 | - | - **Positive surprise:** Password reset token auto-invalidation via embedded password hash — confirms excellent security engineering. | |
| 446 | - | - **Previous items verified:** All 27 prior remediated items confirmed intact. email.rs split, DMARC upgrade, Postmark DKIM all done. | |
| 447 | - | ||
| 448 | - | **Post-audit remediation (2026-03-13):** | |
| 449 | - | - All 3 findings from twenty-sixth audit resolved: forgot-password rate limiting, refund transaction wrapping, constant-time webhook comparison | |
| 450 | - | - Additionally: from_trusted replaced with validated constructor in login/signup, LazyLock for docs.rs regex | |
| 451 | - | - Documentation upgraded to A: SyncKit route handlers documented (15 handlers), SyncKit model field docs added (32 fields), payments.rs and auth.rs module docs expanded, architecture not applicable (MNW docs already existed), README created. | |
| 452 | - | ||
| 453 | - | ### Twenty-fifth audit (2026-03-11, full re-audit) | |
| 454 | - | - **Growth:** 55,223 -> 57,172 total LOC (+3.5%), 765 -> 821 tests (+56), 187 -> 186 files | |
| 455 | - | - **Source LOC:** 40,873 (src/), 16,299 (tests/) | |
| 456 | - | - **Test density:** 14.4 tests/KLOC (up from 13.9) | |
| 457 | - | - **Clippy:** 2 cosmetic warnings (single_match in mnw-admin.rs and lib.rs), down from 0 -- new code introduced minor style nit | |
| 458 | - | - **Scheduler:** New `scheduler.rs` module (180 LOC) for scheduled publish and onboarding drip emails | |
| 459 | - | - **Migrations:** 24 (up from 21) | |
| 460 | - | - **Instrumentation:** 0 `#[instrument]` annotations; 199 tracing log calls across 36 files (manual structured tracing compensates for lack of #[instrument]) | |
| 461 | - | - **Production unwrap hygiene:** ~30 non-test unwrap/expect calls, all justified (startup, infallible HMAC, static regex, static redirect paths) | |
| 462 | - | - **Previous fixes verified:** All 24 remediated items confirmed intact | |
| 463 | - | - **New action items:** 3 (email.rs split, DMARC upgrade, Postmark DKIM verification) | |
| 464 | - | ||
| 465 | - | ### Twenty-fourth audit (2026-03-10, delta review) | |
| 466 | - | - **Growth:** 47,463 -> 55,223 LOC (+16%), 168 -> 187 files, 684 -> 765 tests | |
| 467 | - | - **Trust tiers:** Upload trust system (migration 021) -- new uploads default to `held_for_review` until creator is trusted. Affects 6 scanning/storage tests that expect `clean` status on macOS (no ClamAV + no trust override in test harness). | |
| 468 | - | - **Git repos:** Linked to projects with bidirectional navigation + releases page (migration 020, `db/git_repos.rs`, `routes/git.rs`) | |
| 469 | - | - **Promo codes unification:** Discount codes + download codes merged into unified promo codes system (migration 019, `db/promo_codes.rs`, `routes/api/promo_codes.rs`). Old test files renamed. | |
| 470 | - | - **CI scripts:** `deploy/run-ci.sh` and `deploy/setup-git-ssh.sh` added for automated CI | |
| 471 | - | - **Previous fixes verified:** All 22 remediated items from twenty-second and twenty-third audits confirmed intact | |
| 472 | - | - **6 environment-dependent test failures (fixed 2026-03-10):** All 6 caused by untrusted test users. Fixed by adding `h.trust_user(user_id)` to test setup helpers. | |
| 473 | - | ||
| 474 | - | ### Improved (twenty-third audit -- 2026-03-09) | |
| 475 | - | - **Clippy clean-up:** 33 warnings fixed across 14 files | |
| 476 | - | - **Naming consistency:** `discount_code` -> `promo_code` in item checkout path | |
| 477 | - | - **Test coverage:** 8 new tests + 53 adversarial tests (597 -> 684 tests) | |
| 478 | - | - **Input validation hardened:** 100-char length cap for FreeAccess codes; trial_days capped at 365; `pwyw_min_cents` validated; discount value capped | |
| 479 | - | - **Security:** postmark_token redacted in EmailConfig Debug impl | |
| 480 | - | - **Concurrency:** subscription webhook promo code increment wrapped in DB transaction | |
| 481 | - | ||
| 482 | - | ### Improved (twenty-second audit -- 2026-03-08) | |
| 483 | - | - All 16 findings remediated. 6 dead code functions removed. 8 missing `#[instrument]` attributes added. | |
| 484 | - | - Contact revocation feature shipped. 9 new tests (588 -> 597). | |
| 485 | - | ||
| 486 | - | ### Regressed | |
| 487 | - | - Nothing. | |
| 488 | - | ||
| 489 | - | ### Grade changes (this audit) | |
| 490 | - | - No grade changes from previous audit. All module and project grades stable. | |
| 491 | - | ||
| 492 | - | ## Metrics Over Time | |
| 493 | - | ||
| 494 | - | | Audit Date | LOC | Rust Files | Tests | Tests/KLOC | Clippy Warnings | Cold Spots | Overall | | |
| 495 | - | |------------|-----|-----------|-------|-----------|----------------|------------|---------| | |
| 496 | - | | 2026-03-06 | 45K | 154 | 588 | 13.0 | 0 | 3 | A | | |
| 497 | - | | 2026-03-08 | 47K | 168 | 597 | 12.6 | 0 | 1 | A | | |
| 498 | - | | 2026-03-09 | 52K | 175 | 684 | 13.2 | 0 | 1 | A | | |
| 499 | - | | 2026-03-10 | 55K | 187 | 765 | 13.9 | 0 | 1 | A | | |
| 500 | - | | 2026-03-11 | 57K | 186 | 821 | 14.4 | 2 | 1 | A | | |
| 501 | - | | 2026-03-13 | 57K | 186 | 832 | 14.6 | 2 | 1 | A | | |
| 502 | - | | 2026-03-16 | 50K src | 143 | 968 | 19.4 | 7 | 0 | A | | |
| 503 | - | | 2026-03-17 | 50K src | 143 | 1,000 | 20.0 | 0 | 0 | A | | |
| 504 | - | | 2026-03-18 | 50K src | 142 | 1,000 | 20.0 | 0 | 0 | A | | |
| 505 | - | | 2026-03-22 | 29K src | 147 | 996 | 34.3 | 0 | 0 | A | | |
| 506 | - | | 2026-03-22 | 29K src | 153 | 1,013 | 34.9 | 0 | 0 | A | | |
| 507 | - | | 2026-03-28 | ~30K src | ~160 | 1,174 | ~39 | 0 | 0 | A | | |
| 508 | - | ||
| 509 | 365 | --- | |
| 510 | 366 | ||
| 511 | - | ## Documentation Review | |
| 512 | - | ||
| 513 | - | **Last reviewed:** 2026-03-04 (first doc audit, full scope including unpublished/internal) | |
| 514 | - | ||
| 515 | - | ### Overall Grade: A | |
| 516 | - | ||
| 517 | - | Large doc surface (80+ files across public, unpublished, internal). Public-facing docs had several inaccuracies fixed this audit. Unpublished docs had a systemic issue: many creator-facing docs described planned features as available (analytics, embedding, video hosting, streaming). Internal outreach docs had stale tier names, outdated TikTok deadline, and DEV.to member count inconsistency. competition.md had 30+ features listed as "Planned" that are now "Done". All identified issues fixed. | |
| 518 | - | ||
| 519 | - | ### Document Heatmap | |
| 520 | - | ||
| 521 | - | #### Public Docs (`docs/public/`) | |
| 522 | - | ||
| 523 | - | | Document | Status | Last Verified | Notes | | |
| 524 | - | |----------|:------:|:-------------:|-------| | |
| 525 | - | | about/roadmap.md | Fixed | 2026-03-04 | Passkeys moved to "What's Built", test count 349->386 | | |
| 526 | - | | about/how-we-work.md | Fixed | 2026-03-04 | Removed "forums" from Basic tier, "video/streams" from step 2 | | |
| 527 | - | | about/story.md | Current | 2026-03-04 | Founder story | | |
| 528 | - | | about/guarantees.md | Current | 2026-03-04 | SLA commitments | | |
| 529 | - | | support/faq.md | Fixed | 2026-03-04 | Removed 4 broken links to non-existent sub-pages | | |
| 530 | - | | support/contact.md | Current | 2026-03-04 | Contact info | | |
| 531 | - | | legal/privacy-policy.md | Current | 2026-03-04 | Privacy policy | | |
| 532 | - | | legal/terms-of-service.md | Current | 2026-03-04 | ToS | | |
| 533 | - | | legal/acceptable-use.md | Current | 2026-03-04 | AUP | | |
| 534 | - | ||
| 535 | - | #### CLAUDE.md (MNW Section) | |
| 536 | - | ||
| 537 | - | | Area | Status | Last Verified | Notes | | |
| 538 | - | |------|:------:|:-------------:|-------| | |
| 539 | - | | Source tree | Fixed | 2026-03-04 | Added lib.rs, constants.rs, docs.rs, helpers.rs, monitor.rs, sentry_layer.rs, scanning/, synckit_auth.rs, wordlist.rs | | |
| 540 | - | | Route tree | Fixed | 2026-03-04 | dashboard.rs->dashboard/, stripe.rs->stripe/, added oauth.rs + synckit.rs | | |
| 541 | - | | templates.rs | Fixed | 2026-03-04 | Changed to templates/ (directory module) | | |
| 542 | - | | types.rs | Fixed | 2026-03-04 | Changed to types/ (directory module) | | |
| 543 | - | | Migrations | Fixed | 2026-03-04 | 001-040 -> 001-009 | | |
| 544 | - | | Pricing tiers | Fixed | 2026-03-04 | Removed "forums" from Basic | | |
| 545 | - | ||
| 546 | - | #### Unpublished Docs (`docs/unpublished/`) | |
| 547 | - | ||
| 548 | - | | Area | Status | Last Verified | Notes | | |
| 549 | - | |------|:------:|:-------------:|-------| | |
| 550 | - | | strategy/competition.md | Fixed | 2026-03-04 | 30+ feature statuses updated Planned->Done, test count 28->386, SyncKit status corrected, feature matrix updated | | |
| 551 | - | | strategy/transition.md | Fixed | 2026-03-04 | Codebase stats updated (10->109 files, 3.8K->33K lines), file list modernized | | |
| 552 | - | | strategy/pitch.md | Fixed | 2026-03-04 | Removed "forums" from Basic tier | | |
| 553 | - | | community/forums.md | Fixed | 2026-03-04 | Platform forum marked as Planned (was written as if it exists) | | |
| 554 | - | | community/code-of-conduct.md | Fixed | 2026-03-04 | Forum reference softened to future tense | | |
| 555 | - | | getting-started/tier-basic.md | Fixed | 2026-03-04 | Added status note about analytics placeholder, forums, contacts dashboard | | |
| 556 | - | | getting-started/tier-big-files.md | Fixed | 2026-03-04 | Added status note: video hosting not yet implemented | | |
| 557 | - | | getting-started/tier-streaming.md | Fixed | 2026-03-04 | Added status note: live streaming not yet implemented | | |
| 558 | - | | creator/analytics.md | Fixed | 2026-03-04 | Added status note: analytics tab is placeholder | | |
| 559 | - | | creator/embedding.md | Fixed | 2026-03-04 | Added status note: embeds not yet implemented | | |
| 560 | - | | creator/dashboard.md | Fixed | 2026-03-04 | Added status note: detailed financial metrics not yet built | | |
| 561 | - | | creator/purchases.md | Fixed | 2026-03-04 | "multiple formats" -> "original uploaded format" | | |
| 562 | - | | tech/security.md | Fixed | 2026-03-04 | GitHub URL -> Sourcehut URL | | |
| 563 | - | | tech/open-source.md | Fixed | 2026-03-04 | GitHub URL -> Sourcehut URL | | |
| 564 | - | | tech/portability.md | Current | 2026-03-04 | Properly marks planned features, no issues | | |
| 565 | - | | creator/contact-sharing.md | Current | 2026-03-04 | Properly marks planned features, no issues | | |
| 566 | - | | creator/downloads.md | Current | 2026-03-04 | Accurate: original format only | | |
| 567 | - | | strategy/synckit-plan.md | Fixed | 2026-03-04 | Stripped calendar dates (sequencing only), marked Phase 1 as done, updated to reflect Rust SDK (not Swift), corrected DB schema/architecture to match implementation, updated technical roadmap | | |
| 568 | - | | All legal docs | Current | 2026-03-04 | No feature claims, stable content | | |
| 569 | - | | All business docs | Current | 2026-03-04 | No feature claims, stable content | | |
| 570 | - | ||
| 571 | - | #### Internal Docs (`docs/internal/`) | |
| 572 | - | ||
| 573 | - | | Area | Status | Last Verified | Notes | | |
| 574 | - | |------|:------:|:-------------:|-------| | |
| 575 | - | | outreach/outreach.md | Fixed | 2026-03-04 | Tier naming fixed, TikTok deadline updated | | |
| 576 | - | | outreach/creator_qol.md | Fixed | 2026-03-04 | Path references updated (docs/reference/ -> docs/unpublished/, tier-text.md -> tier-basic.md, transfer.md -> migration.md) | | |
| 577 | - | | outreach/creators/index.md | Fixed | 2026-03-04 | TikTok deadline updated | | |
| 578 | - | | outreach/creators/tech.md | Fixed | 2026-03-04 | DEV.to member count 3M+ -> 5M+ | | |
| 579 | - | | outreach/creators/niches.md | Fixed | 2026-03-04 | Course creator pitch tier $10 -> $20-30 | | |
| 580 | - | | outreach/creators/*.md (other niche files) | Not audited | -- | Creator stats are snapshots; refresh before outreach | | |
| 581 | - | | outreach/creator-investment-strategy.md | Not audited | -- | Consolidated 2026-03-04: removed duplicated sections from outreach.md, added cross-references | | |
| 582 | - | | outreach/creator-budget.md | Not audited | -- | External subscription prices may have changed | | |
| 583 | - | | outreach/creators/dashboard.md | Not audited | -- | Manually maintained counts may drift from niche files | | |
| 584 | - | ||
| 585 | - | #### Root Docs | |
| 586 | - | ||
| 587 | - | | Document | Status | Last Verified | Notes | | |
| 588 | - | |----------|:------:|:-------------:|-------| | |
| 589 | - | | docs/todo.md | Current | 2026-03-04 | Status line matches codebase | | |
| 590 | - | | docs/audit_review.md | Current | 2026-03-04 | Code audit history | | |
| 591 | - | | docs/docs-todo.md | Deleted | 2026-03-10 | Merged into docs/mnw/todo.md | | |
| 592 | - | | docs/notes-todo.md | Deleted | 2026-03-10 | Merged into docs/mnw/todo.md | | |
| 593 | - | | docs/importers.md | Not audited | -- | Phase 13D importer specs | | |
| 594 | - | ||
| 595 | - | ### Stale References Found (2026-03-04 Audit) | |
| 596 | - | ||
| 597 | - | #### Public Docs | |
| 598 | - | | Location | Issue | Resolution | | |
| 599 | - | |----------|-------|------------| | |
| 600 | - | | roadmap.md | Passkeys listed under "What's Next" -- already done (Phase 9.2) | Moved to "What's Built" | | |
| 601 | - | | roadmap.md | Test count says 349 -- actual is 386 | Updated | | |
| 602 | - | | how-we-work.md | "forums" in Basic tier -- not implemented | Removed | | |
| 603 | - | | how-we-work.md | "audio, video, text, or streams" -- video/streaming not built | Changed to "audio, text, or digital files" | | |
| 604 | - | | faq.md | Links to faq-billing.md, faq-content.md, faq-payouts.md, faq-technical.md -- none exist | Removed broken links | | |
| 605 | - | ||
| 606 | - | #### CLAUDE.md | |
| 607 | - | | Location | Issue | Resolution | | |
| 608 | - | |----------|-------|------------| | |
| 609 | - | | MNW section | 5 files listed as .rs that are directory modules | Fixed all 5 | | |
| 610 | - | | MNW section | Migration count 001-040, only 9 exist | Fixed to 001-009 | | |
| 611 | - | | MNW section | Route tree missing oauth.rs, synckit.rs | Added | | |
| 612 | - | | MNW section | Src tree missing ~10 modules | Added all | | |
| 613 | - | | MNW pricing | "forums" in Basic tier | Removed | | |
| 614 | - | ||
| 615 | - | #### Unpublished Docs | |
| 616 | - | | Location | Issue | Resolution | | |
| 617 | - | |----------|-------|------------| | |
| 618 | - | | competition.md | 30+ features listed as "Planned" that are now Done | Updated all statuses | | |
| 619 | - | | competition.md | Test count "Unit tests (15) + integration tests (13)" | Updated to "386 automated tests" | | |
| 620 | - | | competition.md | SyncKit listed as entirely Planned | Updated 11 items to Done | | |
| 621 | - | | competition.md | Feature matrix: discount codes "No", subscriptions "Planned" | Updated both to Yes/Done | | |
| 622 | - | | transition.md | "10 Rust source files", "~3,800 lines of Rust" | Updated to 109 files, ~33,000 lines | | |
| 623 | - | | transition.md | Old file structure (db.rs, routes/pages.rs, etc.) | Updated to current directory modules | | |
| 624 | - | | pitch.md | "forums" in Basic tier | Removed | | |
| 625 | - | | forums.md | Platform forum described as existing | Marked as Planned | | |
| 626 | - | | code-of-conduct.md | References "the main Makenot.work forum" | Updated to future tense | | |
| 627 | - | | tier-big-files.md | Video hosting described as available | Added "not yet implemented" note | | |
| 628 | - | | tier-streaming.md | Live streaming described as available | Added "not yet implemented" note | | |
| 629 | - | | creator/analytics.md | Analytics described as built | Added placeholder status note | | |
| 630 | - | | creator/embedding.md | Embeds described as built | Added "not yet implemented" note | | |
| 631 | - | | creator/dashboard.md | Detailed financial metrics described as built | Added placeholder status note | | |
| 632 | - | | creator/purchases.md | "Downloadable in multiple formats" | Changed to "original uploaded format" | | |
| 633 | - | | tech/security.md | GitHub URL (github.com/makecreative/makenot.work) | Changed to Sourcehut | | |
| 634 | - | | tech/open-source.md | Same GitHub URL | Changed to Sourcehut | | |
| 635 | - | ||
| 636 | - | #### Internal Docs | |
| 637 | - | | Location | Issue | Resolution | | |
| 638 | - | |----------|-------|------------| | |
| 639 | - | | outreach.md | Tier names "Text, Small Files, Audio, Big Files" | Changed to "Basic, Small Files, Big Files, Streaming" | | |
| 640 | - | | outreach.md | TikTok "full shutdown by March 2026" | Updated to "ongoing regulatory limbo" | | |
| 641 | - | | creators/index.md | Same TikTok deadline | Updated | | |
| 642 | - | | creator_qol.md | References docs/reference/ paths | Changed to docs/unpublished/ | | |
| 643 | - | | creator_qol.md | References tier-text.md | Changed to tier-basic.md | | |
| 644 | - | | creator_qol.md | References transfer.md | Changed to migration.md | | |
| 645 | - | | creators/tech.md | DEV.to "3M+ devs" | Updated to "5M+ reach" | | |
| 646 | - | | creators/niches.md | Course creator pitch says "$10/month" | Changed to "$20-30/month" with tier explanation | | |
| 647 | - | ||
| 648 | - | ### Cold Spots (Not Yet Audited) | |
| 649 | - | ||
| 650 | - | | Area | Size | Risk | Notes | | |
| 651 | - | |------|------|------|-------| | |
| 652 | - | | outreach/creators/*.md (niche files) | 9 files, ~321 creators | Medium | Patreon counts, subscriber numbers are snapshots. Refresh before actual outreach. | | |
| 653 | - | | outreach/creator-investment-strategy.md | 1 file | Done | Consolidated 2026-03-04: removed duplicated sections from outreach.md, added cross-references | | |
| 654 | - | | outreach/creator-budget.md | 1 file | Low | External subscription prices may have changed | | |
| 655 | - | | outreach/creators/dashboard.md | 1 file | Low | Manually maintained counts may drift from niche files | | |
| 656 | - | | strategy/synckit-plan.md | 1 file | Done | Audited 2026-03-04: dates stripped, implementation divergences noted, Phase 1 marked done | | |
| 657 | - | | docs-todo.md, notes-todo.md | Deleted | -- | Merged into docs/mnw/todo.md (2026-03-10) | | |
| 658 | - | | legal/copyright.md | 1 file | Low | DMCA agent address placeholder | | |
| 659 | - | | legal/liability.md | 1 file | Low | Multiple [STATE] and [Pending legal review] placeholders | | |
| 660 | - | ||
| 661 | - | ### Action Items | |
| 662 | - | ||
| 663 | - | - Keep roadmap.md test count in sync when test count changes | |
| 664 | - | - When forums are implemented, add back to Basic tier and update forums.md/code-of-conduct.md | |
| 665 | - | - When video hosting ships, remove status notes from tier-big-files.md and related creator docs | |
| 666 | - | - When streaming ships, remove status note from tier-streaming.md | |
| 667 | - | - Refresh creator stats in niche files before beginning actual outreach | |
| 367 | + | See [audit_history.md](./audit_history.md) for full chronological audit log. |
| @@ -50,7 +50,7 @@ The visual identity is minimal, typographic, and warm. It communicates trust, tr | |||
| 50 | 50 | | Wordmark | "Makenot.work" | Young Serif, large. The period is the font's native diamond glyph, colored violet. | | |
| 51 | 51 | | Page/section heading | "Create account" | Young Serif, medium-large. Dark charcoal-brown. | | |
| 52 | 52 | | Subsection heading | "What Stripe handles" | IBM Plex Mono, medium. Dark charcoal-brown. | | |
| 53 | - | | Tagline / subtitle | "Fair and transparent distribution for creators." | IBM Plex Mono, regular, medium. Warm gray. On the landing page, the final period is rendered in violet. | | |
| 53 | + | | Tagline / subtitle | "Fair distribution for creatives of all kinds." | IBM Plex Mono, regular, medium. Warm gray. On the landing page, the final period is rendered in violet. | | |
| 54 | 54 | | Body text | Paragraph content | Lato, regular, small. Dark charcoal-brown. | | |
| 55 | 55 | | Bold labels / inline emphasis | Key terms in body text | Lato Bold or IBM Plex Mono Bold. Dark charcoal-brown. | | |
| 56 | 56 | | Helper text | "Your public url: makenot.work/u/username" | IBM Plex Mono, regular, small. Warm gray. | |
| @@ -0,0 +1,109 @@ | |||
| 1 | + | # Liability & Disputes | |
| 2 | + | ||
| 3 | + | **Note: This document requires legal review before launch. Contents are draft only.** | |
| 4 | + | ||
| 5 | + | --- | |
| 6 | + | ||
| 7 | + | ## Service Disclaimers | |
| 8 | + | ||
| 9 | + | Makenot.work is provided "as is" without warranties of any kind. We don't guarantee: | |
| 10 | + | ||
| 11 | + | - Uninterrupted service availability | |
| 12 | + | - That the platform will meet all your needs | |
| 13 | + | - That content will remain accessible forever | |
| 14 | + | - Specific revenue or audience outcomes | |
| 15 | + | ||
| 16 | + | We do our best to provide reliable service, but we're a small team and things sometimes break. | |
| 17 | + | ||
| 18 | + | --- | |
| 19 | + | ||
| 20 | + | ## Limitation of Liability | |
| 21 | + | ||
| 22 | + | Our liability is limited to the amount you've paid us in the 12 months before any claim. | |
| 23 | + | ||
| 24 | + | We are not liable for: | |
| 25 | + | ||
| 26 | + | - Indirect, incidental, or consequential damages | |
| 27 | + | - Lost profits or revenue | |
| 28 | + | - Data loss (you're responsible for backups) | |
| 29 | + | - Actions of other users | |
| 30 | + | - Third-party services (Stripe, CDN providers, etc.) | |
| 31 | + | ||
| 32 | + | --- | |
| 33 | + | ||
| 34 | + | ## Your Responsibilities | |
| 35 | + | ||
| 36 | + | You are responsible for: | |
| 37 | + | ||
| 38 | + | - Content you upload (including copyright compliance) | |
| 39 | + | - Your interactions with fans | |
| 40 | + | - Your tax and legal obligations | |
| 41 | + | - Backing up your content | |
| 42 | + | - Maintaining account security | |
| 43 | + | ||
| 44 | + | --- | |
| 45 | + | ||
| 46 | + | ## Disputes Between Users | |
| 47 | + | ||
| 48 | + | We don't mediate disputes between creators and fans, or between creators. We provide the platform; relationship management is yours. | |
| 49 | + | ||
| 50 | + | If someone violates our policies, report it. But business disputes, creative disagreements, and personal conflicts are not our domain. | |
| 51 | + | ||
| 52 | + | --- | |
| 53 | + | ||
| 54 | + | ## Dispute Resolution | |
| 55 | + | ||
| 56 | + | *Note: This section is pending formal legal review.* | |
| 57 | + | ||
| 58 | + | ### Governing Law | |
| 59 | + | ||
| 60 | + | This agreement is governed by the laws of the State of Colorado, without regard to conflict of law principles. | |
| 61 | + | ||
| 62 | + | ### Informal Resolution | |
| 63 | + | ||
| 64 | + | Before filing any formal claim, you agree to contact us at legal@makenot.work and attempt to resolve the dispute informally for at least thirty (30) days. | |
| 65 | + | ||
| 66 | + | ### Binding Arbitration | |
| 67 | + | ||
| 68 | + | Any dispute arising from or relating to this agreement or your use of the platform that cannot be resolved informally shall be resolved by binding arbitration administered by the American Arbitration Association (AAA) under its Commercial Arbitration Rules. The arbitration shall be conducted in the State of Colorado. The arbitrator's decision shall be final and binding and may be entered as a judgment in any court of competent jurisdiction. | |
| 69 | + | ||
| 70 | + | ### Class Action Waiver | |
| 71 | + | ||
| 72 | + | You agree that any disputes will be resolved on an individual basis. You waive any right to participate in a class action, class arbitration, or representative proceeding. | |
| 73 | + | ||
| 74 | + | ### Small Claims Exception | |
| 75 | + | ||
| 76 | + | Either party may bring an individual action in small claims court in the State of Colorado if the claim qualifies. | |
| 77 | + | ||
| 78 | + | --- | |
| 79 | + | ||
| 80 | + | ## Indemnification | |
| 81 | + | ||
| 82 | + | You agree to indemnify and hold harmless Make Creative, LLC from claims arising from: | |
| 83 | + | ||
| 84 | + | - Your content | |
| 85 | + | - Your use of the platform | |
| 86 | + | - Your violation of these terms | |
| 87 | + | - Your violation of any third-party rights | |
| 88 | + | ||
| 89 | + | --- | |
| 90 | + | ||
| 91 | + | ## Changes to This Document | |
| 92 | + | ||
| 93 | + | We may update these terms. Material changes will be communicated with advance notice. Continued use after changes constitutes acceptance. | |
| 94 | + | ||
| 95 | + | --- | |
| 96 | + | ||
| 97 | + | ## Questions | |
| 98 | + | ||
| 99 | + | Contact legal@makenot.work for questions about liability or disputes. | |
| 100 | + | ||
| 101 | + | --- | |
| 102 | + | ||
| 103 | + | **Reminder: This document is a draft. Have a lawyer review before publishing.** | |
| 104 | + | ||
| 105 | + | ## See Also | |
| 106 | + | ||
| 107 | + | - [Terms of Service](../../public/legal/terms-of-service.md) — Full legal terms | |
| 108 | + | - [Payments & Revenue](./payments.md) — Stripe, chargebacks, and tax | |
| 109 | + | - [Acceptable Use Policy](../../public/legal/acceptable-use.md) — Content rules |
| @@ -458,7 +458,7 @@ | |||
| 458 | 458 | ||
| 459 | 459 | <div class="hero"> | |
| 460 | 460 | <h1>Makenot<span class="dot">.</span>work</h1> | |
| 461 | - | <div class="tagline">Sell creative work for a flat monthly fee. 0% platform cut.</div> | |
| 461 | + | <div class="tagline">Fair distribution for creatives of all kinds.</div> | |
| 462 | 462 | <div class="sub">A creator platform where the only fee is Stripe's ~3% processing. No revenue share, no lock-in, full data export.</div> | |
| 463 | 463 | </div> | |
| 464 | 464 |
| @@ -0,0 +1,12 @@ | |||
| 1 | + | #!/bin/bash | |
| 2 | + | # Seed demo data into the database | |
| 3 | + | # Usage: ./seed_demo.sh | |
| 4 | + | ||
| 5 | + | cd "$(dirname "$0")" | |
| 6 | + | ||
| 7 | + | # Load .env file | |
| 8 | + | if [ -f .env ]; then | |
| 9 | + | export $(grep -v '^#' .env | xargs) | |
| 10 | + | fi | |
| 11 | + | ||
| 12 | + | psql "$DATABASE_URL" -f seed_demo.sql |
| @@ -0,0 +1,243 @@ | |||
| 1 | + | -- Demo Data Seed Script | |
| 2 | + | -- Run with: psql $DATABASE_URL -f seed_demo.sql | |
| 3 | + | -- Can be safely re-run (uses ON CONFLICT DO UPDATE to ensure data is current) | |
| 4 | + | ||
| 5 | + | -- ============================================================================ | |
| 6 | + | -- USER: Elena Vasquez | |
| 7 | + | -- ============================================================================ | |
| 8 | + | -- Password: demo123 | |
| 9 | + | -- Hash generated with Argon2 default settings | |
| 10 | + | INSERT INTO users ( | |
| 11 | + | id, | |
| 12 | + | username, | |
| 13 | + | email, | |
| 14 | + | password_hash, | |
| 15 | + | display_name, | |
| 16 | + | bio, | |
| 17 | + | stripe_account_id, | |
| 18 | + | stripe_onboarding_complete, | |
| 19 | + | stripe_payouts_enabled, | |
| 20 | + | stripe_charges_enabled | |
| 21 | + | ) VALUES ( | |
| 22 | + | '11111111-1111-1111-1111-111111111111', | |
| 23 | + | 'elena', | |
| 24 | + | 'elena@example.com', | |
| 25 | + | '$argon2id$v=19$m=19456,t=2,p=1$DKKe+bI4TwiyxkLgyCDZMA$oXVcdLHZIxv1hkFGK4JB5HNqoGURavSXhY9gjmvQeEM', | |
| 26 | + | 'Elena Vasquez', | |
| 27 | + | 'Writer and researcher exploring the intersection of creativity, technology, and human flourishing. Author of Slow Craft and the Patterns newsletter.', | |
| 28 | + | 'acct_demo_elena', | |
| 29 | + | true, | |
| 30 | + | true, | |
| 31 | + | true | |
| 32 | + | ) ON CONFLICT (username) DO UPDATE SET | |
| 33 | + | email = EXCLUDED.email, | |
| 34 | + | password_hash = EXCLUDED.password_hash, | |
| 35 | + | display_name = EXCLUDED.display_name, | |
| 36 | + | bio = EXCLUDED.bio, | |
| 37 | + | stripe_account_id = EXCLUDED.stripe_account_id, | |
| 38 | + | stripe_onboarding_complete = EXCLUDED.stripe_onboarding_complete, | |
| 39 | + | stripe_payouts_enabled = EXCLUDED.stripe_payouts_enabled, | |
| 40 | + | stripe_charges_enabled = EXCLUDED.stripe_charges_enabled; | |
| 41 | + | ||
| 42 | + | -- ============================================================================ | |
| 43 | + | -- PROJECT 1: Patterns Newsletter (Writing) | |
| 44 | + | -- ============================================================================ | |
| 45 | + | INSERT INTO projects ( | |
| 46 | + | id, | |
| 47 | + | user_id, | |
| 48 | + | slug, | |
| 49 | + | title, | |
| 50 | + | description, | |
| 51 | + | project_type, | |
| 52 | + | is_public | |
| 53 | + | ) VALUES ( | |
| 54 | + | '22222222-2222-2222-2222-222222222221', | |
| 55 | + | '11111111-1111-1111-1111-111111111111', | |
| 56 | + | 'patterns-newsletter', | |
| 57 | + | 'Patterns', | |
| 58 | + | 'Essays on productivity, creativity, and intentional living.', | |
| 59 | + | 'writing', | |
| 60 | + | true | |
| 61 | + | ) ON CONFLICT (user_id, slug) DO UPDATE SET | |
| 62 | + | title = EXCLUDED.title, | |
| 63 | + | description = EXCLUDED.description, | |
| 64 | + | project_type = EXCLUDED.project_type, | |
| 65 | + | is_public = EXCLUDED.is_public; | |
| 66 | + | ||
| 67 | + | -- Items for Patterns Newsletter | |
| 68 | + | INSERT INTO items (id, project_id, title, description, price_cents, item_type, is_public, sort_order, tags) VALUES | |
| 69 | + | ( | |
| 70 | + | '33333333-3333-3333-3333-333333333331', | |
| 71 | + | '22222222-2222-2222-2222-222222222221', | |
| 72 | + | 'The Art of Doing Less', | |
| 73 | + | 'An essay exploring why our obsession with productivity often undermines the very outcomes we seek. Drawing on research in cognitive science and philosophy, this piece argues for a more intentional approach to work and rest.', | |
| 74 | + | 500, | |
| 75 | + | 'text', | |
| 76 | + | true, | |
| 77 | + | 1, | |
| 78 | + | '["Essays", "Productivity", "Creativity", "Philosophy"]' | |
| 79 | + | ), | |
| 80 | + | ( | |
| 81 | + | '33333333-3333-3333-3333-333333333332', | |
| 82 | + | '22222222-2222-2222-2222-222222222221', | |
| 83 | + | 'Finding Your Own Rhythm', | |
| 84 | + | 'Not everyone thrives on the same schedule. This essay examines how to discover your natural working patterns and structure your days around them, rather than forcing yourself into someone else''s ideal routine.', | |
| 85 | + | 500, | |
| 86 | + | 'text', | |
| 87 | + | true, | |
| 88 | + | 2, | |
| 89 | + | '["Essays", "Productivity"]' | |
| 90 | + | ), | |
| 91 | + | ( | |
| 92 | + | '33333333-3333-3333-3333-333333333333', | |
| 93 | + | '22222222-2222-2222-2222-222222222221', | |
| 94 | + | 'The Productivity Paradox', | |
| 95 | + | 'A free introduction to the themes explored throughout the Patterns newsletter. Why do we feel busier than ever while accomplishing less of what matters?', | |
| 96 | + | 0, | |
| 97 | + | 'text', | |
| 98 | + | true, | |
| 99 | + | 3, | |
| 100 | + | '["Essays", "Productivity"]' | |
| 101 | + | ) | |
| 102 | + | ON CONFLICT (id) DO UPDATE SET | |
| 103 | + | title = EXCLUDED.title, | |
| 104 | + | description = EXCLUDED.description, | |
| 105 | + | price_cents = EXCLUDED.price_cents, | |
| 106 | + | item_type = EXCLUDED.item_type, | |
| 107 | + | is_public = EXCLUDED.is_public, | |
| 108 | + | sort_order = EXCLUDED.sort_order, | |
| 109 | + | tags = EXCLUDED.tags; | |
| 110 | + | ||
| 111 | + | -- ============================================================================ | |
| 112 | + | -- PROJECT 2: Patterns Podcast | |
| 113 | + | -- ============================================================================ | |
| 114 | + | INSERT INTO projects ( | |
| 115 | + | id, | |
| 116 | + | user_id, | |
| 117 | + | slug, | |
| 118 | + | title, | |
| 119 | + | description, | |
| 120 | + | project_type, | |
| 121 | + | is_public | |
| 122 | + | ) VALUES ( | |
| 123 | + | '22222222-2222-2222-2222-222222222222', | |
| 124 | + | '11111111-1111-1111-1111-111111111111', | |
| 125 | + | 'patterns-podcast', | |
| 126 | + | 'Patterns Podcast', | |
| 127 | + | 'Conversations about creativity, rest, and meaningful work.', | |
| 128 | + | 'podcast', | |
| 129 | + | true | |
| 130 | + | ) ON CONFLICT (user_id, slug) DO UPDATE SET | |
| 131 | + | title = EXCLUDED.title, | |
| 132 | + | description = EXCLUDED.description, | |
| 133 | + | project_type = EXCLUDED.project_type, | |
| 134 | + | is_public = EXCLUDED.is_public; | |
| 135 | + | ||
| 136 | + | -- Items for Patterns Podcast | |
| 137 | + | INSERT INTO items (id, project_id, title, description, price_cents, item_type, is_public, sort_order, tags) VALUES | |
| 138 | + | ( | |
| 139 | + | '33333333-3333-3333-3333-333333333341', | |
| 140 | + | '22222222-2222-2222-2222-222222222222', | |
| 141 | + | 'Episode 12: The Space Between Tasks', | |
| 142 | + | 'A conversation with cognitive scientist Dr. Maya Chen about the importance of transition time between activities. We explore how the moments of apparent idleness are often when our best thinking happens.', | |
| 143 | + | 300, | |
| 144 | + | 'audio', | |
| 145 | + | true, | |
| 146 | + | 1, | |
| 147 | + | '["Podcast", "Productivity", "Creativity", "Interviews"]' | |
| 148 | + | ), | |
| 149 | + | ( | |
| 150 | + | '33333333-3333-3333-3333-333333333342', | |
| 151 | + | '22222222-2222-2222-2222-222222222222', | |
| 152 | + | 'Episode 11: Creative Incubation', | |
| 153 | + | 'What happens in your brain when you step away from a problem? This episode dives into the science of incubation and why your best ideas often come in the shower.', | |
| 154 | + | 300, | |
| 155 | + | 'audio', | |
| 156 | + | true, | |
| 157 | + | 2, | |
| 158 | + | '["Podcast", "Creativity"]' | |
| 159 | + | ), | |
| 160 | + | ( | |
| 161 | + | '33333333-3333-3333-3333-333333333343', | |
| 162 | + | '22222222-2222-2222-2222-222222222222', | |
| 163 | + | 'Episode 10: The Neuroscience of Rest', | |
| 164 | + | 'A free episode featuring neuroscientist Dr. James Park discussing why rest is not the opposite of productivity but its essential complement.', | |
| 165 | + | 0, | |
| 166 | + | 'audio', | |
| 167 | + | true, | |
| 168 | + | 3, | |
| 169 | + | '["Podcast", "Interviews"]' | |
| 170 | + | ) | |
| 171 | + | ON CONFLICT (id) DO UPDATE SET | |
| 172 | + | title = EXCLUDED.title, | |
| 173 | + | description = EXCLUDED.description, | |
| 174 | + | price_cents = EXCLUDED.price_cents, | |
| 175 | + | item_type = EXCLUDED.item_type, | |
| 176 | + | is_public = EXCLUDED.is_public, | |
| 177 | + | sort_order = EXCLUDED.sort_order, | |
| 178 | + | tags = EXCLUDED.tags; | |
| 179 | + | ||
| 180 | + | -- ============================================================================ | |
| 181 | + | -- PROJECT 3: Slow Craft (Book) | |
| 182 | + | -- ============================================================================ | |
| 183 | + | INSERT INTO projects ( | |
| 184 | + | id, | |
| 185 | + | user_id, | |
| 186 | + | slug, | |
| 187 | + | title, | |
| 188 | + | description, | |
| 189 | + | project_type, | |
| 190 | + | is_public | |
| 191 | + | ) VALUES ( | |
| 192 | + | '22222222-2222-2222-2222-222222222223', | |
| 193 | + | '11111111-1111-1111-1111-111111111111', | |
| 194 | + | 'slow-craft', | |
| 195 | + | 'Slow Craft', | |
| 196 | + | 'A guide to sustainable creativity and meaningful work.', | |
| 197 | + | 'book', | |
| 198 | + | true | |
| 199 | + | ) ON CONFLICT (user_id, slug) DO UPDATE SET | |
| 200 | + | title = EXCLUDED.title, | |
| 201 | + | description = EXCLUDED.description, | |
| 202 | + | project_type = EXCLUDED.project_type, | |
| 203 | + | is_public = EXCLUDED.is_public; | |
| 204 | + | ||
| 205 | + | -- Items for Slow Craft | |
| 206 | + | INSERT INTO items (id, project_id, title, description, price_cents, item_type, is_public, sort_order, tags) VALUES | |
| 207 | + | ( | |
| 208 | + | '33333333-3333-3333-3333-333333333351', | |
| 209 | + | '22222222-2222-2222-2222-222222222223', | |
| 210 | + | 'Slow Craft: Complete Book', | |
| 211 | + | 'The complete guide to building a sustainable creative practice. Across 12 chapters, explore how to do your best work without burning out, cultivate deep focus in an age of distraction, and create work that matters on your own terms.', | |
| 212 | + | 1500, | |
| 213 | + | 'text', | |
| 214 | + | true, | |
| 215 | + | 1, | |
| 216 | + | '["Book", "Creativity"]' | |
| 217 | + | ), | |
| 218 | + | ( | |
| 219 | + | '33333333-3333-3333-3333-333333333352', | |
| 220 | + | '22222222-2222-2222-2222-222222222223', | |
| 221 | + | 'Chapter 1: Introduction (Preview)', | |
| 222 | + | 'A free preview of Slow Craft. This opening chapter introduces the core philosophy of the book and sets the stage for the practices that follow.', | |
| 223 | + | 0, | |
| 224 | + | 'text', | |
| 225 | + | true, | |
| 226 | + | 2, | |
| 227 | + | '["Book", "Preview"]' | |
| 228 | + | ) | |
| 229 | + | ON CONFLICT (id) DO UPDATE SET | |
| 230 | + | title = EXCLUDED.title, | |
| 231 | + | description = EXCLUDED.description, | |
| 232 | + | price_cents = EXCLUDED.price_cents, | |
| 233 | + | item_type = EXCLUDED.item_type, | |
| 234 | + | is_public = EXCLUDED.is_public, | |
| 235 | + | sort_order = EXCLUDED.sort_order, | |
| 236 | + | tags = EXCLUDED.tags; | |
| 237 | + | ||
| 238 | + | -- ============================================================================ | |
| 239 | + | -- Verification (shows what was seeded) | |
| 240 | + | -- ============================================================================ | |
| 241 | + | SELECT 'Seeded user:' as status, username, email FROM users WHERE username = 'elena'; | |
| 242 | + | SELECT 'Seeded projects:' as status, count(*) as count FROM projects WHERE user_id = (SELECT id FROM users WHERE username = 'elena'); | |
| 243 | + | SELECT 'Seeded items:' as status, count(*) as count FROM items WHERE project_id IN (SELECT id FROM projects WHERE user_id = (SELECT id FROM users WHERE username = 'elena')); |
| @@ -6,7 +6,7 @@ Binding commitments from Makenot.work to every creator on the platform. These ar | |||
| 6 | 6 | ||
| 7 | 7 | ## Revenue | |
| 8 | 8 | ||
| 9 | - | **Guarantee:** 0% platform fee on fan payments. The only deduction is the payment processor's fee (currently Stripe, ~3%). | |
| 9 | + | **Guarantee:** 0% platform fee on fan payments. The only deduction is the payment processor's fee (~3%). | |
| 10 | 10 | ||
| 11 | 11 | - No platform percentage cut, ever. | |
| 12 | 12 | - No transaction fees, payout fees, or skimming. |
| @@ -10,7 +10,7 @@ Flat fee. All your revenue passes through to you. | |||
| 10 | 10 | 2. **Upload content** — text, audio, software, video, or digital files | |
| 11 | 11 | 3. **Organize** using hierarchical tags and projects | |
| 12 | 12 | 4. **Set pricing** — free, pay-what-you-want, fixed price, or subscription | |
| 13 | - | 5. **Get paid** — 0% platform fee, only Stripe processing | |
| 13 | + | 5. **Get paid** — 0% platform fee, only payment processing fees | |
| 14 | 14 | ||
| 15 | 15 | ## For Fans | |
| 16 | 16 | ||
| @@ -25,7 +25,7 @@ Flat fee. All your revenue passes through to you. | |||
| 25 | 25 | Fan pays $10 | |
| 26 | 26 | | | |
| 27 | 27 | v | |
| 28 | - | Stripe (payment processor) | |
| 28 | + | Payment processor | |
| 29 | 29 | | | |
| 30 | 30 | v | |
| 31 | 31 | Funds held in creator's connected account | |
| @@ -34,13 +34,13 @@ Funds held in creator's connected account | |||
| 34 | 34 | Creator withdraws on their schedule | |
| 35 | 35 | ``` | |
| 36 | 36 | ||
| 37 | - | We never touch creator revenue. Payments go directly to creator-controlled Stripe accounts. | |
| 37 | + | We never touch creator revenue. Payments go directly to creator-controlled payment accounts. | |
| 38 | 38 | ||
| 39 | 39 | --- | |
| 40 | 40 | ||
| 41 | 41 | ## The Model | |
| 42 | 42 | ||
| 43 | - | You pay a monthly subscription based on content type ($10-40). We take 0% of your fan revenue — the only deduction is payment processing (~3% from Stripe). | |
| 43 | + | You pay a monthly subscription based on content type ($10-40). We take 0% of your fan revenue — the only deduction is the payment processing fee (~3%). | |
| 44 | 44 | ||
| 45 | 45 | We're funded by subscriptions, not by your success. No ads, no percentage cuts, no hidden fees. | |
| 46 | 46 | ||
| @@ -98,7 +98,7 @@ The prices reflect what it actually costs to store and deliver each content type | |||
| 98 | 98 | - RSS feed generation (project + blog feeds) | |
| 99 | 99 | - License keys, discount codes, download codes | |
| 100 | 100 | - Pay-what-you-want pricing option | |
| 101 | - | - Subscription tiers with Stripe billing | |
| 101 | + | - Subscription tiers with automated billing | |
| 102 | 102 | - Follows, broadcast emails, email notifications (sales, followers, releases, logins) | |
| 103 | 103 | - 2FA/TOTP, passkeys/WebAuthn, session management, account lockout | |
| 104 | 104 |
| @@ -25,7 +25,7 @@ Everything listed here is live and working. | |||
| 25 | 25 | ||
| 26 | 26 | - **Fixed-price purchases**: One-time payment at a set price | |
| 27 | 27 | - **Pay-what-you-want**: Buyer chooses the amount, optional minimum price | |
| 28 | - | - **Subscriptions**: Monthly recurring tiers per project with Stripe billing (multiple tiers, active/inactive toggle) | |
| 28 | + | - **Subscriptions**: Monthly recurring tiers per project with automated billing (multiple tiers, active/inactive toggle) | |
| 29 | 29 | - **License keys**: Auto-generated on purchase, configurable activation limits, machine tracking, public validation endpoint for software phone-home | |
| 30 | 30 | - **Promo codes**: Unified code system supporting percentage/fixed discounts, free access grants, and free trial periods for subscriptions. Item-scoped or project-wide, usage limits, expiration dates, auto-apply via URL parameter | |
| 31 | 31 | ||
| @@ -54,15 +54,15 @@ Everything listed here is live and working. | |||
| 54 | 54 | - **Contacts**: View fans who shared their email at purchase, with purchase count and total spent | |
| 55 | 55 | - **Broadcasts**: Send plain-text email updates to all your followers (rate-limited to one per 24 hours) | |
| 56 | 56 | - **Revenue charting**: Time-series revenue with selectable periods (7d/30d/90d/all), period-over-period comparison, per-project breakdown | |
| 57 | - | - **Getting-started email drip**: 3-step onboarding sequence (welcome, profile tips, Stripe setup guide) | |
| 57 | + | - **Getting-started email drip**: 3-step onboarding sequence (welcome, profile tips, payment setup guide) | |
| 58 | 58 | - **Data export**: All projects, items, blog posts, sales (CSV), and purchases (CSV) downloadable anytime | |
| 59 | 59 | - **Custom links**: Add external links to your profile | |
| 60 | 60 | ||
| 61 | - | ### Payments & Stripe | |
| 61 | + | ### Payments | |
| 62 | 62 | ||
| 63 | - | - **0% platform fee**: Payments go directly to creator-controlled Stripe accounts | |
| 64 | - | - **Stripe Connect onboarding**: Guided setup, connected account status tracking | |
| 65 | - | - **Creator tier subscriptions**: Monthly billing for Basic ($10), Small Files ($20), Big Files ($30), and Streaming ($40) tiers via Stripe | |
| 63 | + | - **0% platform fee**: Payments go directly to creator-controlled payment accounts | |
| 64 | + | - **Payment onboarding**: Guided setup, connected account status tracking | |
| 65 | + | - **Creator tier subscriptions**: Monthly billing for Basic ($10), Small Files ($20), Big Files ($30), and Streaming ($40) tiers | |
| 66 | 66 | - **Automated payment handling**: Checkout completion, refunds, subscription updates (active, past due, canceled), renewal billing | |
| 67 | 67 | - **Reliable checkout**: Purchases are processed safely with no risk of double-charges | |
| 68 | 68 | ||
| @@ -95,7 +95,7 @@ Everything listed here is live and working. | |||
| 95 | 95 | - **Creator tier enforcement**: Storage tracking and per-tier limits with grace period | |
| 96 | 96 | - **Health monitoring**: Uptime tracking, service connectivity checks | |
| 97 | 97 | - **Malware scanning**: Every uploaded file is scanned for malware before it's made available | |
| 98 | - | - **1000+ automated tests**: Comprehensive test suite covering all platform features | |
| 98 | + | - **Comprehensive automated test suite** covering all platform features | |
| 99 | 99 | ||
| 100 | 100 | ### Developer Infrastructure (SyncKit) | |
| 101 | 101 | ||
| @@ -129,7 +129,7 @@ Video upload and streaming (HLS adaptive bitrate). Podcast hosting with private | |||
| 129 | 129 | ||
| 130 | 130 | ### Importers | |
| 131 | 131 | ||
| 132 | - | Bring your existing audience. Substack post + subscriber import, Ghost JSON import, subscriber CSV upload with consent emails. | |
| 132 | + | Bring your existing audience. Newsletter post + subscriber import, newsletter JSON import, subscriber CSV upload with consent emails. | |
| 133 | 133 | ||
| 134 | 134 | ### Embeddable players and widgets | |
| 135 | 135 | ||
| @@ -145,7 +145,7 @@ Sponsor tiers, license display, release hosting, build status badges. Git-backed | |||
| 145 | 145 | ||
| 146 | 146 | ### Payment independence | |
| 147 | 147 | ||
| 148 | - | Reduce Stripe dependency over time. ACH/SEPA payouts, lower-cost processors, micro-transaction support. | |
| 148 | + | Expand payment options over time. ACH/SEPA payouts, lower-cost processors, micro-transaction support. | |
| 149 | 149 | ||
| 150 | 150 | --- | |
| 151 | 151 |
| @@ -14,7 +14,7 @@ And when a platform takes venture capital, the pressure compounds. Investors nee | |||
| 14 | 14 | ||
| 15 | 15 | ## The Alternative | |
| 16 | 16 | ||
| 17 | - | We charge a flat monthly fee based on what you need to host: $10 for text, $20 for audio and software, $30 for video and large files, $40 for live streaming. That's it. We take 0% of your revenue. The only deduction from fan payments is Stripe's processing fee (~3%), which goes to Stripe, not us. | |
| 17 | + | We charge a flat monthly fee based on what you need to host: $10 for text, $20 for audio and software, $30 for video and large files, $40 for live streaming. That's it. We take 0% of your revenue. The only deduction from fan payments is the payment processor's fee (~3%), which goes to the processor, not us. | |
| 18 | 18 | ||
| 19 | 19 | Your subscription funds the platform. We have no financial incentive to take a cut of your sales, show ads to your fans, or lock you into our ecosystem. | |
| 20 | 20 |
| @@ -12,7 +12,7 @@ Session-authenticated endpoints are designed for the HTMX frontend. When called | |||
| 12 | 12 | ||
| 13 | 13 | ### SyncKit JWT | |
| 14 | 14 | ||
| 15 | - | Used by [SyncKit](./synckit.md) cloud sync and [OTA updates](./ota.md). Obtain a token via `POST /api/sync/auth` (email + password + API key) or the [OAuth2 PKCE flow](./oauth.md). Pass it as `Authorization: Bearer <token>`. Tokens expire after 30 days. | |
| 15 | + | Used by [SyncKit](./synckit.md) cloud sync and [OTA updates](./ota.md). Obtain a token via `POST /api/sync/auth` (email + password + API key) or the [OAuth2 PKCE flow](./oauth.md). Pass it as `Authorization: Bearer <token>`. Tokens expire after a period of inactivity. | |
| 16 | 16 | ||
| 17 | 17 | ### No Authentication | |
| 18 | 18 | ||
| @@ -46,15 +46,7 @@ Internal errors return a generic message — no stack traces or database details | |||
| 46 | 46 | ||
| 47 | 47 | ## Rate Limits | |
| 48 | 48 | ||
| 49 | - | Rate limits vary by endpoint category: | |
| 50 | - | ||
| 51 | - | | Category | Burst | Sustained | | |
| 52 | - | |----------|-------|-----------| | |
| 53 | - | | Authentication | 5 | 5/sec | | |
| 54 | - | | Write (POST/PUT/DELETE) | 10 | 2/sec | | |
| 55 | - | | License key validation | 5 | 1/sec | | |
| 56 | - | | Export (CSV/JSON) | 3 | 1/sec | | |
| 57 | - | | OTA update check | 20 | 5/sec | | |
| 49 | + | Rate limits vary by endpoint category. Exact limits may change; check response headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`) for current values. Broadly: authentication and license key endpoints are tightly limited, read endpoints are generous, OTA checks are the most permissive. | |
| 58 | 50 | ||
| 59 | 51 | Exceeding a limit returns HTTP 429. Implement exponential backoff in your client. | |
| 60 | 52 |
| @@ -121,7 +121,7 @@ Good approaches: | |||
| 121 | 121 | ||
| 122 | 122 | ## Rate Limits | |
| 123 | 123 | ||
| 124 | - | All license key endpoints: 200ms per request, burst 20. | |
| 124 | + | License key endpoints are rate-limited per IP. See [API Overview](./api-overview.md) for details. | |
| 125 | 125 | ||
| 126 | 126 | Exceeding the limit returns HTTP 429. Implement exponential backoff in your client. | |
| 127 | 127 |
| @@ -133,7 +133,7 @@ The access token works with all SyncKit endpoints: | |||
| 133 | 133 | - [OTA Updates](./ota.md) — manage releases and artifacts | |
| 134 | 134 | - User info (above) | |
| 135 | 135 | ||
| 136 | - | Tokens expire after 30 days. After expiration, redirect the user through the authorization flow again. | |
| 136 | + | Tokens expire after a period of inactivity. After expiration, redirect the user through the authorization flow again. | |
| 137 | 137 | ||
| 138 | 138 | ## Error Handling | |
| 139 | 139 |
| @@ -125,7 +125,7 @@ Response: | |||
| 125 | 125 | } | |
| 126 | 126 | ``` | |
| 127 | 127 | ||
| 128 | - | Limits: maximum 1000 changes per push. Table names must be 3-64 characters, row IDs 1-256 characters. The server validates device ownership. | |
| 128 | + | Changes per push are capped (the server returns an error if exceeded). Table names and row IDs have length constraints enforced server-side. The server validates device ownership. | |
| 129 | 129 | ||
| 130 | 130 | ### Pulling Changes | |
| 131 | 131 | ||
| @@ -160,7 +160,7 @@ Response: | |||
| 160 | 160 | } | |
| 161 | 161 | ``` | |
| 162 | 162 | ||
| 163 | - | Page size is 500 entries. Keep pulling while `has_more` is `true`. | |
| 163 | + | Results are paginated. Keep pulling while `has_more` is `true`. | |
| 164 | 164 | ||
| 165 | 165 | ### Checking Sync Status | |
| 166 | 166 | ||
| @@ -259,7 +259,7 @@ Content-Type: application/json | |||
| 259 | 259 | } | |
| 260 | 260 | ``` | |
| 261 | 261 | ||
| 262 | - | Maximum blob size: 100MB. | |
| 262 | + | Blob size is capped per tier. The server rejects oversized uploads. | |
| 263 | 263 | ||
| 264 | 264 | ### Downloading Blobs | |
| 265 | 265 |
| @@ -2,8 +2,6 @@ | |||
| 2 | 2 | ||
| 3 | 3 | Your first 15 minutes on Makenot.work — from sign-up to your first published item. | |
| 4 | 4 | ||
| 5 | - | --- | |
| 6 | - | ||
| 7 | 5 | ## Create Your Account | |
| 8 | 6 | ||
| 9 | 7 | 1. Visit the homepage and click **Join** | |
| @@ -11,7 +9,11 @@ Your first 15 minutes on Makenot.work — from sign-up to your first published i | |||
| 11 | 9 | 3. Enter your email and a strong password | |
| 12 | 10 | 4. Verify your email (check your inbox) | |
| 13 | 11 | ||
| 14 | - | Your fan account is ready immediately. You can browse, follow creators, and purchase content right away. | |
| 12 | + | **Username tips:** Use your artist or brand name. Lowercase letters, numbers, and hyphens only (2-30 characters). Keep it short and memorable — it can't be changed easily. | |
| 13 | + | ||
| 14 | + | **Didn't get the verification email?** Check spam/junk, click "Resend verification" on the login page, or contact support. | |
| 15 | + | ||
| 16 | + | Your fan account is ready immediately. You can browse, follow creators, and purchase content right away. Fan accounts are free. | |
| 15 | 17 | ||
| 16 | 18 | ## Apply for Creator Access | |
| 17 | 19 | ||
| @@ -23,25 +25,20 @@ Creator access is currently invite-only via the waitlist. To apply: | |||
| 23 | 25 | ||
| 24 | 26 | Applications are reviewed in waves. You'll get an email when you're approved. | |
| 25 | 27 | ||
| 26 | - | **Requirements:** | |
| 27 | - | - Verified email address | |
| 28 | - | - You haven't already applied | |
| 29 | - | - You don't already have creator access | |
| 28 | + | ## Connect Payments | |
| 30 | 29 | ||
| 31 | - | ## Connect Stripe | |
| 32 | - | ||
| 33 | - | Stripe is the payment service that processes purchases from your fans and deposits money into your bank account. Once approved as a creator, connect your Stripe account to receive payments: | |
| 30 | + | Once approved as a creator, connect your payment account to receive fan payments: | |
| 34 | 31 | ||
| 35 | 32 | 1. Go to your **Dashboard** | |
| 36 | - | 2. Click **Connect Stripe** | |
| 37 | - | 3. Follow the Stripe Connect onboarding flow | |
| 38 | - | 4. Complete identity verification (Stripe requirement) | |
| 33 | + | 2. Click **Connect Payments** | |
| 34 | + | 3. Follow the payment onboarding flow | |
| 35 | + | 4. Complete identity verification (payment processor requirement) | |
| 39 | 36 | ||
| 40 | - | Payments go directly to your Stripe account. We never hold or touch your revenue. | |
| 37 | + | Payments go directly to your payment account. We never hold or touch your revenue. | |
| 41 | 38 | ||
| 42 | 39 | ## Create Your First Project | |
| 43 | 40 | ||
| 44 | - | Projects are how you organize your work. Think of them like albums, podcast feeds, or product lines. | |
| 41 | + | Projects organize your work. Think of them like albums, podcast feeds, or product lines. | |
| 45 | 42 | ||
| 46 | 43 | 1. From your Dashboard, click **New Project** | |
| 47 | 44 | 2. Enter a **URL name** (e.g., `my-album` — this becomes `/p/my-album`) and a **title** | |
| @@ -77,162 +74,65 @@ Items are individual pieces of content inside a project. | |||
| 77 | 74 | ||
| 78 | 75 | - [ ] Account created and email verified | |
| 79 | 76 | - [ ] Waitlist application submitted (or creator access granted) | |
| 80 | - | - [ ] Stripe connected | |
| 77 | + | - [ ] Payment account connected | |
| 81 | 78 | - [ ] First project created with title, slug, and category | |
| 82 | 79 | - [ ] First item created with content uploaded | |
| 83 | 80 | - [ ] Item and project published | |
| 84 | 81 | ||
| 85 | - | --- | |
| 86 | - | ||
| 87 | - | ## Projects | |
| 88 | - | ||
| 89 | - | Projects group your items under a single page with its own URL, settings, and feed. | |
| 90 | - | ||
| 91 | - | ### Creating a Project | |
| 92 | - | ||
| 93 | - | From your Dashboard, click **New Project**. You'll need: | |
| 94 | - | ||
| 95 | - | - **URL name**: A short name for your project's web address (e.g., `my-album` becomes `/p/my-album`). Cannot be changed after creation. | |
| 96 | - | - **Title**: Display name shown on the project page. | |
| 97 | - | ||
| 98 | - | ### Project Settings | |
| 99 | - | ||
| 100 | - | Edit your project to configure: | |
| 101 | - | ||
| 102 | - | - **Description**: Text shown on the project page | |
| 103 | - | - **Category**: Helps fans discover your work. Choose from 12 built-in categories (Music, Band, Podcast, Blog, Software, Art, etc.) or create your own | |
| 104 | - | - **Visibility**: Draft (only you can see it) or Public (visible to everyone) | |
| 105 | - | ||
| 106 | - | ### Categories | |
| 107 | - | ||
| 108 | - | Categories are used for discovery and filtering. Each project can have one category. Built-in options include: | |
| 109 | - | ||
| 110 | - | | Category | Typical Use | | |
| 111 | - | |----------|-------------| | |
| 112 | - | | Music | Albums, singles, EPs | | |
| 113 | - | | Band | Band or artist pages | | |
| 114 | - | | Podcast | Podcast feeds | | |
| 115 | - | | Blog | Writing and newsletters | | |
| 116 | - | | Software | Apps, plugins, tools | | |
| 117 | - | | Art | Visual art, illustration | | |
| 118 | - | | Video | Video content, tutorials | | |
| 119 | - | | Writing | Long-form fiction, non-fiction | | |
| 120 | - | | Photography | Photo sets, stock photography | | |
| 121 | - | | Education | Courses, tutorials | | |
| 122 | - | | Games | Indie games, mods, TTRPG content | | |
| 123 | - | | Comics | Webcomics, graphic novels | | |
| 124 | - | ||
| 125 | - | ### Visibility | |
| 126 | - | ||
| 127 | - | Projects start as **drafts**. Draft projects and their items are invisible to everyone except you. | |
| 128 | - | ||
| 129 | - | To publish, set visibility to **Public**. All published items within the project become discoverable. | |
| 130 | - | ||
| 131 | - | ### Organizing Items | |
| 132 | - | ||
| 133 | - | Items are ordered within a project. You can reorder them from the project dashboard. Each item belongs to exactly one project. | |
| 134 | - | ||
| 135 | - | ### Blog | |
| 136 | - | ||
| 137 | - | Every project gets a blog. Blog posts use markdown, support drafts, and are included in the project's RSS feed. See [Content Types](./02-content.md) for details. | |
| 138 | - | ||
| 139 | - | ### RSS Feeds | |
| 140 | - | ||
| 141 | - | Each project automatically generates an RSS feed containing published items and blog posts. Fans can subscribe using any feed reader. | |
| 142 | - | ||
| 143 | - | ### Deleting a Project | |
| 144 | - | ||
| 145 | - | Deleting a project removes it and all its items permanently. This cannot be undone. Active subscriptions should be canceled first. | |
| 146 | - | ||
| 147 | - | --- | |
| 148 | - | ||
| 149 | - | ## Security | |
| 150 | - | ||
| 151 | - | Protect your account with multiple layers of authentication. | |
| 152 | - | ||
| 153 | - | ### Two-Factor Authentication (2FA) | |
| 154 | - | ||
| 155 | - | Add time-based one-time passwords as a second factor: | |
| 156 | - | ||
| 157 | - | 1. Go to your account security settings | |
| 158 | - | 2. Scan the QR code with an authenticator app (Google Authenticator, Authy, 1Password, etc.) | |
| 159 | - | 3. Enter the 6-digit code to confirm setup | |
| 160 | - | 4. Save your 10 backup codes somewhere safe | |
| 161 | - | ||
| 162 | - | When 2FA is enabled, you'll enter a code from your authenticator app after your password on each login. | |
| 163 | - | ||
| 164 | - | #### Backup Codes | |
| 165 | - | ||
| 166 | - | You get 10 single-use backup codes at setup. Each code works once. If you lose access to your authenticator app, use a backup code to log in, then reconfigure 2FA. | |
| 167 | - | ||
| 168 | - | ### Passkeys | |
| 169 | - | ||
| 170 | - | Passkeys offer passwordless, phishing-resistant login: | |
| 171 | - | ||
| 172 | - | - Register a passkey from your security settings (fingerprint, Face ID, hardware key) | |
| 173 | - | - Log in by touching your device — no password needed | |
| 174 | - | - Multiple passkeys supported (register your phone, laptop, and a hardware key) | |
| 175 | - | - Phishing-resistant by design — passkeys are bound to the domain | |
| 176 | - | ||
| 177 | - | ### Session Management | |
| 178 | - | ||
| 179 | - | View and control active sessions: | |
| 82 | + | ## What Kind of Creator Are You? | |
| 180 | 83 | ||
| 181 | - | - See all active sessions with device type, IP address, and last activity | |
| 182 | - | - Revoke individual sessions (log out a specific device) | |
| 183 | - | - Revoke all other sessions at once (nuclear option) | |
| 84 | + | The two decisions that matter most early on are **how to structure your projects** and **how to price your work**. Both depend on what you're making. | |
| 184 | 85 | ||
| 185 | - | ### Account Lockout | |
| 86 | + | ### Musicians | |
| 186 | 87 | ||
| 187 | - | After 5 failed login attempts, your account locks for 15 minutes. During lockout: | |
| 88 | + | One project per album or EP. Audio items for tracks, ordered by track number. Set the project cover art to your album artwork — it propagates to tracks that don't have their own. Use tags for genre and mood. | |
| 188 | 89 | ||
| 189 | - | - Login attempts are rejected regardless of password | |
| 190 | - | - An email is sent with a bypass link (proves you control the email) | |
| 191 | - | - Lockout clears automatically after 15 minutes | |
| 90 | + | If you release singles between albums, a "Singles" project works as a catch-all. | |
| 192 | 91 | ||
| 193 | - | ### New Device Notifications | |
| 92 | + | Pricing: Pay-what-you-want with a minimum works well for music. Fans who want to support you will pay above minimum. Free streaming with paid downloads is another option. | |
| 194 | 93 | ||
| 195 | - | Opt in to receive an email whenever a new device logs into your account. Useful for detecting unauthorized access early. | |
| 94 | + | ### Podcasters | |
| 196 | 95 | ||
| 197 | - | ### Password Breach Checking | |
| 96 | + | One project per show. Audio items for episodes. Your project RSS feed (`/p/project-name/rss`) works as a podcast feed — submit it to Apple Podcasts, Spotify, etc. Use chapters for timestamp navigation within episodes. | |
| 198 | 97 | ||
| 199 | - | On signup and password change, your password is checked against the Have I Been Pwned database using k-anonymity (your password is never sent to HIBP). If your password appears in known breaches, you'll get a warning. It's advisory — you can proceed, but you should pick a stronger password. | |
| 98 | + | Pricing: Most podcasts are free. Use subscriptions for bonus episodes or early access. | |
| 200 | 99 | ||
| 201 | - | --- | |
| 100 | + | ### Software Developers | |
| 202 | 101 | ||
| 203 | - | ## Profile & Customization | |
| 102 | + | One project per product. Digital items for releases, using versioned uploads with changelogs. Enable license keys if your software needs activation. Blog posts for release notes and tutorials. | |
| 204 | 103 | ||
| 205 | - | Your public presence on the platform. | |
| 104 | + | Pricing: Fixed price for the product, with free updates via versioning. License keys for per-seat or per-machine licensing. | |
| 206 | 105 | ||
| 207 | - | ### Your Profile Page | |
| 106 | + | ### Writers | |
| 208 | 107 | ||
| 209 | - | Every account gets a profile at `/u/yourname`. It shows: | |
| 108 | + | One project per book or series. Text items for chapters (subscriber-only while in progress, then bundled as a complete work). Blog posts for newsletter-style updates. | |
| 210 | 109 | ||
| 211 | - | - Display name and username | |
| 212 | - | - Bio | |
| 213 | - | - Avatar and cover image | |
| 214 | - | - Published projects and items | |
| 215 | - | - Custom links | |
| 216 | - | - Follower count | |
| 110 | + | Pricing: Subscriptions for serialized content. Fixed price for complete works. Free blog posts to build audience. | |
| 217 | 111 | ||
| 218 | - | ### Display Name | |
| 112 | + | ### Visual Artists & Photographers | |
| 219 | 113 | ||
| 220 | - | Your display name appears on your profile, in search results, and on item pages. It can be different from your username. Update it from account settings. | |
| 114 | + | One project per collection or series. Image or digital items per piece. Tags for style, medium, and subject. | |
| 221 | 115 | ||
| 222 | - | ### Bio | |
| 116 | + | Pricing: Fixed price for high-resolution downloads. Free previews at reduced resolution. | |
| 223 | 117 | ||
| 224 | - | A short description shown on your profile page. Keep it concise — this is the first thing fans see. | |
| 118 | + | ### Game Developers | |
| 225 | 119 | ||
| 226 | - | ### Avatar & Cover Image | |
| 120 | + | One project per game. Digital items for builds (versioned uploads). Blog posts for devlogs. Use download codes for press and review copies. | |
| 227 | 121 | ||
| 228 | - | Upload a profile avatar and a cover image for your profile page. Images are resized automatically. | |
| 122 | + | These aren't rules. Experiment, see what works, adjust. You can always restructure later. | |
| 229 | 123 | ||
| 230 | - | ### Custom Links | |
| 124 | + | ## Your First Week | |
| 231 | 125 | ||
| 232 | - | Add external links to your profile — your website, social media, other platforms. Each link has a title and URL. Links appear on your profile page. | |
| 126 | + | After your first publish, here's what to focus on: | |
| 233 | 127 | ||
| 234 | - | Manage links from your Dashboard. Add, edit, reorder, or remove links at any time. | |
| 128 | + | 1. **Fill out your profile.** Bio, avatar, header image, links. This is your storefront. See [Profile](./profile.md). | |
| 129 | + | 2. **Set up security.** Enable two-factor authentication and save your backup codes. See [Security](../tech/security.md). | |
| 130 | + | 3. **Share your link.** Post your profile URL or project URL wherever your audience is. | |
| 131 | + | 4. **Set up RSS cross-posting.** Connect your RSS feed to social media or newsletter tools. See [RSS](./rss.md). | |
| 132 | + | 5. **Fill in metadata.** Good titles, descriptions, tags, and cover art make your content discoverable and shareable. See [Metadata](./metadata.md). | |
| 235 | 133 | ||
| 236 | - | ### Username | |
| 134 | + | ## See Also | |
| 237 | 135 | ||
| 238 | - | Your username is set at signup and determines your profile URL (`/u/yourname`). Choose carefully — it's part of your public identity on the platform. | |
| 136 | + | - [Content Types](./02-content.md) — Items, uploads, and organization | |
| 137 | + | - [Selling & Pricing](./03-selling.md) — Pricing models and payment flow | |
| 138 | + | - [Best Practices](./best-practices.md) — Strategy and audience building |
| @@ -1,27 +1,26 @@ | |||
| 1 | 1 | # Content | |
| 2 | 2 | ||
| 3 | - | Everything about creating, uploading, and managing content on the platform. | |
| 3 | + | Creating, uploading, and managing content on the platform. | |
| 4 | 4 | ||
| 5 | - | --- | |
| 5 | + | ## Choosing What to Create | |
| 6 | 6 | ||
| 7 | - | ## Items | |
| 7 | + | Before diving into the details — what are you making, and how should fans experience it? | |
| 8 | + | ||
| 9 | + | | If you're making... | Use this | Why | | |
| 10 | + | |---------------------|----------|-----| | |
| 11 | + | | An album or EP | Audio items in a project | In-browser player, cover art, track ordering | | |
| 12 | + | | A podcast | Audio items with chapters | RSS-compatible, timestamp navigation | | |
| 13 | + | | A novel or essay collection | Text items in a project | Markdown editor, clean reading view | | |
| 14 | + | | A newsletter | Blog posts | Free, included in RSS, separate from paid items | | |
| 15 | + | | Software or plugins | Digital items with versioning | Upload new versions, license key support | | |
| 16 | + | | Sample packs or presets | Digital items | Any file format, download tracking | | |
| 17 | + | | A course | Mixed item types in a project | Combine text lessons, audio, and downloads | | |
| 8 | 18 | ||
| 9 | - | Items are individual pieces of content — a song, an article, a software release, a file download. | |
| 19 | + | Don't overthink the type choice — pick what matches the content. The type determines which player/viewer fans get and what features are available (chapters, versioning, etc.). | |
| 10 | 20 | ||
| 11 | - | ### Item Types | |
| 21 | + | ## Items | |
| 12 | 22 | ||
| 13 | - | | Type | Content | Player/Viewer | Chapters | Versions | | |
| 14 | - | |------|---------|--------------|----------|----------| | |
| 15 | - | | **Audio** | MP3, WAV, FLAC, OGG, AAC, AIFF | In-browser streaming player | Yes | Yes | | |
| 16 | - | | **Text** | Markdown | Clean reading view | No | No | | |
| 17 | - | | **Digital** | Any file (ZIP, DMG, EXE, PDF, etc.) | Download link | No | Yes | | |
| 18 | - | | **Video** | Video files | Coming soon | No | Yes | | |
| 19 | - | | **Image** | Image files | Gallery view | No | No | | |
| 20 | - | | **Plugin** | Audio plugins (VST, AU, CLAP) | Download link | No | Yes | | |
| 21 | - | | **Preset** | Presets and patches | Download link | No | Yes | | |
| 22 | - | | **Sample** | Sample packs | Download link | No | Yes | | |
| 23 | - | | **Course** | Educational content | Course viewer | No | Yes | | |
| 24 | - | | **Template** | Templates and themes | Download link | No | Yes | | |
| 23 | + | Items are individual pieces of content — a song, an article, a software release, a file download. Ten item types are available (Audio, Text, Digital, Video, Image, Plugin, Preset, Sample, Course, Template). See [Items](./items.md) for the full type matrix with player/viewer support, chapters, and versioning. | |
| 25 | 24 | ||
| 26 | 25 | Choose the type when creating the item. It cannot be changed afterward. | |
| 27 | 26 | ||
| @@ -37,10 +36,10 @@ Choose the type when creating the item. It cannot be changed afterward. | |||
| 37 | 36 | From the item settings, you can update: | |
| 38 | 37 | ||
| 39 | 38 | - **Title**: Display name | |
| 40 | - | - **Description**: Shown on the item page | |
| 39 | + | - **Description**: Shown on the item page (supports markdown) | |
| 41 | 40 | - **Price**: Free, fixed amount, or pay-what-you-want (see [Pricing](./03-selling.md#pricing-models)) | |
| 42 | 41 | - **Tags**: Hierarchical tags for discovery | |
| 43 | - | - **Cover image**: Displayed on the item card | |
| 42 | + | - **Cover image**: Displayed on the item card and in social previews | |
| 44 | 43 | ||
| 45 | 44 | ### Publishing | |
| 46 | 45 | ||
| @@ -53,44 +52,41 @@ Published items appear on your profile, in search results, and in RSS feeds. | |||
| 53 | 52 | ||
| 54 | 53 | ### Scheduling | |
| 55 | 54 | ||
| 56 | - | Set a future publish date to schedule content releases. The item becomes visible automatically at the scheduled time. | |
| 55 | + | Set a future publish date to schedule content releases. The item becomes visible automatically at the scheduled time. Useful for coordinating release dates across time zones. | |
| 57 | 56 | ||
| 58 | 57 | ### Bulk Operations | |
| 59 | 58 | ||
| 60 | - | From the project dashboard, you can perform bulk operations on items: publish, unpublish, or delete multiple items at once. | |
| 59 | + | From the project dashboard, you can publish, unpublish, or delete multiple items at once. | |
| 61 | 60 | ||
| 62 | 61 | ### Duplicating an Item | |
| 63 | 62 | ||
| 64 | - | Duplicate an item to create a copy with the same settings and metadata. Useful for creating similar items quickly. Content (files, text) is not duplicated — only metadata. | |
| 63 | + | Duplicate an item to create a copy with the same settings and metadata. Content (files, text) is not duplicated — only metadata. Useful for creating similar items quickly. | |
| 65 | 64 | ||
| 66 | 65 | ### Deleting an Item | |
| 67 | 66 | ||
| 68 | 67 | Deleting an item removes it permanently. Fans who purchased it will lose access. Active download codes and license keys for the item are invalidated. | |
| 69 | 68 | ||
| 70 | - | --- | |
| 71 | - | ||
| 72 | 69 | ## Content Types | |
| 73 | 70 | ||
| 74 | - | What you can upload and how it's delivered to fans. | |
| 75 | - | ||
| 76 | 71 | ### Audio | |
| 77 | 72 | ||
| 78 | - | Upload audio files in MP3, WAV, FLAC, or OGG format. Each audio item gets: | |
| 73 | + | Upload in MP3, WAV, FLAC, or OGG format. Each audio item gets: | |
| 74 | + | ||
| 75 | + | - **In-browser player**: Stream without downloading | |
| 76 | + | - **Cover image**: Album art in the player and on the item card | |
| 77 | + | - **Chapters**: Timestamp markers for navigating within the track | |
| 78 | + | - **Downloads**: Fans download the original file after purchase | |
| 79 | 79 | ||
| 80 | - | - **In-browser player**: Stream without downloading. Custom player with playback controls. | |
| 81 | - | - **Cover image**: Album art displayed in the player and on the item card. | |
| 82 | - | - **Chapters**: Timestamp markers for navigating within the track. See [Chapters](#chapters) below. | |
| 83 | - | - **Downloads**: Fans can download the original file after purchase. | |
| 80 | + | Per-file size limits depend on your [pricing tier](./pricing.md) (10MB for Basic, 500MB for Small Files, 20GB for Big Files and Streaming). | |
| 84 | 81 | ||
| 85 | - | Upload your audio file from the item's content tab. Per-file size limits depend on your [pricing tier](./pricing.md) (10MB for Basic, 500MB for Small Files, 20GB for Big Files and Streaming). | |
| 82 | + | Metadata (title, artist, track number, genre, cover art) is auto-extracted from uploaded files. See [Metadata & SEO](./metadata.md) for details. | |
| 86 | 83 | ||
| 87 | 84 | ### Text | |
| 88 | 85 | ||
| 89 | - | Write directly in the editor using markdown (a simple formatting language — **bold**, *italic*, links, headers, and lists). Features include: | |
| 86 | + | Write directly in the editor using markdown. Features: | |
| 90 | 87 | ||
| 91 | 88 | - **Live preview**: See rendered output as you type | |
| 92 | - | - **Word count**: Automatically calculated | |
| 93 | - | - **Reading time**: Estimated based on word count | |
| 89 | + | - **Word count and reading time**: Automatically calculated | |
| 94 | 90 | - **Full markdown support**: Headers, lists, code blocks, links, images, tables | |
| 95 | 91 | ||
| 96 | 92 | Text content is stored and rendered on the platform. Fans read it without ads, recommendations, or sidebars. | |
| @@ -100,9 +96,9 @@ Text content is stored and rendered on the platform. Fans read it without ads, r | |||
| 100 | 96 | Upload any file type. Digital items support: | |
| 101 | 97 | ||
| 102 | 98 | - **Any format**: ZIP, DMG, EXE, PDF, images, fonts — whatever you make | |
| 103 | - | - **Versioned releases**: Upload new versions with changelogs. See [Versions](#versions) below. | |
| 99 | + | - **Versioned releases**: Upload new versions with changelogs | |
| 104 | 100 | - **Download tracking**: See how many times each version has been downloaded | |
| 105 | - | - **License keys**: Auto-generated keys for software products. See [Pricing](./03-selling.md#license-keys). | |
| 101 | + | - **License keys**: Auto-generated keys for software products | |
| 106 | 102 | ||
| 107 | 103 | ### Blog Posts | |
| 108 | 104 | ||
| @@ -115,79 +111,35 @@ Every project includes a blog. Blog posts use the same markdown editor as text i | |||
| 115 | 111 | ||
| 116 | 112 | Blog posts are always free. Use them for updates, announcements, liner notes, or changelogs. | |
| 117 | 113 | ||
| 118 | - | --- | |
| 119 | - | ||
| 120 | - | ## Versions | |
| 121 | - | ||
| 122 | - | Track releases of digital and audio items with version numbers and changelogs. | |
| 123 | - | ||
| 124 | - | ### Creating a Version | |
| 125 | - | ||
| 126 | - | From the item dashboard, add a new version: | |
| 127 | - | ||
| 128 | - | - **Version number** (required): Any string up to 50 characters (e.g., `1.0.0`, `v2`, `2024-03-01`) | |
| 129 | - | - **Changelog** (optional): Up to 10,000 characters describing what changed | |
| 130 | - | - **File**: Optionally attach a new file to this version | |
| 131 | - | ||
| 132 | - | ### Current Version | |
| 133 | - | ||
| 134 | - | Only one version per item is the "current" version — the one fans download by default. When you upload a new version, it automatically becomes current. The switch is instant; there's no gap where fans see the wrong version. | |
| 135 | - | ||
| 136 | - | ### Changelogs | |
| 137 | - | ||
| 138 | - | Use changelogs to tell fans what changed. Changelogs are plain text, up to 10,000 characters. Keep them concise — fans want to know what's new, not read a novel. | |
| 139 | - | ||
| 140 | - | ### Download Tracking | |
| 141 | - | ||
| 142 | - | Each version tracks its download count independently. This helps you see which versions fans are using. | |
| 143 | - | ||
| 144 | - | ### Listing Versions | |
| 145 | - | ||
| 146 | - | The public version list is ordered newest-first (by creation date). Only published items show their version list — draft items keep versions hidden. | |
| 147 | - | ||
| 148 | - | ### Version Validation | |
| 149 | - | ||
| 150 | - | | Field | Rule | | |
| 151 | - | |-------|------| | |
| 152 | - | | Version number | 1-50 characters, required | | |
| 153 | - | | Changelog | 0-10,000 characters, optional | | |
| 154 | - | ||
| 155 | - | --- | |
| 156 | - | ||
| 157 | - | ## Chapters | |
| 158 | - | ||
| 159 | - | Chapters are timestamp markers for audio items. They let fans jump to specific sections within a track. | |
| 160 | - | ||
| 161 | - | ### Creating Chapters | |
| 162 | - | ||
| 163 | - | From the item dashboard, add chapters to any audio item: | |
| 114 | + | ## Versions & Chapters | |
| 164 | 115 | ||
| 165 | - | - **Title** (required): 1-200 characters | |
| 166 | - | - **Start seconds** (required): Timestamp where the chapter begins (e.g., `0`, `30.5`, `125`) | |
| 167 | - | - **Sort order**: Number controlling display order (default: 0) | |
| 116 | + | Digital and audio items support **versioned releases** with changelogs and download tracking. Audio items also support **chapters** (timestamp markers for in-track navigation). See [Items](./items.md) for full details on both features. | |
| 168 | 117 | ||
| 169 | - | ### Editing and Deleting | |
| 118 | + | ## Organizing Your Content | |
| 170 | 119 | ||
| 171 | - | Update any chapter's title, timestamp, or sort order. Delete chapters you no longer need. Changes take effect immediately. | |
| 120 | + | ### Three Layers | |
| 172 | 121 | ||
| 173 | - | ### Ordering | |
| 122 | + | 1. **Profile** (`/u/you`) — Your public page, shows all your projects | |
| 123 | + | 2. **Projects** (`/p/project-name`) — Albums, series, collections | |
| 124 | + | 3. **Items** (`/i/uuid`) — Individual tracks, files, posts | |
| 174 | 125 | ||
| 175 | - | Chapters are displayed sorted by sort order first, then by start time. If all chapters have the same sort order, they appear in timestamp order. | |
| 126 | + | ### Tags vs Projects | |
| 176 | 127 | ||
| 177 | - | Create chapters in any order — the system sorts them for display. | |
| 128 | + | | Use Tags For | Use Projects For | | |
| 129 | + | |--------------|------------------| | |
| 130 | + | | Genre, mood, style | Albums, series | | |
| 131 | + | | Cross-cutting attributes | Sequential content | | |
| 132 | + | | Search and filtering | Direct navigation | | |
| 133 | + | | Multiple per item | One per item | | |
| 178 | 134 | ||
| 179 | - | ### Ownership | |
| 135 | + | An item belongs to one project but can have many tags. Example: an ambient track lives in your "Night Sessions" album (project) but is tagged `ambient`, `electronic`, and `downtempo`. | |
| 180 | 136 | ||
| 181 | - | Only the item's creator can add, edit, or delete chapters. | |
| 137 | + | See [Tagging System](./tags.md) for the full tag specification. | |
| 182 | 138 | ||
| 183 | - | ### Chapter Validation | |
| 139 | + | ### Featured Content | |
| 184 | 140 | ||
| 185 | - | | Field | Rule | | |
| 186 | - | |-------|------| | |
| 187 | - | | Title | 1-200 characters, required | | |
| 188 | - | | Start seconds | Non-negative number, required | | |
| 189 | - | | Sort order | Whole number, default 0 | | |
| 141 | + | Pin items or projects to your profile page to highlight new releases or showcase your best work. Rearrange pinned content by dragging. | |
| 190 | 142 | ||
| 191 | - | ### Draft Items | |
| 143 | + | ### Archive | |
| 192 | 144 | ||
| 193 | - | Chapter lists are only visible for published items. If the item is still a draft, chapters won't be shown to anyone except you. You can still manage chapters while the item is in draft. | |
| 145 | + | Hide old content without deleting. Archived items don't appear in search, but direct links still work. Unarchive anytime. |
| @@ -1,239 +1,93 @@ | |||
| 1 | - | # Selling & Audience | |
| 1 | + | # Selling & Pricing | |
| 2 | 2 | ||
| 3 | - | Set prices, accept payments, communicate with fans, and track performance. | |
| 3 | + | Set prices, manage purchases, and get paid. | |
| 4 | 4 | ||
| 5 | - | --- | |
| 5 | + | ## Pricing Models | |
| 6 | 6 | ||
| 7 | - | ## Pricing & Monetization | |
| 7 | + | Three models available per item: **free**, **fixed price**, and **pay-what-you-want** (with optional minimum). Set prices when creating or editing an item. See [Pricing](./pricing.md) for detailed guidance on choosing a model. | |
| 8 | 8 | ||
| 9 | - | ### Pricing Models | |
| 10 | - | ||
| 11 | - | | Model | How It Works | | |
| 12 | - | |-------|-------------| | |
| 13 | - | | **Free** | Price set to $0. Fans add to library without payment. | | |
| 14 | - | | **Fixed price** | You set the price. Fan pays exactly that amount. | | |
| 15 | - | | **Pay-what-you-want** | Fan chooses the amount. You can set a minimum. | | |
| 16 | - | ||
| 17 | - | Set prices per item when creating or editing. | |
| 18 | - | ||
| 19 | - | ### Subscriptions | |
| 9 | + | ## Subscriptions | |
| 20 | 10 | ||
| 21 | 11 | Offer recurring monthly subscriptions per project: | |
| 22 | 12 | ||
| 23 | 13 | - Create multiple tiers with different prices | |
| 24 | 14 | - Toggle tiers active/inactive without deleting them | |
| 25 | - | - Fans subscribe through Stripe Checkout | |
| 26 | - | - Stripe handles billing, renewals, and cancellations | |
| 15 | + | - Fans subscribe through the payment checkout | |
| 16 | + | - The payment processor handles billing, renewals, and cancellations | |
| 27 | 17 | ||
| 28 | - | Subscription status changes (active, past due, canceled, renewed) sync from Stripe via webhooks and update your dashboard automatically. | |
| 18 | + | Subscription status changes (active, past due, canceled, renewed) sync via webhooks and update your dashboard automatically. | |
| 29 | 19 | ||
| 30 | - | ### License Keys | |
| 20 | + | ## License Keys | |
| 31 | 21 | ||
| 32 | 22 | For software products, license keys are generated automatically on purchase: | |
| 33 | 23 | ||
| 34 | - | - **Configurable activation limits**: Set how many machines can activate a key | |
| 35 | - | - **Machine tracking**: See which machines have activated | |
| 36 | - | - **Public validation endpoint**: Your software can phone home to verify keys | |
| 37 | - | - **Revocable**: Deactivate keys if needed | |
| 24 | + | - **Configurable activation limits** — Set how many machines can activate a key | |
| 25 | + | - **Machine tracking** — See which machines have activated | |
| 26 | + | - **Public validation endpoint** — Your software can verify keys against the server | |
| 27 | + | - **Revocable** — Deactivate keys if needed | |
| 38 | 28 | ||
| 39 | 29 | License keys appear in the fan's library after purchase. | |
| 40 | 30 | ||
| 41 | - | ### Discount Codes | |
| 31 | + | ## Discount Codes | |
| 42 | 32 | ||
| 43 | 33 | Create promotional codes with: | |
| 44 | 34 | ||
| 45 | 35 | - **Percentage or fixed amount** off the price | |
| 46 | - | - **Scope**: Apply to a specific item or all your items | |
| 47 | - | - **Usage limits**: Cap how many times a code can be used | |
| 48 | - | - **Expiration dates**: Codes stop working after a date you set | |
| 49 | - | - **Auto-apply via URL**: Share a link with `?discount=CODE` to pre-fill | |
| 36 | + | - **Scope** — Apply to a specific item or all your items | |
| 37 | + | - **Usage limits** — Cap how many times a code can be used | |
| 38 | + | - **Expiration dates** — Codes stop working after a date you set | |
| 39 | + | - **Auto-apply via URL** — Share a link with `?discount=CODE` to pre-fill the code | |
| 50 | 40 | ||
| 51 | - | ### Download Codes | |
| 41 | + | ## Download Codes | |
| 52 | 42 | ||
| 53 | 43 | Generate single-use codes for free access: | |
| 54 | 44 | ||
| 55 | - | - **Batch generation**: Create many codes at once | |
| 56 | - | - **Optional max uses**: Limit how many times each code works | |
| 57 | - | - **Optional expiration**: Codes expire after a date | |
| 58 | - | - **Useful for**: Press copies, review access, promotional giveaways | |
| 45 | + | - **Batch generation** — Create many codes at once | |
| 46 | + | - **Optional max uses** — Limit how many times each code works | |
| 47 | + | - **Optional expiration** — Codes expire after a date | |
| 48 | + | - **Useful for** — Press copies, review access, promotional giveaways | |
| 59 | 49 | ||
| 60 | - | ### Payment Flow | |
| 50 | + | ## Payment Flow | |
| 61 | 51 | ||
| 62 | - | All payments go through Stripe Connect: | |
| 52 | + | All payments go through the payment processor: | |
| 63 | 53 | ||
| 64 | 54 | ``` | |
| 65 | - | Fan pays → Stripe → Creator's connected account | |
| 55 | + | Fan pays → Payment processor → Creator's connected account | |
| 66 | 56 | ``` | |
| 67 | 57 | ||
| 68 | - | We take 0% platform fee. Only Stripe's processing fee (~3%) applies. | |
| 69 | - | ||
| 70 | - | ### Refunds | |
| 71 | - | ||
| 72 | - | Refund policies are set by individual creators. Refunds are processed through Stripe. | |
| 73 | - | ||
| 74 | - | --- | |
| 75 | - | ||
| 76 | - | ## Audience & Communication | |
| 77 | - | ||
| 78 | - | Build and maintain your fan base. Communicate directly. | |
| 79 | - | ||
| 80 | - | ### Follows | |
| 81 | - | ||
| 82 | - | Fans can follow your user account, individual projects, or tags. Follows power: | |
| 83 | - | ||
| 84 | - | - **Personal feed**: Fans see new items and blog posts from followed creators | |
| 85 | - | - **Follower counts**: Visible on your profile and project pages | |
| 86 | - | ||
| 87 | - | Following is free and doesn't require a purchase. | |
| 88 | - | ||
| 89 | - | ### Contact Sharing | |
| 90 | - | ||
| 91 | - | When a fan purchases from you, they can opt in to share their email address. This creates a direct connection you can use outside the platform. | |
| 92 | - | ||
| 93 | - | #### How It Works | |
| 94 | - | ||
| 95 | - | 1. At checkout, the fan sees a "Share your email with this creator" option | |
| 96 | - | 2. If they opt in, their email appears in your contacts | |
| 97 | - | 3. Fans can revoke sharing at any time from their account settings | |
| 98 | - | 4. Revocation takes effect immediately | |
| 58 | + | We take 0% platform fee. Only the payment processing fee (~3%) applies. | |
| 99 | 59 | ||
| 100 | - | #### For Fans | |
| 60 | + | ## Refunds | |
| 101 | 61 | ||
| 102 | - | You control your contact info. Shared your email and changed your mind? Revoke it. The creator loses access immediately. | |
| 62 | + | Refund policies are set by individual creators. Refunds are processed through the payment processor. | |
| 103 | 63 | ||
| 104 | - | #### For Creators | |
| 64 | + | ## Notifications | |
| 105 | 65 | ||
| 106 | - | Respect revocations. Don't export contact lists and continue emailing fans who revoked. The platform enforces revocation — build trust by honoring it. | |
| 107 | - | ||
| 108 | - | ### Broadcast Email | |
| 109 | - | ||
| 110 | - | Send a plain-text email update to all your followers. | |
| 111 | - | ||
| 112 | - | - One broadcast per 24 hours (rate-limited to prevent spam) | |
| 113 | - | - Every broadcast includes a signed unsubscribe URL so fans can opt out | |
| 114 | - | - Compose and send from your creator dashboard | |
| 115 | - | ||
| 116 | - | ### RSS | |
| 117 | - | ||
| 118 | - | Every project generates an RSS feed automatically. Fans subscribe via any feed reader. Feeds include both published items and blog posts. | |
| 119 | - | ||
| 120 | - | Fans also get a personal feed across all creators and projects they follow. | |
| 121 | - | ||
| 122 | - | ### Notifications | |
| 123 | - | ||
| 124 | - | Email notifications for key account events, each individually toggleable in your account settings: | |
| 66 | + | Email notifications for sales and account events, each individually toggleable in your account settings: | |
| 125 | 67 | ||
| 126 | 68 | - **Sale alerts** — When someone purchases your content | |
| 127 | 69 | - **Follower alerts** — When someone follows you or your projects | |
| 128 | 70 | - **New release announcements** — When creators you follow publish new items | |
| 129 | 71 | - **New device login warnings** — When your account is accessed from a new device | |
| 130 | 72 | ||
| 131 | - | --- | |
| 132 | - | ||
| 133 | - | ## Analytics & Data | |
| 134 | - | ||
| 135 | - | Track performance, review transactions, and export everything. | |
| 136 | - | ||
| 137 | - | ### Dashboard Analytics | |
| 138 | - | ||
| 139 | - | Your creator dashboard shows: | |
| 140 | - | ||
| 141 | - | - Revenue and sales counts per project | |
| 142 | - | - Recent transactions | |
| 143 | - | - Follower counts | |
| 144 | - | ||
| 145 | - | The dashboard includes revenue charting with selectable time ranges (7 days, 30 days, 90 days, all time), period-over-period comparison showing percentage changes, and per-project revenue breakdown. | |
| 146 | - | ||
| 147 | - | ### Transaction History | |
| 73 | + | ## Data Export | |
| 148 | 74 | ||
| 149 | - | View complete purchase and sales history from your Dashboard. Transactions include: | |
| 150 | - | ||
| 151 | - | - Buyer info (username, email if shared) | |
| 152 | - | - Item and project | |
| 153 | - | - Amount paid | |
| 154 | - | - Payment status | |
| 155 | - | - Date | |
| 156 | - | ||
| 157 | - | Filter by project, date range, or status. | |
| 158 | - | ||
| 159 | - | ### Data Exports | |
| 160 | - | ||
| 161 | - | Export all your data at any time. Five export types are available: | |
| 75 | + | Export all your data at any time: | |
| 162 | 76 | ||
| 163 | 77 | | Export | Format | Contents | | |
| 164 | 78 | |--------|--------|----------| | |
| 165 | - | | **Projects** | JSON (structured data file) | All project metadata, settings, categories | | |
| 166 | - | | **Items** | JSON (structured data file) | All items with metadata, pricing, tags | | |
| 167 | - | | **Blog posts** | JSON (structured data file) | All blog posts with content and publish status | | |
| 168 | - | | **Sales** | CSV (spreadsheet file — opens in Excel, Google Sheets, etc.) | Complete sales transaction history | | |
| 169 | - | | **Purchases** | CSV (spreadsheet file) | Your purchase history as a fan | | |
| 170 | - | ||
| 171 | - | Exports are generated on demand and download as files. No limits on how often you export. | |
| 172 | - | ||
| 173 | - | ### Your Data Rights | |
| 174 | - | ||
| 175 | - | You own your data. We facilitate hosting and delivery, but everything is exportable. If you leave, you take it all with you. See [How We Work](../about/how-we-work.md) for the full data ownership commitment. | |
| 176 | - | ||
| 177 | - | --- | |
| 178 | - | ||
| 179 | - | ## For Fans | |
| 180 | - | ||
| 181 | - | Everything you can do as a fan on Makenot.work — no creator subscription needed. | |
| 182 | - | ||
| 183 | - | ### Discover | |
| 184 | - | ||
| 185 | - | Browse content on the Discover page: | |
| 186 | - | ||
| 187 | - | - **Search**: Search across titles, descriptions, and usernames | |
| 188 | - | - **Filters**: Filter by item type (Audio, Text, Digital), price range, tags, and project category | |
| 189 | - | - **Sort**: Newest, price (low/high), or popularity | |
| 190 | - | ||
| 191 | - | ### Purchasing | |
| 192 | - | ||
| 193 | - | When you find something you want: | |
| 194 | - | ||
| 195 | - | 1. Click to view the item | |
| 196 | - | 2. Click **Buy** (or **Add to Library** for free items) | |
| 197 | - | 3. For paid items, complete checkout through Stripe | |
| 198 | - | 4. The item appears in your Library immediately | |
| 199 | - | ||
| 200 | - | #### Pay-What-You-Want | |
| 201 | - | ||
| 202 | - | Some items let you choose your price. You'll see a minimum (which can be $0) and can pay more if you want to support the creator. | |
| 203 | - | ||
| 204 | - | #### Contact Sharing | |
| 205 | - | ||
| 206 | - | At checkout, you can opt in to share your email with the creator. This is completely optional. If you share and later change your mind, you can revoke access anytime. | |
| 207 | - | ||
| 208 | - | ### Library | |
| 209 | - | ||
| 210 | - | Your Library holds everything you've purchased or claimed: | |
| 211 | - | ||
| 212 | - | - **Download files**: Original quality, no DRM | |
| 213 | - | - **View license keys**: For software purchases | |
| 214 | - | - **Stream audio**: Play in the browser | |
| 215 | - | - **Read text**: Clean reading view | |
| 216 | - | ||
| 217 | - | Access your Library from the navigation bar when logged in. | |
| 218 | - | ||
| 219 | - | ### Following | |
| 220 | - | ||
| 221 | - | Follow creators, projects, or tags to build a personalized feed: | |
| 222 | - | ||
| 223 | - | - **Follow a creator**: See all their new content | |
| 224 | - | - **Follow a project**: See new items and blog posts from that project | |
| 225 | - | - **Follow a tag**: See new items tagged with topics you care about | |
| 226 | - | ||
| 227 | - | Your feed shows new content from everything you follow. | |
| 228 | - | ||
| 229 | - | ### Contact Sharing & Revocation | |
| 230 | - | ||
| 231 | - | If you shared your email with a creator during a purchase and want to revoke it: | |
| 79 | + | | **Projects** | JSON | All project metadata, settings, categories | | |
| 80 | + | | **Items** | JSON | All items with metadata, pricing, tags | | |
| 81 | + | | **Blog posts** | JSON | All blog posts with content and publish status | | |
| 82 | + | | **Sales** | CSV | Complete sales transaction history | | |
| 83 | + | | **Purchases** | CSV | Your purchase history as a fan | | |
| 232 | 84 | ||
| 233 | - | - The creator immediately loses access to your email | |
| 234 | - | - Revocation is permanent until you make a new purchase and opt in again | |
| 235 | - | - You can revoke even if you made multiple purchases from the same creator | |
| 85 | + | No limits on how often you export. You own your data — see [How We Work](../about/how-we-work.md). | |
| 236 | 86 | ||
| 237 | - | ### Free Accounts | |
| 87 | + | ## See Also | |
| 238 | 88 | ||
| 239 | - | Fan accounts are free. You never pay the platform — only for content you choose to buy. | |
| 89 | + | - [Pricing](./pricing.md) — Detailed pricing model guidance | |
| 90 | + | - [Receiving Payouts](./payouts.md) — How and when you get paid | |
| 91 | + | - [Analytics & Dashboard](./analytics.md) — Revenue tracking and stats | |
| 92 | + | - [Best Practices](./best-practices.md) — Pricing strategy and audience building | |
| 93 | + | - [Fan Guide](./fan-guide.md) — The fan perspective on purchasing |
| @@ -1,82 +1,102 @@ | |||
| 1 | 1 | # Analytics & Dashboard | |
| 2 | 2 | ||
| 3 | - | Your dashboard at `/dashboard` shows your projects, content, revenue, transaction count, and follower count. | |
| 3 | + | Your dashboard at `/dashboard` is your home base. It shows your projects, content, revenue, and follower count — the numbers that matter, without the noise. | |
| 4 | 4 | ||
| 5 | - | ## Earnings Summary | |
| 5 | + | ## Earnings Overview | |
| 6 | 6 | ||
| 7 | - | Top-level stat cards with period-over-period comparison: | |
| 7 | + | Three stat cards at the top of your dashboard: | |
| 8 | 8 | ||
| 9 | - | - **Revenue**: Total earnings for selected period, with percentage change vs. prior period | |
| 10 | - | - **Sales**: Transaction count for selected period | |
| 11 | - | - **Followers**: Current follower count with change | |
| 9 | + | - **Revenue** — Total earnings for the selected period, with percentage change vs. the prior period | |
| 10 | + | - **Sales** — Transaction count for the selected period | |
| 11 | + | - **Followers** — Current follower count with change over the period | |
| 12 | 12 | ||
| 13 | 13 | Time range selector: 7 days, 30 days, 90 days, all-time. | |
| 14 | 14 | ||
| 15 | - | ## Revenue Chart | |
| 15 | + | ### Reading the Numbers | |
| 16 | 16 | ||
| 17 | - | Bar chart showing revenue bucketed by time period. Available at both the user level (all projects) and per-project. | |
| 17 | + | **Revenue up, sales flat:** Your average transaction value increased. Fans are buying higher-priced items or paying above minimum on pay-what-you-want. | |
| 18 | 18 | ||
| 19 | - | ## Per-Project Analytics | |
| 19 | + | **Revenue flat, sales up:** More transactions at lower values. Could mean a free item is driving traffic, or a lower-priced item is gaining traction. | |
| 20 | 20 | ||
| 21 | - | Each project has its own analytics tab with: | |
| 21 | + | **Revenue down:** Check if it's seasonal, or if a specific project's sales dropped. Per-project analytics will tell you which. | |
| 22 | 22 | ||
| 23 | - | - Revenue and sales stat cards | |
| 24 | - | - Revenue chart | |
| 25 | - | - Top items by revenue | |
| 23 | + | **Followers increasing:** People are finding you. Check what you published or shared recently — that's what's working. | |
| 26 | 24 | ||
| 27 | - | ## What We Track | |
| 25 | + | ## Revenue Chart | |
| 28 | 26 | ||
| 29 | - | ### Plays/Views | |
| 30 | - | - Total play count per item | |
| 31 | - | - Total download count per item | |
| 27 | + | Bar chart showing revenue over time, bucketed by day, week, or month depending on your selected range. Available at the dashboard level (all projects combined) and per-project. | |
| 32 | 28 | ||
| 33 | - | ### Revenue | |
| 34 | - | - Sales count per item | |
| 35 | - | - Total revenue per project | |
| 29 | + | Look for patterns: regular spikes on release days, gradual decline between releases, or steady baseline revenue from subscriptions. | |
| 36 | 30 | ||
| 37 | - | ## What We Don't Track | |
| 31 | + | ## Per-Project Analytics | |
| 38 | 32 | ||
| 39 | - | - Individual user behavior across sessions | |
| 40 | - | - Browsing patterns | |
| 41 | - | - Device fingerprints | |
| 42 | - | - Cross-site activity | |
| 43 | - | - Demographic profiling | |
| 33 | + | Each project has its own analytics tab showing: | |
| 34 | + | ||
| 35 | + | - Revenue and sales stat cards (same format as the dashboard) | |
| 36 | + | - Revenue chart scoped to that project | |
| 37 | + | - Top items by revenue | |
| 38 | + | ||
| 39 | + | This is where you figure out what resonates. If one project consistently outperforms others, that's signal. If a specific item drives most of a project's revenue, that tells you something about what your audience wants. | |
| 44 | 40 | ||
| 45 | 41 | ## Transaction History | |
| 46 | 42 | ||
| 47 | 43 | Searchable list of all transactions: | |
| 48 | 44 | ||
| 49 | 45 | - Date and time | |
| 50 | - | - Fan (anonymized or named based on their preference) | |
| 46 | + | - Fan (anonymized or named, based on their preference) | |
| 51 | 47 | - Item purchased | |
| 52 | 48 | - Amount paid | |
| 53 | 49 | - Status (completed, refunded) | |
| 54 | 50 | ||
| 55 | - | Export to CSV for your records. | |
| 51 | + | Export to CSV for your records or for import into accounting software. | |
| 56 | 52 | ||
| 57 | - | ## Payout Information | |
| 53 | + | ## Per-Item Stats | |
| 58 | 54 | ||
| 59 | - | Payout details (pending transfers, completed payouts, payout schedule) are managed in your Stripe dashboard. We link to it from the dashboard for convenience. | |
| 55 | + | Each item tracks: | |
| 60 | 56 | ||
| 61 | - | ## Exports | |
| 57 | + | - **Play count** — total streams or views | |
| 58 | + | - **Download count** — total file downloads | |
| 62 | 59 | ||
| 63 | - | Export your project data as JSON from the dashboard (includes items, sales counts, chapters, versions, license keys, and more). Transaction history is available as CSV. | |
| 60 | + | These appear on the item's page in your dashboard. High plays with low purchases might mean the preview is doing its job but the price isn't right, or that the item works better as a free gateway to your paid content. | |
| 64 | 61 | ||
| 65 | - | ## Privacy-First Design | |
| 62 | + | ## Data Exports | |
| 66 | 63 | ||
| 67 | - | Our analytics work without: | |
| 68 | - | - Cookies (beyond session) | |
| 69 | - | - Local storage tracking | |
| 70 | - | - Third-party scripts | |
| 71 | - | - IP address logging (beyond 30 days) | |
| 64 | + | - **Project data** — Export as JSON from the dashboard. Includes items, sales counts, chapters, versions, license keys, and more. | |
| 65 | + | - **Transaction history** — Export as CSV. | |
| 66 | + | - **Contact list** — Fans who opted in to share their email. See [Contact Sharing](./contact-sharing.md). | |
| 72 | 67 | ||
| 73 | - | You get useful data. Fans get privacy. | |
| 68 | + | Everything you put in, you can get out. | |
| 69 | + | ||
| 70 | + | ## Payouts | |
| 71 | + | ||
| 72 | + | Payout details — pending transfers, completed payouts, payout schedule — are managed in your payment processor's dashboard. We link to it from your dashboard for convenience. | |
| 73 | + | ||
| 74 | + | For more on how payouts work, see [Receiving Payouts](./payouts.md). | |
| 75 | + | ||
| 76 | + | ## What We Track (and What We Don't) | |
| 77 | + | ||
| 78 | + | ### We track | |
| 79 | + | ||
| 80 | + | - Play and download counts per item | |
| 81 | + | - Transaction records (who bought what, when, for how much) | |
| 82 | + | - Follower counts | |
| 83 | + | - Aggregate revenue by period | |
| 84 | + | ||
| 85 | + | ### We don't track | |
| 86 | + | ||
| 87 | + | - Individual user behavior across sessions | |
| 88 | + | - Browsing patterns or page views | |
| 89 | + | - Device fingerprints | |
| 90 | + | - Cross-site activity | |
| 91 | + | - Demographic profiling | |
| 74 | 92 | ||
| 75 | - | ## API Access | |
| 93 | + | No analytics cookies. No third-party tracking scripts. No IP address logging beyond 30 days. | |
| 76 | 94 | ||
| 77 | - | Analytics data is available through the dashboard and the project JSON export. | |
| 95 | + | This isn't a limitation — it's the point. Your fans get privacy. You get the numbers that actually matter for running a creative business: what sold, for how much, and whether it's trending up or down. | |
| 78 | 96 | ||
| 79 | 97 | ## See Also | |
| 80 | 98 | ||
| 81 | - | - [Receiving Payouts](./payouts.md) — Stripe payouts and tax info | |
| 99 | + | - [Receiving Payouts](./payouts.md) — Payouts and tax info | |
| 82 | 100 | - [Best Practices](./best-practices.md) — Understanding what resonates | |
| 101 | + | - [Contact Sharing](./contact-sharing.md) — Building direct relationships with fans | |
| 102 | + | - [Data Portability](./03-selling.md#data-export) — Exporting all your data |