diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-14 13:34:47 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-14 13:34:47 +0200 |
| commit | 51705056a578d2649a989bf14a056fbcfae58a6b (patch) | |
| tree | dfd078aba518427387fca29b9d43c3723b030008 /docs/MESHBAY_DESIGN.md | |
| parent | daec4bc6a2a9f9e038e5269586b06018ff327a27 (diff) | |
| download | meshbay-51705056a578d2649a989bf14a056fbcfae58a6b.tar.gz | |
feat(hub): replace reCAPTCHA with a self-hosted proof of work
Sign-up and passphrase reset sent every visitor's address and browser
details to Google, which needed a consent the sign-up form could not ask
for freely. The hub now issues its own challenge (ALTCHA's scheme, signed
with a key derived from the hub's private key, single-use, 30 minutes)
and the page solves it with SubtleCrypto while the form is filled in.
- GET /v1/users/captcha issues a challenge; 404 captcha_disabled when off.
- [captcha] enabled / max_number replace site_key / secret_key, which are
now ignored with a warning; MESHBAY_CAPTCHA_ENABLED replaces the old
variables.
- No Google origin left in the CSP of the hub, the desktop shell or the
Android shell; captcha_site_key leaves /v1/hub/info.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index f9ceaa2..2174879 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -2533,9 +2533,25 @@ Two things survive on purpose: the nodes refuse the account and close the groups' sessions now. A node that is offline misses it. -Registration is gated by a CAPTCHA whenever one is configured — **unconditionally**, +**Registration and the passphrase-reset request are gated by a proof of work** +(`captcha.py`), on unless `[captcha] enabled = false`, and **unconditionally** — not only when some other field is absent, or the real client's ordinary request -skips it. The desktop client renders the widget too. +skips it. The hub signs a challenge (the SHA-256 of a nonce, an expiry and a +number below `max_number`) with a key derived from its own private key; the page +finds the number while the form is being filled in and sends it with the form. +An answer is checked against the hub's signature, its expiry, and the answers +already spent, so one solve is one form. **No third party is involved**: no +script, cookie or visitor address leaves for anyone else, which is what keeps the +sign-up form free of a consent it could not ask for freely. What it buys is cost +per attempt, not a verdict on who is human; the bound that holds against a +caller with machines to spare is the mail budget (§7.4), since these two routes +are what make the hub send mail. + +**The sign-up form states what the hub keeps**, as a notice and never a checkbox: +the account rests on the terms of use and the connection log on a legal +obligation, so there is no consent to collect, and one that blocks sign-up would +be neither free nor withdrawable. The notice links to the legal pages of the hub +in use (`/legal/`), which are its operator's to write. **Sessions end three ways, all admin settings in hours** (`session.*`, §7.4): |