|
1 |
+ |
# MNW Business Assumptions — Machine-Readable Source of Truth
|
|
2 |
+ |
#
|
|
3 |
+ |
# Status: draft / not yet consumed by tooling. Target consumer is a future
|
|
4 |
+ |
# docengine feature that substitutes `{{ key }}` placeholders in markdown
|
|
5 |
+ |
# at build time. See `MNW/server/docs/todo.md` § "Docengine — assumption
|
|
6 |
+ |
# substitution" for the proposed implementation.
|
|
7 |
+ |
#
|
|
8 |
+ |
# Until that feature ships, this file is informational. The canonical
|
|
9 |
+ |
# values still live in the markdown docs listed in each section header.
|
|
10 |
+ |
#
|
|
11 |
+ |
# Conventions:
|
|
12 |
+ |
# - All currency in USD unless suffixed `_eur`.
|
|
13 |
+ |
# - All time in months unless suffixed `_days` / `_years`.
|
|
14 |
+ |
# - Percentages as decimals (0.029 not 2.9).
|
|
15 |
+ |
# - Each section names the canonical markdown source.
|
|
16 |
+ |
# - Open decisions tagged `# OPEN:` — these are placeholder values
|
|
17 |
+ |
# pending founder approval per `pricing.md` §7 and `reserve_policy.md` §8.
|
|
18 |
+ |
#
|
|
19 |
+ |
# As of: 2026-05-16
|
|
20 |
+ |
# Last manual-verified: 2026-05-16
|
|
21 |
+ |
|
|
22 |
+ |
|
|
23 |
+ |
# ─── Expenses (canonical: expenses.md) ────────────────────────────────────
|
|
24 |
+ |
[expenses]
|
|
25 |
+ |
F_monthly = 580 # Total fixed monthly burn (rounded from $579.08)
|
|
26 |
+ |
F_monthly_exact = 579.08
|
|
27 |
+ |
as_of = "2026-05-16"
|
|
28 |
+ |
|
|
29 |
+ |
[expenses.lines]
|
|
30 |
+ |
hetzner = 31.00
|
|
31 |
+ |
coworking_industrious = 332.00
|
|
32 |
+ |
claude_code = 200.00
|
|
33 |
+ |
fastmail = 5.00
|
|
34 |
+ |
domains = 2.00
|
|
35 |
+ |
cloudflare = 0.00
|
|
36 |
+ |
postmark = 0.00
|
|
37 |
+ |
apple_developer_amortized = 8.25 # $99/yr ÷ 12
|
|
38 |
+ |
co_llc_periodic_amortized = 0.83 # $10/yr ÷ 12
|
|
39 |
+ |
|
|
40 |
+ |
|
|
41 |
+ |
# ─── Stripe fees (canonical: stripe_fees.md) ──────────────────────────────
|
|
42 |
+ |
[stripe]
|
|
43 |
+ |
percent = 0.029 # US standard online card
|
|
44 |
+ |
fixed = 0.30 # Per-transaction flat fee, USD
|
|
45 |
+ |
dispute_fee = 15.00 # Charged once at dispute creation
|
|
46 |
+ |
instant_payout_us_pct = 0.015 # US/AU/NZ/AE region (was incorrectly cited as flat 1%)
|
|
47 |
+ |
instant_payout_intl_pct = 0.010 # CA/EU/UK/SG/NO/HK/MY region
|
|
48 |
+ |
instant_payout_min = 0.50
|
|
49 |
+ |
chargeback_protection_pct = 0.004 # Stripe Radar add-on (fraud-only)
|
|
50 |
+ |
tax_pct = 0.005 # Stripe Tax per transaction
|
|
51 |
+ |
|
|
52 |
+ |
[stripe.connect_standard]
|
|
53 |
+ |
# What MNW currently uses for creator subs.
|
|
54 |
+ |
per_account_fee = 0
|
|
55 |
+ |
per_payout_fee = 0
|
|
56 |
+ |
payout_volume_pct = 0
|
|
57 |
+ |
|
|
58 |
+ |
[stripe.connect_express]
|
|
59 |
+ |
# Reference only — not used by MNW for creators.
|
|
60 |
+ |
per_active_account = 2.00
|
|
61 |
+ |
per_payout = 0.25
|
|
62 |
+ |
payout_volume_pct = 0.0025
|
|
63 |
+ |
|
|
64 |
+ |
[stripe.instant_payout]
|
|
65 |
+ |
# Nested form of the flat instant_payout_* keys above; substituted into
|
|
66 |
+ |
# legal/payments.md and guide/stripe.md as {{ stripe.instant_payout.* }}.
|
|
67 |
+ |
# Kept alongside the flat form for backward compatibility.
|
|
68 |
+ |
us_pct = 0.015
|
|
69 |
+ |
intl_pct = 0.010
|
|
70 |
+ |
min_usd = 0.50
|
|
71 |
+ |
|
|
72 |
+ |
|
|
73 |
+ |
# ─── Policy commitments (canonical: guarantees.md) ───────────────────────
|
|
74 |
+ |
# Time-based commitments that appear verbatim across guarantees.md, pricing.md,
|
|
75 |
+ |
# tiers.md, faq.md, fan-guide.md, account-lifecycle.md, generative-ai.md, and
|
|
76 |
+ |
# how-we-work.md. Semantically distinct even where the number matches today.
|
|
77 |
+ |
[policy]
|
|
78 |
+ |
buyer_access_days = 90 # guarantees §Buyer Access — post-close fan download window
|
|
79 |
+ |
shutdown_notice_days = 90 # guarantees §Shutdown, §Continuity
|
|
80 |
+ |
price_change_notice_days = 90 # guarantees §Price Stability, pricing.md
|
|
81 |
+ |
grandfather_months = 12 # guarantees §Price Stability
|
|
82 |
+ |
cancellation_grace_days = 30 # tiers.md §Cancellation, getting-started.md
|
|
83 |
+ |
content_archive_months = 12 # guarantees §Content Archive (planned)
|
|
84 |
+ |
generative_ai_notice_days = 30 # generative-ai.md §Governance
|
|
85 |
+ |
|
|
86 |
+ |
|
|
87 |
+ |
# ─── Uptime (canonical: guarantees.md §Availability) ─────────────────────
|
|
88 |
+ |
[uptime]
|
|
89 |
+ |
target_pct = 0.995
|
|
90 |
+ |
target_downtime_hours_year = 44 # (1 - 0.995) × 8760 ≈ 43.8, rounded up
|
|
91 |
+ |
|
|
92 |
+ |
|
|
93 |
+ |
# ─── Broadcasts (canonical: tiers.md, mailing-lists.md) ──────────────────
|
|
94 |
+ |
[broadcasts]
|
|
95 |
+ |
per_day = 1
|
|
96 |
+ |
recipients_per_send = 10000
|
|
97 |
+ |
# Display string with thousands separator; docengine has no thousands filter
|
|
98 |
+ |
# so the display form lives here (same pattern as `cohort.cap_display`).
|
|
99 |
+ |
recipients_per_send_display = "10,000"
|
|
100 |
+ |
|
|
101 |
+ |
|
|
102 |
+ |
# ─── Fan+ (canonical: fan-plus.md) ───────────────────────────────────────
|
|
103 |
+ |
[fan_plus]
|
|
104 |
+ |
monthly_price_usd = 8
|
|
105 |
+ |
# Per-fan monthly credit allowance; funded by Fan+ subscription revenue,
|
|
106 |
+ |
# not per-account overhead. Fan-only — creators don't receive it.
|
|
107 |
+ |
monthly_credit_usd = 5
|
|
108 |
+ |
|
|
109 |
+ |
|
|
110 |
+ |
# ─── Hetzner prices (canonical: hetzner_prices.md) ────────────────────────
|
|
111 |
+ |
[hetzner]
|
|
112 |
+ |
fx_eur_to_usd = 1.085 # Verify when FX moves >5%
|
|
113 |
+ |
|
|
114 |
+ |
# Compute SKUs (EUR/mo)
|
|
115 |
+ |
ccx13_eur = 13.10 # 2 dedicated vCPU / 8 GB / 80 GB — production
|
|
116 |
+ |
ccx23_eur = 30.00 # 4 dedicated vCPU / 16 GB / 160 GB
|
|
117 |
+ |
cpx11_eur = 3.85 # 2 shared vCPU / 2 GB / 40 GB
|
|
118 |
+ |
cpx31_eur = 13.10 # 4 shared vCPU / 8 GB / 160 GB
|
|
119 |
+ |
cx22_eur = 4.59 # 2 shared vCPU / 4 GB / 40 GB
|
|
120 |
+ |
|
|
121 |
+ |
# Storage and bandwidth
|
|
122 |
+ |
object_storage_eur_per_tb = 5.99
|
|
123 |
+ |
object_storage_included_tb = 1
|
|
124 |
+ |
egress_eur_per_tb_overage = 1.00
|
|
125 |
+ |
egress_included_per_server_tb = 1
|
|
126 |
+ |
volume_eur_per_gb = 0.044
|
|
127 |
+ |
ipv4_eur = 0.60
|
|
128 |
+ |
backup_pct_of_server = 0.20
|
|
129 |
+ |
|
|
130 |
+ |
|
|
131 |
+ |
# ─── Tier prices (canonical: pricing.md) ──────────────────────────────────
|
|
132 |
+ |
[tiers.founding]
|
|
133 |
+ |
# Founder pricing — exactly 50% of standard sticker, locked for life. Active
|
|
134 |
+ |
# until the founder window closes (1,000 creators OR exit-beta, whichever
|
|
135 |
+ |
# first). Decision 2026-05-18; raised to 8/12/18/30 on 2026-05-31 to track
|
|
136 |
+ |
# the standard-tier raise; reset to Refined-A (4/7/9/12) on 2026-07-08 per
|
|
137 |
+ |
# pricing_interrogation_2026_07.md §6.
|
|
138 |
+ |
basic = 4
|
|
139 |
+ |
small_files = 7
|
|
140 |
+ |
big_files = 9
|
|
141 |
+ |
everything = 12
|
|
142 |
+ |
|
|
143 |
+ |
[tiers.standard]
|
|
144 |
+ |
# Refined-A sticker — set 2026-07-08 per pricing_interrogation_2026_07.md §6.
|
|
145 |
+ |
# Priced against residency-scale labor ($41.5/hr Y1 support, ~$50/hr blended
|
|
146 |
+ |
# engineering) rather than the earlier $80/hr market rate, with right-sized
|
|
147 |
+ |
# Basic (10GB) and Small Files (100GB) envelopes. Founder rate is exactly
|
|
148 |
+ |
# 50% of these. History: 16/24/36/60 (2026-05-31), 12/18/24/40 (earlier).
|
|
149 |
+ |
basic = 8
|
|
150 |
+ |
small_files = 14
|
|
151 |
+ |
big_files = 18
|
|
152 |
+ |
everything = 24
|
|
153 |
+ |
|
|
154 |
+ |
# ─── Tier envelope limits (storage caps + file size caps) ────────────────
|
|
155 |
+ |
# Per-tier file-size envelope. Public-facing copy substitutes these via
|
|
156 |
+ |
# {{ tier_limits.* }} so the toml is the single source of truth and a future
|
|
157 |
+ |
# limit change is a one-line edit. Stored as display strings (no NBSP, no
|
|
158 |
+ |
# trailing periods) because that's the form the docs always need.
|
|
159 |
+ |
#
|
|
160 |
+ |
# `[tier_bytes]` below carries the same envelopes as machine-readable integer
|
|
161 |
+ |
# byte counts — that's the form Rust needs for upload gating, storage-cap
|
|
162 |
+ |
# accounting, and the metrics fill-ratio SQL. Docengine validates that each
|
|
163 |
+ |
# `tier_bytes.<k>` parses back to the matching `tier_limits.<k>` string, so
|
|
164 |
+ |
# a drift between the two forms fails at boot instead of shipping to prod.
|
|
165 |
+ |
[tier_limits]
|
|
166 |
+ |
# Refined-A envelopes (2026-07-08): Basic shrunk to 5MB/10GB and Small Files
|
|
167 |
+ |
# total shrunk to 100GB to make the tier ladder honest to actual content shape
|
|
168 |
+ |
# rather than "which tier gives me headroom I'll never use." Big Files and
|
|
169 |
+ |
# Everything envelopes unchanged.
|
|
170 |
+ |
basic_per_file = "5MB"
|
|
171 |
+ |
basic_total = "10GB"
|
|
172 |
+ |
small_files_per_file = "500MB"
|
|
173 |
+ |
small_files_total = "100GB"
|
|
174 |
+ |
big_files_per_file = "20GB"
|
|
175 |
+ |
big_files_total = "500GB"
|
|
176 |
+ |
everything_per_file = "20GB"
|
|
177 |
+ |
everything_total = "500GB"
|
|
178 |
+ |
|
|
179 |
+ |
[tier_bytes]
|
|
180 |
+ |
# Binary units (KB = 1024 B). Must match [tier_limits] display strings above.
|
|
181 |
+ |
basic_per_file = 5242880 # 5 MB
|
|
182 |
+ |
basic_total = 10737418240 # 10 GB
|
|
183 |
+ |
small_files_per_file = 524288000 # 500 MB
|
|
184 |
+ |
small_files_total = 107374182400 # 100 GB
|
|
185 |
+ |
big_files_per_file = 21474836480 # 20 GB
|
|
186 |
+ |
big_files_total = 536870912000 # 500 GB
|
|
187 |
+ |
everything_per_file = 21474836480 # 20 GB
|
|
188 |
+ |
everything_total = 536870912000 # 500 GB
|
|
189 |
+ |
|
|
190 |
+ |
|
|
191 |
+ |
[annual_discount]
|
|
192 |
+ |
# Annual billing is 10% off the monthly × 12 total at every tier, founder
|
|
193 |
+ |
# and standard. Two-digit discount, clean pitch. Decision 2026-05-18.
|
|
194 |
+ |
#
|
|
195 |
+ |
# What this actually covers:
|
|
196 |
+ |
# - Stripe per-transaction fees ($0.30 each, charged 12x for monthly vs 1x
|
|
197 |
+ |
# for annual): saves MNW ~$3.30/yr per customer regardless of tier.
|
|
198 |
+ |
# - The 2.9% percent fee is identical either way (a wash).
|
|
199 |
+ |
# - At Basic, 10% off ≈ the literal Stripe saving (close to pass-through).
|
|
200 |
+ |
# - At Everything, 10% off ($36/yr) > the Stripe saving ($3.30/yr); MNW
|
|
201 |
+ |
# absorbs the difference (~$15/yr per Everything customer at founder
|
|
202 |
+ |
# pricing, ~$32/yr per Everything customer at sticker) as a cashflow +
|
|
203 |
+ |
# reduced-billing-failure benefit. Defensible but not pure pass-through.
|
|
204 |
+ |
#
|
|
205 |
+ |
# Computed values (monthly × 12 × 0.9, rounded to nearest dollar):
|
|
206 |
+ |
# Founder: $86 / $130 / $194 / $324
|
|
207 |
+ |
# Standard: $173 / $259 / $389 / $648
|
|
208 |
+ |
multiplier = 0.9
|
|
209 |
+ |
months_equivalent_free = 1.2 # 10% of 12 months
|
|
210 |
+ |
|
|
211 |
+ |
|
|
212 |
+ |
# ─── Tier mix (canonical: tier_mix.md) ────────────────────────────────────
|
|
213 |
+ |
[tier_mix.assumed]
|
|
214 |
+ |
# Pre-launch placeholder distribution. A4 in assumptions.md.
|
|
215 |
+ |
# Updated monthly from SQL once creators exist.
|
|
216 |
+ |
basic_pct = 0.40
|
|
217 |
+ |
small_files_pct = 0.30
|
|
218 |
+ |
big_files_pct = 0.20
|
|
219 |
+ |
everything_pct = 0.10
|
|
220 |
+ |
|
|
221 |
+ |
|
|
222 |
+ |
# ─── Reserve policy (canonical: reserve_policy.md) ────────────────────────
|
|
223 |
+ |
[reserve]
|
|
224 |
+ |
# OPEN: all values in this block per reserve_policy.md §8
|
|
225 |
+ |
|
|
226 |
+ |
T_fixed_months = 12 # OPEN: §8 item 1
|
|
227 |
+ |
S_legal = 50000 # OPEN: §8 item 2 — pre-quote
|
|
228 |
+ |
S_shock = 5000 # OPEN: §8 item 3
|
|
229 |
+ |
|
|
230 |
+ |
R_opp = 10000 # OPEN: §8 item 4
|
|
231 |
+ |
rho_annual = 0.50 # OPEN: §8 item 5 — max % of R_opp/yr
|
|
232 |
+ |
rho_incident = 0.25 # OPEN: §8 item 6 — max % of R_opp/decision
|
|
233 |
+ |
|
|
234 |
+ |
surplus_split_reserve = 0.20 # OPEN: §8 item 7 — steady state
|
|
235 |
+ |
surplus_split_earnback = 0.80
|
|
236 |
+ |
# Sum must equal 1.0
|
|
237 |
+ |
|
|
238 |
+ |
transition_buffer_pct = 1.00 # OPEN: §8 item 10 — multiplier on R_cap before personal-to-company transition
|
|
239 |
+ |
|
|
240 |
+ |
|
|
241 |
+ |
# ─── Cohort (canonical: pricing.md) ───────────────────────────────────────
|
|
242 |
+ |
[cohort]
|
|
243 |
+ |
# OPEN: all values per pricing.md §7
|
|
244 |
+ |
|
|
245 |
+ |
cap_count = 1000 # Decided 2026-05-31; raised from 500 to match public copy in tiers.md / pricing.md.
|
|
246 |
+ |
cap_display = "1,000" # Display string with thousands separator; substituted via {{ cohort.cap_display }}. Keep in sync with cap_count.
|
|
247 |
+ |
cap_months = 12 # OPEN: §7 item 1 — or first M months (whichever first)
|
|
248 |
+ |
lock_duration = "lifetime" # OPEN: §7 item 4 — lifetime-of-subscription
|
|
249 |
+ |
# Counting rule (cumulative vs active) — see sops/founding-cohort-tracking.md, founder decision pending
|
|
250 |
+ |
|
|
251 |
+ |
|
|
252 |
+ |
# ─── Per-creator marginal cost inputs (canonical: assumptions.md A1-A10) ─
|
|
253 |
+ |
# All currently unmeasured — pre-launch placeholders.
|
|
254 |
+ |
[creator_marginal]
|
|
255 |
+ |
storage_basic_gb = 0.05 # A1: ~50 MB (right-sized 2026-07-08 alongside 10GB Basic envelope)
|
|
256 |
+ |
storage_small_files_gb = 2 # A2: ~2 GB
|
|
257 |
+ |
storage_big_files_gb = 10 # A3: ~10 GB
|
|
258 |
+ |
storage_everything_gb = 30 # A10: tier-dependent, placeholder
|
|
259 |
+ |
storage_cost_per_gb_per_month = 0.0065 # = €5.99/TB-mo × FX
|
|
260 |
+ |
egress_origin_hit_assumed_pct = 0.10 # A6: Cloudflare cache absorbs ~90% (assumed)
|
|
261 |
+ |
chargeback_rate_tier_subs = 0.001 # A12: 0.1% for recurring (lower than one-shot)
|
|
262 |
+ |
|
|
263 |
+ |
|
|
264 |
+ |
# ─── Derived values (for docengine feature; NOT stored — computed) ────────
|
|
265 |
+ |
#
|
|
266 |
+ |
# These are documented here for the future implementation. The docengine
|
|
267 |
+ |
# feature should compute them from the values above. Listed as commented
|
|
268 |
+ |
# pseudo-code; actual implementation belongs in Rust.
|
|
269 |
+ |
#
|
|
270 |
+ |
# R_cap = T_fixed_months · F_monthly + S_legal + S_shock
|
|
271 |
+ |
# = 12 · 580 + 50000 + 5000 = 61960
|
|
272 |
+ |
#
|
|
273 |
+ |
# ARPU_founding = Σ (tier_mix[k] · tiers.founding[k])
|
|
274 |
+ |
# = 0.4·8 + 0.3·12 + 0.2·18 + 0.1·30 = 13.40
|
|
275 |
+ |
#
|
|
276 |
+ |
# ARPU_standard = Σ (tier_mix[k] · tiers.standard[k])
|
|
277 |
+ |
# = 0.4·16 + 0.3·24 + 0.2·36 + 0.1·60 = 26.80
|
|
278 |
+ |
#
|
|
279 |
+ |
# stripe_fee(amount) = stripe.percent · amount + stripe.fixed
|
|
280 |
+ |
# stripe_fee_basic_std = 0.029 · 16 + 0.30 = 0.76
|
|
281 |
+ |
# stripe_fee_small_std = 0.029 · 24 + 0.30 = 1.00
|
|
282 |
+ |
# stripe_fee_big_std = 0.029 · 36 + 0.30 = 1.34
|
|
283 |
+ |
# stripe_fee_ev_std = 0.029 · 60 + 0.30 = 2.04
|
|
284 |
+ |
#
|
|
285 |
+ |
# break_even(rate_class) = F_monthly / (ARPU_{rate_class} − marginal_avg)
|
|
286 |
+ |
# break_even_standard = 580 / (26.80 − 1) ≈ 22.5 creators
|
|
287 |
+ |
# break_even_founding = 580 / (13.40 − 1) ≈ 46.8 creators
|
|
288 |
+ |
#
|
|
289 |
+ |
# surplus(N, rate_class) = N · (ARPU − marginal) − F_monthly
|
|
290 |
+ |
# surplus(100, "standard") = 100 · 25.80 − 580 = 2000
|
|
291 |
+ |
# surplus(500, "standard") = 500 · 25.80 − 580 = 12320
|
|
292 |
+ |
#
|
|
293 |
+ |
# fill_time_months(N) = (R_cap + R_opp) / surplus(N, "standard")
|
|
294 |
+ |
# fill_time(100) = 71960 / 2000 ≈ 36 mo
|
|
295 |
+ |
# fill_time(500) = 71960 / 12320 ≈ 6 mo
|
|
296 |
+ |
#
|
|
297 |
+ |
# infra_cost(tier, N) = storage_{tier}_gb · storage_cost_per_gb_per_month
|
|
298 |
+ |
# + egress_share + stripe_fee(price) + F_monthly/N
|
|
299 |
+ |
#
|
|
300 |
+ |
# Validation rules the build step should enforce:
|
|
301 |
+ |
# - 100 < F_monthly < 10000 (typo guard)
|
|
302 |
+ |
# - sum(tier_mix.*) = 1.00 (tier mix must sum to 100%)
|
|
303 |
+ |
# - surplus_split_reserve + surplus_split_earnback = 1.00
|
|
304 |
+ |
# - 0 < rho_annual ≤ 1
|
|
305 |
+ |
# - 0 < rho_incident ≤ 1
|
|
306 |
+ |
# - rho_incident ≤ rho_annual (single decision ≤ annual budget)
|
|
307 |
+ |
# - All `tiers.founding[k]` ≤ `tiers.standard[k]`
|
|
308 |
+ |
# - cap_count > 0, cap_months > 0
|
|
309 |
+ |
|
|
310 |
+ |
|
|
311 |
+ |
# ─── Runway disclosure (transparency block on /docs/about/economics) ─────
|
|
312 |
+ |
#
|
|
313 |
+ |
# Operator-set. Refreshed quarterly at sprint close, alongside the
|
|
314 |
+ |
# /changelog recap. The paying-creator count on the page is pulled live
|
|
315 |
+ |
# from the DB; this number is the cash-runway bucket.
|
|
316 |
+ |
#
|
|
317 |
+ |
# Units: quarters at current burn (whole-number bucket; we round down
|
|
318 |
+ |
# rather than mislead in either direction).
|
|
319 |
+ |
#
|
|
320 |
+ |
# A value of 0 means "not yet published" — the page renders without a
|
|
321 |
+ |
# number until the first quarterly refresh sets it.
|
|
322 |
+ |
[runway]
|
|
323 |
+ |
# 17 = $190K committed funding ÷ $3,580/mo current actual burn (ops + $3K
|
|
324 |
+ |
# salary), rounded down. Excludes the draw-on-request $50K reserve (would be
|
|
325 |
+ |
# 22). Basis: server-internal/business/financial_dashboard.md. Refresh at
|
|
326 |
+ |
# sprint close — bump if burn rises (Year 2 $5K salary) or funding changes.
|
|
327 |
+ |
quarters = 17
|
|
328 |
+ |
last_updated_iso = "2026-06-04"
|
|
329 |
+ |
|
|
330 |
+ |
|
|
331 |
+ |
# ─── Pricing calculator (canonical: this file + stripe_fees.md) ───────────
|
|
332 |
+ |
#
|
|
333 |
+ |
# Powers the public /pricing comparison. Consumed by MNW's
|
|
334 |
+ |
# `src/pricing_comparison.rs` (parsed directly from this file, not through
|
|
335 |
+ |
# docengine's scalar lookup — arrays of tables aren't substitutable). The
|
|
336 |
+ |
# net-revenue formula and every competitor's parameters live HERE, not in
|
|
337 |
+ |
# client JS, so the public "you keep more" claim has one source of truth and
|
|
338 |
+ |
# a pinning test (`pricing_comparison::tests`). Stripe fees are read from the
|
|
339 |
+ |
# [stripe] block above (percent, fixed).
|
|
340 |
+ |
#
|
|
341 |
+ |
# Per-competitor net(revenue) is computed as:
|
|
342 |
+ |
# txns = max(1, round(revenue / pricing_calc.avg_transaction))
|
|
343 |
+ |
# pct = platform_pct_over if (platform_pct_threshold set AND rev > it)
|
|
344 |
+ |
# else platform_pct
|
|
345 |
+ |
# ap = revenue * (1 - pct) - txns * platform_per_tx
|
|
346 |
+ |
# net = { none: ap
|
|
347 |
+ |
# , percent: ap - ap*stripe.percent
|
|
348 |
+ |
# , percent_and_fixed: ap - ap*stripe.percent - txns*stripe.fixed }
|
|
349 |
+ |
# - flat_monthly
|
|
350 |
+ |
# MNW's own row = platform_pct 0, percent_and_fixed, flat_monthly = tier price.
|
|
351 |
+ |
#
|
|
352 |
+ |
# Fee structures are each platform's public pricing as of 2026-06. Verify at
|
|
353 |
+ |
# the linked page before editing; a change here updates the live page + test.
|
|
354 |
+ |
[pricing_calc]
|
|
355 |
+ |
avg_transaction = 25 # assumed average sale, for per-tx fee estimation
|
|
356 |
+ |
|
|
357 |
+ |
[[competitors]]
|
|
358 |
+ |
name = "Patreon"
|
|
359 |
+ |
url = "https://www.patreon.com/pricing"
|
|
360 |
+ |
fee_label = "10% + ~3% + $0.30"
|
|
361 |
+ |
platform_pct = 0.10
|
|
362 |
+ |
stripe_mode = "percent_and_fixed"
|
|
363 |
+ |
|
|
364 |
+ |
[[competitors]]
|
|
365 |
+ |
name = "Gumroad"
|
|
366 |
+ |
url = "https://gumroad.com/pricing"
|
|
367 |
+ |
fee_label = "10% + $0.50/tx + ~3%"
|
|
368 |
+ |
platform_pct = 0.10
|
|
369 |
+ |
platform_per_tx = 0.50
|
|
370 |
+ |
stripe_mode = "percent"
|
|
371 |
+ |
|
|
372 |
+ |
[[competitors]]
|
|
373 |
+ |
name = "Bandcamp"
|
|
374 |
+ |
url = "https://bandcamp.com/pricing"
|
|
375 |
+ |
fee_label = "15% (10% after $5k) + ~3%"
|
|
376 |
+ |
platform_pct = 0.15
|
|
377 |
+ |
platform_pct_over = 0.10
|
|
378 |
+ |
platform_pct_threshold = 5000
|
|
379 |
+ |
stripe_mode = "percent"
|
|
380 |
+ |
|
|
381 |
+ |
[[competitors]]
|
|
382 |
+ |
name = "Ko-fi Free"
|
|
383 |
+ |
url = "https://ko-fi.com/pricing"
|
|
384 |
+ |
fee_label = "0% tips, 5% shop/memberships + ~3%"
|
|
385 |
+ |
platform_pct = 0.05
|
|
386 |
+ |
stripe_mode = "percent"
|
|
387 |
+ |
|
|
388 |
+ |
[[competitors]]
|
|
389 |
+ |
name = "Ko-fi Gold"
|
|
390 |
+ |
url = "https://ko-fi.com/pricing"
|
|
391 |
+ |
fee_label = "$12/mo + 0% + ~3%"
|
|
392 |
+ |
flat_monthly = 12
|
|
393 |
+ |
stripe_mode = "percent_and_fixed"
|
|
394 |
+ |
|
|
395 |
+ |
[[competitors]]
|
|
396 |
+ |
name = "Itch.io"
|
|
397 |
+ |
url = "https://itch.io/docs/creators/faq"
|
|
398 |
+ |
fee_label = "10% default + ~3%"
|
|
399 |
+ |
platform_pct = 0.10
|
|
400 |
+ |
stripe_mode = "percent"
|
|
401 |
+ |
|
|
402 |
+ |
[[competitors]]
|
|
403 |
+ |
name = "Lemon Squeezy"
|
|
404 |
+ |
url = "https://www.lemonsqueezy.com/pricing"
|
|
405 |
+ |
fee_label = "5% + $0.50/tx (incl. processing)"
|
|
406 |
+ |
platform_pct = 0.05
|
|
407 |
+ |
platform_per_tx = 0.50
|
|
408 |
+ |
stripe_mode = "none"
|
|
409 |
+ |
|
|
410 |
+ |
[[competitors]]
|
|
411 |
+ |
name = "Buy Me a Coffee"
|
|
412 |
+ |
url = "https://www.buymeacoffee.com/about"
|
|
413 |
+ |
fee_label = "5% + ~3%"
|
|
414 |
+ |
platform_pct = 0.05
|
|
415 |
+ |
stripe_mode = "percent"
|
|
416 |
+ |
|
|
417 |
+ |
[[competitors]]
|
|
418 |
+ |
name = "Substack"
|
|
419 |
+ |
url = "https://substack.com/going-paid"
|
|
420 |
+ |
fee_label = "10% on paid subs"
|
|
421 |
+ |
platform_pct = 0.10
|
|
422 |
+ |
stripe_mode = "percent"
|