feat(resolve): choose whether Moshpit overrides clearnet for a contested name - #48
Merged
Conversation
…ted name
Two namespaces now answer to the same shape of name. `profullstack.ai` is
a real clearnet domain someone can squat, and it is also a name the
Moshpit registry can hold. Something has to decide which one a navigation
means, and that cannot be hardcoded: a user who has never heard of
Moshpit must keep getting clearnet, while an operator who registered the
name expects their version to win.
So it is a setting, with two honest positions:
clearnet (default) — clearnet owns any name clearnet can answer;
Moshpit is consulted only where DNS came up empty, making the
registry a backfill that fills gaps rather than shadowing the
existing web. Default because silently redirecting a domain that
resolves perfectly well is indistinguishable from hijacking it.
moshpit — a registered name wins even when clearnet has an answer.
This is the override: the point of registering profullstack.ai in
Moshpit is that your version is the one you get.
Names under an ending clearnet has never heard of (.yeah, .sploof)
resolve through Moshpit in either mode — nothing conflicts, and refusing
them would defeat the namespace.
decideResolution is pure and total: every branch returns a decision with
a reason, so the caller never invents behaviour for an unhandled
combination and an override never looks like a glitch. A registry that is
down, slow, or serving nonsense yields null and falls back to clearnet —
resolution sits in front of every navigation, so a registry outage must
not take the ordinary web down with it.
parseRegistryName refuses anything that is not exactly one label and one
TLD, so `a.b.c`, `localhost` and IP literals are never sent to the
registry as if they were names.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
ralyodio
added a commit
that referenced
this pull request
Jul 31, 2026
…ured parts CI has been red on main since #48: noUncheckedIndexedAccess types the destructured `label`/`tld` as `string | undefined`, because TS can't narrow an array to a 2-tuple from a `parts.length !== 2` check. Four TS2345/TS2322 errors failed `apps/desktop` and took the whole `pnpm -r build` with it. Guard explicitly. Behavior is unchanged: length 2 already guarantees both values, and an empty label would fail LABEL.test() on the next line anyway. Unrelated to this branch's auth work, but it blocks the PR from going green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ralyodio
added a commit
that referenced
this pull request
Jul 31, 2026
* fix(auth): extension CoinPay sign-in never picked up the session
Clicking "Sign in with CoinPay" in the extension's options page signed the
WEBSITE in and left the extension signed out.
coinpay-auth.js asked the backend to redirect back to
chrome.identity.getRedirectURL() -> https://<ext-id>.chromiumapp.org/.
That target is off-origin, so safeRedirect() rejected it (correctly — the
session token rides in the redirect fragment) and /api/auth/coinpay/callback
fell through to `c.redirect(`${APP_URL}/?signedin=1`)`. The OAuth dance
completed, tronbrowser.dev got its tb_session cookie, and the extension got
nothing. (Ungoogled Chromium also rewrites chromiumapp.org to a
non-resolving .qjz9zk host, so launchWebAuthFlow could not have worked
either.) bittorrented.js already worked around this with ext-callback;
CoinPay never got the same treatment.
- Add GET /api/auth/ext-login: validates an on-origin callback, and when the
user is ALREADY signed in on the website hands a session straight back
instead of running CoinPay a second time. Otherwise forwards to the
CoinPay dance preserving the callback.
- coinpaySignIn() opens that URL in a tab and waits for the token, matching
the bittorrented flow. Listener is registered before the tab is created so
a fast redirect can't race it.
- ext-callback.js now serves both flows, keyed on `?src=tb`, and background
handles the generalized 'ext-token' message.
- Drop the now-unused "identity" permission from the manifest.
Redirect targets stay same-origin only, so a drive-by navigation to
/api/auth/ext-login lands the token in a fragment the navigating site
cannot read.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(resolve): unbreak the build — narrow parseRegistryName's destructured parts
CI has been red on main since #48: noUncheckedIndexedAccess types the
destructured `label`/`tld` as `string | undefined`, because TS can't narrow an
array to a 2-tuple from a `parts.length !== 2` check. Four TS2345/TS2322
errors failed `apps/desktop` and took the whole `pnpm -r build` with it.
Guard explicitly. Behavior is unchanged: length 2 already guarantees both
values, and an empty label would fail LABEL.test() on the next line anyway.
Unrelated to this branch's auth work, but it blocks the PR from going green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
Two namespaces now answer to the same shape of name.
profullstack.aiis a real clearnet domain someone can squat — and it's also a name the Moshpit registry can hold. Something has to decide which one a navigation means, and it can't be hardcoded:The setting
clearnet(default)moshpitprofullstack.aiin Moshpit is that your version is the one you get.clearnetis the default because silently redirecting a domain that resolves perfectly well is indistinguishable from hijacking it. Opting into the override is a decision the user makes, not one we make for them.Names under an ending clearnet has never heard of —
.yeah,.sploof,.oranges— resolve through Moshpit in either mode. Nothing conflicts, and refusing them would defeat the whole namespace.Design
decideResolutionis pure and total: no network, no browser, every branch returns a decision with a reason. The caller never has to invent behaviour for an unhandled combination, and because the reason is surfaced, an override never looks like a glitch.A registry outage must not take the ordinary web down with it. Resolution sits in front of every navigation, so
lookupMoshpitreturnsnull— never throws — on a timeout, a non-2xx, a connection failure, or a nonsense payload, and every one of those falls back to clearnet.parseRegistryNamerefuses anything that isn't exactly one label and one TLD, soa.b.c,localhost,192.168.1.1andhost:portare never sent to the registry as if they were names. Verified by test that the fetch isn't even attempted.Extension UI
New Name resolution section in the extension options: the mode picker plus an advanced registry field, so a self-hosted pit can be pointed at instead of
pit.moshcode.sh. Absent config reads as the default rather than as "off".Tests
16 new cases in
apps/desktop/src/moshpit-resolve.test.ts, including the squatting scenario from both sides, alias following (profullstack.agentic→profullstack.agent), outage fallback in both modes, and an exhaustive sweep asserting every mode × DNS × registry combination produces a non-empty reason.Full desktop suite: 53 passed.
Scope
This lands the resolution policy, the registry client and the setting. Wiring it into live navigation interception is not included — that needs a
webNavigationpermission bump in the MV3 manifest and a decision about the gateway path (gatewayUrlForcurrently assumes/n/<name>, which PRD 0002 R7 leaves open). Worth settling that before intercepting real traffic.🤖 Generated with Claude Code