diff options
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 50 |
1 files changed, 35 insertions, 15 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 2a0e354..c89be6d 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -1949,8 +1949,9 @@ listed with what they check. The hub publishes no API description — no `/docs` `/redoc` or `/openapi.json` — in the code, not in a proxy rule, so a packaged install behind any proxy publishes none either. -The mail bounds (`mail.*`) and the sign-in lockout (`login.max_failures`, -default 4, and `login.lockout_minutes`, default 60 — §7.7) live in the same table +The mail bounds (`mail.*`), the report policy (`reports.*`, §7.5) and the sign-in +lockout (`login.max_failures`, default 4, and `login.lockout_minutes`, default 60 — +§7.7) live in the same table for the same reason: they are what an operator changes while the hub is serving, from the panel, without a restart. Each value is clamped to published bounds, and `max_failures = 0` turns the lockout off. @@ -1982,10 +1983,32 @@ they received, so the hub and the nodes then disagree until each operator clears **Moderator is not administrator.** The user-patch handler is split by field: a moderator may act on the fields moderation needs and may not write `role`. -**Content reporting requires authentication, distinct reporters and a rate limit, -and is refused when public groups are off.** An unauthenticated endpoint that -blocklists a content hash after two reports is a network-wide censorship and DoS -primitive for anyone who learns a public file's id. +**A file is reported by a member of the public group it was seen in, and an +administrator decides.** An unauthenticated endpoint that blocklists a content hash +after two reports is a network-wide censorship and DoS primitive for anyone who +learns a public file's id, so every bound here answers what a report costs somebody +else — a file taken out of a group everyone else uses, and an administrator's time: + +- a **person's** account — a node's token is refused — that has existed for + `reports.min_account_age_hours` (24 by default); +- **active membership of the public group** named in the report, answered with one + refusal whatever the reason, so the endpoint says nothing about which groups exist + or who is in them. The hub cannot check that the file is in that group — it holds + no index, by design — only that the reporter could have seen it there; +- a **daily allowance per account** (`reports.daily_per_account`, 20) besides the + per-address rate limit, since an address is one of thousands a subscriber holds; + one report per account per hash; a closed list of reasons and a bounded detail; +- once `reports.review_threshold` distinct accounts (3) have reported a hash, it is + **queued for review** and the administrators are notified. An administrator blocks + it — which pushes it to the nodes — or dismisses it, and a dismissed hash is not + reopened by more reports. The reporter is told the report was recorded and never + how close the file is to review. + +`reports.auto_block` blocks at the threshold without a review. It is off by +default and stays an instance's explicit choice, because it makes a handful of +accounts made for the purpose enough to take a file down. The whole flow is refused +while public groups are switched off. The interface offers **Report** on a file's +menu in a public group only. **The content blocklist is applied by the nodes that host a public group, in their public groups only.** A node holds the list (`blocklist.py`, persisted beside the @@ -3257,15 +3280,12 @@ the wrong thing** — a per-IP rate limit bounds a caller, never the mailbox tha receives what they cause, which is why `AV10` is a cooldown per *account* under a limit per IP rather than a tighter limit. -**Open for decision, not a defect:** `moderation.AUTO_BLOCK_THRESHOLD` is 3. -Three distinct accounts blocking a hash adds it to the list every node -enforces, network-wide, automatically, with manual admin removal the only -undo. That is already far better than the anonymous version it replaced, and -it is still a censorship primitive an attacker buys for the price of three -email addresses. Raising it buys little; requiring the reporting accounts to -be more than a day old would cost a patient attacker a day and cost an honest -reporter nothing after their first. Left as it is because it is a moderation -policy rather than a bug, and the person who sets that policy is the operator. +**Reports lead to a review, not a block** (§7.5). Distinct accounts reaching +the threshold used to add a hash to the list every node enforces, automatically, +with manual removal the only undo — a censorship primitive bought for the price of +three email addresses. Now the reporter must be a day-old account and a member of +the public group, and the threshold queues the hash for an administrator; blocking +without review is an instance setting, off by default. `admin.py` was read under this lens and needed nothing. Its moderator/admin line is drawn explicitly — a moderator may not change a role, may not revoke, |