Skip to main content

max / makenotwork

Update docs: sandbox guide, tips/bundles/splits, Stripe naming, fuzz findings New guide pages: sandbox.md, tips.md, bundles.md, splits.md. Payouts/payments: replace "payment processor" with "Stripe" throughout, add direct links to Stripe docs (global, currencies, payouts, refunds). Getting started: add sandbox link, creator application criteria, Stripe naming. Content protection: disclose text fingerprinting, scope watermark claims. Mailing lists: reword import as roadmap item. Docs index: add sandbox to Getting Started subcategory. Todo: document round 3 fuzz findings (20 fixed), creator trust audit resolutions (doc contradictions, missing docs, trust gaps). MT todo: record code fuzz findings (cross-community auth bypasses, SSRF, panics). Cargo.lock: version bump to 0.4.2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-04-26 19:46 UTC
Commit: 80f412740202c9295ecb0cacfdc16368b70839ba
Parent: be9c4b0
13 files changed, +337 insertions, -40 deletions
@@ -6,6 +6,25 @@ v0.3.2. Audit grade A. 225 tests.
6 6
7 7 ---
8 8
9 + ## Code Fuzz Findings (2026-04-26)
10 +
11 + ### Critical
12 + - [ ] Cross-community post removal via flag: `remove_flagged_post_handler` and `dismiss_flag_handler` verify mod/owner of the URL slug community but act on any `flag_id` without checking it belongs to that community. A mod of community-A can remove posts from community-B. Fix: after fetching the flag row, join through posts→threads→categories to get community_id and verify it matches. (`src/routes/flagging.rs:101,128`)
13 +
14 + ### Serious
15 + - [ ] Cross-community category edit: `edit_category_handler` verifies owner of slug community, then updates any category by UUID. Fix: add `AND community_id = $2` to the UPDATE in `update_category`. (`src/routes/settings.rs:231`, `crates/mt-db/src/mutations.rs:349`)
16 + - [ ] Cross-community tag delete: `delete_tag_handler` verifies owner of slug community, then deletes any tag by UUID. Fix: add `AND community_id = $2` to the DELETE in `delete_tag`. (`src/routes/settings.rs:363`, `crates/mt-db/src/mutations.rs:666`)
17 + - [ ] Search query panics on multi-byte UTF-8: `&q[..200]` panics if byte 200 is mid-character. Fix: use `is_char_boundary` loop to find a safe truncation point. (`src/routes/search.rs:32`)
18 + - [ ] SSRF bypass in link preview via alternative IP encodings (octal, decimal, hex, shorthand, IPv6-mapped IPv4). String-based host check doesn't catch them. Fix: parse host with `std::net::IpAddr` to normalize all IP representations before checking private ranges. (`src/link_preview.rs:13`)
19 +
20 + ### Minor
21 + - [ ] `parse_duration` silently defaults unknown strings to permanent ban (e.g. typo "3d" → permanent). Fix: return error on unrecognized values. (`src/routes/helpers.rs:275`)
22 + - [ ] `/internal/threads/{id}/stats` is unauthenticated and publicly accessible (thread UUIDs visible in URLs). Fix: add `InternalAuth` extractor or accept the info leak. (`src/routes/internal.rs:276`)
23 + - [ ] `auto_hide_if_threshold_met` records the flagger's user_id as `removed_by`, not a system/mod account. Misleading audit trail. (`src/routes/flagging.rs:82`)
24 + - [ ] `/search` endpoint has no rate limiting (GET in read_routes). Full-text + trigram similarity queries are expensive. Fix: add per-IP rate limit or move to write_routes group. (`src/routes/search.rs`)
25 +
26 + ---
27 +
9 28 ## Code Review Remediation — Remaining
10 29 - [ ] Transitive dep advisories: rand 0.8/0.9 (RUSTSEC-2026-0097), rsa (RUSTSEC-2023-0071), lru (RUSTSEC-2026-0002) — no direct fix available, monitor upstream
11 30 - [ ] Add partial index on `posts.removed_at` (`WHERE removed_at IS NOT NULL`) when data volume warrants it
@@ -3385,7 +3385,7 @@ dependencies = [
3385 3385
3386 3386 [[package]]
3387 3387 name = "makenotwork"
3388 - version = "0.4.0"
3388 + version = "0.4.2"
3389 3389 dependencies = [
3390 3390 "anyhow",
3391 3391 "argon2",
@@ -120,7 +120,7 @@ Files > 100 MB are now held for review instead of downloaded into RAM. Next step
120 120
121 121 ## Code Fuzz Findings (2026-04-25)
122 122
123 - Two rounds of adversarial code review. 31 findings total: 30 fixed, 1 accepted risk, 1 deferred.
123 + Three rounds of adversarial code review. 51 findings total: 50 fixed, 1 accepted risk, 1 deferred.
124 124
125 125 ### Accepted Risk
126 126 - Idempotency check not atomic with operation — concurrent requests both execute (`db/idempotency.rs`). Safe because underlying ops are themselves idempotent.
@@ -131,6 +131,46 @@ Two rounds of adversarial code review. 31 findings total: 30 fixed, 1 accepted r
131 131 ### Resolved (28 findings)
132 132 All critical, serious, and minor findings from rounds 1 and 2 are fixed. See git history for details.
133 133
134 + ## Code Fuzz Findings -- Round 3 (2026-04-25)
135 +
136 + ### Fixed
137 + - [x] SubscriptionStatus missing Trialing/Incomplete/IncompleteExpired variants (db/enums.rs)
138 + - [x] SyncKit auth does not block deactivated users (synckit_auth.rs)
139 + - [x] Build token comparison not constant-time (routes/builds.rs)
140 + - [x] Subscription access checks ignore paused_at (db/subscriptions.rs)
141 + - [x] Cover/MediaImage uploads bypass storage cap with i64::MAX (db/creator_tiers.rs)
142 + - [x] Promo code use_count consumed even when user already owns item (db/transactions.rs)
143 + - [x] Idempotency middleware caches empty string instead of real body (metrics.rs)
144 + - [x] OAuth: no dummy hash on user-not-found path (timing leak, routes/oauth.rs)
145 + - [x] OAuth: no password length cap (Argon2 DoS, routes/oauth.rs)
146 + - [x] OAuth: suspended/deactivated users can still authorize (routes/oauth.rs)
147 + - [x] OAuth session validation: result.valid not checked before suspension (routes/oauth.rs)
148 + - [x] OAuth legacy session path ignores deactivated flag (routes/oauth.rs)
149 + - [x] Subscription test fixture missing paused_at field (db/models/subscription.rs)
150 + - [x] Git routes have no rate limiting (routes/git/mod.rs)
151 + - [x] No Content-Security-Policy header (lib.rs)
152 + - [x] /metrics endpoint unprotected (lib.rs)
153 + - [x] Upload confirm: no idempotency, no S3 cleanup on storage error (routes/storage/uploads.rs)
154 + - [x] Version confirm: no idempotency, no S3 cleanup on storage error (routes/storage/versions.rs)
155 + - [x] Image confirm: storage increment after DB write, no idempotency (routes/storage/images.rs)
156 + - [x] Media confirm: no content_type/extension re-validation, no S3 cleanup (routes/storage/media.rs)
157 +
158 + - [x] CSRF token not rotated after login -- now regenerated in login_user (auth.rs, csrf.rs)
159 + - [x] Read-modify-write race on sort_order/position -- atomic INSERT...SELECT (db/custom_links.rs, db/collections.rs)
160 + - [x] N+1 reorder loops without transactions -- wrapped in transactions (db/custom_links.rs, db/collections.rs)
161 + - [x] Race condition in custom domain creation -- SELECT FOR UPDATE in transaction (db/custom_domains.rs)
162 +
163 + - [x] Double-purchase TOCTOU -- partial unique index on (buyer_id, item_id/project_id) WHERE status='pending' prevents concurrent checkouts (migration 073, checkout handlers)
164 +
165 + - [x] Promo code max_uses bypassable on paid path -- use_count now reserved at checkout time, released by scheduler on stale cleanup (migration 074, checkout handlers, scheduler)
166 +
167 + ### Deferred
168 + - Rate limit IP extraction trusts X-Forwarded-For when traffic bypasses Cloudflare (helpers.rs). Fix requires splitting rate limit extraction by path: CF-Connecting-IP for public web routes, peer socket for internal/CLI/git. Needs careful routing since CLI, git smart HTTP, and SyncKit all hit the same server but some bypass Cloudflare.
169 + - S3 key/file size UPDATE queries lack ownership in SQL -- defense-in-depth; callers verify ownership (db/items.rs)
170 +
171 + ### Accepted
172 + - Revoked session usable for up to 30s -- session cache IS cleared by revoke_session and revoke_other_sessions handlers; window only applies to direct DB manipulation (admin)
173 +
134 174 ---
135 175
136 176 ## Creator Trust Audit (2026-04-25)
@@ -140,8 +180,24 @@ Systematic creator-perspective audit of docs, legal, code, and competitive posit
140 180 ### Resolved (20+ findings)
141 181 All doc/code fixes, trust gaps, security issues, and doc clarity items are complete. Key changes: subscription export endpoint, offsite backups with WAM alerting, API key hashing, security headers, fan subscription pause on suspension, account limbo state, support ticket portal, expanded tax/payout/discovery/storage docs, privacy policy updates. See git history.
142 182
143 - ### Remaining
144 - - [ ] No incident post-mortems or public historical incident log (process, not code)
183 + ### Doc/Code Contradictions (all resolved)
184 + - [x] Payout minimum: aligned payouts.md with Stripe reality ($1 minimum), named Stripe explicitly
185 + - [x] Content protection watermarking: scoped claim to "audio and image files delivered without watermarks", disclosed text fingerprinting
186 + - [x] Mailing list import: reworded as roadmap item, linked to migration guide
187 + - [x] IP retention: added daily scheduler job scrubbing IPs from user_sessions, download_fingerprints, streaming_sessions at 30 days
188 +
189 + ### Missing Creator-Facing Documentation
190 + - [x] Named Stripe explicitly in payouts.md and payments.md (replaced all "payment processor" references)
191 + - [x] Creator application criteria added to getting-started.md (what we look for, what gets rejected)
192 + - [x] Tips documented (new guide/tips.md)
193 + - [x] Bundles documented (new guide/bundles.md)
194 + - [x] Revenue splits documented (new guide/splits.md)
195 + - [x] Currencies/countries: linked to Stripe's own docs (46+ countries, 135+ currencies) rather than maintaining stale tables; fixed grammar from bulk find-replace
196 +
197 + ### Trust Gaps
198 + - [x] 30-day post-termination export window -- migration 076 adds terminated_at; admin terminate route (requires prior suspension); scheduler deletes after 30 days; Stripe subscriptions canceled; termination email sent with export instructions
199 + - [x] Per-item content removal admin action -- migration 075, admin routes (remove/restore), email notifications, publish guards prevent re-publishing removed items
200 + - ~~No incident post-mortems or public historical incident log~~ — will publish as posts in the MNW Changelog blog project
145 201
146 202 ### Competitive Positioning (acknowledged, not bugs)
147 203 - No free tier — deliberate tradeoff. Earn-back credit program planned.
@@ -2,6 +2,8 @@
2 2
3 3 Your first 15 minutes on Makenot.work — from sign-up to your first published item.
4 4
5 + **Not ready to commit?** [Try sandbox mode](./sandbox.md) to explore the full creator dashboard without signing up. It lasts 1 hour and requires no account.
6 +
5 7 ## Create Your Account
6 8
7 9 1. Visit the homepage and click **Join**
@@ -26,16 +28,18 @@ To sell your work, apply for creator access:
26 28
27 29 Most applications are approved within a few days. You'll get an email when you're in.
28 30
31 + **What we look for:** We approve anyone who makes original creative work -- music, writing, software, art, video, courses. We don't require a minimum audience or existing sales history. Applications are rejected only for content that violates our [acceptable use policy](../legal/acceptable-use.md) (e.g., reselling others' work, prohibited content).
32 +
29 33 ## Connect Payments
30 34
31 - Once approved as a creator, connect your payment account to receive fan payments:
35 + Once approved as a creator, connect Stripe to receive fan payments:
32 36
33 37 1. Go to your **Dashboard**
34 38 2. Click **Connect Payments**
35 - 3. Follow the payment onboarding flow
36 - 4. Complete identity verification (payment processor requirement)
39 + 3. Follow the Stripe onboarding flow
40 + 4. Complete identity verification (Stripe requirement)
37 41
38 - Payments go directly to your payment account. We never hold or touch your revenue.
42 + Payments go directly to your Stripe account. We never hold or touch your revenue.
39 43
40 44 ## Create Your First Project
41 45
@@ -0,0 +1,47 @@
1 + # Bundles
2 +
3 + Bundles let you sell multiple items together as a single purchase. A "Complete Discography" pack, a "Starter Kit" with templates and presets, or a "Full Course" combining individual modules.
4 +
5 + ## Creating a Bundle
6 +
7 + 1. Navigate to your project
8 + 2. Click **New Item**
9 + 3. Choose **Bundle** as the item type
10 + 4. Set a title, description, and price
11 +
12 + Bundles are items themselves -- they appear in your project listing alongside regular items.
13 +
14 + ## Adding Items to a Bundle
15 +
16 + After creating the bundle:
17 +
18 + 1. Open the bundle from your dashboard
19 + 2. Add items from the same project
20 +
21 + **Rules:**
22 + - Items must be in the same project as the bundle
23 + - Bundles cannot contain other bundles
24 + - An item can belong to multiple bundles
25 + - Items in a bundle can also be sold individually
26 +
27 + ## What Fans Get
28 +
29 + When a fan buys a bundle, they get immediate access to every item in it. Each child item appears in their library as if they had purchased it individually.
30 +
31 + If you add new items to a bundle later, existing buyers get access to them automatically.
32 +
33 + ## Pricing
34 +
35 + Set the bundle price independently of its contents. A bundle containing three $10 items could be priced at $25 (a discount) or $35 (a premium for convenience) -- your choice.
36 +
37 + Fans can also buy individual items without buying the bundle. Both paths are valid.
38 +
39 + ## Unlisted Items in Bundles
40 +
41 + Items marked as **unlisted** are hidden from your project's main listing but still appear as bundle contents. This is useful for items you only want available as part of a package.
42 +
43 + ## See Also
44 +
45 + - [Items](./items.md) -- Creating and managing individual items
46 + - [Collections](./collections.md) -- Curated lists (display-only, not purchasable as a unit)
47 + - [Promo Codes](./promo-codes.md) -- Discounts on bundles and items
@@ -53,7 +53,7 @@ Fans can unsubscribe from individual lists via the link in any email, or by unfo
53 53
54 54 ## Importing Subscribers
55 55
56 - You can import external email addresses (people without Makenot.work accounts) to your mailing lists. This is useful when migrating from another platform.
56 + Subscriber import from other platforms (CSV upload with consent emails) is on the roadmap but not yet available. When migrating, use the [data import tools](./migration.md) to bring over your content; your audience can follow your new project to subscribe.
57 57
58 58 ## See Also
59 59
@@ -4,14 +4,14 @@
4 4
5 5 We use a direct-deposit payment model. When you sign up:
6 6
7 - 1. Connect or create a payment account
8 - 2. Verify your identity (payment processor requirement)
7 + 1. Connect or create a Stripe account
8 + 2. Verify your identity (Stripe requirement)
9 9 3. Link your bank account
10 10 4. Payments flow directly to you
11 11
12 12 ## Payout Schedule
13 13
14 - You control this in your payment processor's dashboard:
14 + You control this in your [Stripe dashboard](https://dashboard.stripe.com):
15 15
16 16 - **Instant**: Available immediately (small fee)
17 17 - **Daily**: Next business day
@@ -22,11 +22,11 @@ Default is standard payouts (2-3 business days).
22 22
23 23 ## Minimum Payout
24 24
25 - No minimum. Even $0.01 will transfer (though processing fees make tiny amounts impractical).
25 + Stripe requires a minimum balance before payout (typically $1). You can set a higher threshold if you prefer larger, less frequent payouts. Processing fees make very small amounts impractical regardless of the minimum.
26 26
27 27 ## International Creators
28 28
29 - Our payment processor supports 40+ countries. Payouts are converted to your local currency by the payment processor when your country's currency differs from the purchase currency (USD). Conversion rates and any currency conversion fees are set by the payment processor, not by us.
29 + Stripe supports creators in [46+ countries](https://stripe.com/global). Payouts are converted to your local currency by Stripe when your country's currency differs from the purchase currency (USD). Conversion rates and any currency conversion fees are set by Stripe, not by us.
30 30
31 31 If your country isn't supported, contact us — we're working on alternatives.
32 32
@@ -36,19 +36,19 @@ You are responsible for your own tax obligations. We do not withhold taxes, calc
36 36
37 37 ### US Creators
38 38
39 - - The payment processor issues a **1099-K** if your gross payments exceed IRS reporting thresholds ($600/year as of 2024)
39 + - Stripe issues a **1099-K** if your gross payments exceed IRS [reporting thresholds](https://support.stripe.com/topics/1099-tax-forms) ($600/year as of 2024)
40 40 - You are responsible for reporting all income, including amounts below the 1099-K threshold
41 - - We do not issue 1099-MISC or W-2 forms — your relationship is with the payment processor, not with us as an employer
41 + - We do not issue 1099-MISC or W-2 forms — your relationship is with Stripe, not with us as an employer
42 42 - Keep your own records: your dashboard shows revenue charts and full transaction history, and you can export sales as CSV at any time
43 43 - Consult a tax professional if you are unsure about your obligations — this is not tax advice
44 44
45 45 ### Non-US Creators
46 46
47 47 - Follow your local tax laws for self-employment or platform income
48 - - The payment processor provides transaction records and may issue tax forms required by your jurisdiction
48 + - Stripe provides transaction records and may issue tax forms required by your jurisdiction
49 49 - VAT/GST collection is your responsibility — we do not currently collect or remit taxes on your behalf
50 50 - We provide annual revenue summaries on request (email billing@makenot.work)
51 - - If your country requires tax withholding on cross-border payments, the payment processor handles this based on your W-8BEN or equivalent tax form
51 + - If your country requires tax withholding on cross-border payments, Stripe handles this based on your W-8BEN or equivalent tax form
52 52
53 53 <!-- PENDING: This section should be reviewed by a tax professional. -->
54 54 <!-- Specific areas: 1099-K threshold accuracy, international withholding guidance, VAT/GST obligations. -->
@@ -62,15 +62,15 @@ Your dashboard shows:
62 62 - Per-project and per-item breakdowns
63 63 - Full transaction history
64 64
65 - Your payment processor's dashboard provides additional payout detail (pending transfers, completed payouts, payout schedule).
65 + Your [Stripe dashboard](https://dashboard.stripe.com) provides additional payout detail (pending transfers, completed payouts, payout schedule).
66 66
67 67 ## Refunds
68 68
69 69 If a fan requests a refund and you approve:
70 70
71 71 - Funds deducted from your next payout
72 - - Or from your payment account balance
73 - - Refund fees may apply (payment processor policy)
72 + - Or from your Stripe balance
73 + - Refund fees may apply ([Stripe refund policy](https://docs.stripe.com/refunds))
74 74
75 75 You have full control over refund decisions.
76 76
@@ -78,3 +78,5 @@ You have full control over refund decisions.
78 78
79 79 - [Analytics & Dashboard](./analytics.md) — Revenue charts and transaction history
80 80 - [Pricing & Monetization](./03-selling.md) — Setting prices and payment flow
81 + - [Stripe Global](https://stripe.com/global) — Supported countries
82 + - [Stripe Payouts](https://docs.stripe.com/payouts) — Payout schedules and timing
@@ -0,0 +1,65 @@
1 + # Sandbox Mode
2 +
3 + Try the full creator dashboard without signing up. Sandbox mode gives you an ephemeral account that works exactly like a real one — create projects, manage items, set pricing, write blog posts, generate license keys — with a few guardrails.
4 +
5 + ## Starting a Sandbox
6 +
7 + 1. Visit [/sandbox](/sandbox) (or click **Try the Dashboard** on the homepage)
8 + 2. Click **Start sandbox**
9 + 3. You're dropped into a pre-seeded dashboard with a demo project and two sample items
10 +
11 + No email, no password, no commitment. The session starts immediately.
12 +
13 + ## What Works
14 +
15 + Everything a creator can do in the dashboard works in sandbox mode:
16 +
17 + - **Projects** — Create, edit, delete, change visibility and settings
18 + - **Items** — Create any item type, set pricing (fixed, free, pay-what-you-want), add descriptions, manage chapters and sections
19 + - **Blog** — Write and publish blog posts with the full markdown editor
20 + - **Tags & metadata** — Organize content with tags, categories, and labels
21 + - **Subscription tiers** — Create tiers with simulated payment details
22 + - **License keys** — Configure license key settings and generate keys
23 + - **Promo codes** — Create discount and free-access codes
24 + - **Collections** — Group items into curated collections
25 + - **Custom links** — Add links to your profile
26 + - **File uploads** — Upload real files up to 5 MB each (50 MB total)
27 + - **Analytics** — View the revenue dashboard and sales charts (with your sandbox data)
28 + - **Export** — Export your sandbox data as CSV or JSON
29 +
30 + ## What's Different
31 +
32 + A few things are restricted to keep sandbox mode safe and ephemeral:
33 +
34 + | Feature | Sandbox behavior |
35 + |---------|-----------------|
36 + | **Stripe payments** | Simulated. Subscription tiers get placeholder IDs. No real money moves. |
37 + | **Broadcast email** | Blocked. No emails are sent from sandbox accounts. |
38 + | **Follows** | Blocked. Sandbox accounts can't follow real users. |
39 + | **Public visibility** | Sandbox content never appears on the discover page, search, or public profiles. |
40 + | **2FA / passkeys** | Disabled. Security setup on a 1-hour account isn't useful. |
41 + | **File size** | 5 MB per file, 50 MB total (real accounts get up to 20 GB per file depending on tier). |
42 + | **Duration** | 1 hour. After that, the account and all its data are permanently deleted. |
43 +
44 + ## Limits
45 +
46 + - **1 hour** — your sandbox expires automatically. There is no way to extend it.
47 + - **5 MB per file** — enough to upload sample audio, cover images, or small downloads.
48 + - **50 MB total storage** — keeps sandbox resource usage bounded.
49 + - **Rate limited** — a maximum of 2 sandboxes can be created per 30 seconds from the same IP, with 3 concurrent sandboxes active at once.
50 +
51 + ## Converting to a Real Account
52 +
53 + Sandbox data cannot be transferred to a real account. The sandbox is for exploration, not production use. When you're ready:
54 +
55 + 1. [Create a real account](/join)
56 + 2. [Apply for creator access](/creators)
57 + 3. Recreate the projects and items you liked from your sandbox session
58 +
59 + The sandbox is designed to answer "what would this feel like?" — not to be a staging environment.
60 +
61 + ## See Also
62 +
63 + - [Getting Started](./01-getting-started.md) — Full account setup walkthrough
64 + - [Content Types](./02-content.md) — Items, uploads, and organization
65 + - [Pricing Tiers](./tiers.md) — File limits and features per tier
@@ -0,0 +1,58 @@
1 + # Revenue Splits
2 +
3 + Revenue splits let you share earnings with collaborators on a project. Add a co-author, producer, or contributor and specify what percentage of each sale they receive.
4 +
5 + ## Adding a Collaborator
6 +
7 + 1. Go to your project's **Members** tab in the dashboard
8 + 2. Enter the collaborator's Makenot.work username
9 + 3. Set their split percentage (1-99%)
10 + 4. Click **Add Member**
11 +
12 + The collaborator must have a Makenot.work account. They do not need to be a creator or have Stripe connected (though they will need Stripe to receive payouts).
13 +
14 + ## How Splits Work
15 +
16 + When a fan buys any item in the project or sends a tip:
17 +
18 + - The split is calculated automatically based on the configured percentages
19 + - You (the project owner) receive the remainder after all member splits
20 + - Split records are created immediately when payment completes
21 +
22 + **Example:** You set a collaborator at 30%. A fan buys a $10 item. After Stripe's ~$0.59 processing fee, the collaborator's share is $2.82 (30% of $9.41) and yours is $6.59.
23 +
24 + ## Payouts
25 +
26 + Split obligations are **recorded, not transferred automatically**. The full payment goes to your Stripe account (you are the merchant of record), and you settle with collaborators directly. Your dashboard tracks what you owe each collaborator so nothing falls through the cracks.
27 +
28 + Automated split payouts (direct Stripe transfers to collaborators) are on the roadmap.
29 +
30 + ## What Collaborators See
31 +
32 + Collaborators can view their split records in their own dashboard:
33 +
34 + - Amount owed per transaction
35 + - Their percentage
36 + - Source (item sale or tip)
37 + - Running total of unsettled splits
38 +
39 + They can also export split records as CSV.
40 +
41 + ## Roles
42 +
43 + | Role | Can edit project | Can add members | Receives splits |
44 + |------|-----------------|-----------------|-----------------|
45 + | **Owner** | Yes | Yes | Remainder after splits |
46 + | **Member** | No | No | Configured percentage |
47 +
48 + Only the project owner can add, remove, or change member splits.
49 +
50 + ## Removing a Collaborator
51 +
52 + Removing a member sets their split to 0% for future sales. Existing split records (for past sales) are preserved -- you still owe what was already earned.
53 +
54 + ## See Also
55 +
56 + - [Projects](./projects.md) -- Project setup and management
57 + - [Payouts](./payouts.md) -- Receiving your earnings
58 + - [Tips](./tips.md) -- Tips are also split among collaborators
@@ -0,0 +1,42 @@
1 + # Tips
2 +
3 + Tips let fans send one-time payments to creators without buying a specific item. Think of it as a "thank you" or "keep going" button.
4 +
5 + ## Enabling Tips
6 +
7 + Tips are off by default. To turn them on:
8 +
9 + 1. Go to **Dashboard > Settings**
10 + 2. Enable **Accept Tips**
11 + 3. Stripe must be connected with charges enabled
12 +
13 + Once enabled, a tip button appears on your public profile page.
14 +
15 + ## How Fans Tip
16 +
17 + 1. Fan visits your profile (`/u/yourusername`)
18 + 2. Clicks the tip button
19 + 3. Enters an amount ($1 - $10,000) and an optional message (up to 280 characters)
20 + 4. Completes payment through Stripe Checkout
21 +
22 + The full amount minus Stripe's processing fee (~2.9% + $0.30) goes directly to your Stripe account. We take no cut.
23 +
24 + ## Viewing Tips
25 +
26 + Your dashboard shows all tips received: who tipped, how much, any message they included, and when. You can also view total tip revenue in your analytics.
27 +
28 + Fans can see their tip history in their own account.
29 +
30 + ## Tips and Revenue Splits
31 +
32 + If your project has collaborators with revenue splits configured, tips are split according to the same percentages as item sales.
33 +
34 + ## Notifications
35 +
36 + When you receive a tip, you get an email notification (if enabled in your notification settings).
37 +
38 + ## See Also
39 +
40 + - [Payments & Refunds](../legal/payments.md) -- How payments work
41 + - [Payouts](./payouts.md) -- Receiving your earnings
42 + - [Revenue Splits](./splits.md) -- Sharing revenue with collaborators
@@ -6,7 +6,7 @@ How payments work on Makenot.work.
6 6
7 7 ## The Short Version
8 8
9 - Our payment processor handles all payments. You're the merchant of record. We don't take a cut of your revenue. Fans handle refunds with you or dispute via their bank.
9 + Stripe handles all payments. You're the merchant of record. We don't take a cut of your revenue. Fans handle refunds with you or dispute via their bank.
10 10
11 11 ---
12 12
@@ -16,8 +16,8 @@ Our payment processor handles all payments. You're the merchant of record. We do
16 16
17 17 When fans buy your content or subscribe:
18 18
19 - 1. Fan pays via payment processor (~2.9% + $0.30 per transaction)
20 - 2. Funds go directly to your connected payment account
19 + 1. Fan pays via Stripe (~2.9% + $0.30 per transaction)
20 + 2. Funds go directly to your connected Stripe account
21 21 3. Deposits to your bank on your chosen schedule
22 22 4. We never touch or hold your money
23 23
@@ -33,12 +33,12 @@ Your monthly Makenot.work subscription ($10-40) is separate:
33 33
34 34 ## You're the Merchant of Record
35 35
36 - When you connect payments, the processor creates an account for you. This means:
36 + When you connect Stripe, it creates an account for you. This means:
37 37
38 38 - **You are the merchant** - Fans are paying you, not us
39 39 - **You set prices** - Including pay-what-you-want options
40 40 - **You handle disputes** - Chargebacks and refunds are your responsibility
41 - - **Your payment account is yours** - If you leave, it stays with you
41 + - **Your Stripe account is yours** - If you leave, it stays with you
42 42
43 43 This keeps your revenue in your hands. The tradeoff: you absorb the (typically rare) chargeback risk.
44 44
@@ -46,7 +46,7 @@ This keeps your revenue in your hands. The tradeoff: you absorb the (typically r
46 46
47 47 ## Payouts
48 48
49 - You control when funds move from your payment account balance to your bank:
49 + You control when funds move from your Stripe balance to your bank:
50 50
51 51 | Schedule | Timing | Notes |
52 52 |----------|--------|-------|
@@ -75,7 +75,7 @@ We don't process refunds on your behalf. You're the merchant—refund decisions
75 75
76 76 Fans should contact you directly. You can:
77 77
78 - - Issue a full or partial refund via your payment processor's dashboard
78 + - Issue a full or partial refund via your [Stripe dashboard](https://dashboard.stripe.com)
79 79 - Offer alternative resolution (access fix, different content)
80 80 - Decline if the purchase was delivered as promised
81 81
@@ -96,7 +96,7 @@ A $5 refund is cheaper than a $15 chargeback fee.
96 96 If a fan disputes a charge with their bank:
97 97
98 98 1. **Your balance is debited** - Disputed amount plus fee (~$15)
99 - 2. **You respond through the payment processor** - Provide evidence the charge was legitimate
99 + 2. **You respond through Stripe** - Provide evidence the charge was legitimate
100 100 3. **Bank decides** - You either get the money back or lose it
101 101
102 102 ### Why This Model
@@ -134,7 +134,7 @@ Consult a tax professional for your specific situation.
134 134 ### What We Provide
135 135
136 136 - **Transaction records** - Exportable history of all payments
137 - - **1099-K** (US creators) - Issued by the payment processor if you meet thresholds
137 + - **1099-K** (US creators) - Issued by Stripe if you meet [reporting thresholds](https://support.stripe.com/topics/1099-tax-forms)
138 138 - **Invoices** - For your platform subscription
139 139
140 140 ### Your Responsibilities
@@ -153,22 +153,22 @@ Your Makenot.work subscription may be deductible as a business expense. Keep you
153 153
154 154 ## International Payments
155 155
156 - Our payment processor supports 40+ countries. Creators outside the US:
156 + Stripe supports creators in [46+ countries](https://stripe.com/global). Creators outside the US:
157 157
158 - - Set up a payment account in your country
159 - - Receive payments in multiple currencies
160 - - The payment processor handles currency conversion
158 + - Set up a Stripe account in your country
159 + - Receive payments in [135+ currencies](https://docs.stripe.com/currencies)
160 + - Stripe handles currency conversion automatically
161 161
162 - Conversion rates and timing vary by country. Check your payment processor's documentation for specifics.
162 + Conversion rates and timing vary by country. See [Stripe's payout documentation](https://docs.stripe.com/payouts) for specifics.
163 163
164 164 ---
165 165
166 166 ## What We Don't Do
167 167
168 - - **Hold creator funds** - Money goes directly to your payment account
168 + - **Hold creator funds** - Money goes directly to your Stripe account
169 169 - **Take a percentage** - Our fee is flat monthly subscription only
170 170 - **Control your pricing** - Set whatever prices you want
171 - - **Process payments ourselves** - Our payment processor handles everything
171 + - **Process payments ourselves** - Stripe handles everything
172 172 - **Provide tax advice** - Consult a professional
173 173
174 174 ---
@@ -177,4 +177,6 @@ Conversion rates and timing vary by country. Check your payment processor's docu
177 177
178 178 - [Creator Guarantees](../about/guarantees.md) - our commitments on revenue
179 179 - [Pricing Tiers](../guide/tiers.md) - tier features and pricing
180 - - [Infrastructure & Vendors](../tech/infrastructure.md) - current payment processor details
180 + - [Stripe Global](https://stripe.com/global) - supported countries
181 + - [Stripe Currencies](https://docs.stripe.com/currencies) - supported currencies
182 + - [Stripe Connect](https://stripe.com/connect) - how the payment model works
@@ -12,7 +12,9 @@ We won't be part of that.
12 12
13 13 ## What We Do Instead
14 14
15 - We give fans clean, unencumbered files. Presigned download URLs are time-limited and tied to authenticated accounts, so casual link-sharing doesn't work. But we don't embed watermarks, restrict playback, or limit how fans use files they've paid for.
15 + We give fans clean, unencumbered files. Presigned download URLs are time-limited and tied to authenticated accounts, so casual link-sharing doesn't work. We don't restrict playback or limit how fans use files they've paid for.
16 +
17 + For text files (README, LICENSE, liner notes), we may embed an invisible fingerprint tied to the buyer's purchase. This lets creators trace leaked files back to a specific download without affecting the content's usability. Audio and image files are delivered without watermarks.
16 18
17 19 If you need stronger protection than this, we may not be the right platform for your content. We'd rather be honest about that upfront.
18 20
@@ -53,7 +53,7 @@ pub async fn docs_index(
53 53 // Post-process the Guide section: bucket entries into subcategories.
54 54 if let Some(guide) = sections.iter_mut().find(|s| s.name == "Guide") {
55 55 const SUBCATEGORIES: &[(&str, &[&str])] = &[
56 - ("Getting Started", &["01-getting-started", "profile", "security", "best-practices"]),
56 + ("Getting Started", &["01-getting-started", "sandbox", "profile", "security", "best-practices"]),
57 57 ("Content & Organization", &["02-content", "items", "projects", "files", "tags", "metadata", "collections", "blog"]),
58 58 ("Selling & Revenue", &["03-selling", "pricing", "payouts", "analytics", "promo-codes", "contact-sharing", "fan-plus"]),
59 59 ("Fans & Distribution", &["fan-guide", "rss", "mailing-lists", "export"]),