Skip to main content

max / docengine

A soak target over the render chain, and the oracle it took three tries to get right docengine is the widest-reach target in the soak plan and the doors were counted before the harness was written, as infra 15991c40 instructs: 14 files across 6 repos call these entry points -- MNW server and multithreaded, goingson, balanced_breakfast, mnw-assumptions, bb-core -- with one implementation behind all of them. A single authority with many consumers is what a fuzz target is for; two parsers of one grammar is what a shared crate is for, which is the distinction git_ssh settled. The oracle lives in the crate, not the fuzz target, copying MNW/shared/git-command: tests/regressions.rs replays the committed corpus against the same function on stable, so a crash becomes a unit test by copying one file and neither side can drift into checking less than the other. Getting it right took three attempts, and the first two reported the sanitizer WORKING as findings: - A whole-string scan for " onerror" fired within two minutes on a markdown image whose title quote made it fail to parse, so it fell through as literal prose while the real <img> beside it had been stripped clean. - Narrowing to inside <...> then fired on an entity-escaped payload sitting in a title attribute VALUE, which a browser renders as tooltip text. Both are correct behaviour, and a check that reports correct behaviour as a finding is worse than no check: it is the 3am false alarm that trains people to skim the tier. What ships parses tag and attribute NAMES and skips quoted values by construction. Plain idempotence went the same way and for a better reason: it is FALSE. Pass one can emit a <p> nested inside a <p> through an <a>, and pass two restructures it, because that is what html5ever does with malformed nesting. Nothing dangerous appears in either and the floor holds on both. So the assertion is now the two things that are true and that matter -- the floor survives a second sanitization, which is the mutation-XSS property, and sanitize_html reaches a fixed point by the second pass. The input that found it is committed as the first regression, so the pair is pinned against the case that shaped them. 113,360 runs found nothing after the oracle was correct, at 9,434 edges and 583 MB peak. That is the state a soak target should start in: the searching is the tier's job, not the first afternoon's.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-23 19:55 UTC
Signed with PGP, not checked
Commit: 1d6ed94a68cf830457c146f37e7072ccbdfd3332
Parent: eb0a442
20 files changed, +1721 insertions, -0 deletions
M src/lib.rs +221
@@ -139,6 +139,227 @@
139 139 .to_string()
140 140 }
141 141
142 + /// What every rendering path must be true of, whatever else it does.
143 + ///
144 + /// **The oracle lives here rather than in the fuzz target**, and that is the
145 + /// point of the arrangement rather than a filing preference. `tests/regressions.rs`
146 + /// replays the committed corpus against this same function on stable, so a crash
147 + /// the fuzzer finds becomes a unit test by copying one file into `fuzz/regressions/`,
148 + /// and neither side can drift into checking less than the other. The pattern is
149 + /// `MNW/shared/git-command`'s, copied deliberately.
150 + ///
151 + /// Infra `15991c40`. The doors, counted before the harness was written as that task
152 + /// instructs: this crate's render entry points are called from **14 files across 6
153 + /// repos** -- MNW server and multithreaded, goingson, balanced_breakfast,
154 + /// mnw-assumptions, bb-core -- with `render_permissive` the most used (10 sites),
155 + /// then `render_standard` (5), `sanitize_html` (3), `render_strict` (3),
156 + /// `restrict_media_hosts` (2), `render_chat` (1). There is one implementation
157 + /// behind all of them, so this is a single authority with many consumers rather
158 + /// than a grammar with two parsers, and a fuzz target is the right shape.
159 + pub mod oracle {
160 + /// Element names no output of any preset may contain.
161 + ///
162 + /// Checked as parsed tag NAMES rather than as substrings, for the reason
163 + /// written on [`check_output`].
164 + const FORBIDDEN_TAGS: &[&str] = &[
165 + "script", "iframe", "object", "embed", "form", "svg", "math", "base", "meta", "link",
166 + "style", "template", "noscript",
167 + ];
168 +
169 + /// Attribute values that must never appear on a URL-bearing attribute.
170 + const FORBIDDEN_SCHEMES: &[&str] = &["javascript:", "vbscript:", "data:text/html"];
171 +
172 + /// Attributes whose value is fetched or navigated to, so a scheme in one is
173 + /// a live capability rather than a string.
174 + const URL_ATTRS: &[&str] = &[
175 + "href",
176 + "src",
177 + "srcset",
178 + "action",
179 + "formaction",
180 + "poster",
181 + "xlink:href",
182 + "data",
183 + "codebase",
184 + "background",
185 + ];
186 +
187 + /// Assert the safety floor on one rendered output.
188 + ///
189 + /// **Parses tag and attribute NAMES; never pattern-matches the whole
190 + /// string.** That distinction is the correctness of this function rather
191 + /// than an optimisation, and the fuzzer proved it twice within four minutes
192 + /// of the target first running -- both times against the oracle, not the
193 + /// sanitizer:
194 + ///
195 + /// 1. A whole-string substring scan fired on a markdown image whose title
196 + /// quote made it fail to parse, so it fell through as literal prose
197 + /// reading `![img](x" onerror="alert(1))`. ` onerror` was in the output
198 + /// as escaped TEXT while the real `<img>` beside it had been stripped
199 + /// clean.
200 + /// 2. Narrowing the scan to inside `<...>` was not enough either. It then
201 + /// fired on `<p title="&lt;/noscript&gt;&lt;img src=x onerror=...&gt;">`,
202 + /// where the string sits inside an entity-escaped attribute VALUE and a
203 + /// browser renders it as tooltip text.
204 + ///
205 + /// Both are the sanitizer working. A check that reports correct behaviour as
206 + /// a finding is worse than no check: it is the 3am false alarm that trains
207 + /// people to skim the tier, which is the failure the whole evidence
208 + /// programme exists to avoid. So the oracle skips quoted values by
209 + /// construction, and escaped content inside one can no longer reach it.
210 + ///
211 + /// Splitting on `<` is sound BECAUSE of what is under test: a sanitizer that
212 + /// escapes text is the premise, so an unescaped `<` in the output can only
213 + /// be a tag. If that ever stopped being true, the tag-name assertion is what
214 + /// would fire, which is the right failure.
215 + ///
216 + /// # Panics
217 + /// If any tag is a forbidden element, carries an `on*` handler, or points a
218 + /// URL-bearing attribute at a forbidden scheme.
219 + pub fn check_output(preset: &str, input: &str, out: &str) {
220 + let lower = out.to_ascii_lowercase();
221 + let bytes = lower.as_bytes();
222 + let fail =
223 + |what: &str| -> ! { panic!("{preset} {what}\n input: {input:?}\n output: {out:?}") };
224 +
225 + let mut i = 0;
226 + while let Some(off) = lower[i..].find('<') {
227 + let mut p = i + off + 1;
228 + if bytes.get(p) == Some(&b'/') {
229 + p += 1;
230 + }
231 + let name_start = p;
232 + while p < bytes.len()
233 + && (bytes[p].is_ascii_alphanumeric() || bytes[p] == b':' || bytes[p] == b'-')
234 + {
235 + p += 1;
236 + }
237 + let tag = &lower[name_start..p];
238 + if FORBIDDEN_TAGS.contains(&tag) {
239 + fail(&format!("emitted a <{tag}> element"));
240 + }
241 +
242 + // Attributes, until the tag closes. Quoted values are skipped whole,
243 + // which is what keeps escaped text inside one out of the scan.
244 + while p < bytes.len() && bytes[p] != b'>' {
245 + if bytes[p].is_ascii_whitespace() || bytes[p] == b'/' {
246 + p += 1;
247 + continue;
248 + }
249 + let attr_start = p;
250 + while p < bytes.len()
251 + && !bytes[p].is_ascii_whitespace()
252 + && bytes[p] != b'='
253 + && bytes[p] != b'>'
254 + {
255 + p += 1;
256 + }
257 + let attr = &lower[attr_start..p];
258 + if attr.starts_with("on") && attr.len() > 2 {
259 + fail(&format!("emitted the event handler {attr:?} on <{tag}>"));
260 + }
261 + while p < bytes.len() && bytes[p].is_ascii_whitespace() {
262 + p += 1;
263 + }
264 + if bytes.get(p) != Some(&b'=') {
265 + continue;
266 + }
267 + p += 1;
268 + while p < bytes.len() && bytes[p].is_ascii_whitespace() {
269 + p += 1;
270 + }
271 + let (value, next) = match bytes.get(p) {
272 + Some(&q @ (b'"' | b'\'')) => {
273 + let vs = p + 1;
274 + let mut e = vs;
275 + while e < bytes.len() && bytes[e] != q {
276 + e += 1;
277 + }
278 + (&lower[vs..e.min(bytes.len())], (e + 1).min(bytes.len()))
279 + }
280 + _ => {
281 + let vs = p;
282 + let mut e = vs;
283 + while e < bytes.len() && !bytes[e].is_ascii_whitespace() && bytes[e] != b'>'
284 + {
285 + e += 1;
286 + }
287 + (&lower[vs..e], e)
288 + }
289 + };
290 + if URL_ATTRS.contains(&attr) {
291 + let v = value.trim();
292 + for bad in FORBIDDEN_SCHEMES {
293 + if v.starts_with(bad) {
294 + fail(&format!("pointed {attr}= at {bad:?} on <{tag}>"));
295 + }
296 + }
297 + }
298 + p = next;
299 + }
300 + i = p.max(i + off + 1);
301 + }
302 + }
303 +
304 + /// Run every rendering entry point over one input and assert what must hold.
305 + ///
306 + /// Three properties:
307 + ///
308 + /// 1. **The safety floor**, on every preset. See [`check_output`].
309 + /// 2. **The floor survives a second sanitization.** This is the mutation-XSS
310 + /// property and the one worth having: the risk is that re-parsing the
311 + /// output reveals markup the first pass did not emit, because somewhere
312 + /// downstream something parses the same bytes twice.
313 + /// 3. **`sanitize_html` reaches a fixed point by the second pass.**
314 + ///
315 + /// ## Why (3) rather than plain idempotence, which is what this asserted first
316 + ///
317 + /// Exact idempotence is FALSE, and the fuzzer proved it in thirty minutes on
318 + /// input built from the XSS seeds: pass one can emit a `<p>` nested inside a
319 + /// `<p>` through an `<a>`, and pass two restructures it because that is what
320 + /// html5ever's tree builder does with malformed nesting. Nothing dangerous
321 + /// appears in either output -- the floor holds on both -- and the difference
322 + /// is upstream tree-building we do not control.
323 + ///
324 + /// So exact equality is the wrong STRENGTH, not the wrong idea. It fires
325 + /// forever on malformed input nobody sends, which would bury the property
326 + /// that matters under noise -- and a check that cries wolf is the failure
327 + /// this whole tier is built to avoid. Convergence is true, checkable, and
328 + /// still catches the real thing: a sanitizer that never settles is one where
329 + /// each parse sees something new. Measured on the input that found this,
330 + /// `s(s(x)) == s(s(s(x)))` holds.
331 + ///
332 + /// The input itself is committed under `fuzz/regressions/`, so the pair of
333 + /// properties is pinned against exactly the case that shaped them.
334 + pub fn check(input: &str) {
335 + for (name, out) in [
336 + ("permissive", super::render_permissive(input)),
337 + ("standard", super::render_standard(input)),
338 + ("strict", super::render_strict(input)),
339 + ("chat", super::render_chat(input)),
340 + ("phrase", super::render_phrase(input)),
341 + ("sanitize_html", super::sanitize_html(input)),
342 + ] {
343 + check_output(name, input, &out);
344 +
345 + // Re-sanitization, on the one entry point where it is meaningful:
346 + // `sanitize_html` takes HTML and returns HTML, so its output is a
347 + // legal input to itself. The render presets take markdown, so
348 + // feeding their HTML back in asks a different question and would
349 + // fail for reasons that are not defects.
350 + if name == "sanitize_html" {
351 + let twice = super::sanitize_html(&out);
352 + check_output("sanitize_html (second pass)", input, &twice);
353 + let thrice = super::sanitize_html(&twice);
354 + assert_eq!(
355 + twice, thrice,
356 + "sanitize_html had not settled by the third pass\n input: {input:?}\n twice: {twice:?}\n thrice: {thrice:?}"
357 + );
358 + }
359 + }
360 + }
361 + }
362 +
142 363 #[cfg(test)]
143 364 mod top_level_tests {
144 365 use super::*;
@@ -1,0 +1,4 @@
1 + target
2 + corpus
3 + artifacts
4 + coverage
@@ -1,0 +1,31 @@
1 + [package]
2 + name = "docengine-fuzz"
3 + version = "0.0.0"
4 + publish = false
5 + edition = "2024"
6 +
7 + [package.metadata]
8 + cargo-fuzz = true
9 +
10 + [dependencies]
11 + # `arbitrary` with `derive` is what lets the target take `&str` instead of
12 + # `&[u8]`. The input is markdown and HTML, both text grammars, and byte input
13 + # would spend most of the fuzzer's budget rediscovering UTF-8 before it reached
14 + # an angle bracket.
15 + libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] }
16 +
17 + [dependencies.docengine]
18 + path = ".."
19 + # Every feature, because the doors counted for infra `15991c40` reach across
20 + # them: `restrict_media_hosts` and `img_to_video` are `media-urls`, and the
21 + # consumers that call them are the same consumers that call the presets. A
22 + # default-features fuzz of a crate whose consumers turn features on is fuzzing
23 + # a build nobody ships.
24 + features = ["full"]
25 +
26 + [[bin]]
27 + name = "render"
28 + path = "fuzz_targets/render.rs"
29 + test = false
30 + doc = false
31 + bench = false
@@ -1,0 +1,36 @@
1 + //! Structured fuzz over docengine's rendering and sanitization chain.
2 + //!
3 + //! Row 3 of `astra-soak-overview`, and the crate with the widest reach of any
4 + //! soak target so far. The doors were counted before this was written, as infra
5 + //! `15991c40` instructs: **14 files across 6 repos** call these entry points --
6 + //! MNW server and multithreaded, goingson, balanced_breakfast, mnw-assumptions,
7 + //! bb-core -- and there is one implementation behind all of them. So this is a
8 + //! single authority with many consumers rather than a grammar with two parsers,
9 + //! which is what makes a fuzz target the right shape here and made a shared
10 + //! crate the right answer for `git_ssh`.
11 + //!
12 + //! ## The oracle lives in the crate, not here
13 + //!
14 + //! Everything asserted is `docengine::oracle::check`. That is deliberate: the
15 + //! committed regression replay in `tests/regressions.rs` calls the same function
16 + //! on stable, so a crash found here becomes a unit test by copying one file, and
17 + //! neither side can drift into checking less than the other.
18 + //!
19 + //! What it asserts: a safety floor on the output of every preset (no script,
20 + //! iframe, object, embed, form, svg, math, `javascript:`, `vbscript:` or event
21 + //! handler, however the input was shaped), and that `sanitize_html` is
22 + //! idempotent. The second is the one worth having and nothing else in the crate
23 + //! checks it: a sanitizer that is not idempotent is the mutation-XSS shape,
24 + //! where a second parse of the same bytes sees different markup than the first.
25 + //!
26 + //! Not-panicking is the weakest thing a fuzz target can assert, and a target
27 + //! that asserts only that reports clean forever while a preset quietly starts
28 + //! emitting an `onerror` attribute.
29 +
30 + #![no_main]
31 +
32 + use libfuzzer_sys::fuzz_target;
33 +
34 + fuzz_target!(|input: &str| {
35 + docengine::oracle::check(input);
36 + });
@@ -1,0 +1,33 @@
1 + <img src=x onerror=alert(1)>
2 + <svg/onload=alert(1)>
3 + <a href="javascript:alert(1)">x</a>
4 + <iframe src="data:text/html,<script>alert(1)</script>"></iframe>
5 + <math><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>
6 + <data:tex/thtml,<script>alert(1)</script>"></iframe>
7 + <mat.h src=x onerror=alert(1)>
8 + <svg/onload=alert(1)>
9 + <a href="javascript:alert(1)">x</a>
10 + <iframe src="data:text/html,<script>alert(1)</script>"></iframe>
11 + <math><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>
12 + <data:tex/thtml,<script>alert(1)</script>"></iframe>
13 + <mat.h><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>
14 + <noscript><p title="</noscript><img src=x onerror=alert(1)>">
15 + <![CDATA[<script>alert(1)</script>]]>
16 + <form><button formaction="javascript:alert(1)">x</button></form>
17 + <base href="//evil.example/">
18 + <a href="&#106;avascript:alert(1)">enoscript><p title="</noscript><img src=x onerror=alert(1)>">
19 + <![C><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>
20 + <noscript><p title="</noscript><img src=x onerror=alert(1)>">
21 + <![CDATA[<script>alert(1)</script>]]>
22 + <form><button formaction="javascript:alert(1)">x</button></form>
23 + <base href="//evil.example/">
24 + <a href="&#106;avascript:alert(1)">enoscript><p title="</noscript><img src=x onerror=alert(1)>">
25 + <![CDATA[<script>alert(1)</script>]]>
26 + <form><button formaction="javascrme>
27 + <mat.h><mtext><table><mg(1)">enoscript><p title="</noscript><img src=x onerror=alert(1)>">
28 + <![CDATA[<script>alert(1)</script>]]>
29 + <form><button formaction="javascript:alert(1)">x</button></form>
30 + <base href="//evil.example/">
31 + <a href="&#106;avascript:alipt:alert(1)">x</button></form>
32 + <base href="//evil.example/">
33 + <a href="&#106;avascript:alert(1)">entity-escaped scheme</a>
@@ -1,0 +1,16 @@
1 + # Seeds
2 +
3 + Curated, read-only, and small. These are the inputs a person chose: real site
4 + docs for the ordinary shapes, plus the classic XSS payloads and the two cases
5 + that caught the oracle out on its first run (a markdown image that falls through
6 + as prose carrying ` onerror=`, and an entity-escaped payload inside a `title`
7 + attribute value).
8 +
9 + They are NOT the corpus. Run the fuzzer with the corpus first and these second:
10 +
11 + cargo +nightly fuzz run render fuzz/corpus/render fuzz/seeds/render -- -max_total_time=1800
12 +
13 + libFuzzer writes new inputs into the FIRST directory only, so this arrangement
14 + keeps the curated set curated and lets `fuzz/corpus/` grow. Passing seeds as the
15 + first argument by mistake put 3,042 machine-generated files in here on
16 + 2026-08-23, which is how the note came to be written down.
@@ -1,0 +1,181 @@
1 + # Payments & Refunds
2 +
3 + How payments work on Makenotwork.
4 +
5 + ---
6 +
7 + ## The Short Version
8 +
9 + Stripe handles all payments. You're the merchant of record. We don't take a cut of your revenue. Fans handle refunds with you or dispute via their bank.
10 +
11 + ---
12 +
13 + ## How Payments Work
14 +
15 + ### Fan Payments
16 +
17 + When fans buy your content or join a membership:
18 +
19 + 1. Fan pays via Stripe (~{{ stripe.percent | percent }} + {{ stripe.fixed | money }} per transaction)
20 + 2. Funds go directly to your connected Stripe account
21 + 3. Deposits to your bank on your chosen schedule
22 + 4. We never touch or hold your money
23 +
24 + ### Platform Membership
25 +
26 + Your monthly Makenotwork membership (${{ tiers.standard.basic }}-${{ tiers.standard.everything }}) is separate:
27 +
28 + - Billed to your payment method
29 + - Goes to us for platform access
30 + - Has nothing to do with your fan revenue
31 +
32 + ---
33 +
34 + ## You're the Merchant of Record
35 +
36 + When you connect Stripe, it creates an account for you:
37 +
38 + - **You are the merchant** - Fans are paying you, not us
39 + - **You set prices** - Including pay-what-you-want options
40 + - **You handle disputes** - Chargebacks and refunds are your responsibility
41 + - **Your Stripe account is yours** - If you leave, it stays with you
42 +
43 + The tradeoff: you absorb the (typically rare) chargeback risk.
44 +
45 + ---
46 +
47 + ## Payouts
48 +
49 + You control payout timing from your [Stripe dashboard](https://dashboard.stripe.com): standard (2-3 days), daily, weekly, monthly, or [instant](https://docs.stripe.com/payouts/instant-payouts) (1% fee in CA/EU/UK/SG/NO/HK/MY; 1.5% fee in US/AU/NZ/AE; min $0.50 USD or local equivalent, per [Stripe Instant Payouts docs](https://docs.stripe.com/payouts/instant-payouts)). See [Receiving Payouts](../guide/payouts.md) for full details on schedules, minimums, and international payouts. Payout schedule options are documented at https://docs.stripe.com/payouts.
50 +
51 + ---
52 +
53 + ## Refunds
54 +
55 + ### Our Policy
56 +
57 + Refund decisions are yours; you're the merchant of record. You can issue a full refund directly from the sale in your MNW dashboard, or handle full and partial refunds from your Stripe dashboard.
58 +
59 + ### How Fans Request Refunds
60 +
61 + Fans should contact you directly. You can:
62 +
63 + - Issue a full refund from the sale in your MNW dashboard, or a full or partial refund via your [Stripe dashboard](https://dashboard.stripe.com)
64 + - Offer alternative resolution (access fix, different content)
65 + - Decline if the purchase was delivered as promised
66 +
67 + ### Our Recommendation
68 +
69 + Clear refund policies reduce disputes. Consider:
70 +
71 + - Stating your policy on your page
72 + - Being generous with genuine mistakes
73 + - Responding promptly to requests
74 +
75 + A $5 refund is cheaper than a ${{ stripe.dispute_fee | int }} chargeback fee.
76 +
77 + ---
78 +
79 + ## Chargebacks
80 +
81 + If a fan disputes a charge with their bank:
82 +
83 + 1. **Your balance is debited** - Disputed amount plus fee (~${{ stripe.dispute_fee | int }})
84 + 2. **You respond through Stripe** - Provide evidence the charge was legitimate
85 + 3. **Bank decides** - You either get the money back or lose it
86 +
87 + ### Why This Model
88 +
89 + The alternative (us being merchant of record) would mean we'd take on chargeback risk and price that into fees, hold reserves from your earnings, and have incentive to restrict what you can sell.
90 +
91 + ### Reducing Chargeback Risk
92 +
93 + Most chargebacks come from:
94 +
95 + - Unrecognized charges (fan forgot they joined a membership)
96 + - Unauthorized use (stolen card)
97 + - Dissatisfaction (fan expected something different)
98 +
99 + Clear communication prevents most disputes. Make sure fans know what they're paying for, how your billing descriptor appears on statements, and how to cancel memberships. See [Receiving Payouts](../guide/payouts.md#chargebacks-disputes) for dispute resolution details.
100 +
101 + ---
102 +
103 + ## Taxes
104 +
105 + ### What We Provide
106 +
107 + - **Transaction records** - Exportable history of all payments
108 + - **1099-K** (US creators) - Issued by Stripe if you meet [reporting thresholds](https://support.stripe.com/topics/1099-tax-forms)
109 + - **Invoices** - For your platform membership
110 +
111 + ### Your Responsibilities
112 +
113 + You're responsible for:
114 +
115 + - Reporting income from fan payments
116 + - Collecting and remitting sales tax if required in your jurisdiction
117 + - Understanding tax obligations for digital goods in your location
118 +
119 + ### VAT, GST, and Sales Tax
120 +
121 + If you sell digital goods to fans in the EU, UK, Australia, Canada, or other jurisdictions with digital sales tax, you may be required to collect and remit VAT, GST, or sales tax, even if you are outside those jurisdictions.
122 +
123 + Stripe may collect sales tax automatically in some regions depending on your account configuration. Check your [Stripe Tax settings](https://dashboard.stripe.com/tax) to see what's enabled.
124 +
125 + Since we do not act as Merchant of Record, tax collection and remittance is your responsibility. Consult a tax professional if you're unsure.
126 +
127 + ### Platform Fee Deductions
128 +
129 + Your Makenotwork membership fee may be deductible as a business expense. Keep your receipts.
130 +
131 + ---
132 +
133 + ## International Payments
134 +
135 + Each creator sells in one currency, taken from their Stripe account. Supported: USD, CAD, GBP, AUD, NZD, EUR. Every price that creator sets, and every charge and subscription for them, is in that currency.
136 +
137 + To sell here, your country must be supported by [Stripe Connect](https://stripe.com/global) and your Stripe account must settle in one of those six. Those are two separate conditions and both must hold.
138 +
139 + ### Who Pays for Currency Conversion
140 +
141 + The buyer does, and the buyer chooses how. When a buyer's card is in a different currency from the creator's, checkout offers two paths:
142 +
143 + 1. **Stripe converts at checkout.** Stripe presents the price in the buyer's currency and charges that. Stripe's exchange rate includes a currency conversion fee, which Stripe sets and which is between 2% and 4%. Stripe does not report that fee to us as a separate figure, so we do not itemise it and we do not estimate it. Stripe displays the exact converted total on its own checkout page before the buyer confirms, and the buyer's receipt from us records the exact amount charged.
144 + 2. **The buyer's bank converts.** The charge is made in the creator's currency and the buyer's card issuer converts at its own rate, which is set by that issuer. We cannot see it, display it, or estimate it, and we do not.
145 +
146 + In both cases the creator receives their price in their own currency and the buyer bears the cost of conversion. Neither path involves an exchange rate set by Makenotwork; we hold no rates and perform no conversions.
147 +
148 + ### Revenue Splits
149 +
150 + A project owner can invite another creator to a share of that project's revenue. **An invitation has to be accepted before it pays anything.** While it is pending the percentage is reserved, so the owner cannot promise it to someone else, but sales in the meantime belong entirely to the owner. A share begins at acceptance and is not backdated.
151 +
152 + Declining removes the invitation and frees the percentage. There is no obligation to accept, and nothing happens if you ignore it.
153 +
154 + ### Revenue Splits Across Currencies
155 +
156 + A revenue split is denominated in the currency of the project that generated it, which is that project's owner's currency. If you receive a split in a currency you do not settle in, Stripe converts it when it reaches you and the conversion cost comes out of your share, not the project's. We cannot tell you that rate in advance, because Stripe sets it at payout.
157 +
158 + You are shown this before you accept, which is the reason acceptance exists: nobody should carry a conversion cost they were never given the chance to refuse. The project owner is told the same thing when they send the invitation.
159 +
160 + See [Receiving Payouts](../guide/payouts.md#international-creators) for the detail.
161 +
162 + ---
163 +
164 + ## What We Don't Do
165 +
166 + - **Hold creator funds** - Money goes directly to your Stripe account
167 + - **Take a percentage** - Our fee is flat monthly membership only
168 + - **Control your pricing** - Set whatever prices you want
169 + - **Process payments ourselves** - Stripe handles everything
170 + - **Provide tax advice** - Consult a professional
171 +
172 + ---
173 +
174 + ## See Also
175 +
176 + - [You're the Merchant of Record](../about/merchant-of-record.md): what it means, pros and cons, tax tools
177 + - [Creator Guarantees](../about/guarantees.md): our commitments on revenue
178 + - [Pricing Tiers](../guide/tiers.md): tier features and pricing
179 + - [Stripe Global](https://stripe.com/global): supported countries
180 + - [Stripe: What You Need to Know](../guide/stripe.md): fees by country, currency and conversion, Stripe Tax
181 + - [Stripe Connect](https://stripe.com/connect): how the payment model works
@@ -1,0 +1,196 @@
1 + # Content Moderation & Enforcement
2 +
3 + What happens when accounts violate our policies, and what you should know about how moderation works right now.
4 +
5 + ---
6 +
7 + ## Current Limitations
8 +
9 + Moderation is currently handled by one person. Decisions are fast but not independently reviewable. 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, the founder makes moderation decisions directly and in good faith, and every decision can be appealed.
10 +
11 + ---
12 +
13 + ## Our Approach
14 +
15 + 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.
16 +
17 + ---
18 +
19 + ## What's Not Allowed
20 +
21 + ### Harmful Content
22 +
23 + - **Dehumanization, harassment, and incitement to violence**: Content that attacks, degrades, or encourages violence toward individuals or groups
24 + - **Stricter review for content targeting marginalized groups**: Content that would be tolerable if directed at dominant groups may be removed when it targets communities facing systemic discrimination
25 + - **Doxxing**: Sharing private information (addresses, phone numbers, workplaces, etc.) without consent
26 +
27 + ### Platform Integrity
28 +
29 + - **Spam and fraud**: Deceptive practices, scams, or manipulative content
30 + - **Impersonation**: Misrepresenting identity to deceive others
31 + - **Illegal content**: Content that violates applicable law
32 +
33 + ### Content Type Restrictions
34 +
35 + - **Adult/NSFW content**: Not permitted on Makenotwork. We intend to launch a separate platform for adult creators with identical commitments when infrastructure is ready
36 + - **Unqualified promotion**: Creators promoting health products, financial services, or similar without appropriate qualification may be subject to scrutiny or moderation
37 +
38 + ---
39 +
40 + ## On Marginalized Groups
41 +
42 + We apply stricter review when content targets groups that face systemic discrimination or historical oppression.
43 +
44 + **Why this exists:** Harmful content targeting marginalized groups contributes to real-world discrimination and violence. Identical rules applied without context produce unequal effects.
45 +
46 + ---
47 +
48 + ## Enforcement Actions
49 +
50 + We enforce proportionally. Minor issues get a direct message. Serious or repeated issues get serious responses.
51 +
52 + ### Direct Message
53 +
54 + For minor or first-time issues:
55 +
56 + - We email you explaining what policy was violated and what needs to change
57 + - No account restrictions
58 + - No formal record on your account (formal warning tracking is not yet built)
59 +
60 + Most issues end here. People make mistakes.
61 +
62 + ### Content Removal
63 +
64 + For content that violates policy:
65 +
66 + - Specific content is removed or hidden
67 + - You're notified with explanation
68 + - Account remains active
69 + - You can appeal the decision
70 +
71 + ### Suspension
72 +
73 + For moderate violations or repeated issues:
74 +
75 + - Account access is restricted
76 + - You can still export your data
77 + - You can still appeal
78 +
79 + During suspension:
80 + - Your content remains but is hidden from fans
81 + - Fan memberships to your content are paused (fans aren't charged)
82 + - You can't upload or modify content
83 +
84 + ### Permanent Termination
85 +
86 + For serious violations or repeated moderate violations:
87 +
88 + - Account is permanently closed
89 + - Content is removed from public access
90 + - You have 30 days to export your data
91 + - Future accounts will also be terminated
92 +
93 + ---
94 +
95 + ## What Leads to Each Level
96 +
97 + ### Usually a Direct Message
98 +
99 + - Borderline content that could be interpreted as policy-violating
100 + - Technical policy violations (wrong format, metadata issues)
101 + - Minor behavioral issues (heated argument, single incident)
102 +
103 + ### Usually Content Removal
104 +
105 + - Clear policy violations in specific content
106 + - Content that received valid complaints and can't be defended
107 +
108 + ### Usually Suspension
109 +
110 + - Continued issues after a direct message
111 + - Moderate harassment or abuse
112 + - Repeated content violations
113 + - Deceptive practices
114 +
115 + ### Usually Termination
116 +
117 + - Severe harassment, threats, or doxxing
118 + - CSAM or content sexualizing minors
119 + - Fraud or scams
120 + - Ban evasion
121 + - Illegal activity
122 + - Repeated moderate violations
123 +
124 + ---
125 +
126 + ## Immediate Termination
127 +
128 + Some violations skip the escalation ladder:
129 +
130 + - **CSAM** - Immediate termination, law enforcement referral
131 + - **Credible threats of violence** - Immediate termination
132 + - **Illegal content** - Immediate termination
133 + - **Fraud** - Immediate termination
134 +
135 + For these, there's no warning period.
136 +
137 + ---
138 +
139 + ## Proactive Measures
140 +
141 + Every uploaded file is scanned for malware before it is served (see [Content Scanning](../tech/content-scanning.md)). We do not yet run automated image-matching for illegal content such as CSAM; that detection is report-driven and human-reviewed today. When we discover clearly illegal content, we remove it immediately, preserve it as required by law, and report it to the appropriate authorities (CSAM is reported to NCMEC's CyberTipline). Otherwise we operate reactively based on reports. Not every complaint results in action; false reports are common and sometimes coordinated.
142 +
143 + ---
144 +
145 + ## Data Handling for Terminated Accounts
146 +
147 + When your account is terminated:
148 +
149 + - **30-day export window** - You can download your content and data
150 + - **Content removed from public access** - Fans can't access it
151 + - **Financial records retained** - As required by law and payment processors
152 +
153 + After 30 days, your account data and content are deleted unless:
154 +
155 + - **Content we cannot ethically host** (CSAM, credible threats, illegal material) - Removed immediately. No export window for this content.
156 + - **Ban evasion records** - Account identifiers retained to enforce termination. Deleted after 2 years.
157 +
158 + Content may also be unlisted from discovery rather than removed, depending on the nature of the violation. Unlisted content remains accessible via direct link but does not appear in search or browse.
159 +
160 + ---
161 +
162 + ## Ban Evasion
163 +
164 + Creating new accounts after termination:
165 +
166 + - New accounts will be terminated when detected
167 + - Evasion may extend data retention periods
168 + - Repeated evasion may result in legal action
169 +
170 + ---
171 +
172 + ## Appeals
173 +
174 + Per our [Creator Guarantees](../about/guarantees.md):
175 +
176 + - Clear explanation of what policy was violated
177 + - Opportunity to appeal
178 + - Access to export your data even if suspended
179 +
180 + You can appeal any enforcement action, including terminations. See [Appeal Process](./appeals.md).
181 +
182 + ---
183 +
184 + ## Transparency
185 +
186 + - **Public moderation code**: Our moderation tools are in the public source code (excluding anti-spam measures that would be defeated by disclosure)
187 + - **Versioned policies**: This document is version-controlled; view history in our repository
188 + - **Major decisions documented**: Significant moderation decisions and policy changes are documented on The Changelog
189 +
190 + ---
191 +
192 + ## See Also
193 +
194 + - [Creator Guarantees](../about/guarantees.md)
195 + - [Appeal Process](./appeals.md)
196 + - [Acceptable Use Policy](./acceptable-use.md)
@@ -1,0 +1,131 @@
1 + # Copyright & DMCA
2 +
3 + How we handle copyright on Makenotwork.
4 +
5 + ---
6 +
7 + ## Our Position
8 +
9 + When you upload content to Makenotwork, you retain all rights. We don't claim any ownership, and our license to your content is limited to what's necessary to operate the platform (displaying it to fans, generating thumbnails, etc.).
10 +
11 + ---
12 +
13 + ## DMCA Compliance
14 +
15 + We comply with the Digital Millennium Copyright Act (DMCA). We respond to valid takedown notices and have procedures for handling copyright disputes.
16 +
17 + **Handled by email, not in-app.** The DMCA workflow on Makenotwork (takedown notices, counter-notifications, the statutory waiting period, and repeat-infringer tracking) runs through the designated agent email below, reviewed by a human. There is no self-serve in-app filing form today. We respond within the statutory window. In-app tooling for filing, tracking, and counter-notification is on the roadmap; this page describes how the process works regardless of where you submit.
18 +
19 + ### Designated Agent
20 +
21 + DMCA takedown notices should be sent to:
22 +
23 + **Email:** dmca@makenot.work
24 +
25 + **Mail:**
26 + Make Creative, LLC
27 + c/o Northwest Registered Agent LLC
28 + ATTN: DMCA Agent
29 + 1500 N Grant St, Ste N
30 + Denver, CO 80203
31 +
32 + ---
33 +
34 + ## Filing a Takedown Notice
35 +
36 + If you believe content on Makenotwork infringes your copyright, send a notice containing:
37 +
38 + 1. **Your contact information** - Name, address, phone number, email
39 + 2. **Identification of the copyrighted work** - What work is being infringed
40 + 3. **Identification of the infringing material** - URL or other specific location on our platform
41 + 4. **Statement of good faith** - "I have a good faith belief that use of the material in the manner complained of is not authorized by the copyright owner, its agent, or the law"
42 + 5. **Statement of accuracy** - "The information in this notification is accurate, and under penalty of perjury, I am the owner, or authorized to act on behalf of the owner, of an exclusive right that is allegedly infringed"
43 + 6. **Your signature** - Physical or electronic
44 +
45 + Incomplete notices may not receive a response.
46 +
47 + ---
48 +
49 + ## What Happens When We Receive a Notice
50 +
51 + 1. **Review** - We review the notice for completeness and validity
52 + 2. **Removal** - If valid, we remove or disable access to the content
53 + 3. **Notification** - We notify the creator whose content was removed
54 + 4. **Counter-notification** - Creator may file a counter-notification (see below)
55 +
56 + Complex cases may require additional review.
57 +
58 + ---
59 +
60 + ## Counter-Notification
61 +
62 + If your content was removed and you believe the takedown was invalid, you can file a counter-notification.
63 +
64 + File a counter-notification if:
65 +
66 + - You own the copyright to the removed content
67 + - You have a license to use the content
68 + - The use is fair use or otherwise lawful
69 + - The content was misidentified
70 +
71 + Do not file a counter-notification if you know the content infringes someone's copyright. Counter-notifications are made under penalty of perjury.
72 +
73 + ### Required Elements
74 +
75 + Your counter-notification must include:
76 +
77 + 1. **Your contact information** - Name, address, phone number, email
78 + 2. **Identification of removed content** - Describe the content and where it appeared before removal
79 + 3. **Statement under penalty of perjury** - "I swear, under penalty of perjury, that I have a good faith belief that the material was removed or disabled as a result of mistake or misidentification of the material to be removed or disabled"
80 + 4. **Consent to jurisdiction** - "I consent to the jurisdiction of the Federal District Court for the judicial district in which my address is located, or if my address is outside the United States, the judicial district in which Make Creative, LLC is located, and I will accept service of process from the person who provided the original DMCA notification or an agent of such person"
81 + 5. **Your signature** - Physical or electronic
82 +
83 + Send counter-notifications to: **dmca@makenot.work**
84 +
85 + ### What Happens After Filing
86 +
87 + 1. We review for completeness
88 + 2. We notify the original complainant (they receive a copy)
89 + 3. **10-14 business day waiting period**: The complainant has this time to file a court action
90 + 4. **Content restored**: If no court action is filed, we restore your content
91 +
92 + The waiting period gives the complainant time to seek a court order. If the complainant files suit, your content stays down pending resolution. At that point, this is a legal matter between you and the complainant.
93 +
94 + ---
95 +
96 + ## Repeat Infringer Policy
97 +
98 + We terminate accounts of users who repeatedly infringe copyright.
99 +
100 + **How it works:**
101 +
102 + - **First valid DMCA:** Content removed, creator notified
103 + - **Second valid DMCA:** Content removed, creator notified of repeat status
104 + - **Third valid DMCA:** Account terminated
105 +
106 + "Valid" means the notice was complete, we removed content, and no successful counter-notification was filed.
107 +
108 + We may skip steps and terminate immediately for egregious infringement (large-scale piracy, commercial counterfeiting, etc.).
109 +
110 + ---
111 +
112 + ## What We Won't Do
113 +
114 + - **Remove content without proper notice** - Informal complaints or social media callouts aren't DMCA notices
115 + - **Act on incomplete notices** - All required elements must be present
116 + - **Ignore counter-notifications** - If a creator disputes a takedown, we follow the legal process
117 + - **Share creator contact info with complainants** - We forward notices to creators, not the reverse
118 +
119 + ---
120 +
121 + ## Abuse of DMCA
122 +
123 + Filing false DMCA notices is perjury. If you knowingly misrepresent that content is infringing, you may be liable for damages. We track abuse patterns and may block repeat false filers from our DMCA process.
124 +
125 + ---
126 +
127 + ## See Also
128 +
129 + - [Content Moderation](./moderation.md): Our broader moderation approach
130 + - [Appeal Process](./appeals.md): For non-copyright content issues
131 + - [Terms of Service](./terms-of-service.md): Content ownership terms
@@ -1,0 +1,113 @@
1 + # Terms of Service
2 +
3 + *Last updated: June 8, 2026*
4 +
5 + Makenotwork is operated by Make Creative, LLC ("we", "us", "our"). By using our service, you agree to these terms.
6 +
7 + ## The Short Version
8 +
9 + - You own your content
10 + - You're responsible for your content
11 + - We provide the platform, you provide the creativity
12 + - Don't do illegal things
13 + - Either party can end this relationship anytime
14 +
15 + ## Accounts
16 +
17 + ### Eligibility
18 + You must be 18+ or the age of majority in your jurisdiction to create a creator account. Fan accounts require you to be 13+ (or your jurisdiction's minimum).
19 +
20 + ### Account Security
21 + You're responsible for your account credentials. Enable two-factor authentication. If you suspect unauthorized access, contact us immediately at security@makenot.work.
22 +
23 + ### Account Types
24 + - **Creator accounts**: Pay a monthly tier fee, can upload and sell content
25 + - **Fan accounts**: Free, can purchase and access content
26 +
27 + ## Creator Terms
28 +
29 + ### Your Content
30 + You retain all rights to content you upload. By uploading, you grant us a license to host, display, and distribute your content as necessary to operate the service.
31 +
32 + ### Revenue
33 + We charge 0% platform fee on fan payments. Payment processing fees (~3%) are deducted before funds reach you.
34 +
35 + ### Prohibited Content
36 + Don't upload:
37 + - Content you don't have rights to distribute
38 + - Illegal content (varies by jurisdiction)
39 + - Content that violates others' rights
40 + - Malware or harmful code
41 +
42 + ### Content Moderation
43 + We remove content that violates these terms. We try to give notice before removal when possible. See [Content Moderation](./moderation.md) for details.
44 +
45 + ## Fan Terms
46 +
47 + ### Purchases
48 + When you buy content:
49 + - You get a license for personal use
50 + - You can download and keep files
51 + - You can't redistribute or resell
52 + - One-time purchases: access persists even if the creator leaves ({{ policy.buyer_access_days }}-day download window)
53 + - Membership-gated content: access persists during the {{ policy.buyer_access_days }}-day grace period if a creator deletes their account. After {{ policy.buyer_access_days }} days, content is removed. Active memberships are not billed during this period
54 +
55 + ### Memberships
56 + - Billed monthly
57 + - Cancel anytime
58 + - Access continues until end of billing period
59 + - No refunds for partial months
60 +
61 + ## Data & Privacy
62 +
63 + See our [Privacy Policy](./privacy-policy.md). Summary:
64 + - We collect minimal data
65 + - We don't track you
66 + - We don't sell your data
67 + - You can export or delete your data
68 +
69 + ## Termination
70 +
71 + ### By You
72 + Cancel anytime. Export your data first. Creator memberships end at the billing period.
73 +
74 + ### By Us
75 + We may terminate accounts that violate these terms. We'll provide notice when possible and allow data export for non-egregious violations.
76 +
77 + ## Disclaimers
78 +
79 + The service is provided "as is." We do our best but can't guarantee uptime, data integrity, or that you'll make money. Back up your content.
80 +
81 + ## Liability
82 +
83 + Our liability is limited to the amount you've paid us in the past 12 months. We're not liable for indirect damages, lost profits, or lost data.
84 +
85 + The disclaimers and limits in this section do not override the specific commitments in [What We Guarantee](../about/guarantees.md), including uptime, data preservation, backup, shutdown notice, and founder-pricing lock-in. Where this section and the Guarantees conflict, the Guarantees control.
86 +
87 + ## Disputes
88 +
89 + We prefer to resolve disputes directly. If that fails, disputes are governed by Colorado law and handled in Colorado courts.
90 +
91 + ## Changes
92 +
93 + We may update these terms:
94 +
95 + - **General changes**: 90 days notice before they take effect
96 + - **Privacy policy changes**: 90 days notice before they take effect
97 + - **Pricing changes**: {{ policy.price_change_notice_days }} days notice + grandfathering at current rate for at least {{ policy.grandfather_months }} months (see [guarantees](../about/guarantees.md))
98 + - **Exceptions**: Changes required by law, court order, or to address an active security threat may take effect immediately
99 +
100 + We'll notify you by email when terms change. Continued use after the notice period constitutes acceptance.
101 +
102 + ## Contact
103 +
104 + Questions? Email legal@makenot.work.
105 +
106 + ---
107 +
108 + ## See Also
109 +
110 + - [Privacy Policy](./privacy-policy.md): Data collection and handling
111 + - [Acceptable Use Policy](./acceptable-use.md): Content rules
112 + - [What We Guarantee](../about/guarantees.md): Platform commitments
113 + - [How We Work](../about/how-we-work.md): Business model and pricing
@@ -1,0 +1,139 @@
1 + # Privacy Policy
2 +
3 + *Last updated: June 8, 2026*
4 +
5 + Makenotwork is operated by Make Creative, LLC. This policy explains what data we collect and how we use it.
6 +
7 + ## The Short Version
8 +
9 + - We collect the minimum data needed to operate
10 + - We don't track you across the web
11 + - We don't sell your data
12 + - We don't show ads
13 + - You can export or delete your data anytime
14 +
15 + ## What We Collect
16 +
17 + ### Account Information
18 + - Email address
19 + - Username
20 + - Password (hashed, we can't read it)
21 + - Payment information (handled by our payment processor, we don't store card numbers)
22 +
23 + ### Content
24 + - Files you upload
25 + - Metadata you provide (titles, descriptions, tags)
26 + - Projects and organization
27 +
28 + ### Transactions
29 + - Purchases and memberships
30 + - Payment amounts and dates
31 + - Payout records
32 +
33 + ### Technical Data
34 + - IP address (for security, rate limiting)
35 + - Browser type (for compatibility)
36 + - Error logs (for debugging)
37 +
38 + IP addresses are retained for 30 days, then deleted.
39 +
40 + ### Login and Session Data
41 + - IP address and browser type per session (for security alerts and session management)
42 + - Failed login attempt counts and timestamps (for account lockout protection)
43 +
44 + Session records are retained for 90 days after the session ends. Failed login data is reset after a successful login.
45 +
46 + ## What We Don't Collect
47 +
48 + - Browsing behavior or history
49 + - Location data (beyond IP-derived country for tax purposes)
50 + - Cross-site tracking or device fingerprinting for identification
51 + - Third-party tracking data
52 + - Social media profiles
53 + - Analytics of any kind. We run no analytics product, first-party or third-party, and no page-view or visitor measurement.
54 +
55 + The one thing your browser reports back to us is a Content Security Policy violation: if a page tries to load a script or style the policy forbids, the browser posts the blocked address and the page it happened on. That report carries no cookie, no identifier, and nothing about you, and it is sent only when a violation occurs. It exists so an injected script shows up in our logs instead of going unnoticed.
56 +
57 + ## How We Use Data
58 +
59 + - **Operating the service**: Hosting content, processing payments
60 + - **Communication**: Account notifications, support responses
61 + - **Security**: Detecting abuse, preventing fraud
62 + - **Legal compliance**: Tax reporting, responding to valid legal requests
63 +
64 + ## Data Sharing
65 +
66 + We share data only with:
67 +
68 + - **Payment processor**: Payment processing (see [Infrastructure & Vendors](../tech/infrastructure.md) for current provider)
69 + - **Email provider**: Sending the mail you receive, which means your address and the message itself (see [Infrastructure & Vendors](../tech/infrastructure.md) for current provider)
70 + - **Infrastructure providers**: Hosting, CDN (they process but don't access your data)
71 + - **Legal authorities**: Only when legally required, and we'll notify you unless prohibited
72 +
73 + We don't sell or share data for advertising.
74 +
75 + ## Your Rights
76 +
77 + ### Access
78 + Download all your data anytime from Settings > Export.
79 +
80 + ### Correction
81 + Edit your account information directly.
82 +
83 + ### Deletion
84 + Delete your account from Settings > Account > Delete. We remove your personal data within 30 days, except:
85 + - Transaction records (legal requirement)
86 + - If you have completed sales, purchased content remains accessible to buyers for 90 days after deletion, then is removed
87 +
88 + ### Portability
89 + Export includes all content, metadata, and transaction history in standard formats.
90 +
91 + ## Cookies
92 +
93 + We use cookies for:
94 + - **Session management**: Keeping you logged in
95 + - **Security**: Preventing unauthorized actions on your account
96 +
97 + We don't use tracking cookies or third-party cookies.
98 +
99 + ## Children
100 +
101 + We don't knowingly collect data from children under 13.
102 +
103 + ## International Transfers
104 +
105 + Your data is processed in the United States (application server and database) and the European Union (file storage in Germany and Finland). For the specific providers and regions that host it, see our [Infrastructure](../tech/infrastructure.md) page and the service list in [Credits](../about/credits.md), which links to each vendor (including Hetzner).
106 +
107 + For transfers of personal data from the EU/EEA to the United States, we rely on Standard Contractual Clauses (SCCs) as approved by the European Commission. Copies of our SCCs are available upon request at privacy@makenot.work.
108 +
109 + We also maintain data processing agreements with our infrastructure providers that include SCC commitments for any EU personal data they process on our behalf.
110 +
111 + ## GDPR (EU Users)
112 +
113 + You have rights under GDPR including access, rectification, erasure, and portability. Contact privacy@makenot.work to exercise these rights.
114 +
115 + Legal basis for processing: contract performance (operating your account), legitimate interest (security), legal obligation (tax records).
116 +
117 + We respond to data subject requests within 30 days.
118 +
119 + ## CCPA (California Users)
120 +
121 + You have the right to know what data we collect, request deletion, and opt out of data sales (we don't sell data, so there's nothing to opt out of).
122 +
123 + ## Changes
124 +
125 + We'll notify you of material changes via email.
126 +
127 + ## Contact
128 +
129 + Privacy questions: privacy@makenot.work
130 +
131 + Data protection inquiries: dpo@makenot.work
132 +
133 + ---
134 +
135 + ## See Also
136 +
137 + - [Terms of Service](./terms-of-service.md): Full legal terms
138 + - [Acceptable Use Policy](./acceptable-use.md): Content rules
139 + - [What We Guarantee](../about/guarantees.md): Data export and portability commitments
@@ -1,0 +1,99 @@
1 + # Transparency Reports
2 +
3 + What we will publish about moderation and legal requests.
4 +
5 + > **Status: pre-launch.** No transparency reports have been published yet. The first report will appear once moderation volume is high enough to anonymize effectively (see [Pre-Launch Note](#pre-launch-note)). This page describes the program we are committing to, not reports that already exist.
6 +
7 + ---
8 +
9 + ## Our Commitment
10 +
11 + We will publish regular transparency reports so you can verify that our enforcement matches our stated policies:
12 +
13 + - Content moderation actions
14 + - Account enforcement
15 + - Legal and government requests
16 + - DMCA notices
17 +
18 + ---
19 +
20 + ## What We Report
21 +
22 + ### Moderation Statistics
23 +
24 + - **Content removed** - Total pieces of content removed, by category
25 + - **Accounts warned** - Number of accounts receiving warnings
26 + - **Accounts suspended** - Temporary suspensions issued
27 + - **Accounts terminated** - Permanent terminations
28 + - **Appeals received** - How many decisions were appealed
29 + - **Appeals overturned** - How often we reversed our decisions
30 +
31 + ### Legal Requests
32 +
33 + - **Subpoenas received** - Requests from courts or government
34 + - **Requests complied with** - How many we provided data for
35 + - **Requests rejected** - How many we challenged or refused
36 + - **User notifications** - How many affected users we notified
37 +
38 + ### DMCA
39 +
40 + - **Takedown notices received** - Total valid DMCA notices
41 + - **Content removed via DMCA** - Items taken down
42 + - **Counter-notifications filed** - Disputes by creators
43 + - **Content restored** - Items put back after counter-notification
44 +
45 + ---
46 +
47 + ## Report Format
48 +
49 + Each report covers a calendar quarter and includes:
50 +
51 + - Raw numbers for each category
52 + - Comparison to previous quarters
53 + - Notable trends or changes
54 + - Explanation of any anomalies
55 +
56 + Reports are published as blog posts and archived in this documentation.
57 +
58 + ---
59 +
60 + ## Publication Schedule
61 +
62 + Once reporting begins, reports will be published quarterly, within 30 days of quarter end:
63 +
64 + - Q1 (Jan-Mar): Published by April 30
65 + - Q2 (Apr-Jun): Published by July 31
66 + - Q3 (Jul-Sep): Published by October 31
67 + - Q4 (Oct-Dec): Published by January 31
68 +
69 + ---
70 +
71 + ## What We Don't Publish
72 +
73 + - **Specific content details** - We don't republish removed content
74 + - **User identities** - We don't name individuals involved in moderation actions
75 + - **Ongoing investigations** - We don't disclose active legal matters
76 + - **Security vulnerabilities** - We don't detail exploitation attempts
77 +
78 + ---
79 +
80 + ## Pre-Launch Note
81 +
82 + Reports will begin once moderation volume is high enough to anonymize effectively.
83 +
84 + ---
85 +
86 + ## Source-Available Transparency
87 +
88 + Beyond reports:
89 +
90 + - **Policies are public** - All moderation criteria are documented here
91 + - **Code is public** - Automated systems can be inspected
92 + - **Decisions are explained** - Users receive specific reasons for actions
93 +
94 + ---
95 +
96 + ## See Also
97 +
98 + - [Content Moderation](./moderation.md): how we moderate
99 + - [Privacy Policy](./privacy-policy.md): how we handle data requests
@@ -1,0 +1,121 @@
1 + # Appeal Process
2 +
3 + How to dispute moderation decisions.
4 +
5 + *Makenotwork is currently a one-person operation. Appeals are reviewed directly by the founder. Independent review by a separate decision-maker is planned once the team grows.*
6 +
7 + ---
8 +
9 + ## What Can Be Appealed
10 +
11 + You can appeal:
12 +
13 + - Content removal
14 + - Account suspension
15 + - Account termination
16 + - Any other moderation action
17 +
18 + ---
19 +
20 + ## How to Appeal
21 +
22 + Every appeal reaches the same person and gets the same review. How you send it
23 + depends on what you're appealing.
24 +
25 + **Account suspension:** submit from your account dashboard. The form appears there
26 + while the suspension is active.
27 +
28 + **Content removal, account termination, and anything else:** email
29 + **appeals@makenot.work**. There is no dashboard form for these yet. If your account
30 + was terminated you are signed out and cannot reach the dashboard at all, so email is
31 + the only route, and it works.
32 +
33 + Include:
34 +
35 + 1. **Your username** or account email
36 + 2. **What action you're appealing** - What was removed or what restriction was applied
37 + 3. **Why you believe the decision was wrong** - Be specific
38 + 4. **Any relevant context** - Information we might have missed
39 +
40 + Emailed appeals are not second-class. The dashboard form exists for suspensions
41 + because that is where it was built first, and the rest are on the list.
42 +
43 + ---
44 +
45 + ## What Happens Next
46 +
47 + 1. **Acknowledgment** - We confirm receipt (usually within 24 hours)
48 +
49 + 2. **Review** - Your appeal is reviewed with fresh eyes. Currently, appeals are reviewed by the founder. Once the team grows, appeals will be reviewed by someone other than the original decision-maker
50 +
51 + 3. **Decision** - We notify you of the outcome with explanation
52 +
53 + 4. **If overturned** - Content is restored or restrictions are lifted
54 +
55 + 5. **If upheld** - We explain why and what options remain
56 +
57 + ---
58 +
59 + ## Timeline
60 +
61 + We resolve appeals within:
62 +
63 + - **Content removal:** 3-5 business days
64 + - **Account suspension:** 5-10 business days
65 + - **Account termination:** 10-14 business days
66 +
67 + Complex cases may take longer. We'll keep you informed of delays.
68 +
69 + ---
70 +
71 + ## What We Consider
72 +
73 + When reviewing appeals, we look at:
74 +
75 + - **Original context** - Was important context missed?
76 + - **Policy interpretation** - Was the policy applied correctly?
77 + - **Consistency** - Is this consistent with how we've handled similar cases?
78 + - **New information** - Did you provide information that changes the picture?
79 +
80 + ---
81 +
82 + ## Data Access During Appeals
83 +
84 + Even if your account is suspended or terminated:
85 +
86 + - You can request a data export
87 + - We preserve your data during the appeal window
88 +
89 + ---
90 +
91 + ## Limits
92 +
93 + Some decisions cannot be appealed:
94 +
95 + - **Legal requirements** - If we're legally required to remove content or terminate an account
96 + - **Imminent harm** - If we believe content poses immediate danger to someone
97 + - **CSAM** - Content involving minors is not subject to appeal
98 +
99 + For these cases, we'll explain why the decision is final.
100 +
101 + ---
102 +
103 + ## If You Disagree with the Appeal Outcome
104 +
105 + If your appeal is denied and you believe we made an error, you can appeal again after
106 + 30 days. Provide new information or arguments not considered the first time; your
107 + case will be re-examined from scratch.
108 +
109 + For suspensions the 30-day wait is enforced by the dashboard form, which will tell
110 + you how many days remain. One appeal is open at a time; submit again once the
111 + previous one has been decided.
112 +
113 + The wait exists so that a second look is a genuine second look rather than the same
114 + case resubmitted the same week. It is not a limit on how many times you can be heard.
115 +
116 + ---
117 +
118 + ## See Also
119 +
120 + - [Content Moderation](./moderation.md): How we make decisions
121 + - [Acceptable Use Policy](./acceptable-use.md): What we enforce
@@ -1,0 +1,119 @@
1 + # Acceptable Use Policy
2 +
3 + What you can and cannot do on Makenotwork.
4 +
5 + ---
6 +
7 + ## The Short Version
8 +
9 + Create and share your work. Respect others. Don't break the law.
10 +
11 + ---
12 +
13 + ## What's Allowed
14 +
15 + Makenotwork is for creators sharing original work with their fans. You can:
16 +
17 + - Upload and distribute your original content
18 + - Sell access to your work
19 + - Build a paying audience
20 + - Use creative tools and customization features
21 + - Export your data anytime
22 + - Include sponsorships and affiliate links in your content
23 +
24 + We support a wide range of creative expression, including work that's controversial, challenging, or provocative.
25 +
26 + ### Sponsorships and Endorsements
27 +
28 + Creators are free to include sponsored segments, affiliate links, and paid endorsements in their content. Sponsored content must be clearly labeled as advertising, and creators must comply with all applicable disclosure laws (including FTC guidelines, GDPR, and equivalent regulations in their jurisdiction). We strongly encourage creators to only recommend products they've personally used and are qualified to evaluate. Your audience trusts you. Treat that trust as the asset it is.
29 +
30 + Undisclosed paid advertisement is a serious moderation issue. Presenting sponsored content as organic recommendation undermines fan trust across the entire platform.
31 +
32 + ---
33 +
34 + ## What's Not Allowed
35 +
36 + ### Content That Harms People
37 +
38 + - **Dehumanization** - Content that treats people as less than human based on identity
39 + - **Harassment** - Targeted abuse, threats, or intimidation of individuals
40 + - **Incitement to violence** - Content encouraging real-world harm
41 + - **Doxxing** - Publishing private information to enable harassment
42 + - **CSAM** - Any sexual content involving minors (immediate removal, law enforcement referral)
43 +
44 + ### Content That Harms the Platform
45 +
46 + - **Spam** - Automated posting, fake engagement, or promotional flooding
47 + - **Fraud** - Scams, deceptive schemes, or financial manipulation, including misrepresenting your [generative AI tier](../about/generative-ai.md) (e.g., claiming Handmade when generative AI was used)
48 + - **Impersonation** - Pretending to be someone else to deceive
49 + - **Malware and harmful software** - Including but not limited to:
50 + - Uploading software that contains malware, spyware, or backdoors
51 + - Uploading software that misrepresents its functionality
52 + - Distributing cracked, pirated, or license-circumventing software
53 + - New uploads from unestablished accounts may be held for review
54 +
55 + ### Content We Don't Host
56 +
57 + - **Adult/NSFW content** - Not permitted on Makenotwork. We intend to launch a separate platform for adult creators with identical commitments when infrastructure is ready
58 + - **Illegal content** - Content that violates applicable law
59 +
60 + ---
61 +
62 + ## Context Matters
63 +
64 + We evaluate content in context. Satire, critique, historical documentation, and artistic exploration are considered when reviewing reported content. A documentary about hate groups isn't the same as hate group recruitment. We look at intent and effect, not surface content.
65 +
66 + ---
67 +
68 + ## Higher Standards for Discussing Marginalized Groups
69 +
70 + Content discussing marginalized groups is held to a higher standard. The reason is not that these groups are fragile; it is that platforms have historically amplified harm against them. The same words carry different weight depending on historical context and power dynamics.
71 +
72 + ---
73 +
74 + ## Account Behavior
75 +
76 + Beyond content, we expect users to:
77 +
78 + - **Use one account** - No sockpuppets or ban evasion
79 + - **Respect rate limits** - No automated abuse of platform features
80 + - **Honor content licenses** - Don't redistribute others' paid content
81 + - **Engage honestly** - No fake reviews, inflated metrics, or deceptive practices
82 +
83 + ---
84 +
85 + ## Consequences
86 +
87 + Violations result in action proportional to severity:
88 +
89 + - Minor first-time issues: Direct message explaining the issue
90 + - Repeated or moderate issues: Content removal, temporary restrictions
91 + - Serious or repeated violations: Account suspension or termination
92 +
93 + See [Content Moderation & Enforcement](./moderation.md) for details.
94 +
95 + You can appeal any action. See [Appeal Process](./appeals.md).
96 +
97 + ---
98 +
99 + ## Reporting Violations
100 +
101 + **Email:** reports@makenot.work
102 +
103 + Include:
104 + - Link to the content
105 + - Which policy you believe is violated
106 + - Any relevant context
107 +
108 + We review all reports. We don't disclose reporter identities to the reported user.
109 +
110 + ---
111 +
112 + ## See Also
113 +
114 + - [Generative AI Policy](../about/generative-ai.md): AI tier definitions and disclosure requirements
115 + - [Terms of Service](./terms-of-service.md): Full legal terms
116 + - [Privacy Policy](./privacy-policy.md): Data collection and handling
117 + - [FAQ](../support/faq.md): Content policy questions
118 + - [Content Moderation & Enforcement](./moderation.md): How we moderate and what happens when rules are broken
119 + - [Appeal Process](./appeals.md): Disputing decisions