From 2d8c6bc449e343a80569e45d4ceae0423616cedd Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 2 Sep 2026 11:54:22 +0200 Subject: fix(hub): a desktop solve reports no hostname at all, not "meshbay" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registering from the native client failed with `captcha_failed` while the checkbox was green — a worse symptom than the one being fixed, because the widget now looked fine and only the hub's own log said otherwise: captcha solved on an unexpected host ''; allowed: ['localhost', 'meshbay', 'meshbay.org'] The previous commit assumed Google would report the host component of the origin, so `app://meshbay` would come back as `meshbay` and could sit in `allowed_hosts`. It does not. A solve Google cannot attribute to a domain reports an **empty** hostname, and no allowlist entry can match that. An empty entry is not the answer either: a blank in a TOML list is a typo far more often than an intention, and `load_config` drops blanks for that reason — `captcha.allow_unattributed_host` is a named flag instead, so the trade is stated where it is made. What it admits, plainly: every non-web client, not only ours. A file:// page or somebody else's Electron application look identical from here. That is the same bar the client's own origin would have been — main.js already records that `app://meshbay` is not a credential — and it is a bar: the captcha still has to be solved, per token, in something that can render it. What is given up is the origin restriction for non-web clients, not the captcha. Off by default, and a hub without the desktop client should leave it off. The refusal now names which of the two it is, since they need different answers: an unexpected host names the host, an unattributed one says to set the flag. docs/captcha.md §6 said `meshbay` was the value and told operators to add it; it now records what was measured and why the guess was wrong. The packaged example config carries the flag with the same warning. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014UtzVrzM7e2tG9fSpkR9ML --- docs/captcha.md | 61 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'docs/captcha.md') diff --git a/docs/captcha.md b/docs/captcha.md index 84dadc4..4054e9f 100644 --- a/docs/captcha.md +++ b/docs/captcha.md @@ -415,7 +415,9 @@ solutions" on the key, and check the origin on the hub, where it belongs: [captcha] site_key = "6Le..." secret_key = "6Le..." -allowed_hosts = ["meshbay.org", "localhost", "meshbay"] +allowed_hosts = ["meshbay.org", "localhost"] +# Only with the desktop client. See below — this is the loose one. +allow_unattributed_host = true ``` `verify_captcha` then refuses a solve whose reported hostname is not in that @@ -430,20 +432,37 @@ setting keeps the behaviour it has, with reCAPTCHA doing the origin check itself. **The two settings go together**: turning the console check off without setting `allowed_hosts` leaves no origin check anywhere. -**The last entry is the desktop client's own, and it is the weak one.** Any -Electron application can claim the same scheme and host — `main.js` already -records that `app://meshbay` is not a credential, which is why the hub's API is -reachable from no web origin at all and every call leaves from the main -process. So `meshbay` in that list is spoofable by someone who builds an -equivalent application. What it still costs them is a per-token captcha solve -inside a real Chromium, rather than a token farmed from any web page. That is -the trade, stated plainly; a hub that does not ship the desktop client should -leave the entry out. - -**Confirming the hostname.** `meshbay` is the host component of -`app://meshbay`. If a solve is refused, `captcha.py` logs it at WARNING with -the hostname spelled out and the allowed list beside it, which is how to read -the value a given client actually reports rather than guess at it. +### The hostname a desktop solve reports is empty, not `meshbay` + +Built first as an allowlist entry, on the assumption that Google would report +the host component of the origin. It does not, and registration from the +client failed with `captcha_failed` while the checkbox was green — a worse +symptom than the one being fixed, because the widget now looked fine. The log +line said it outright: + +``` +captcha solved on an unexpected host ''; allowed: ['localhost', 'meshbay', 'meshbay.org'] +``` + +A solve Google cannot attribute to a domain reports an **empty** hostname. No +allowlist entry can match that, and an empty entry is not the answer either: +a blank in a TOML list is a typo far more often than an intention, and +`load_config` drops blanks for that reason. `allow_unattributed_host` is a +named flag instead, so the trade is stated where it is made. + +**What it admits, plainly.** Every non-web client, not only ours — a `file://` +page or somebody else's Electron application report the same nothing. That is +the same bar the client's own origin would have been (`main.js` already records +that `app://meshbay` is not a credential; any application can claim it), and it +*is* a bar: the captcha still has to be solved, per token, in something that +can render it. What is given up is the origin restriction for non-web clients, +not the captcha. A hub that does not ship the desktop client should leave the +flag off. + +**Reading the value yourself.** Any refusal is logged at WARNING, with the +hostname spelled out and the allowed list beside it when there is one. That is +how the empty hostname was found, and it is the way to check what a given +client actually reports rather than guess — which is what went wrong here. --- @@ -508,7 +527,9 @@ from Google — no npm package. site_key = "6Le..." secret_key = "6Le..." # Required whenever the console's origin check is off, and only then. - allowed_hosts = ["meshbay.org", "localhost", "meshbay"] + allowed_hosts = ["meshbay.org", "localhost"] + # Only with the desktop client — §6 says what it gives up. + allow_unattributed_host = true ``` 3. Deploy the new hub code (`deploy-hub.sh` — runs `alembic upgrade head` + restart; no migration needed for this change). @@ -517,6 +538,8 @@ from Google — no npm package. solving the captcha. 5. Verify reset: open `https://meshbay.org/#/reset`, confirm the checkbox appears. Request a reset code, confirm email arrives only after solving it. -6. Verify the desktop client: register from it and confirm the widget solves - rather than showing "Invalid domain for site key". A refusal logged as - `captcha solved on an unexpected host` names the hostname to add. +6. Verify the desktop client by actually registering from it. Two distinct + failures, and the first hides the second: "Invalid domain for site key" + inside the widget means the console's origin check is still on, while a + green checkbox followed by `captcha_failed` means the hub refused it — the + WARNING in the journal says which host, or that there was none. -- cgit v1.2.3