aboutsummaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md50
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,