max / makenotwork
45 files changed,
+1817 insertions,
-64 deletions
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | [package] | |
| 2 | 2 | name = "makenotwork" | |
| 3 | - | version = "0.3.25" | |
| 3 | + | version = "0.3.26" | |
| 4 | 4 | edition = "2024" | |
| 5 | 5 | license-file = "LICENSE" | |
| 6 | 6 |
| @@ -0,0 +1,264 @@ | |||
| 1 | + | # Embeddable Widgets | |
| 2 | + | ||
| 3 | + | Lightweight embeds that let creators put MNW buy buttons, audio previews, and product cards on external websites. | |
| 4 | + | ||
| 5 | + | --- | |
| 6 | + | ||
| 7 | + | ## Why | |
| 8 | + | ||
| 9 | + | Ko-fi's embed model is how many creators discover the platform — a button on a blog, a widget in a stream overlay, a card in a portfolio site. MNW currently requires fans to visit makenot.work directly. Embeds extend the storefront to wherever the creator already has an audience: their personal site, a blog post, a forum, a link-in-bio page. | |
| 10 | + | ||
| 11 | + | This also complements the tips feature — a "support this creator" embed is the simplest possible widget and the most natural entry point for fans who haven't visited MNW before. | |
| 12 | + | ||
| 13 | + | --- | |
| 14 | + | ||
| 15 | + | ## How It Works | |
| 16 | + | ||
| 17 | + | 1. Creator goes to an item or their profile in the dashboard | |
| 18 | + | 2. Clicks "Get embed code" | |
| 19 | + | 3. Copies an HTML snippet (iframe or script tag) | |
| 20 | + | 4. Pastes it into their external site | |
| 21 | + | 5. Visitors see a styled card/button that links to MNW for checkout | |
| 22 | + | ||
| 23 | + | All embeds are read-only views. No checkout happens inside the embed — clicking "Buy" or "Support" opens MNW in a new tab (or a popup, see overlay variant below). This avoids cross-origin payment complexity and keeps Stripe Checkout on our domain. | |
| 24 | + | ||
| 25 | + | --- | |
| 26 | + | ||
| 27 | + | ## Embed Types | |
| 28 | + | ||
| 29 | + | ### 1. Buy Button | |
| 30 | + | ||
| 31 | + | Minimal embed. Shows item title, price, and a buy button. Clicking opens the purchase page on MNW. | |
| 32 | + | ||
| 33 | + | ``` | |
| 34 | + | Dimensions: ~300x60px (horizontal strip) | |
| 35 | + | Content: Item title | Price | [Buy] button | |
| 36 | + | URL: /embed/i/{item_id}/button | |
| 37 | + | ``` | |
| 38 | + | ||
| 39 | + | Use case: inline in a blog post, sidebar widget, link-in-bio page. | |
| 40 | + | ||
| 41 | + | ### 2. Product Card | |
| 42 | + | ||
| 43 | + | Richer embed. Shows cover art, title, creator name, price, and description excerpt. | |
| 44 | + | ||
| 45 | + | ``` | |
| 46 | + | Dimensions: ~350x400px (vertical card) or ~600x200px (horizontal card) | |
| 47 | + | Content: Cover image, title, creator, price, description (truncated), [Buy] button | |
| 48 | + | URL: /embed/i/{item_id}/card | |
| 49 | + | Variants: ?layout=vertical (default) | ?layout=horizontal | |
| 50 | + | ``` | |
| 51 | + | ||
| 52 | + | Use case: portfolio site, project page, "my latest release" section. | |
| 53 | + | ||
| 54 | + | ### 3. Audio Preview | |
| 55 | + | ||
| 56 | + | For audio items. Shows cover art, title, 30-second preview player, and buy button. Preview plays directly in the embed (no redirect needed for preview — only for purchase). | |
| 57 | + | ||
| 58 | + | ``` | |
| 59 | + | Dimensions: ~350x120px (horizontal) | |
| 60 | + | Content: Cover image (small), title, 30-sec waveform/progress bar, play/pause, price, [Buy] button | |
| 61 | + | URL: /embed/i/{item_id}/player | |
| 62 | + | Audio source: /embed/i/{item_id}/preview.mp3 (30-sec clip, generated server-side) | |
| 63 | + | ``` | |
| 64 | + | ||
| 65 | + | Use case: music blog, artist website, social media link page. | |
| 66 | + | ||
| 67 | + | ### 4. Tip Button | |
| 68 | + | ||
| 69 | + | For creators with tips enabled. Shows creator name/avatar and a "Support" button. | |
| 70 | + | ||
| 71 | + | ``` | |
| 72 | + | Dimensions: ~250x50px (horizontal strip) | |
| 73 | + | Content: Creator avatar (small) | "Support @username" | [Tip] button | |
| 74 | + | URL: /embed/u/{username}/tip | |
| 75 | + | ``` | |
| 76 | + | ||
| 77 | + | Use case: personal website footer, blog sidebar, about page. | |
| 78 | + | ||
| 79 | + | ### 5. Project Card | |
| 80 | + | ||
| 81 | + | Shows project cover, title, creator, item count, and a link to the project page. | |
| 82 | + | ||
| 83 | + | ``` | |
| 84 | + | Dimensions: ~350x300px (vertical card) | |
| 85 | + | Content: Project cover, title, creator, item count, description (truncated), [View] button | |
| 86 | + | URL: /embed/p/{project_slug}/card | |
| 87 | + | ``` | |
| 88 | + | ||
| 89 | + | Use case: creator's external portfolio showing their MNW projects. | |
| 90 | + | ||
| 91 | + | --- | |
| 92 | + | ||
| 93 | + | ## Technical Design | |
| 94 | + | ||
| 95 | + | ### Routes | |
| 96 | + | ||
| 97 | + | | Method | Path | Returns | Auth | | |
| 98 | + | |--------|------|---------|------| | |
| 99 | + | | GET | `/embed/i/{item_id}/button` | HTML page (iframe content) | None | | |
| 100 | + | | GET | `/embed/i/{item_id}/card` | HTML page (iframe content) | None | | |
| 101 | + | | GET | `/embed/i/{item_id}/player` | HTML page (iframe content) | None | | |
| 102 | + | | GET | `/embed/i/{item_id}/preview.mp3` | 30-sec audio clip | None | | |
| 103 | + | | GET | `/embed/u/{username}/tip` | HTML page (iframe content) | None | | |
| 104 | + | | GET | `/embed/p/{project_slug}/card` | HTML page (iframe content) | None | | |
| 105 | + | ||
| 106 | + | All embed routes are public (no auth required). They serve self-contained HTML pages designed to be loaded in iframes. | |
| 107 | + | ||
| 108 | + | ### Embed HTML Structure | |
| 109 | + | ||
| 110 | + | Each embed is a standalone HTML page with: | |
| 111 | + | - Inline CSS (no external stylesheet dependency) | |
| 112 | + | - MNW brand styling (beige background, charcoal text, violet accent — matches platform) | |
| 113 | + | - Responsive within its container | |
| 114 | + | - No JavaScript required for basic embeds (button, card, project card) | |
| 115 | + | - Minimal JS for audio preview player only | |
| 116 | + | - All links open in `_blank` (new tab) via `target="_blank" rel="noopener"` | |
| 117 | + | ||
| 118 | + | ### Embed Code Generator | |
| 119 | + | ||
| 120 | + | On the item dashboard page and creator settings, add an "Embed" section that shows: | |
| 121 | + | ||
| 122 | + | ```html | |
| 123 | + | <!-- Buy Button --> | |
| 124 | + | <iframe src="https://makenot.work/embed/i/{item_id}/button" | |
| 125 | + | width="300" height="60" frameborder="0" | |
| 126 | + | title="Buy {item_title} on Makenot.work"></iframe> | |
| 127 | + | ||
| 128 | + | <!-- Product Card --> | |
| 129 | + | <iframe src="https://makenot.work/embed/i/{item_id}/card" | |
| 130 | + | width="350" height="400" frameborder="0" | |
| 131 | + | title="{item_title} on Makenot.work"></iframe> | |
| 132 | + | ||
| 133 | + | <!-- Audio Preview --> | |
| 134 | + | <iframe src="https://makenot.work/embed/i/{item_id}/player" | |
| 135 | + | width="350" height="120" frameborder="0" | |
| 136 | + | title="Preview {item_title} on Makenot.work"></iframe> | |
| 137 | + | ``` | |
| 138 | + | ||
| 139 | + | Copy-to-clipboard button for each variant. | |
| 140 | + | ||
| 141 | + | ### CORS & Security | |
| 142 | + | ||
| 143 | + | - Embed routes set `X-Frame-Options: ALLOWALL` (or omit the header) to permit iframe embedding on any domain | |
| 144 | + | - Embed routes set `Content-Security-Policy: frame-ancestors *` to allow embedding anywhere | |
| 145 | + | - All other MNW routes retain `X-Frame-Options: DENY` (no change to existing security) | |
| 146 | + | - Embed pages contain no forms, no auth, no cookies — purely read-only | |
| 147 | + | - Links to checkout/purchase pages open in new tabs on makenot.work where normal auth and CSRF apply | |
| 148 | + | ||
| 149 | + | ### Audio Preview Generation | |
| 150 | + | ||
| 151 | + | For the audio player embed, generate a 30-second preview clip: | |
| 152 | + | ||
| 153 | + | - **When**: On first request to `/embed/i/{item_id}/preview.mp3`, generate and cache | |
| 154 | + | - **How**: ffmpeg on the server — extract 30 seconds starting at a configurable offset (default: 0s, creator can set preview start time in item settings) | |
| 155 | + | - **Format**: MP3 128kbps (small, universally playable) | |
| 156 | + | - **Storage**: Cache in S3 alongside the original file (`{s3_key}.preview.mp3`) | |
| 157 | + | - **Cache**: Serve from S3 cache on subsequent requests. Invalidate if source file changes. | |
| 158 | + | - **Fallback**: If item has no audio (non-audio item type), `/preview.mp3` returns 404 | |
| 159 | + | ||
| 160 | + | Optional enhancement (deferred): let creators set preview start/end times in item settings. Default to first 30 seconds. | |
| 161 | + | ||
| 162 | + | ### Caching | |
| 163 | + | ||
| 164 | + | - Embed HTML responses: `Cache-Control: public, max-age=300` (5 minutes). Short enough that price changes reflect quickly, long enough to avoid hammering the DB on popular embeds. | |
| 165 | + | - Preview audio: `Cache-Control: public, max-age=86400` (1 day). Regenerate only on source file change. | |
| 166 | + | - Cover images: already served from S3/CDN with existing cache headers. | |
| 167 | + | ||
| 168 | + | --- | |
| 169 | + | ||
| 170 | + | ## Data Model Changes | |
| 171 | + | ||
| 172 | + | ### Item table addition (optional) | |
| 173 | + | ||
| 174 | + | | Column | Type | Notes | | |
| 175 | + | |--------|------|-------| | |
| 176 | + | | `preview_start_seconds` | INT, nullable | Where the 30-sec preview starts (default 0) | | |
| 177 | + | ||
| 178 | + | This is optional for v1 — default to first 30 seconds. Add later if creators want control. | |
| 179 | + | ||
| 180 | + | ### No other schema changes needed | |
| 181 | + | ||
| 182 | + | Embeds are read-only views over existing data. No new tables required. | |
| 183 | + | ||
| 184 | + | --- | |
| 185 | + | ||
| 186 | + | ## What Embeds Do NOT Do | |
| 187 | + | ||
| 188 | + | - **No checkout inside the embed.** Clicking "Buy" opens MNW in a new tab. Stripe Checkout requires our domain. This is simpler, more secure, and avoids cross-origin iframe payment issues. | |
| 189 | + | - **No authentication.** Embeds are always the public/anonymous view. If the fan already owns the item, the embed still shows the buy button — they'll see "already in library" when they click through. | |
| 190 | + | - **No tracking.** No analytics cookies, no referrer tracking, no pixel. The embed is a static view. We know someone loaded it (server log) but we don't track who or where. | |
| 191 | + | - **No JavaScript widget/SDK.** Just iframes. A JS snippet that injects an iframe is a possible future enhancement (easier copy-paste for non-technical creators), but v1 is plain iframe embeds. | |
| 192 | + | ||
| 193 | + | --- | |
| 194 | + | ||
| 195 | + | ## Overlay Variant (Deferred) | |
| 196 | + | ||
| 197 | + | A future enhancement: instead of opening a new tab, clicking "Buy" opens a popup/modal on the external site that loads the MNW checkout page. This keeps the fan on the creator's site. | |
| 198 | + | ||
| 199 | + | Implementation: a small JS script (`<script src="https://makenot.work/embed/widget.js">`) that intercepts embed buy-button clicks and opens a centered popup window. The popup loads the normal MNW purchase page. On successful checkout, the popup closes and the embed updates to show "Purchased." | |
| 200 | + | ||
| 201 | + | This requires: | |
| 202 | + | - `widget.js` served from MNW | |
| 203 | + | - postMessage communication between popup and parent | |
| 204 | + | - Popup fallback for browsers that block popups | |
| 205 | + | ||
| 206 | + | Deferred because iframes work today and the popup UX has edge cases (popup blockers, mobile browsers). Revisit after v1 embeds ship and we get creator feedback. | |
| 207 | + | ||
| 208 | + | --- | |
| 209 | + | ||
| 210 | + | ## UI: Embed Code in Dashboard | |
| 211 | + | ||
| 212 | + | ### Item dashboard (`/dashboard/items/{id}`) | |
| 213 | + | - New "Embed" section or tab | |
| 214 | + | - Shows all applicable embed variants for that item type: | |
| 215 | + | - All items: Buy Button, Product Card | |
| 216 | + | - Audio items: + Audio Preview | |
| 217 | + | - Each variant: live preview + copyable code snippet | |
| 218 | + | - Layout toggle for product card (vertical/horizontal) | |
| 219 | + | ||
| 220 | + | ### Creator settings or profile dashboard | |
| 221 | + | - Tip Button embed code (if tips enabled) | |
| 222 | + | ||
| 223 | + | ### Project dashboard (`/dashboard/projects/{id}`) | |
| 224 | + | - Project Card embed code | |
| 225 | + | ||
| 226 | + | --- | |
| 227 | + | ||
| 228 | + | ## Implementation Order | |
| 229 | + | ||
| 230 | + | 1. **Buy button embed** — simplest, most useful, proves the routing and CORS setup | |
| 231 | + | 2. **Product card** — richer, uses cover images, validates the styling approach | |
| 232 | + | 3. **Tip button** — depends on tips feature being implemented first | |
| 233 | + | 4. **Audio preview player** — most complex (ffmpeg preview generation, JS player in embed) | |
| 234 | + | 5. **Project card** — straightforward once product card pattern exists | |
| 235 | + | 6. **Embed code generator UI** — add to dashboard after at least button + card work | |
| 236 | + | 7. **Overlay popup** (deferred) — revisit based on creator feedback | |
| 237 | + | ||
| 238 | + | --- | |
| 239 | + | ||
| 240 | + | ## Templates | |
| 241 | + | ||
| 242 | + | New template directory: `templates/embeds/` | |
| 243 | + | ||
| 244 | + | ``` | |
| 245 | + | templates/embeds/ | |
| 246 | + | button.html -- buy button strip | |
| 247 | + | card.html -- product card (vertical + horizontal) | |
| 248 | + | player.html -- audio preview with mini player | |
| 249 | + | tip_button.html -- tip/support button | |
| 250 | + | project_card.html -- project card | |
| 251 | + | ``` | |
| 252 | + | ||
| 253 | + | Each template is a full HTML page (not a partial — no `{% extends "base.html" %}`). Inline styles only. No external CSS or JS dependencies. Must render correctly inside an iframe with no parent page context. | |
| 254 | + | ||
| 255 | + | --- | |
| 256 | + | ||
| 257 | + | ## Testing | |
| 258 | + | ||
| 259 | + | - Integration tests for each embed route (returns 200, correct content-type, no auth required) | |
| 260 | + | - Test that non-public items return 404 on embed routes | |
| 261 | + | - Test CORS/frame headers are set correctly on embed routes and NOT changed on other routes | |
| 262 | + | - Test audio preview generation (mock ffmpeg or use a test fixture) | |
| 263 | + | - Test embed code generator produces valid HTML | |
| 264 | + | - Manual test: embed on a local HTML file, verify rendering and click-through |
| @@ -0,0 +1,177 @@ | |||
| 1 | + | # Tips Feature | |
| 2 | + | ||
| 3 | + | One-time payments to a creator without a product attached. No file delivery, no license keys, no inventory. Just money in, receipt out. | |
| 4 | + | ||
| 5 | + | --- | |
| 6 | + | ||
| 7 | + | ## Why | |
| 8 | + | ||
| 9 | + | Ko-fi's core feature. Many creators receive more from tips than from sales — especially early on when they have an audience but haven't built a catalog yet. Tips also lower the barrier for fans: "I like what you do" is easier than "I want to buy this specific thing." Adding tips makes MNW viable for creators who aren't ready to sell products but want to accept support while they build. | |
| 10 | + | ||
| 11 | + | Tips also feed the embeddable widget feature — a "support this creator" button on an external site is the simplest possible embed. | |
| 12 | + | ||
| 13 | + | --- | |
| 14 | + | ||
| 15 | + | ## How It Works | |
| 16 | + | ||
| 17 | + | 1. Creator enables tips on their profile (off by default, toggle in settings) | |
| 18 | + | 2. Fan visits `/u/username` or `/p/project-slug`, sees a "Support" button | |
| 19 | + | 3. Fan enters an amount (minimum $1, no maximum) and an optional short message | |
| 20 | + | 4. Redirected to Stripe Checkout on the creator's connected account | |
| 21 | + | 5. On success: transaction recorded, creator notified, fan sees confirmation | |
| 22 | + | 6. No item delivered, no library entry created — just a payment record and optional message | |
| 23 | + | ||
| 24 | + | --- | |
| 25 | + | ||
| 26 | + | ## Scope | |
| 27 | + | ||
| 28 | + | ### In Scope | |
| 29 | + | - Enable/disable tips per creator (profile-level toggle) | |
| 30 | + | - Fan-chosen amount with minimum ($1) | |
| 31 | + | - Optional short message (280 chars, plain text) | |
| 32 | + | - Stripe Checkout session on creator's connected account (same direct-charge model as purchases) | |
| 33 | + | - Webhook handler for completed tips | |
| 34 | + | - Tips appear in creator's transaction history and analytics | |
| 35 | + | - Tips appear in fan's payment history | |
| 36 | + | - Email notification to creator on tip received | |
| 37 | + | - Tip button on creator profile page and project pages | |
| 38 | + | - Tips included in data export | |
| 39 | + | - Tips count toward earn-back credit calculation | |
| 40 | + | ||
| 41 | + | ### Out of Scope (for now) | |
| 42 | + | - Recurring tips / monthly support (subscriptions already cover this) | |
| 43 | + | - Tip goals / fundraising progress bars (separate feature, see todo.md) | |
| 44 | + | - Anonymous tips (Stripe requires identity on their end anyway) | |
| 45 | + | - Tipping on individual items (too close to PWYW, which already exists) | |
| 46 | + | ||
| 47 | + | --- | |
| 48 | + | ||
| 49 | + | ## Data Model | |
| 50 | + | ||
| 51 | + | ### New table: `tips` | |
| 52 | + | ||
| 53 | + | | Column | Type | Notes | | |
| 54 | + | |--------|------|-------| | |
| 55 | + | | `id` | UUID PK | | | |
| 56 | + | | `tipper_id` | UUID FK → users | Fan who tipped | | |
| 57 | + | | `recipient_id` | UUID FK → users | Creator who received | | |
| 58 | + | | `project_id` | UUID FK → projects, nullable | If tipped from a project page | | |
| 59 | + | | `amount_cents` | INT, CHECK > 0 | | | |
| 60 | + | | `message` | TEXT, nullable | 280 char limit enforced in app | | |
| 61 | + | | `status` | VARCHAR(20) | pending → completed / failed | | |
| 62 | + | | `stripe_payment_intent_id` | VARCHAR(255) | | | |
| 63 | + | | `stripe_checkout_session_id` | VARCHAR(255) | | | |
| 64 | + | | `created_at` | TIMESTAMPTZ | | | |
| 65 | + | | `completed_at` | TIMESTAMPTZ, nullable | | | |
| 66 | + | ||
| 67 | + | Indexes: `(recipient_id, created_at DESC)`, `(tipper_id, created_at DESC)`. | |
| 68 | + | ||
| 69 | + | **Why a separate table instead of reusing `transactions`?** Transactions have `item_id` as a core concept, and the webhook handler branches on metadata type to determine post-checkout behavior (license keys, file access grants, library entries, sales count increments). Tips need none of that. A separate table keeps the webhook handler clean and avoids nullable `item_id` semantics leaking into transaction queries. Tips still appear in analytics and export by joining or unioning where needed. | |
| 70 | + | ||
| 71 | + | ### User table addition | |
| 72 | + | ||
| 73 | + | | Column | Type | Notes | | |
| 74 | + | |--------|------|-------| | |
| 75 | + | | `tips_enabled` | BOOLEAN DEFAULT false | Creator opt-in | | |
| 76 | + | ||
| 77 | + | --- | |
| 78 | + | ||
| 79 | + | ## Routes | |
| 80 | + | ||
| 81 | + | | Method | Path | Handler | Auth | | |
| 82 | + | |--------|------|---------|------| | |
| 83 | + | | POST | `/stripe/checkout/tip/{recipient_id}` | Create tip checkout session | Logged in | | |
| 84 | + | | GET | `/stripe/tip/success?session_id={id}` | Tip success redirect | Logged in | | |
| 85 | + | ||
| 86 | + | Webhook handling reuses `POST /stripe/webhook` — add a new metadata type `TipCheckoutMetadata` with `tipper_id`, `recipient_id`, `project_id`, `message`. The webhook handler dispatches on metadata type (same pattern as existing item vs subscription vs fan-plus checkout discrimination). | |
| 87 | + | ||
| 88 | + | --- | |
| 89 | + | ||
| 90 | + | ## Checkout Session | |
| 91 | + | ||
| 92 | + | Same direct-charge model as item purchases: | |
| 93 | + | ||
| 94 | + | ``` | |
| 95 | + | session created on creator's connected account via with_stripe_account() | |
| 96 | + | application_fee_amount omitted (0% platform fee) | |
| 97 | + | metadata: { type: "tip", tipper_id, recipient_id, project_id?, message? } | |
| 98 | + | mode: "payment" (one-time, not subscription) | |
| 99 | + | ``` | |
| 100 | + | ||
| 101 | + | Amount is fan-chosen. The checkout form on MNW collects the amount and message, then creates the Stripe session with that amount. Stripe Checkout handles the actual payment UI. | |
| 102 | + | ||
| 103 | + | --- | |
| 104 | + | ||
| 105 | + | ## UI | |
| 106 | + | ||
| 107 | + | ### Creator profile page (`/u/username`) | |
| 108 | + | - If `tips_enabled`: show "Support" button below bio/links | |
| 109 | + | - Clicking opens a small inline form: amount input ($1 min), optional message textarea, submit button | |
| 110 | + | - Submit POSTs to `/stripe/checkout/tip/{recipient_id}`, redirects to Stripe | |
| 111 | + | ||
| 112 | + | ### Project page (`/p/slug`) | |
| 113 | + | - Same "Support" button if creator has tips enabled | |
| 114 | + | - `project_id` included in metadata so creator can see which project inspired the tip | |
| 115 | + | ||
| 116 | + | ### Creator dashboard | |
| 117 | + | - Tips tab or section in analytics showing: total tips received, recent tips with amounts and messages | |
| 118 | + | - Tips included in revenue totals and charts (distinguished from sales in breakdown) | |
| 119 | + | ||
| 120 | + | ### Fan payment history | |
| 121 | + | - Tips appear as "Tip to @username" with amount and date | |
| 122 | + | ||
| 123 | + | ### Creator settings | |
| 124 | + | - Toggle: "Accept tips on your profile and project pages" | |
| 125 | + | ||
| 126 | + | --- | |
| 127 | + | ||
| 128 | + | ## Notifications | |
| 129 | + | ||
| 130 | + | - Email to creator on tip received: "@username tipped you $X" with message if present | |
| 131 | + | - Reuses existing email notification infrastructure (Postmark) | |
| 132 | + | - Respects creator's notification preferences (new `tip_notifications` toggle, default on) | |
| 133 | + | ||
| 134 | + | --- | |
| 135 | + | ||
| 136 | + | ## Export | |
| 137 | + | ||
| 138 | + | Tips included in creator data export as `tips.csv`: | |
| 139 | + | - date, amount, tipper_username, message, status | |
| 140 | + | ||
| 141 | + | Tips included in fan data export as `tips_sent.csv`: | |
| 142 | + | - date, amount, recipient_username, message, status | |
| 143 | + | ||
| 144 | + | --- | |
| 145 | + | ||
| 146 | + | ## Analytics | |
| 147 | + | ||
| 148 | + | - Tips contribute to revenue totals on dashboard | |
| 149 | + | - Revenue chart distinguishes tips from sales (separate series or breakout) | |
| 150 | + | - "Tips received" count as a dashboard metric | |
| 151 | + | - Tips count toward earn-back credit calculation (creator earned revenue on the platform) | |
| 152 | + | ||
| 153 | + | --- | |
| 154 | + | ||
| 155 | + | ## Edge Cases | |
| 156 | + | ||
| 157 | + | - **Creator disables tips after receiving some**: Existing tip records remain. No new tip button shown. Pending checkouts still complete (Stripe session already created). | |
| 158 | + | - **Creator doesn't have Stripe connected**: Tip button not shown (same guard as purchase buttons). | |
| 159 | + | - **Fan tips themselves**: Prevented — recipient_id != session user check. | |
| 160 | + | - **Minimum amount**: $1.00 (below this, Stripe's $0.30 fixed fee makes it irrational for the fan). | |
| 161 | + | - **Maximum amount**: No platform maximum. Stripe's own limits apply. | |
| 162 | + | - **Message content**: Plain text only, 280 chars, no markup. Displayed as-is to creator. Standard content policy applies (harassment via tip messages = moderation action on tipper's account). | |
| 163 | + | ||
| 164 | + | --- | |
| 165 | + | ||
| 166 | + | ## Migration Path | |
| 167 | + | ||
| 168 | + | 1. Migration: add `tips` table, add `tips_enabled` column to `users` | |
| 169 | + | 2. `db::tips` module: `create_tip`, `complete_tip`, `tips_for_recipient`, `tips_by_user` | |
| 170 | + | 3. `payments/checkout.rs`: `create_tip_checkout_session` | |
| 171 | + | 4. `routes/stripe/checkout/tips.rs`: POST handler | |
| 172 | + | 5. `routes/stripe/webhook/checkout.rs`: add `TipCheckoutMetadata` branch | |
| 173 | + | 6. Templates: tip button partial, tip form, success page | |
| 174 | + | 7. Dashboard: tips in analytics, tips in transaction history | |
| 175 | + | 8. Settings: tips toggle | |
| 176 | + | 9. Export: tips in data export | |
| 177 | + | 10. Tests: checkout creation, webhook completion, analytics aggregation, export inclusion |
| @@ -195,6 +195,23 @@ v0.3.23. Audit grade A. ~1,233 tests. | |||
| 195 | 195 | - [ ] Shipping address collection at checkout | |
| 196 | 196 | - [ ] Order management in creator dashboard (mark shipped, tracking number) | |
| 197 | 197 | ||
| 198 | + | ### Ko-fi Comparison Gaps (2026-04-22) | |
| 199 | + | Weak points identified vs Ko-fi. Ordered by effort/impact. | |
| 200 | + | ||
| 201 | + | #### Easy Wins | |
| 202 | + | - [ ] Tips/donations — accept one-time payments without a product attached (Ko-fi's core feature, trivial on Stripe, no inventory/file delivery needed) | |
| 203 | + | - [ ] Embeddable widgets — buy button / audio preview / checkout popup for external sites (Ko-fi's embed model is how many creators discover the platform) | |
| 204 | + | - [ ] Fundraising goals — display campaign target + progress bar on project page (simple DB field + UI, high engagement signal) | |
| 205 | + | ||
| 206 | + | #### Medium Effort | |
| 207 | + | - [ ] Commissions — listing with portfolio, slot limits, client messaging, upfront payment (Ko-fi has full workflow; artists expect this) | |
| 208 | + | - [ ] Physical product listings — already planned above in Phase 20C | |
| 209 | + | ||
| 210 | + | #### Structural Gaps (acknowledged, not urgent) | |
| 211 | + | - [ ] Zero-cost entry — Ko-fi free tier costs $0 and takes 0% on tips. MNW's minimum is $10/mo. Earn-back credit softens this but doesn't eliminate the barrier for artists earning nothing yet. No action needed — this is a deliberate model difference, not a bug. | |
| 212 | + | - [ ] Social integrations — Ko-fi integrates with Discord (roles for supporters), WordPress, Zapier. MNW has none. Consider Discord webhook or Zapier integration post-beta. | |
| 213 | + | - [ ] Moderation team size — Ko-fi has dedicated Trust & Safety staff. MNW is one person. Acknowledged in docs, hiring is priority #2 in surplus allocation. | |
| 214 | + | ||
| 198 | 215 | ||
| 199 | 216 | ### Phase 21: Scheduled Content — Remaining | |
| 200 | 217 | - [ ] Pre-save + pre-order, countdown display, calendar view |
| @@ -0,0 +1,72 @@ | |||
| 1 | + | -- Tips, project members, and revenue splits. | |
| 2 | + | -- | |
| 3 | + | -- Project members allow multi-author projects with configurable revenue splits. | |
| 4 | + | -- Tips are one-time payments to a creator without a product attached. | |
| 5 | + | -- Revenue splits apply to tips AND regular item/project purchases. | |
| 6 | + | ||
| 7 | + | -- ── Project Members ── | |
| 8 | + | ||
| 9 | + | CREATE TABLE project_members ( | |
| 10 | + | id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| 11 | + | project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, | |
| 12 | + | user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | |
| 13 | + | role VARCHAR(50) NOT NULL DEFAULT 'member', | |
| 14 | + | split_percent SMALLINT NOT NULL CHECK (split_percent >= 0 AND split_percent <= 100), | |
| 15 | + | added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| 16 | + | added_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | |
| 17 | + | UNIQUE (project_id, user_id) | |
| 18 | + | ); | |
| 19 | + | ||
| 20 | + | CREATE INDEX idx_project_members_project ON project_members (project_id); | |
| 21 | + | CREATE INDEX idx_project_members_user ON project_members (user_id); | |
| 22 | + | ||
| 23 | + | -- ── Tips ── | |
| 24 | + | ||
| 25 | + | CREATE TABLE tips ( | |
| 26 | + | id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| 27 | + | tipper_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | |
| 28 | + | recipient_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | |
| 29 | + | project_id UUID REFERENCES projects(id) ON DELETE SET NULL, | |
| 30 | + | amount_cents INT NOT NULL CHECK (amount_cents > 0), | |
| 31 | + | message VARCHAR(280), | |
| 32 | + | status VARCHAR(20) NOT NULL DEFAULT 'pending', | |
| 33 | + | stripe_payment_intent_id VARCHAR(255), | |
| 34 | + | stripe_checkout_session_id VARCHAR(255), | |
| 35 | + | stripe_transfer_group VARCHAR(255), | |
| 36 | + | created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| 37 | + | completed_at TIMESTAMPTZ | |
| 38 | + | ); | |
| 39 | + | ||
| 40 | + | CREATE INDEX idx_tips_recipient ON tips (recipient_id, created_at DESC); | |
| 41 | + | CREATE INDEX idx_tips_tipper ON tips (tipper_id, created_at DESC); | |
| 42 | + | ||
| 43 | + | -- ── Revenue Splits ── | |
| 44 | + | -- Records the actual split of a payment after it completes. | |
| 45 | + | -- Created for both tips and item purchases when the project has members. | |
| 46 | + | ||
| 47 | + | CREATE TABLE revenue_splits ( | |
| 48 | + | id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| 49 | + | -- Exactly one of tip_id or transaction_id is set | |
| 50 | + | tip_id UUID REFERENCES tips(id) ON DELETE CASCADE, | |
| 51 | + | transaction_id UUID REFERENCES transactions(id) ON DELETE CASCADE, | |
| 52 | + | recipient_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, | |
| 53 | + | amount_cents INT NOT NULL CHECK (amount_cents >= 0), | |
| 54 | + | split_percent SMALLINT NOT NULL, | |
| 55 | + | stripe_transfer_id VARCHAR(255), | |
| 56 | + | status VARCHAR(20) NOT NULL DEFAULT 'pending', | |
| 57 | + | created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| 58 | + | completed_at TIMESTAMPTZ, | |
| 59 | + | CHECK ( | |
| 60 | + | (tip_id IS NOT NULL AND transaction_id IS NULL) | |
| 61 | + | OR (tip_id IS NULL AND transaction_id IS NOT NULL) | |
| 62 | + | ) | |
| 63 | + | ); | |
| 64 | + | ||
| 65 | + | CREATE INDEX idx_revenue_splits_tip ON revenue_splits (tip_id) WHERE tip_id IS NOT NULL; | |
| 66 | + | CREATE INDEX idx_revenue_splits_transaction ON revenue_splits (transaction_id) WHERE transaction_id IS NOT NULL; | |
| 67 | + | CREATE INDEX idx_revenue_splits_recipient ON revenue_splits (recipient_id, created_at DESC); | |
| 68 | + | ||
| 69 | + | -- ── User tips toggle ── | |
| 70 | + | ||
| 71 | + | ALTER TABLE users ADD COLUMN tips_enabled BOOLEAN NOT NULL DEFAULT false; | |
| 72 | + | ALTER TABLE users ADD COLUMN notify_tip BOOLEAN NOT NULL DEFAULT true; |
| @@ -0,0 +1,149 @@ | |||
| 1 | + | # Platform Economics | |
| 2 | + | ||
| 3 | + | What it costs to run Makenot.work, how subscriptions cover it, and where the money goes. | |
| 4 | + | ||
| 5 | + | --- | |
| 6 | + | ||
| 7 | + | ## Why Publish This | |
| 8 | + | ||
| 9 | + | Most platforms treat their economics as proprietary. We think that's backwards. If you're paying us $10-40/month, you should know what that money does. This page is a plain-language accounting of our cost structure, what it takes to keep the lights on, and what happens with the difference between costs and revenue. | |
| 10 | + | ||
| 11 | + | --- | |
| 12 | + | ||
| 13 | + | ## What It Costs to Run | |
| 14 | + | ||
| 15 | + | Our costs fall into two categories: fixed costs that exist regardless of how many people use the platform, and per-creator costs that scale with each new subscription. | |
| 16 | + | ||
| 17 | + | ### Fixed Costs | |
| 18 | + | ||
| 19 | + | | Category | Range | What's In It | | |
| 20 | + | |----------|-------|-------------| | |
| 21 | + | | Infrastructure | $135-550/mo | Application servers, database, cache, object storage, load balancer | | |
| 22 | + | | Operations | $47-215/mo | Domains, SSL, DNS, monitoring, security scanning, log management | | |
| 23 | + | | Business | $220-580/mo | Payment processing on our own billing, legal, insurance, compliance | | |
| 24 | + | | Development | $0-100/mo | Hosting, CI, development environments | | |
| 25 | + | | **Total** | **$402-1,445/mo** | | | |
| 26 | + | ||
| 27 | + | The range reflects what minimal vs. fully-provisioned operation looks like. At current scale, we spend roughly **$600/month** on fixed costs. | |
| 28 | + | ||
| 29 | + | ### Per-Creator Costs | |
| 30 | + | ||
| 31 | + | Each creator on the platform costs us money to serve — storage, bandwidth, payment processing, and a share of infrastructure load. The amount depends on the tier, because delivering video costs more than delivering audio, which costs more than delivering text. | |
| 32 | + | ||
| 33 | + | | Tier | Your Price | Our Cost | Our Margin | | |
| 34 | + | |------|-----------|----------|-----------| | |
| 35 | + | | Basic | $10/mo | $3-4/mo | $6-7/mo | | |
| 36 | + | | Small Files | $20/mo | $4-6/mo | $14-16/mo | | |
| 37 | + | | Big Files | $30/mo | $6-11/mo | $19-24/mo | | |
| 38 | + | | Streaming | $40/mo | $7-12/mo | $28-33/mo | | |
| 39 | + | ||
| 40 | + | Costs include storage, CDN, transcoding, payment processing fees, and Stripe's per-account fees for handling creator payouts. The ranges reflect that a creator uploading weekly to a large audience costs more than one uploading monthly to a small one. | |
| 41 | + | ||
| 42 | + | Flat pricing means high-activity creators are subsidized by the average. We think this is fair — your subscription price shouldn't punish you for success. See the [pricing calculator](/pricing) to compare what you'd keep here versus other platforms at any revenue level. | |
| 43 | + | ||
| 44 | + | --- | |
| 45 | + | ||
| 46 | + | ## Break-Even | |
| 47 | + | ||
| 48 | + | At current fixed costs (~$600/month), the platform breaks even at roughly **36 creators** with a realistic mix of tiers. Fewer if the mix skews toward higher tiers, more if it skews toward Basic. | |
| 49 | + | ||
| 50 | + | That's a low number. It's low because we don't have costs that other platforms have: no sales team, no marketing budget, no investor relations, no office space, no algorithmic recommendation infrastructure, no data analytics pipeline. Those things cost real money, and that money has to come from somewhere — usually from taking a cut of your earnings or selling ads against your audience. We'd rather not have the costs in the first place. | |
| 51 | + | ||
| 52 | + | --- | |
| 53 | + | ||
| 54 | + | ## Where the Surplus Goes | |
| 55 | + | ||
| 56 | + | At any point beyond break-even, the platform generates more in subscriptions than it spends on infrastructure. Here is what that surplus funds, in priority order: | |
| 57 | + | ||
| 58 | + | 1. **A livable wage.** This is a full-time job. The person building and running the platform needs to pay rent and buy groceries. This is the first claim on any surplus, because a platform whose maintainer can't afford to eat is not sustainable. | |
| 59 | + | ||
| 60 | + | 2. **Hiring.** Right now this is a one-person operation. That's a known risk — it's addressed honestly in our [FAQ](../support/faq.md#what-if-the-founder-is-unable-to-run-the-platform). Adding a second person is the top financial priority once the platform has stable membership covering operating costs. After that, the goal is a small team — enough to provide reliable support, faster development, and independent moderation review. | |
| 61 | + | ||
| 62 | + | 3. **Reserves.** A bad month, a viral spike in CDN costs, or a legal issue shouldn't kill the platform. We maintain reserves to absorb the unexpected without passing it on to you as a price increase. | |
| 63 | + | ||
| 64 | + | 4. **Development.** New features, better infrastructure, improved tools. Everything on the [roadmap](./roadmap.md) costs time and sometimes money. | |
| 65 | + | ||
| 66 | + | ### The Residency Program | |
| 67 | + | ||
| 68 | + | The first hires won't be traditional software engineering recruits. MNW runs a residency program modeled on a medical residency or traditional apprenticeship: we hire exceptionally smart people who lack programming experience — often people leaving academia — and train them into full-stack generalists by working across the entire codebase. | |
| 69 | + | ||
| 70 | + | The goal is not retention. It's graduation. Residents who complete the program leave as strong, independent engineers. MNW benefits from their work during the residency and from a growing network of skilled alumni afterward. | |
| 71 | + | ||
| 72 | + | This matters to you because it means your subscription funds real training and real jobs — not a hiring pipeline optimized for credential-holders, but an investment in people who wouldn't otherwise get a shot at this kind of work. It also means the platform gets maintained by people who understand it deeply, because they learned to build software by building *this* software. | |
| 73 | + | ||
| 74 | + | That's it. There are no shareholders, no dividends, no investor returns. Surplus goes to labor, training, resilience, and reinvestment. | |
| 75 | + | ||
| 76 | + | ### What Surplus Does Not Fund | |
| 77 | + | ||
| 78 | + | - Investor returns (we have no investors) | |
| 79 | + | - Stock buybacks (we have no stock) | |
| 80 | + | - Executive compensation beyond a livable wage | |
| 81 | + | - Acquisitions or expansion for expansion's sake | |
| 82 | + | - Marketing or paid user acquisition | |
| 83 | + | ||
| 84 | + | --- | |
| 85 | + | ||
| 86 | + | ## The Margin Question | |
| 87 | + | ||
| 88 | + | You might look at the per-creator margin table and think: "You're making $14-16 on my $20 subscription. That's a 70-80% margin. Isn't that a lot?" | |
| 89 | + | ||
| 90 | + | It is, relative to cost of goods. But those margins fund everything listed above — not just the infrastructure to serve your content, but the salary of the person maintaining the platform, the legal costs of operating a payment-adjacent business, the reserves against a bad quarter, and the development of new features you'll use next year. | |
| 91 | + | ||
| 92 | + | A percentage-cut platform hides this math. When Bandcamp takes 15% of a $10 album sale, you don't see their cost structure — you just see $1.50 disappear. Our model puts the cost on the table: here's what you pay, here's what it costs us, here's where the rest goes. | |
| 93 | + | ||
| 94 | + | We think this is more honest, even when the margins look wide. The alternative is a percentage cut that scales with your success and funds the same things less transparently. | |
| 95 | + | ||
| 96 | + | --- | |
| 97 | + | ||
| 98 | + | ## Ownership | |
| 99 | + | ||
| 100 | + | Makenot.work will never be sold to private equity, a competitor, or any outside buyer. The only future owners of this company are the people who use it. | |
| 101 | + | ||
| 102 | + | We don't have the details yet — the legal structure, the governance model, the transition timeline. These are big decisions that require legal advice and real thought, and we'd rather get them right than announce something premature. What we can commit to now is the boundary: no outside sale, ever. When we're ready to figure out the rest, we'll do it with the community, not behind closed doors. | |
| 103 | + | ||
| 104 | + | --- | |
| 105 | + | ||
| 106 | + | ## Why These Prices Won't Go Up | |
| 107 | + | ||
| 108 | + | The most common way a platform raises prices is that it was never sustainable at its original prices. Ride-sharing companies burned billions in investor money to keep fares artificially low, then raised them once competition was gone. Streaming services launched at a loss to build subscriber counts, then hiked prices once everyone was locked in. | |
| 109 | + | ||
| 110 | + | We are not doing that. Here's why: | |
| 111 | + | ||
| 112 | + | **There's no hidden subsidy.** The platform is self-funded from personal savings, not investor money being burned down. Both the founder and the company are completely debt-free — no loans, no lines of credit, no financial obligations beyond operating costs. Current prices cover current costs with room to spare. There is no cliff where the real economics kick in. | |
| 113 | + | ||
| 114 | + | **Margins widen with growth, they don't shrink.** Fixed costs barely move as the creator count grows — the same servers, the same monitoring, the same legal and compliance overhead. Each new subscription adds mostly margin. A platform with 500 members costs roughly the same to operate as one with 100, but earns five times as much. | |
| 115 | + | ||
| 116 | + | **We don't have the costs that force other platforms to raise prices.** No sales team. No office. No investor returns. No algorithmic infrastructure. No paid user acquisition funnels. These are the line items that eat margins at other companies and eventually get passed to users. We spend money on things like sponsoring events, hackathons, and community programs — but that's a rounding error compared to the growth marketing budgets that force other platforms to raise prices. | |
| 117 | + | ||
| 118 | + | **Hiring is funded by the surplus, not by price increases.** The margin between what you pay and what your tier costs to serve is wide enough to fund salaries, reserves, and development at current prices. We don't need more revenue per creator to grow the team — we need more creators at the same price. | |
| 119 | + | ||
| 120 | + | **Hosting costs trend down, not up.** Storage, bandwidth, and compute have gotten cheaper every year for two decades. CDN pricing is a commodity. Cloud providers compete on price. The long-term trend works in our favor, not against it. | |
| 121 | + | ||
| 122 | + | None of this is a guarantee against the unexpected — a payment processor doubling its fees or a regulatory change could force adjustments. But the ordinary pressures that cause platforms to raise prices (growth costs, investor returns, executive compensation, marketing budgets) don't apply here. The model works at current prices with room to grow. | |
| 123 | + | ||
| 124 | + | --- | |
| 125 | + | ||
| 126 | + | ## Price Stability | |
| 127 | + | ||
| 128 | + | We will not raise prices beyond inflation unless there are substantial changes in the hosting and delivery infrastructure market. The things that would justify a price increase: | |
| 129 | + | ||
| 130 | + | - Major cloud providers significantly increase storage or bandwidth pricing | |
| 131 | + | - CDN costs rise due to market consolidation or regulatory changes | |
| 132 | + | - Payment processors increase their fees in ways we can't absorb | |
| 133 | + | - New compliance requirements add unavoidable costs | |
| 134 | + | ||
| 135 | + | The things that will never justify a price increase: | |
| 136 | + | ||
| 137 | + | - We want to grow faster | |
| 138 | + | - We want to hire more people | |
| 139 | + | - Competitors charge more | |
| 140 | + | ||
| 141 | + | If we ever raise prices, we'll give 90 days notice, explain exactly what changed, and grandfather existing members at their current rate for at least 12 months. | |
| 142 | + | ||
| 143 | + | --- | |
| 144 | + | ||
| 145 | + | ## See Also | |
| 146 | + | ||
| 147 | + | - [How We Work](./how-we-work.md) — Business model and pricing | |
| 148 | + | - [What We Guarantee](./guarantees.md) — Binding commitments, in writing | |
| 149 | + | - [Roadmap](./roadmap.md) — What we're building next |
| @@ -0,0 +1,130 @@ | |||
| 1 | + | # Generative AI Policy | |
| 2 | + | ||
| 3 | + | How Makenot.work defines generative AI, why it matters, and how it affects what you see and sell on the platform. | |
| 4 | + | ||
| 5 | + | --- | |
| 6 | + | ||
| 7 | + | ## Why This Policy Exists | |
| 8 | + | ||
| 9 | + | Most generative AI tools are built on training sets that contain copyrighted work used without permission or payment. Artists, musicians, writers, and developers whose work was scraped to build these models were never asked and never compensated. This policy exists because we think that matters. | |
| 10 | + | ||
| 11 | + | We are not against new tools. We are against tools built on mass uncompensated use of other people's work. If that changes — if models start being trained on properly licensed, compensated datasets — this policy will evolve. Until then, the default is skepticism. | |
| 12 | + | ||
| 13 | + | --- | |
| 14 | + | ||
| 15 | + | ## What We Mean by "Generative AI" | |
| 16 | + | ||
| 17 | + | On this platform, **generative AI** refers to any tool whose existence depends on training sets that are either: | |
| 18 | + | ||
| 19 | + | 1. **Not publicly disclosed**, or | |
| 20 | + | 2. **Known to contain copyrighted material used without compensation** to the rights holders | |
| 21 | + | ||
| 22 | + | This includes derivative products — any application, plugin, or service that relies on the API or model weights of a covered tool. If the underlying model is covered, the wrapper is covered. "I didn't use ChatGPT, I used an app that calls the ChatGPT API" is the same thing. | |
| 23 | + | ||
| 24 | + | ### What this covers | |
| 25 | + | ||
| 26 | + | - Large language models with undisclosed or disputed training data (GPT, Claude, Llama, Mistral, etc.) | |
| 27 | + | - Image generators trained on scraped artwork (Midjourney, DALL-E, Stable Diffusion, etc.) | |
| 28 | + | - Music generators trained on copyrighted recordings (Suno, Udio, etc.) | |
| 29 | + | - Code generators trained on public repositories without clear contributor consent (GitHub Copilot, etc.) | |
| 30 | + | - Any application, plugin, or service built on top of these models via their APIs | |
| 31 | + | ||
| 32 | + | ### What this does not cover | |
| 33 | + | ||
| 34 | + | - Tools trained entirely on the creator's own work | |
| 35 | + | - Tools trained on verifiably public domain material | |
| 36 | + | - Tools with fully disclosed training sets composed of properly licensed, compensated content | |
| 37 | + | - Traditional automation and digital tools: EQ, compression, batch processing, non-generative spell check, programmatic effects, scripting, macros | |
| 38 | + | ||
| 39 | + | The distinction is not about what the tool does. It is about how the tool was built. | |
| 40 | + | ||
| 41 | + | --- | |
| 42 | + | ||
| 43 | + | ## The Three Tiers | |
| 44 | + | ||
| 45 | + | Every item published on Makenot.work must declare one of three tiers. This is required at publish time — there is no unlabeled option. | |
| 46 | + | ||
| 47 | + | ### Handmade | |
| 48 | + | ||
| 49 | + | No generative AI tools were used at any stage of creating this product — from brainstorming to final output. Traditional digital tools (DAWs, image editors, IDEs, effects processors) are fine. The line is generative AI as defined above. | |
| 50 | + | ||
| 51 | + | ### Assisted | |
| 52 | + | ||
| 53 | + | This product was created by a human with generative AI tools as part of the process. Choosing this tier requires a **disclosure statement**: a brief, honest explanation of what tools were used, for what purpose, and at what stage. This disclosure is visible to fans on the item page before purchase. | |
| 54 | + | ||
| 55 | + | Examples of good disclosure: | |
| 56 | + | - "I used GPT-4 to help outline the chapter structure. All writing is mine." | |
| 57 | + | - "Background textures were generated with Stable Diffusion and painted over. Character art and environments are hand-drawn." | |
| 58 | + | - "Code completion (Copilot) was used during development. Game design, art, and music are original." | |
| 59 | + | ||
| 60 | + | The disclosure must accurately represent the scope of AI use. If you say you used AI for brainstorming but the final product contains AI-generated art, that is misrepresentation and will be treated as fraud under our [Acceptable Use Policy](../legal/acceptable-use.md). | |
| 61 | + | ||
| 62 | + | ### Generated | |
| 63 | + | ||
| 64 | + | This product was primarily generated by AI tools. The human contribution is direction, curation, or assembly rather than direct authorship of the creative output. | |
| 65 | + | ||
| 66 | + | --- | |
| 67 | + | ||
| 68 | + | ## How Fans Use This | |
| 69 | + | ||
| 70 | + | Fans can filter what they see on the platform: | |
| 71 | + | ||
| 72 | + | - **Handmade only** — only items with no generative AI involvement | |
| 73 | + | - **Human-led** — Handmade and Assisted items (excludes Generated) | |
| 74 | + | - **Everything** — no filter | |
| 75 | + | ||
| 76 | + | These filters apply to the Discover page, search results, and tag browsing. The tier is also displayed on every item and project page. | |
| 77 | + | ||
| 78 | + | --- | |
| 79 | + | ||
| 80 | + | ## Enforcement | |
| 81 | + | ||
| 82 | + | The tier system is self-reported by creators. We enforce it through: | |
| 83 | + | ||
| 84 | + | 1. **The disclosure statement.** Assisted-tier creators put their AI use in writing. If the actual product contradicts the disclosure, that is documented misrepresentation. | |
| 85 | + | 2. **Community reports.** Fans and fellow creators can flag items they believe are misclassified. | |
| 86 | + | 3. **Moderation review.** Misrepresenting your tier — particularly claiming Handmade when generative AI was used — is treated as fraud. Consequences follow the [Acceptable Use Policy](../legal/acceptable-use.md), up to and including account termination. | |
| 87 | + | ||
| 88 | + | We would rather have honest disclosure than a platform full of false Handmade claims. If you used AI, say so, explain how, and let your audience decide. That is the point of the Assisted tier. | |
| 89 | + | ||
| 90 | + | --- | |
| 91 | + | ||
| 92 | + | ## AI in the Platform Itself | |
| 93 | + | ||
| 94 | + | We will never build generative AI tools or features into the product you use. No AI writing assistants, no AI-generated thumbnails, no AI recommendations. The platform you interact with will always be free of generative AI. | |
| 95 | + | ||
| 96 | + | **Discovery** on Makenot.work is an explicit function of your active choices — purchases, follows, likes, search, and tags. We do not track your behavior to build a profile, and we do not use machine learning to guess what you might want. What you see is determined by what you've told us you care about, not by a model. The discovery and feed algorithms are public and readable: | |
| 97 | + | ||
| 98 | + | - [discover.rs](https://makenot.work/git/max/makenotwork/tree/main/server/src/db/discover.rs) — Search, browse, and all filtering/sorting logic for the Discover page. Uses PostgreSQL trigram matching for fuzzy text search. Sorting is newest, popular (by sales count), or price — no behavioral signals. | |
| 99 | + | - [follows.rs](https://makenot.work/git/max/makenotwork/tree/main/server/src/db/follows.rs) — Feed generation. Your feed shows items from users, projects, and tags you follow, ordered newest first. No algorithmic reordering. | |
| 100 | + | - [tags.rs](https://makenot.work/git/max/makenotwork/tree/main/server/src/db/tags.rs) — Tag facets and hierarchical browsing. Tag counts reflect actual item counts, not weighted relevance. | |
| 101 | + | ||
| 102 | + | **Security and spam filtering** are the exception to a blanket no-ML stance. We will always use the best available tools to protect the platform and its users. At the time of writing, our security and spam toolchains do not use LLMs or generative AI, but we reserve the right to adopt whatever keeps the platform safe. We will never use these tools to influence what you see or recommend content. | |
| 103 | + | ||
| 104 | + | **Platform development** uses LLM-assisted tooling — most of the code is written with the help of tools that fall under our own generative AI definition. We are transparent about this in our commit history. We think there is a meaningful difference between using these tools to build infrastructure and selling their output as creative work, and our tier system reflects that distinction. | |
| 105 | + | ||
| 106 | + | A note from the founder: | |
| 107 | + | ||
| 108 | + | > The honest truth is that I would prefer not to use these tools. In my non-legal opinion, they are unethically built. They are also, frankly, mind-numbingly boring. At the same time, they allow us to write simple code and features at an impressive pace. As the codebase grows, they become less useful and less coherent. Already, before we are out of beta, their usage is diminishing in favor of a more human approach as the focus moves from scaffolding to testing and refinement. We will always be honest about what we use on our end. I will always try to be honest about how I feel about those tools. And as always, we would love to someday replace them with tools that we make or that our community makes — tools that are well-built, ethical, and that I can feel good about using. | |
| 109 | + | > | |
| 110 | + | > — Max | |
| 111 | + | ||
| 112 | + | --- | |
| 113 | + | ||
| 114 | + | ## This Definition Will Change | |
| 115 | + | ||
| 116 | + | The generative AI landscape moves fast. New models launch, training data disclosures shift, lawsuits settle, licensing norms emerge. This policy is a living document. When we update it, we will: | |
| 117 | + | ||
| 118 | + | - Publish the change with a version date | |
| 119 | + | - Explain what changed and why | |
| 120 | + | - Give creators reasonable time to update their tier classifications if the definitions shift | |
| 121 | + | ||
| 122 | + | If a model that is currently covered demonstrates verifiable, fully compensated training data in the future, it may be reclassified. The principle stays the same: we follow the ethics of the training data, not the capabilities of the tool. | |
| 123 | + | ||
| 124 | + | --- | |
| 125 | + | ||
| 126 | + | ## See Also | |
| 127 | + | ||
| 128 | + | - [Acceptable Use Policy](../legal/acceptable-use.md) — What happens when rules are broken | |
| 129 | + | - [Our Story](./story.md) — Why this platform exists | |
| 130 | + | - [What We Guarantee](./guarantees.md) — Binding commitments, in writing |
| @@ -1,6 +1,6 @@ | |||
| 1 | - | # Service Level Agreement | |
| 1 | + | # What We Guarantee | |
| 2 | 2 | ||
| 3 | - | Binding commitments from Makenot.work to every creator on the platform. These are not aspirations — they are verifiable guarantees backed by public source code and versioned documentation. If we break them, you can see it in the code and export your data immediately. | |
| 3 | + | Things we promise, in writing, that you can verify in the code. If we break any of these, you can see it and leave. | |
| 4 | 4 | ||
| 5 | 5 | --- | |
| 6 | 6 | ||
| @@ -10,9 +10,9 @@ Binding commitments from Makenot.work to every creator on the platform. These ar | |||
| 10 | 10 | ||
| 11 | 11 | - No platform percentage cut, ever. | |
| 12 | 12 | - No transaction fees, payout fees, or skimming. | |
| 13 | - | - This is a permanent structural commitment, not a promotional rate. | |
| 13 | + | - This is how it works, not a promotional rate. | |
| 14 | 14 | ||
| 15 | - | **Your subscription and your fan revenue are separate.** Your $10-40/month covers platform access. Revenue from fans is untouched. | |
| 15 | + | Your $10-40/month covers platform access. Revenue from fans is untouched. | |
| 16 | 16 | ||
| 17 | 17 | --- | |
| 18 | 18 | ||
| @@ -69,6 +69,8 @@ If you leave, your fans come with you. | |||
| 69 | 69 | 4. Fan notification — we help communicate the transition to your audience. | |
| 70 | 70 | 5. Source code remains available for reference. | |
| 71 | 71 | ||
| 72 | + | **What it costs to keep the lights on during shutdown:** Platform infrastructure runs on roughly $600/month in fixed costs — a single application server, database, object storage, and DNS. A full 90-day wind-down costs under $2,000 in hosting. Fan payments go directly to creator-controlled Stripe accounts, so there are no pending payouts to settle on our side — Stripe handles that independently. The founder and the company carry zero debt, and the platform is backed by personal savings sufficient to cover years of operation at current costs, with no investors or creditors who could force an early shutdown. A worst-case wind-down is a small, manageable expense — not a financial crisis. | |
| 73 | + | ||
| 72 | 74 | --- | |
| 73 | 75 | ||
| 74 | 76 | ## Price Stability | |
| @@ -105,11 +107,11 @@ No browsing profiles. No behavioral tracking. No selling data. Verifiable in the | |||
| 105 | 107 | ||
| 106 | 108 | ## Enforcement | |
| 107 | 109 | ||
| 108 | - | These guarantees are enforceable through three mechanisms: | |
| 110 | + | If you think we've broken any of these: | |
| 109 | 111 | ||
| 110 | - | 1. **Source code:** Technical claims are verifiable in the public codebase. | |
| 111 | - | 2. **Published documentation:** This document is versioned and timestamped. | |
| 112 | - | 3. **Structural accountability:** Our business model depends on honoring these commitments. Breaking them would be self-destructive. | |
| 112 | + | 1. **Check the code.** Technical claims are verifiable in the public codebase. | |
| 113 | + | 2. **Check the docs.** This document is versioned and timestamped. | |
| 114 | + | 3. **Leave.** Export your data, take your audience, and cancel. We've made that as easy as possible because we'd rather you be able to leave than feel trapped. | |
| 113 | 115 | ||
| 114 | 116 | --- | |
| 115 | 117 | ||
| @@ -119,7 +121,7 @@ The following guarantees are commitments we are building toward. They are not ye | |||
| 119 | 121 | ||
| 120 | 122 | ### Content Archive | |
| 121 | 123 | ||
| 122 | - | *The 12-month clock will not start counting until after the alpha period ends.* | |
| 124 | + | *Launching before we leave beta — among the first priorities after our initial round of community engagement and testing.* | |
| 123 | 125 | ||
| 124 | 126 | Any content that has existed on the platform for 12 months or more (not including comped months from the earn-back credit program) will remain hosted and accessible to fans even if the creator stops paying for their account. | |
| 125 | 127 |
| @@ -71,7 +71,7 @@ Your subscription funds the platform. We have no reason to take a cut of your re | |||
| 71 | 71 | ||
| 72 | 72 | Every tier also includes a separate download budget for general-purpose compressed downloads (zips, bundles, supplementary materials). Big Files and Streaming creators can request a per-file size increase beyond 20GB from their dashboard. | |
| 73 | 73 | ||
| 74 | - | The prices reflect what it actually costs to store and deliver each content type. | |
| 74 | + | The prices reflect what it actually costs to store and deliver each content type. Use the [pricing calculator](/pricing) to see exactly what you'd keep at any revenue level, with a side-by-side comparison against other platforms. | |
| 75 | 75 | ||
| 76 | 76 | ### Choosing Your Tier | |
| 77 | 77 | ||
| @@ -104,7 +104,7 @@ The prices reflect what it actually costs to store and deliver each content type | |||
| 104 | 104 | ||
| 105 | 105 | ### Earn-Back Credit Program | |
| 106 | 106 | ||
| 107 | - | *Not yet implemented. The 12-month clock will not start counting until after the alpha period ends.* | |
| 107 | + | *Launching before we leave beta — among the first priorities after our initial round of community engagement and testing.* | |
| 108 | 108 | ||
| 109 | 109 | If you earn less on the platform than you paid in subscription fees during a 12-month period, the difference will be credited as free months for the following year (capped at 12 months). Credits will be calculated annually on your account anniversary. | |
| 110 | 110 | ||
| @@ -119,7 +119,7 @@ Base tiers include all platform software features. Some capabilities with real m | |||
| 119 | 119 | - No setup fees, no hidden charges | |
| 120 | 120 | - Upgrade or downgrade at any time | |
| 121 | 121 | ||
| 122 | - | See our [Service Level Agreement](./guarantees.md) for pricing commitments. | |
| 122 | + | See our [written guarantees](./guarantees.md) for pricing commitments. | |
| 123 | 123 | ||
| 124 | 124 | --- | |
| 125 | 125 | ||
| @@ -142,47 +142,65 @@ Your audience is your business. We facilitate the connection; you own it. | |||
| 142 | 142 | ||
| 143 | 143 | ### Content Archive Policy | |
| 144 | 144 | ||
| 145 | - | *Not yet implemented. The 12-month clock will not start counting until after the alpha period ends.* | |
| 145 | + | *Launching before we leave beta — among the first priorities after our initial round of community engagement and testing.* | |
| 146 | 146 | ||
| 147 | - | Content that has been on the platform for 12 months or more (not including comped months from the earn-back credit program) stays hosted even if you cancel. Your fans keep access. You just can't upload new content without reactivating. See our [Service Level Agreement](./guarantees.md) for the full commitment. | |
| 147 | + | Content that has been on the platform for 12 months or more (not including comped months from the earn-back credit program) stays hosted even if you cancel. Your fans keep access. You just can't upload new content without reactivating. See our [written guarantees](./guarantees.md) for the full commitment. | |
| 148 | 148 | ||
| 149 | 149 | --- | |
| 150 | 150 | ||
| 151 | - | ## Discovery: Intentional | |
| 151 | + | ## No Algorithm | |
| 152 | 152 | ||
| 153 | - | We use search and hierarchical tags. Fans find content because they're looking for it. | |
| 153 | + | There is no recommendation engine. No trending page. No feed designed to maximize engagement. Fans find your work through search, tags, and links — the same way people find anything worth finding on the internet. | |
| 154 | 154 | ||
| 155 | - | This means you need to bring an audience (or build one elsewhere). We're infrastructure for creator-fan relationships, not a discovery engine. | |
| 155 | + | This means you need to bring an audience or build one elsewhere. We host and sell your work. We don't pretend to be a marketing department. | |
| 156 | + | ||
| 157 | + | --- | |
| 158 | + | ||
| 159 | + | ## Community | |
| 160 | + | ||
| 161 | + | Every project can have a discussion forum powered by [Multithreaded](https://forums.makenot.work) — threaded conversations with no algorithmic sorting, built-in moderation tools, and a public mod log. It's a place for your audience to talk to each other and to you, without a third-party platform owning the conversation. | |
| 162 | + | ||
| 163 | + | The platform-wide forum is where feedback, feature requests, and creator-to-creator discussion happen. See [Forums](../support/forums.md). | |
| 156 | 164 | ||
| 157 | 165 | --- | |
| 158 | 166 | ||
| 159 | 167 | ## Source Available | |
| 160 | 168 | ||
| 161 | - | Source code is publicly available. You can audit our privacy practices, data handling, and every claim on this site by reading the code. | |
| 169 | + | The source code is public. Read it if you want to verify anything we say on this site. | |
| 170 | + | ||
| 171 | + | We use the PolyForm Noncommercial license rather than a permissive license like MIT or GPL. The reason is simple: permissive licenses let corporations take community-built software and use it to compete against the people who made it, without giving anything back. PolyForm Noncommercial means anyone can read, audit, and learn from the code — but no company can clone this platform and run it for profit. | |
| 172 | + | ||
| 173 | + | Your content is never used to train AI models. See our [Generative AI Policy](./generative-ai.md) for how we define generative AI and how it affects what's allowed on the platform. | |
| 162 | 174 | ||
| 163 | 175 | --- | |
| 164 | 176 | ||
| 165 | 177 | ## Self-Funded | |
| 166 | 178 | ||
| 167 | - | No outside investors means no one can force us to squeeze creators. Without investor timelines, we can keep prices stable for years, add features when they're ready, say no to profitable-but-harmful choices, and shut down gracefully if needed. | |
| 179 | + | No outside investors. No debt — the founder and the company both carry a zero balance. Nobody can make us raise prices, add ads, or sell the company. There are no creditors, no loan payments, and no financial obligations beyond operating costs. If we can't sustain this on subscriptions, we'll wind down honestly — not pivot into something exploitative. | |
| 180 | + | ||
| 181 | + | Prices won't go up unless infrastructure costs force it. If they ever do, we'll say exactly what changed, give 90 days notice, and grandfather everyone at their current rate. | |
| 182 | + | ||
| 183 | + | ### Where Your Money Goes | |
| 168 | 184 | ||
| 169 | - | Our commitment: we will never raise prices beyond inflation unless there are substantial changes in the hosting and delivery infrastructure market. If we ever do, we'll explain exactly what changed, give 90 days notice, and grandfather existing creators. | |
| 185 | + | Surplus from subscriptions funds, in order: a livable wage for the maintainer, hiring, reserves, and development. The first hires will come through a residency program — training people without traditional credentials into full-stack engineers by working across the real codebase. The goal is graduation, not retention. Your subscription funds real jobs and real training, not a pipeline optimized for people who already have access. See [Platform Economics](./economics.md) for the full breakdown. | |
| 170 | 186 | ||
| 171 | 187 | --- | |
| 172 | 188 | ||
| 173 | 189 | ## Who This Is For | |
| 174 | 190 | ||
| 175 | - | Creators who: | |
| 191 | + | Artists, musicians, writers, developers, and makers who: | |
| 192 | + | - Are tired of platforms that profit more from their work than they do | |
| 176 | 193 | - Have an audience (even small) or are building one through other channels | |
| 177 | - | - Want to start their projects on a pro-creator platform | |
| 178 | - | - Want direct relationships with fans | |
| 179 | - | - Value ownership over convenience | |
| 180 | - | - Prefer predictable costs over revenue sharing | |
| 194 | + | - Want direct relationships with the people who support their work | |
| 195 | + | - Want to own their data, their audience, and their revenue | |
| 196 | + | - Prefer predictable costs over giving up a percentage of every sale | |
| 181 | 197 | - Care about privacy and transparency | |
| 182 | 198 | ||
| 183 | 199 | --- | |
| 184 | 200 | ||
| 185 | 201 | ## See Also | |
| 186 | 202 | ||
| 187 | - | - [Service Level Agreement](./guarantees.md) — Binding commitments | |
| 203 | + | - [Platform Economics](./economics.md) — What it costs to run, where the money goes | |
| 204 | + | - [Generative AI Policy](./generative-ai.md) — Content tiers and disclosure requirements | |
| 205 | + | - [What We Guarantee](./guarantees.md) — Binding commitments, in writing | |
| 188 | 206 | - [FAQ](../support/faq.md) — Quick answers |
| @@ -85,6 +85,7 @@ Everything listed here is live and working. | |||
| 85 | 85 | - **Rich link previews**: Your content shows up properly when shared on social media, search engines, and podcast apps | |
| 86 | 86 | - **Documentation**: Creator guide covering the full platform | |
| 87 | 87 | - **Transactional email**: Password reset, email verification, purchase receipts, subscription updates, sale and follower notifications | |
| 88 | + | - **Community forums**: Per-project discussion forums powered by [Multithreaded](https://forums.makenot.work) — threaded conversations, moderation tools, mod logs, no algorithmic sorting | |
| 88 | 89 | - **Git source browser**: Browse server-hosted repositories with syntax highlighting | |
| 89 | 90 | - **SSH git access**: Clone and push to hosted repositories with SSH key authentication | |
| 90 | 91 | - **Email-first issue tracker**: File issues and reply via email, close via commit message | |
| @@ -156,6 +157,8 @@ Expand payment options over time. ACH/SEPA payouts, lower-cost processors, micro | |||
| 156 | 157 | - DRM that punishes paying customers | |
| 157 | 158 | - Features that create platform lock-in | |
| 158 | 159 | - Attention-harvesting engagement tricks | |
| 160 | + | - Generative AI tools or features for users — no AI writing assistants, no AI-generated recommendations, no AI anything in the product you interact with | |
| 161 | + | - Algorithmic discovery based on tracked behavior — what you see is a direct function of your purchases, follows, and likes, not a model's guess about what you might engage with | |
| 159 | 162 | ||
| 160 | 163 | --- | |
| 161 | 164 | ||
| @@ -170,4 +173,4 @@ We'd rather build what creators actually need than guess. If something on this p | |||
| 170 | 173 | ## See Also | |
| 171 | 174 | ||
| 172 | 175 | - [How We Work](./how-we-work.md) — Business model and pricing | |
| 173 | - | - [Service Level Agreement](./guarantees.md) — Binding commitments | |
| 176 | + | - [What We Guarantee](./guarantees.md) — Binding commitments, in writing |
| @@ -22,9 +22,9 @@ Your subscription funds the platform. We have no financial incentive to take a c | |||
| 22 | 22 | ||
| 23 | 23 | Makenot.work is self-funded. No venture capital, no angel investors, no outside money. One person built it, one person runs it. | |
| 24 | 24 | ||
| 25 | - | This is a deliberate choice with real consequences. Growth is slower. Features take longer. There's no marketing budget, no sales team, no growth hacks. | |
| 25 | + | Growth is slower. Features take longer. There's no marketing budget, no sales team, no growth hacks. | |
| 26 | 26 | ||
| 27 | - | But it also means: no board meetings about monetization strategy. No pressure to raise prices. No investors pushing for an exit. No acqui-hire that shuts everything down. We can keep prices stable, add features when they're ready, and say no to profitable-but-harmful choices. | |
| 27 | + | But there are also no board meetings about monetization strategy. No pressure to raise prices. No investors pushing for an exit. No acqui-hire that shuts everything down. Prices stay stable, features ship when they're ready, and nobody can force us to make profitable-but-harmful choices. | |
| 28 | 28 | ||
| 29 | 29 | If the platform can't sustain itself on subscriptions, it will wind down honestly, not pivot into something exploitative. | |
| 30 | 30 | ||
| @@ -32,18 +32,22 @@ If the platform can't sustain itself on subscriptions, it will wind down honestl | |||
| 32 | 32 | ||
| 33 | 33 | The source code is publicly available under the PolyForm Noncommercial license. You can read every line. Audit our privacy claims. Verify that we handle data the way we say we do. See exactly what happens when you upload a file, make a purchase, or delete your account. | |
| 34 | 34 | ||
| 35 | - | This isn't a marketing gesture. It's accountability. When we say "we don't track you" or "your data is exportable," you can check. | |
| 35 | + | When we say "we don't track you" or "your data is exportable," you can check. That's the point. | |
| 36 | 36 | ||
| 37 | - | ## What We Believe | |
| 37 | + | ## What This Means in Practice | |
| 38 | 38 | ||
| 39 | - | Platforms should be infrastructure, not landlords. A good platform does three things: hosts your content reliably, processes payments honestly, and stays out of the way. Everything else is the platform serving its own interests, not yours. | |
| 39 | + | A good platform does three things: hosts your content reliably, processes payments honestly, and stays out of the way. Everything else is the platform serving its own interests, not yours. | |
| 40 | 40 | ||
| 41 | 41 | Your fans are your fans. Your revenue is your revenue. Your data is your data. A platform that respects this doesn't need to take a percentage, because it doesn't pretend to own the relationship between you and the people who support your work. | |
| 42 | 42 | ||
| 43 | + | Platforms should be infrastructure, not landlords. | |
| 44 | + | ||
| 43 | 45 | --- | |
| 44 | 46 | ||
| 45 | 47 | ## See Also | |
| 46 | 48 | ||
| 47 | 49 | - [How We Work](./how-we-work.md) — Pricing, payment flow, and data portability | |
| 48 | - | - [Service Level Agreement](./guarantees.md) — Binding commitments | |
| 50 | + | - [Platform Economics](./economics.md) — What it costs to run, where the money goes | |
| 51 | + | - [Generative AI Policy](./generative-ai.md) — What we mean by generative AI and how content is classified | |
| 52 | + | - [What We Guarantee](./guarantees.md) — Binding commitments, in writing | |
| 49 | 53 | - [Roadmap](./roadmap.md) — What we're building next |
| @@ -45,6 +45,14 @@ Add links to your website, social media, other platforms, or booking/contact inf | |||
| 45 | 45 | ||
| 46 | 46 | Don't overthink this. If you have a website, link it. If you're active on social media, link your main one or two. Booking inquiries? Add an email link. The goal is to let fans find you elsewhere when they want to. | |
| 47 | 47 | ||
| 48 | + | ## Customizing Your Storefront | |
| 49 | + | ||
| 50 | + | Your profile isn't a template — it's a canvas. You can write custom CSS and rearrange elements on your page to make it look and feel like your own site, within a structure that keeps the experience consistent for fans browsing the platform. | |
| 51 | + | ||
| 52 | + | Think of it like the old MySpace or early YouTube: the platform provides the frame, you control the aesthetic. Change colors, fonts, spacing, and layout to match your visual identity. The consistent navigation and purchase flow stay intact so fans always know how to find, buy, and access your work — but everything in between is yours to shape. | |
| 53 | + | ||
| 54 | + | You don't need to know CSS to have a good-looking page. The defaults work. But if you want your storefront to feel like *yours* rather than a profile on someone else's site, the tools are there. | |
| 55 | + | ||
| 48 | 56 | ## What Fans See | |
| 49 | 57 | ||
| 50 | 58 | When someone visits your profile, they see: |
| @@ -1,16 +1,20 @@ | |||
| 1 | 1 | # Content Moderation & Enforcement | |
| 2 | 2 | ||
| 3 | - | How we balance creative freedom with maintaining a platform free from harassment, and what happens when accounts violate our policies. | |
| 3 | + | What happens when accounts violate our policies, and what you should know about how moderation works right now. | |
| 4 | 4 | ||
| 5 | 5 | --- | |
| 6 | 6 | ||
| 7 | - | ## Our Approach | |
| 7 | + | ## Current Limitations | |
| 8 | + | ||
| 9 | + | Moderation is currently handled by one person. That means decisions are fast but not independently reviewable. We know this is a weakness — independent appeal review is a planned commitment in our [written guarantees](../about/guarantees.md#planned-guarantees), and hiring a second person is the top financial priority. Until then, moderation decisions are made directly and in good faith, and every decision can be appealed. | |
| 8 | 10 | ||
| 9 | - | We have a dual mandate: supporting creative expression while ensuring Makenot.work remains a space free from harassment and harm. | |
| 11 | + | We'd rather be honest about this than pretend we have a trust and safety team. | |
| 10 | 12 | ||
| 11 | - | We enforce policies against harassment, illegal content, and fraud to keep the platform safe for creators and fans. We reserve the right to refuse service to creators whose conduct harms this environment. | |
| 13 | + | --- | |
| 14 | + | ||
| 15 | + | ## Our Approach | |
| 12 | 16 | ||
| 13 | - | **Context and intent matter.** We evaluate work as a whole, not isolated elements. Satire, critique, historical documentation, and artistic exploration are considered in full context. | |
| 17 | + | Context and intent matter. We evaluate work as a whole, not isolated elements. Satire, critique, historical documentation, and artistic exploration are considered in full context. We enforce policies against harassment, illegal content, and fraud. We reserve the right to refuse service to anyone whose conduct makes the platform worse for everyone else. | |
| 14 | 18 | ||
| 15 | 19 | --- | |
| 16 | 20 |
| @@ -119,4 +119,4 @@ Data protection inquiries: dpo@makenot.work | |||
| 119 | 119 | ||
| 120 | 120 | - [Terms of Service](./terms-of-service.md) — Full legal terms | |
| 121 | 121 | - [Acceptable Use Policy](./acceptable-use.md) — Content rules | |
| 122 | - | - [Service Level Agreement](../about/guarantees.md) — Data export and portability commitments | |
| 122 | + | - [What We Guarantee](../about/guarantees.md) — Data export and portability commitments |
| @@ -98,5 +98,5 @@ Questions? Email legal@makenot.work. | |||
| 98 | 98 | ||
| 99 | 99 | - [Privacy Policy](./privacy-policy.md) — Data collection and handling | |
| 100 | 100 | - [Acceptable Use Policy](./acceptable-use.md) — Content rules | |
| 101 | - | - [Service Level Agreement](../about/guarantees.md) — Platform commitments | |
| 101 | + | - [What We Guarantee](../about/guarantees.md) — Platform commitments | |
| 102 | 102 | - [How We Work](../about/how-we-work.md) — Business model and pricing |
| @@ -6,7 +6,7 @@ One person runs this platform. Responses are direct and personal. | |||
| 6 | 6 | ||
| 7 | 7 | Your answer might already be here: | |
| 8 | 8 | - [FAQ](./faq.md) — common questions and hard questions | |
| 9 | - | - [Service Level Agreement](../about/guarantees.md) — what we commit to | |
| 9 | + | - [What We Guarantee](../about/guarantees.md) — what we commit to | |
| 10 | 10 | - [How We Work](../about/how-we-work.md) — pricing, payments, data export | |
| 11 | 11 | ||
| 12 | 12 |
| @@ -25,13 +25,13 @@ Yes. Fan accounts are completely free. You only pay for content you choose to bu | |||
| 25 | 25 | Through our payment system. Payments go directly to your linked bank account. You control payout timing. | |
| 26 | 26 | ||
| 27 | 27 | ### What if I earn nothing — do I still pay? | |
| 28 | - | Yes. The subscription covers platform access, not a share of revenue. We may accept applications for fee remission for supported causes and open source work. | |
| 28 | + | Yes. The subscription covers platform access, not a share of revenue. We know $10/month is real money if you're just starting out or making work that doesn't sell in volume. That's a real tension with this model, and we don't pretend otherwise. The tradeoff is that when you do sell, you keep everything — there's no percentage cut that grows as you grow. We're also building an [earn-back credit program](#what-if-i-dont-earn-back-my-subscription-cost) to soften this for artists whose revenue doesn't cover their subscription. We may accept applications for fee remission for supported causes and open source work. | |
| 29 | 29 | ||
| 30 | 30 | ### What if I don't earn back my subscription cost? | |
| 31 | 31 | We're planning an earn-back credit program: if you earn less on the platform than you paid in subscription fees over 12 months, the difference will be credited as free months for the following year (capped at 12 months). The 12-month clock will not start counting until after the alpha period ends. Note: comped months will not count toward content archive eligibility (see [Content Archive](../about/guarantees.md#planned-guarantees)). | |
| 32 | 32 | ||
| 33 | 33 | ### What's the catch? | |
| 34 | - | No catch. We make money from subscriptions, so our interests align with yours. We want you to succeed and stay. | |
| 34 | + | We make money from subscriptions regardless of whether you sell anything. That means we don't need to take a cut of your sales, show ads, or mine your data. Our incentive is to keep the platform working well enough that you don't cancel — which is the same as your incentive. | |
| 35 | 35 | ||
| 36 | 36 | ### Can I leave? | |
| 37 | 37 | Anytime. Export all your data, take your fans, cancel subscription. No lock-in, no exit fees. | |
| @@ -88,16 +88,16 @@ The code is public, the documentation is versioned, and you control your data at | |||
| 88 | 88 | Account info you provide, content you upload, transactions you conduct. No browsing profiles, no behavioral tracking. Verifiable in the source code. | |
| 89 | 89 | ||
| 90 | 90 | ### Will you ever take VC money? | |
| 91 | - | No. Outside investment creates pressure to maximize extraction. We answer to creators, not shareholders. | |
| 91 | + | No. Outside investment means someone eventually needs a return. Returns come from raising fees, adding ads, or selling data. We'd rather stay small and stay honest. | |
| 92 | 92 | ||
| 93 | 93 | ### Will you ever run ads? | |
| 94 | - | No. Advertising means optimizing for attention instead of creator success. | |
| 94 | + | No. Ads mean tracking. Tracking means your fans become the product. We'd rather charge you $10/month and leave your audience alone. | |
| 95 | 95 | ||
| 96 | 96 | ### Will you ever take a cut of revenue? | |
| 97 | - | No. This is the core commitment. Changing it would mean becoming a different platform entirely. | |
| 97 | + | No. If we did, we'd be a different platform — one that profits from your success instead of just hosting it. | |
| 98 | 98 | ||
| 99 | 99 | ### Will you ever sell the company? | |
| 100 | - | Not to private equity or competitors. The long-term plan is to sell the company to its creators. | |
| 100 | + | Makenot.work will never be sold to private equity, a competitor, or any outside buyer. The only people this company will ever be sold to are the artists and makers who use it. What that looks like — the legal structure, the governance, the timeline — is something we want to figure out carefully as the platform matures and stabilizes. We won't rush a half-baked answer to a question this important. What we will commit to now is the boundary: no outside sale, ever. | |
| 101 | 101 | ||
| 102 | 102 | ## Risk & Sustainability | |
| 103 | 103 | ||
| @@ -105,13 +105,13 @@ Not to private equity or competitors. The long-term plan is to sell the company | |||
| 105 | 105 | The payment processor holds funds in your account, not ours. No commingling of creator revenue with operating funds. A shutdown would not affect your payment account balance. The [SLA](../about/guarantees.md) guarantees 90 days notice with full export access maintained throughout. | |
| 106 | 106 | ||
| 107 | 107 | ### How long can you sustain this? | |
| 108 | - | A few years minimum at current costs. The model is profitable with a small number of creators relative to competitors. Self-funded, no debt. | |
| 108 | + | A few years minimum at current costs. The platform breaks even at roughly 36 creators with a realistic tier mix. Self-funded, no debt. See [Platform Economics](../about/economics.md) for the full cost structure and where the money goes. | |
| 109 | 109 | ||
| 110 | 110 | ### Is this a full-time operation? | |
| 111 | 111 | Yes. | |
| 112 | 112 | ||
| 113 | 113 | ### What if the founder is unable to run the platform? | |
| 114 | - | We'd accelerate the plan to sell the company to its creators. | |
| 114 | + | This is a real risk and we won't pretend otherwise. Right now, Makenot.work is one person. Adding a second person is the top financial priority once the platform has stable membership covering its operating costs. Until then, the mitigations are: source code is public, all data is exportable at any time, the [shutdown protocol](../about/guarantees.md#shutdown-protocol) guarantees 90 days notice with full export access, and your payment account funds are held separately from platform operating funds. The long-term goal is to sell the company to its creators, but that's a goal, not a guarantee. | |
| 115 | 115 | ||
| 116 | 116 | ## Content Policy | |
| 117 | 117 | ||
| @@ -119,7 +119,7 @@ We'd accelerate the plan to sell the company to its creators. | |||
| 119 | 119 | Content that dehumanizes, harasses, or incites violence. See [Acceptable Use](../legal/acceptable-use.md) for the full policy. | |
| 120 | 120 | ||
| 121 | 121 | ### What's your stance on AI-generated content? | |
| 122 | - | Creators can indicate what tools were used. AI disclosure isn't required, but creators who advertise as AI-free and aren't will be disciplined. | |
| 122 | + | Every item on the platform must declare one of three tiers: **Handmade** (no generative AI), **Assisted** (human-made with AI tools, disclosure required), or **Generated** (primarily AI output). Fans can filter to see only Handmade, only human-led work (Handmade + Assisted), or everything. Misrepresenting your tier is treated as fraud. See our full [Generative AI Policy](../about/generative-ai.md) for definitions, examples, and enforcement. | |
| 123 | 123 | ||
| 124 | 124 | ### Do you comply with law enforcement requests? | |
| 125 | 125 | We prioritize creator privacy, with exceptions for content that is illegal or threatens the platform's existence. We cannot share encryption keys because we don't have them. | |
| @@ -132,6 +132,6 @@ Still stuck? [Contact Support](./contact.md). | |||
| 132 | 132 | ||
| 133 | 133 | ## See Also | |
| 134 | 134 | ||
| 135 | - | - [Service Level Agreement](../about/guarantees.md) — Binding commitments | |
| 135 | + | - [What We Guarantee](../about/guarantees.md) — Binding commitments, in writing | |
| 136 | 136 | - [How We Work](../about/how-we-work.md) — Business model | |
| 137 | 137 | - [Contact](./contact.md) — Reach a human |
| @@ -151,7 +151,7 @@ macro_rules! bind_item_discover_filters { | |||
| 151 | 151 | /// | |
| 152 | 152 | /// When a search term is present but no explicit sort is requested, results | |
| 153 | 153 | /// are ordered by relevance (`match_score DESC`). Otherwise, the caller can | |
| 154 | - | /// choose `popular`, `price_asc`, `price_desc`, or the default `newest`. | |
| 154 | + | /// choose `most_sold`, `price_asc`, `price_desc`, or the default `newest`. | |
| 155 | 155 | #[tracing::instrument(skip_all)] | |
| 156 | 156 | pub async fn discover_items( | |
| 157 | 157 | pool: &PgPool, | |
| @@ -256,7 +256,7 @@ pub async fn discover_items( | |||
| 256 | 256 | "match_score DESC NULLS LAST, i.created_at DESC" | |
| 257 | 257 | } else { | |
| 258 | 258 | match filters.sort_by { | |
| 259 | - | Some(DiscoverSort::Popular) => "sales_count DESC, i.created_at DESC", | |
| 259 | + | Some(DiscoverSort::MostSold) => "sales_count DESC, i.created_at DESC", | |
| 260 | 260 | Some(DiscoverSort::PriceAsc) => "i.price_cents ASC, i.created_at DESC", | |
| 261 | 261 | Some(DiscoverSort::PriceDesc) => "i.price_cents DESC, i.created_at DESC", | |
| 262 | 262 | _ => "i.created_at DESC", |
| @@ -273,14 +273,14 @@ impl_str_enum!(AppealDecision { | |||
| 273 | 273 | #[serde(rename_all = "snake_case")] | |
| 274 | 274 | pub enum DiscoverSort { | |
| 275 | 275 | Newest, | |
| 276 | - | Popular, | |
| 276 | + | MostSold, | |
| 277 | 277 | PriceAsc, | |
| 278 | 278 | PriceDesc, | |
| 279 | 279 | } | |
| 280 | 280 | ||
| 281 | 281 | impl_str_enum!(DiscoverSort { | |
| 282 | 282 | Newest => "newest", | |
| 283 | - | Popular => "popular", | |
| 283 | + | MostSold => "most_sold", | |
| 284 | 284 | PriceAsc => "price_asc", | |
| 285 | 285 | PriceDesc => "price_desc", | |
| 286 | 286 | }); | |
| @@ -900,7 +900,7 @@ mod tests { | |||
| 900 | 900 | #[test] | |
| 901 | 901 | fn discover_sort_round_trip() { | |
| 902 | 902 | assert_eq!(DiscoverSort::Newest.to_string(), "newest"); | |
| 903 | - | assert_eq!("popular".parse::<DiscoverSort>().unwrap(), DiscoverSort::Popular); | |
| 903 | + | assert_eq!("most_sold".parse::<DiscoverSort>().unwrap(), DiscoverSort::MostSold); | |
| 904 | 904 | assert_eq!("price_asc".parse::<DiscoverSort>().unwrap(), DiscoverSort::PriceAsc); | |
| 905 | 905 | assert_eq!("price_desc".parse::<DiscoverSort>().unwrap(), DiscoverSort::PriceDesc); | |
| 906 | 906 | assert!("invalid".parse::<DiscoverSort>().is_err()); |
| @@ -179,6 +179,9 @@ define_pg_uuid_id!( | |||
| 179 | 179 | ItemSectionId, | |
| 180 | 180 | ImportJobId, | |
| 181 | 181 | MediaFileId, | |
| 182 | + | TipId, | |
| 183 | + | ProjectMemberId, | |
| 184 | + | RevenueSplitId, | |
| 182 | 185 | ); | |
| 183 | 186 | ||
| 184 | 187 | #[cfg(test)] |