Skip to main content

max / makenotwork

13.0 KB · 316 lines History Blame Raw
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
65 # ─── Hetzner prices (canonical: hetzner_prices.md) ────────────────────────
66 [hetzner]
67 fx_eur_to_usd = 1.085 # Verify when FX moves >5%
68
69 # Compute SKUs (EUR/mo)
70 ccx13_eur = 13.10 # 2 dedicated vCPU / 8 GB / 80 GB — production
71 ccx23_eur = 30.00 # 4 dedicated vCPU / 16 GB / 160 GB
72 cpx11_eur = 3.85 # 2 shared vCPU / 2 GB / 40 GB
73 cpx31_eur = 13.10 # 4 shared vCPU / 8 GB / 160 GB
74 cx22_eur = 4.59 # 2 shared vCPU / 4 GB / 40 GB
75
76 # Storage and bandwidth
77 object_storage_eur_per_tb = 5.99
78 object_storage_included_tb = 1
79 egress_eur_per_tb_overage = 1.00
80 egress_included_per_server_tb = 1
81 volume_eur_per_gb = 0.044
82 ipv4_eur = 0.60
83 backup_pct_of_server = 0.20
84
85
86 # ─── Tier prices (canonical: pricing.md) ──────────────────────────────────
87 [tiers.founding]
88 # Founder pricing — exactly 50% of standard sticker, locked for life. Active
89 # until the founder window closes (1,000 creators OR exit-beta, whichever
90 # first). Decision 2026-05-18; raised to 8/12/18/30 on 2026-05-31 to track
91 # the standard-tier raise (see memory `project_founder_pricing.md`).
92 basic = 8
93 small_files = 12
94 big_files = 18
95 everything = 30
96
97 [tiers.standard]
98 # Post-founder sticker — set 2026-05-31 to clear the irreducible
99 # ~$5/creator/mo support floor at every tier with even-dollar prices.
100 # Founder rate is exactly 50% of these. See launchplan_final.md §4.5.
101 basic = 16
102 small_files = 24
103 big_files = 36
104 everything = 60
105
106 # ─── Tier envelope limits (storage caps + file size caps) ────────────────
107 # Per-tier file-size envelope. Public-facing copy substitutes these via
108 # {{ tier_limits.* }} so the toml is the single source of truth and a future
109 # limit change is a one-line edit. Stored as display strings (no NBSP, no
110 # trailing periods) because that's the form the docs always need.
111 [tier_limits]
112 basic_per_file = "10MB"
113 basic_total = "50GB"
114 small_files_per_file = "500MB"
115 small_files_total = "250GB"
116 big_files_per_file = "20GB"
117 big_files_total = "500GB"
118 everything_per_file = "20GB"
119 everything_total = "500GB"
120
121
122 [annual_discount]
123 # Annual billing is 10% off the monthly × 12 total at every tier, founder
124 # and standard. Two-digit discount, clean pitch. Decision 2026-05-18.
125 #
126 # What this actually covers:
127 # - Stripe per-transaction fees ($0.30 each, charged 12x for monthly vs 1x
128 # for annual): saves MNW ~$3.30/yr per customer regardless of tier.
129 # - The 2.9% percent fee is identical either way (a wash).
130 # - At Basic, 10% off ≈ the literal Stripe saving (close to pass-through).
131 # - At Everything, 10% off ($36/yr) > the Stripe saving ($3.30/yr); MNW
132 # absorbs the difference (~$15/yr per Everything customer at founder
133 # pricing, ~$32/yr per Everything customer at sticker) as a cashflow +
134 # reduced-billing-failure benefit. Defensible but not pure pass-through.
135 #
136 # Computed values (monthly × 12 × 0.9, rounded to nearest dollar):
137 # Founder: $86 / $130 / $194 / $324
138 # Standard: $173 / $259 / $389 / $648
139 multiplier = 0.9
140 months_equivalent_free = 1.2 # 10% of 12 months
141
142
143 # ─── Tier mix (canonical: tier_mix.md) ────────────────────────────────────
144 [tier_mix.assumed]
145 # Pre-launch placeholder distribution. A4 in assumptions.md.
146 # Updated monthly from SQL once creators exist.
147 basic_pct = 0.40
148 small_files_pct = 0.30
149 big_files_pct = 0.20
150 everything_pct = 0.10
151
152
153 # ─── Reserve policy (canonical: reserve_policy.md) ────────────────────────
154 [reserve]
155 # OPEN: all values in this block per reserve_policy.md §8
156
157 T_fixed_months = 12 # OPEN: §8 item 1
158 S_legal = 50000 # OPEN: §8 item 2 — pre-quote
159 S_shock = 5000 # OPEN: §8 item 3
160
161 R_opp = 10000 # OPEN: §8 item 4
162 rho_annual = 0.50 # OPEN: §8 item 5 — max % of R_opp/yr
163 rho_incident = 0.25 # OPEN: §8 item 6 — max % of R_opp/decision
164
165 surplus_split_reserve = 0.20 # OPEN: §8 item 7 — steady state
166 surplus_split_earnback = 0.80
167 # Sum must equal 1.0
168
169 transition_buffer_pct = 1.00 # OPEN: §8 item 10 — multiplier on R_cap before personal-to-company transition
170
171
172 # ─── Cohort (canonical: pricing.md) ───────────────────────────────────────
173 [cohort]
174 # OPEN: all values per pricing.md §7
175
176 cap_count = 1000 # Decided 2026-05-31; raised from 500 to match public copy in tiers.md / pricing.md.
177 cap_display = "1,000" # Display string with thousands separator; substituted via {{ cohort.cap_display }}. Keep in sync with cap_count.
178 cap_months = 12 # OPEN: §7 item 1 — or first M months (whichever first)
179 lock_duration = "lifetime" # OPEN: §7 item 4 — lifetime-of-subscription
180 # Counting rule (cumulative vs active) — see sops/founding-cohort-tracking.md, founder decision pending
181
182
183 # ─── Per-creator marginal cost inputs (canonical: assumptions.md A1-A10) ─
184 # All currently unmeasured — pre-launch placeholders.
185 [creator_marginal]
186 storage_basic_gb = 0.1 # A1: ~100 MB
187 storage_small_files_gb = 2 # A2: ~2 GB
188 storage_big_files_gb = 10 # A3: ~10 GB
189 storage_everything_gb = 30 # A10: tier-dependent, placeholder
190 storage_cost_per_gb_per_month = 0.0065 # = €5.99/TB-mo × FX
191 egress_origin_hit_assumed_pct = 0.10 # A6: Cloudflare cache absorbs ~90% (assumed)
192 chargeback_rate_tier_subs = 0.001 # A12: 0.1% for recurring (lower than one-shot)
193
194
195 # ─── Derived values (for docengine feature; NOT stored — computed) ────────
196 #
197 # These are documented here for the future implementation. The docengine
198 # feature should compute them from the values above. Listed as commented
199 # pseudo-code; actual implementation belongs in Rust.
200 #
201 # R_cap = T_fixed_months · F_monthly + S_legal + S_shock
202 # = 12 · 580 + 50000 + 5000 = 61960
203 #
204 # ARPU_founding = Σ (tier_mix[k] · tiers.founding[k])
205 # = 0.4·8 + 0.3·12 + 0.2·18 + 0.1·30 = 13.40
206 #
207 # ARPU_standard = Σ (tier_mix[k] · tiers.standard[k])
208 # = 0.4·16 + 0.3·24 + 0.2·36 + 0.1·60 = 26.80
209 #
210 # stripe_fee(amount) = stripe.percent · amount + stripe.fixed
211 # stripe_fee_basic_std = 0.029 · 16 + 0.30 = 0.76
212 # stripe_fee_small_std = 0.029 · 24 + 0.30 = 1.00
213 # stripe_fee_big_std = 0.029 · 36 + 0.30 = 1.34
214 # stripe_fee_ev_std = 0.029 · 60 + 0.30 = 2.04
215 #
216 # break_even(rate_class) = F_monthly / (ARPU_{rate_class} − marginal_avg)
217 # break_even_standard = 580 / (26.80 − 1) ≈ 22.5 creators
218 # break_even_founding = 580 / (13.40 − 1) ≈ 46.8 creators
219 #
220 # surplus(N, rate_class) = N · (ARPU − marginal) − F_monthly
221 # surplus(100, "standard") = 100 · 25.80 − 580 = 2000
222 # surplus(500, "standard") = 500 · 25.80 − 580 = 12320
223 #
224 # fill_time_months(N) = (R_cap + R_opp) / surplus(N, "standard")
225 # fill_time(100) = 71960 / 2000 ≈ 36 mo
226 # fill_time(500) = 71960 / 12320 ≈ 6 mo
227 #
228 # infra_cost(tier, N) = storage_{tier}_gb · storage_cost_per_gb_per_month
229 # + egress_share + stripe_fee(price) + F_monthly/N
230 #
231 # Validation rules the build step should enforce:
232 # - 100 < F_monthly < 10000 (typo guard)
233 # - sum(tier_mix.*) = 1.00 (tier mix must sum to 100%)
234 # - surplus_split_reserve + surplus_split_earnback = 1.00
235 # - 0 < rho_annual ≤ 1
236 # - 0 < rho_incident ≤ 1
237 # - rho_incident ≤ rho_annual (single decision ≤ annual budget)
238 # - All `tiers.founding[k]` ≤ `tiers.standard[k]`
239 # - cap_count > 0, cap_months > 0
240
241
242 # ─── Per-tier monthly fee allocation (Mode 1 widget on /pricing) ─────────
243 #
244 # Each row sums to the standard tier price. The widget renders standard-rate
245 # numbers only; founder-rate creators pay 50% of standard with the subsidy
246 # absorbed by reserves + earnback, so the allocation shape is the same but
247 # the visible total scales down — a one-line note on the pricing page covers
248 # that case.
249 #
250 # Model (per `site-docs/public/about/economics.md`):
251 # - Stripe: 2.9% × price + $0.30 (Stripe's actual fee formula)
252 # - Storage: at-rest + egress + virus-scan + backups + provider-price
253 # cushion; ~5–15× the bare Hetzner at-rest math from
254 # [creator_marginal] to absorb spikes
255 # - Support: flat $5.00/creator — per-creator floor, doesn't scale
256 # with tier (a Basic text creator and a $60 video creator
257 # each get the same human availability)
258 # - Engineering: flat $6.00/creator — engineering hours don't scale with
259 # what tier someone bought; the same platform serves all
260 # - Reserves: 12.5% of revenue (risk float for refunds/chargebacks/runway)
261 # - Earn-back: residual; the surplus from higher tiers funds platform
262 # development that benefits everyone
263 #
264 # Numbers below are pinned by `tier_prices::tests::cost_allocation_*`.
265 [cost_allocation.basic]
266 stripe = 0.76
267 storage = 0.40
268 support = 5.00
269 engineering = 6.00
270 reserves = 2.00
271 earnback = 1.84
272
273 [cost_allocation.small_files]
274 stripe = 1.00
275 storage = 1.00
276 support = 5.00
277 engineering = 6.00
278 reserves = 3.00
279 earnback = 8.00
280
281 [cost_allocation.big_files]
282 stripe = 1.34
283 storage = 3.00
284 support = 5.00
285 engineering = 6.00
286 reserves = 4.50
287 earnback = 16.16
288
289 [cost_allocation.everything]
290 stripe = 2.04
291 storage = 6.00
292 support = 5.00
293 engineering = 6.00
294 reserves = 7.50
295 earnback = 33.46
296
297
298 # ─── Runway disclosure (transparency block on /docs/about/economics) ─────
299 #
300 # Operator-set. Refreshed quarterly at sprint close, alongside the
301 # /changelog recap. The paying-creator count on the page is pulled live
302 # from the DB; this number is the cash-runway bucket.
303 #
304 # Units: quarters at current burn (whole-number bucket; we round down
305 # rather than mislead in either direction).
306 #
307 # A value of 0 means "not yet published" — the page renders without a
308 # number until the first quarterly refresh sets it.
309 [runway]
310 # 17 = $190K committed funding ÷ $3,580/mo current actual burn (ops + $3K
311 # salary), rounded down. Excludes the draw-on-request $50K reserve (would be
312 # 22). Basis: server-internal/business/financial_dashboard.md. Refresh at
313 # sprint close — bump if burn rises (Year 2 $5K salary) or funding changes.
314 quarters = 17
315 last_updated_iso = "2026-06-04"
316