Skip to main content

max / makenotwork

Site-docs consolidation, landing page polish, deploy + test harness updates - Consolidate unpublished docs into public (tech/, legal/copyright, payments, transparency) and delete all remaining unpublished/ stubs - Rewrite guide docs for clarity and brevity (getting-started, content, selling, analytics, tiers, profile, metadata, rss, fan-guide, contact-sharing, etc.) - Landing page: social share link partial, pricing page copy tweaks, Stripe disclaimer and purchase flow template updates - Deploy script enhancements, internal API route additions - mnw.js: share/copy helpers, pricing interaction JS - Seed demo data script (seed_demo.sh + seed_demo.sql) - Audit history expanded, audit_review trimmed to current scorecard - Test harness and load runner updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-03-31 16:31 UTC
Commit: 580b8a33e2bf8c4a794503166963ee12bef5d650
Parent: 0ec4ab3
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
A seed_demo.sh +12
@@ -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
A seed_demo.sql +243
@@ -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
M src/lib.rs +3