Repository navigation
fix(communities): accept invite links and bare hosts in Join a community - #2412
Conversation
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 One input-validation regression: bare invite codes now enter community discovery as hostnames.
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
d1a91a1 to
3b7a1f9
Compare
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
3b7a1f9 to
aa38b48
Compare
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 Canonical bare codes are now rejected. One remaining normalization edge case is noted inline.
Review of #2412 found that the bare-code refusal matched the raw text, so a v2 code copied with a sentence-ending dot, a trailing slash, :443 or an uppercase V2. slipped past it. Address normalization then turned the code into an https://v2.<code> origin. The dialog and landing started discovery against it and showed the generic error, not the full-invite guidance. isBareInviteCode now normalizes scheme-less input with enteredRelayOrigin and tests the resulting hostname against the v2. prefix plus one base64url label. pastedInviteError calls the same helper, so every variant that is refused gets "Paste the whole invite link, not only its code." and no lookup starts. Still accepted: - explicit origins such as wss://v2.invite - hosts with more labels such as v2.relay.example Because the hostname is classified, a non-default port such as v2.invite:8443 is now also refused; wss://v2.invite:8443 still works. The docs already say a code on its own is refused, so they are unchanged. Tests: - invite-link rejects the trailing-dot code, V2.invite, v2.invite/, v2.invite:443 and v2.invite:8443. It also checks pastedInviteError copy for the variants and the fallback for other unreadable input. - CommunityDialog's no-fetch validation table and CommunityLanding's no-communityRequest refusal add the trailing-dot code. - The 10 new variant cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (112 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 One minor input-classification issue remains.
Review of #2412 found that the bare-code refusal matched `v2.` and any single base64url label on the normalized hostname. Short bare hosts such as v2.dev and v2.io were refused with "Paste the whole invite link, not only its code.", while wss://v2.dev worked. That contradicts the documented bare-host contract. The relay mints a code with encode_v2_code over a 32-byte (V2_SECRET_LEN) random secret: `v2.` plus unpadded base64url, which is 43 characters (buzz-core invite.rs, buzz-db relay_invite.rs). isBareInviteCode now requires exactly 43 base64url characters after `v2.`. It still tests the hostname that enteredRelayOrigin normalizes, so a minted code with a trailing dot, in uppercase, with a root slash, :443 or another port is still refused. The canonical final-character check is not applied, because hostname normalization lowercases the code. Now read as addresses: - short bare v2 hosts, such as v2.dev, V2.io and v2.invite:8443 - a v2. label of any other length, including a truncated code, which now fails at lookup rather than inline The previous commit's accepted tradeoff is gone: an apex such as v2.<tld> typed without a scheme now works. The docs only say that a code on its own is refused, which still holds, so they are unchanged. Tests: - invite-link builds every bare-code variant from one minted-length code, adds v2.dev and V2.io accepts, and uses the code for the explicit-scheme accept and fallback-copy cases. - CommunityDialog drops the short v2.invite refusal and adds a v2.dev lookup to its lookup table, with the origin as a column. - CommunityLanding runs its bare-host lookup for v2.dev as well as relay.example. - The 4 new v2.dev/V2.io cases fail against the previous production code, and all other cases pass there. All pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (114 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched the raw text, so a v2 code copied with a sentence-ending dot, a trailing slash, :443 or an uppercase V2. slipped past it. Address normalization then turned the code into an https://v2.<code> origin. The dialog and landing started discovery against it and showed the generic error, not the full-invite guidance. isBareInviteCode now normalizes scheme-less input with enteredRelayOrigin and tests the resulting hostname against the v2. prefix plus one base64url label. pastedInviteError calls the same helper, so every variant that is refused gets "Paste the whole invite link, not only its code." and no lookup starts. Still accepted: - explicit origins such as wss://v2.invite - hosts with more labels such as v2.relay.example Because the hostname is classified, a non-default port such as v2.invite:8443 is now also refused; wss://v2.invite:8443 still works. The docs already say a code on its own is refused, so they are unchanged. Tests: - invite-link rejects the trailing-dot code, V2.invite, v2.invite/, v2.invite:443 and v2.invite:8443. It also checks pastedInviteError copy for the variants and the fallback for other unreadable input. - CommunityDialog's no-fetch validation table and CommunityLanding's no-communityRequest refusal add the trailing-dot code. - The 10 new variant cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (112 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
d96063c to
9f14743
Compare
Review of #2412 found that the bare-code refusal matched `v2.` and any single base64url label on the normalized hostname. Short bare hosts such as v2.dev and v2.io were refused with "Paste the whole invite link, not only its code.", while wss://v2.dev worked. That contradicts the documented bare-host contract. The relay mints a code with encode_v2_code over a 32-byte (V2_SECRET_LEN) random secret: `v2.` plus unpadded base64url, which is 43 characters (buzz-core invite.rs, buzz-db relay_invite.rs). isBareInviteCode now requires exactly 43 base64url characters after `v2.`. It still tests the hostname that enteredRelayOrigin normalizes, so a minted code with a trailing dot, in uppercase, with a root slash, :443 or another port is still refused. The canonical final-character check is not applied, because hostname normalization lowercases the code. Now read as addresses: - short bare v2 hosts, such as v2.dev, V2.io and v2.invite:8443 - a v2. label of any other length, including a truncated code, which now fails at lookup rather than inline The previous commit's accepted tradeoff is gone: an apex such as v2.<tld> typed without a scheme now works. The docs only say that a code on its own is refused, which still holds, so they are unchanged. Tests: - invite-link builds every bare-code variant from one minted-length code, adds v2.dev and V2.io accepts, and uses the code for the explicit-scheme accept and fallback-copy cases. - CommunityDialog drops the short v2.invite refusal and adds a v2.dev lookup to its lookup table, with the origin as a column. - CommunityLanding runs its bare-host lookup for v2.dev as well as relay.example. - The 4 new v2.dev/V2.io cases fail against the previous production code, and all other cases pass there. All pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (114 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…9047) Both mobile community-link fields (Add community and the welcome/onboarding "Enter a community link" page) already accept a typed or pasted `https://<host>/invite/<code>` link and keep the code. They also read a bare host as `wss://`. This PR adds regression coverage for both. **It doesn't change any production code.** Companion: block/buzz-app#2412 fixes the same two inputs in buzz-app's Join a community. ## What's covered - **`community_link_input_test.dart`** - The address loop is now an input → relay table, with two new rows: `secondary.example` and `relay.example.com:8443` (the port is kept). - The invite case now uses `https://primary.example/invite/fixture`, and expects relay `wss://primary.example` with code `fixture`. - Two new rejected inputs: `ws://secondary.example`, and the invite path with a trailing slash, which the relay doesn't serve. - **`onboarding_entry_routes_test.dart`**: before the bare-host step, the journey pastes the invite link into the Add route's field. It expects the app-level `InviteJoinSheet` to show host `primary.example` and code `fixture`. ## Shown to catch a regression I removed the invite/add-community short-circuit in `parseCommunityLinkInput`. Both the invite unit case and the new journey step then failed. ## Not in this PR Two parser differences are left for a separate decision, along with the tests that depend on them: - `buzz://connect?relay=…`: mobile rejects it in the fields and silently drops it as an OS link, but buzz-app, desktop and the web repo browser's Connect button all use it. - An invite-page link with a query string: mobile accepts it and throws the query away, but buzz-app rejects it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord / Guardians full-feature review: recommend approval; no new blocking source finding.
Reviewed head 9f14743c7d144d4427539064fe68d9d08b75e165 against base/merge base 0db9aded728df70e2aa4848d2b3233238b730181. Star Lord, Rocket, Mantis and Groot reviewed the full eight-file feature and prior feedback.
All three prior input findings are repaired: canonical bare v2 invite codes are rejected before discovery, normalized trailing-dot/case/root-slash/port variants use the same classification, and short hosts such as v2.dev remain valid. Both web entry surfaces have source tests asserting refusal without lookup. Secure-origin validation still rejects credentials, insecure schemes and unrelated paths/query/fragment; typed addresses do not resolve deployment aliases.
The dialog preserves an opened invite's code when resubmitting its canonical origin, fills codes from pasted links, and clears old destination/code/consent state when the address is edited. Discovery and explicit admission/profile publication retain their existing owners. Edit connection intentionally shares the expanded input forms. Native first-run parsing remains unchanged and is not evidence of identical bare-code rejection there.
Optional description update: retain the short, noncanonical v2.invite example, but explain that canonical 43-character-payload codes are now refused inline and update the repair/test discussion. The old validation counts are not independent evidence for this head.
Source-only: no tests, builds or apps run. Hosted snapshot: 39 checks succeeded, two skipped, including successful required CI and both browser-engine journey sets; the browser flake job was skipped. New parser/dialog/landing cases are covered in unit sources, with no new committed browser input journey. One video was not viewed. Native execution, assistive technology, the unmerged relay trust-policy integration and human acceptance remain unverified.
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched the raw text, so a v2 code copied with a sentence-ending dot, a trailing slash, :443 or an uppercase V2. slipped past it. Address normalization then turned the code into an https://v2.<code> origin. The dialog and landing started discovery against it and showed the generic error, not the full-invite guidance. isBareInviteCode now normalizes scheme-less input with enteredRelayOrigin and tests the resulting hostname against the v2. prefix plus one base64url label. pastedInviteError calls the same helper, so every variant that is refused gets "Paste the whole invite link, not only its code." and no lookup starts. Still accepted: - explicit origins such as wss://v2.invite - hosts with more labels such as v2.relay.example Because the hostname is classified, a non-default port such as v2.invite:8443 is now also refused; wss://v2.invite:8443 still works. The docs already say a code on its own is refused, so they are unchanged. Tests: - invite-link rejects the trailing-dot code, V2.invite, v2.invite/, v2.invite:443 and v2.invite:8443. It also checks pastedInviteError copy for the variants and the fallback for other unreadable input. - CommunityDialog's no-fetch validation table and CommunityLanding's no-communityRequest refusal add the trailing-dot code. - The 10 new variant cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (112 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched `v2.` and any single base64url label on the normalized hostname. Short bare hosts such as v2.dev and v2.io were refused with "Paste the whole invite link, not only its code.", while wss://v2.dev worked. That contradicts the documented bare-host contract. The relay mints a code with encode_v2_code over a 32-byte (V2_SECRET_LEN) random secret: `v2.` plus unpadded base64url, which is 43 characters (buzz-core invite.rs, buzz-db relay_invite.rs). isBareInviteCode now requires exactly 43 base64url characters after `v2.`. It still tests the hostname that enteredRelayOrigin normalizes, so a minted code with a trailing dot, in uppercase, with a root slash, :443 or another port is still refused. The canonical final-character check is not applied, because hostname normalization lowercases the code. Now read as addresses: - short bare v2 hosts, such as v2.dev, V2.io and v2.invite:8443 - a v2. label of any other length, including a truncated code, which now fails at lookup rather than inline The previous commit's accepted tradeoff is gone: an apex such as v2.<tld> typed without a scheme now works. The docs only say that a code on its own is refused, which still holds, so they are unchanged. Tests: - invite-link builds every bare-code variant from one minted-length code, adds v2.dev and V2.io accepts, and uses the code for the explicit-scheme accept and fallback-copy cases. - CommunityDialog drops the short v2.invite refusal and adds a v2.dev lookup to its lookup table, with the origin as a column. - CommunityLanding runs its bare-host lookup for v2.dev as well as relay.example. - The 4 new v2.dev/V2.io cases fail against the previous production code, and all other cases pass there. All pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (114 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
9f14743 to
84978a6
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord / Guardians integration follow-up: no new actionable source findings.
Reviewed head 84978a64ad35b88832b03338969f2410bc26df2e against base cae3922574c5c1f0ce5d32231810229e94b1bb40, preserving the parser-repair coverage from the previous review. Rocket checked lifecycle/read bounds, Mantis input/identity/concurrency, Groot product/focus/landing behavior, and Star Lord integrated the results.
The retained invite-link and bare-host changes fit the newer cancellation and sign-in paths: lookup cancellation fences late UI updates, editing the destination clears the old code and consent, and the landing handoff preserves the invite code and typed link across Back. Sign-in uses the canonical destination's identity; completion clears the refusal without replaying an earlier submission. An already-started profile read retains its own bounded timeout rather than inheriting Cancel; its abandoned result is fenced.
The earlier optional description-update note still applies: document canonical bare-code refusal and distinguish the old validation counts from this revision.
Source-only: inspected implementation and regression-test sources; no tests, builds or apps run. Browser/native behavior, assistive-technology behavior and focus after sign-in completion remain unverified. The description was inspected, but its attached video was not visually reviewed; publication-surface coverage is incomplete for that recording. This is a COMMENT review, not approval.
|
🤖 Must fix at Enter the existing protected Joining phase before admission writes, and fence any cancelled continuation before it can claim or change the form. The underlying direct-admission issue predates this patch; the pasted-code fix now routes pasted links through it too. Evidence: direct admission, Cancel and dismissal state, claim continuation. The pending-operation rule is in the design guide. The effective-code fix itself is correct, and refused invites retain the existing recovery field. Source-only follow-up; no tests or app execution. Current-head visuals, native/live joining, keyboard/focus, assistive technology, narrow layouts, dark mode and enlarged text remain unchecked. The earlier recording does not cover this change. |
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched the raw text, so a v2 code copied with a sentence-ending dot, a trailing slash, :443 or an uppercase V2. slipped past it. Address normalization then turned the code into an https://v2.<code> origin. The dialog and landing started discovery against it and showed the generic error, not the full-invite guidance. isBareInviteCode now normalizes scheme-less input with enteredRelayOrigin and tests the resulting hostname against the v2. prefix plus one base64url label. pastedInviteError calls the same helper, so every variant that is refused gets "Paste the whole invite link, not only its code." and no lookup starts. Still accepted: - explicit origins such as wss://v2.invite - hosts with more labels such as v2.relay.example Because the hostname is classified, a non-default port such as v2.invite:8443 is now also refused; wss://v2.invite:8443 still works. The docs already say a code on its own is refused, so they are unchanged. Tests: - invite-link rejects the trailing-dot code, V2.invite, v2.invite/, v2.invite:443 and v2.invite:8443. It also checks pastedInviteError copy for the variants and the fallback for other unreadable input. - CommunityDialog's no-fetch validation table and CommunityLanding's no-communityRequest refusal add the trailing-dot code. - The 10 new variant cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (112 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
84978a6 to
f6c68a7
Compare
Review of #2412 found that the bare-code refusal matched `v2.` and any single base64url label on the normalized hostname. Short bare hosts such as v2.dev and v2.io were refused with "Paste the whole invite link, not only its code.", while wss://v2.dev worked. That contradicts the documented bare-host contract. The relay mints a code with encode_v2_code over a 32-byte (V2_SECRET_LEN) random secret: `v2.` plus unpadded base64url, which is 43 characters (buzz-core invite.rs, buzz-db relay_invite.rs). isBareInviteCode now requires exactly 43 base64url characters after `v2.`. It still tests the hostname that enteredRelayOrigin normalizes, so a minted code with a trailing dot, in uppercase, with a root slash, :443 or another port is still refused. The canonical final-character check is not applied, because hostname normalization lowercases the code. Now read as addresses: - short bare v2 hosts, such as v2.dev, V2.io and v2.invite:8443 - a v2. label of any other length, including a truncated code, which now fails at lookup rather than inline The previous commit's accepted tradeoff is gone: an apex such as v2.<tld> typed without a scheme now works. The docs only say that a code on its own is refused, which still holds, so they are unchanged. Tests: - invite-link builds every bare-code variant from one minted-length code, adds v2.dev and V2.io accepts, and uses the code for the explicit-scheme accept and fallback-copy cases. - CommunityDialog drops the short v2.invite refusal and adds a v2.dev lookup to its lookup table, with the origin as a column. - CommunityLanding runs its bare-host lookup for v2.dev as well as relay.example. - The 4 new v2.dev/V2.io cases fail against the previous production code, and all other cases pass there. All pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (114 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord / Guardians integration follow-up: one new pasted-invite finding inline.
Reviewed head f6c68a7e0ee15917ab4e000e9e7993344793633e against base c78fa990c2e8ee78aefb5620ca4ee5b9417f7e67, retaining the parser coverage from the prior review. Rocket reviewed admission/cancellation, Mantis identity/consent/concurrency, Groot product/landing/focus paths, and Star Lord integrated the results.
Separate inherited-base observation, not attributed to this PR's patch: direct admission runs while the destination step still exposes Cancel. Its mount/journal fence ignores the aborted attempt, so a delayed policy response can trigger the old claim after Cancel, and late success/refusal can change the edited form's step. This deserves a separate fix in the admission owner: protect the write phase and fence any cancellable continuation by the attempt lifetime. The earlier cancellation assessment covered discovery before this base introduced direct admission.
Source-only: no tests, builds or app execution. The PR description was inspected; its earlier optional update remains relevant. The attached recording was not visually inspected by this review, and rendered focus, native behavior and real-relay joining remain unverified.
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched the raw text, so a v2 code copied with a sentence-ending dot, a trailing slash, :443 or an uppercase V2. slipped past it. Address normalization then turned the code into an https://v2.<code> origin. The dialog and landing started discovery against it and showed the generic error, not the full-invite guidance. isBareInviteCode now normalizes scheme-less input with enteredRelayOrigin and tests the resulting hostname against the v2. prefix plus one base64url label. pastedInviteError calls the same helper, so every variant that is refused gets "Paste the whole invite link, not only its code." and no lookup starts. Still accepted: - explicit origins such as wss://v2.invite - hosts with more labels such as v2.relay.example Because the hostname is classified, a non-default port such as v2.invite:8443 is now also refused; wss://v2.invite:8443 still works. The docs already say a code on its own is refused, so they are unchanged. Tests: - invite-link rejects the trailing-dot code, V2.invite, v2.invite/, v2.invite:443 and v2.invite:8443. It also checks pastedInviteError copy for the variants and the fallback for other unreadable input. - CommunityDialog's no-fetch validation table and CommunityLanding's no-communityRequest refusal add the trailing-dot code. - The 10 new variant cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (112 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
f6c68a7 to
3cf1111
Compare
Review of #2412 found that the bare-code refusal matched `v2.` and any single base64url label on the normalized hostname. Short bare hosts such as v2.dev and v2.io were refused with "Paste the whole invite link, not only its code.", while wss://v2.dev worked. That contradicts the documented bare-host contract. The relay mints a code with encode_v2_code over a 32-byte (V2_SECRET_LEN) random secret: `v2.` plus unpadded base64url, which is 43 characters (buzz-core invite.rs, buzz-db relay_invite.rs). isBareInviteCode now requires exactly 43 base64url characters after `v2.`. It still tests the hostname that enteredRelayOrigin normalizes, so a minted code with a trailing dot, in uppercase, with a root slash, :443 or another port is still refused. The canonical final-character check is not applied, because hostname normalization lowercases the code. Now read as addresses: - short bare v2 hosts, such as v2.dev, V2.io and v2.invite:8443 - a v2. label of any other length, including a truncated code, which now fails at lookup rather than inline The previous commit's accepted tradeoff is gone: an apex such as v2.<tld> typed without a scheme now works. The docs only say that a code on its own is refused, which still holds, so they are unchanged. Tests: - invite-link builds every bare-code variant from one minted-length code, adds v2.dev and V2.io accepts, and uses the code for the explicit-scheme accept and fallback-copy cases. - CommunityDialog drops the short v2.invite refusal and adds a v2.dev lookup to its lookup table, with the origin as a column. - CommunityLanding runs its bare-host lookup for v2.dev as well as relay.example. - The 4 new v2.dev/V2.io cases fail against the previous production code, and all other cases pass there. All pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (114 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…ssion Review of PR #2412 found that the destination step only stored a pasted invite link's code with setCode, which takes effect on the next render. The submission that parsed the link still read the previous code, normally empty. With a signed-in account whose consent covers the community and a readable profile answer with exists: false, the direct admission called admit with that empty code, so admitCommunity skipped policy acceptance and the invite claim and went on to profile setup without redeeming the link. With a denied first read, the stale code only sent the user through an unneeded access step. The destination step now computes the effective code as the parsed code or the entered one, uses it for the direct-admission decision and passes it to admit, which takes an optional code and defaults to the rendered state. The state update stays, so the access step and later retries still show and reuse the code. The access step's own admission is unchanged. Tests: the two signed-in-account CommunityDialog tests are parametrized over an opened invite and a pasted link. The consent test also covers a readable new-profile first read, and both assert the exact accept-policy and claim bodies carry v2.abc. The 3 pasted-link cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (133 passed), pnpm typecheck and pnpm lint. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord / Guardians repair follow-up: the prior pasted-invite P2 is repaired; one P3 CI failure is reported inline.
Reviewed head 81ff35c02cdc64171209d8efe17dafb0a03e626a against base c9239f737f4985979eb5939e84c33ab3da2b1321. Rocket checked closure/lifecycle and test ordering, Mantis consent/destination/code binding, Groot product/retry/focus paths, and Star Lord integrated the results. Prior parser and rebase coverage is retained.
The same parsed code now drives both the direct-admission decision and the admission request, including denied-read and readable-new-profile cases. State still retains it for access-step retries, and editing the destination clears it. The added test sources assert exact policy/claim bodies and retained code after refusal. No new production correctness finding in this bounded repair.
The inherited Cancel/admission concern recorded in the previous review remains unchanged and is not counted as a new finding here.
Source-only: no local tests, builds or apps run. A single hosted snapshot shows CI required failed; the JavaScript (1/6) log confirms the formatting error reported inline. The PR description was inspected; its prior optional update remains relevant. The attached recording was not visually inspected, and rendered focus, native behavior and real-relay joining remain unverified.
Review of PR #2412 noted that the request mock's callback body and closing braces kept their old indentation when the consent test moved inside it.each, so the Lint and types job's format check failed. Formatted with Biome; whitespace only. Validation: biome format . reports no fixes, and the CommunityDialog unit tests pass (39). Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched the raw text, so a v2 code copied with a sentence-ending dot, a trailing slash, :443 or an uppercase V2. slipped past it. Address normalization then turned the code into an https://v2.<code> origin. The dialog and landing started discovery against it and showed the generic error, not the full-invite guidance. isBareInviteCode now normalizes scheme-less input with enteredRelayOrigin and tests the resulting hostname against the v2. prefix plus one base64url label. pastedInviteError calls the same helper, so every variant that is refused gets "Paste the whole invite link, not only its code." and no lookup starts. Still accepted: - explicit origins such as wss://v2.invite - hosts with more labels such as v2.relay.example Because the hostname is classified, a non-default port such as v2.invite:8443 is now also refused; wss://v2.invite:8443 still works. The docs already say a code on its own is refused, so they are unchanged. Tests: - invite-link rejects the trailing-dot code, V2.invite, v2.invite/, v2.invite:443 and v2.invite:8443. It also checks pastedInviteError copy for the variants and the fallback for other unreadable input. - CommunityDialog's no-fetch validation table and CommunityLanding's no-communityRequest refusal add the trailing-dot code. - The 10 new variant cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (112 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
be12469 to
64a185d
Compare
Review of #2412 found that the bare-code refusal matched `v2.` and any single base64url label on the normalized hostname. Short bare hosts such as v2.dev and v2.io were refused with "Paste the whole invite link, not only its code.", while wss://v2.dev worked. That contradicts the documented bare-host contract. The relay mints a code with encode_v2_code over a 32-byte (V2_SECRET_LEN) random secret: `v2.` plus unpadded base64url, which is 43 characters (buzz-core invite.rs, buzz-db relay_invite.rs). isBareInviteCode now requires exactly 43 base64url characters after `v2.`. It still tests the hostname that enteredRelayOrigin normalizes, so a minted code with a trailing dot, in uppercase, with a root slash, :443 or another port is still refused. The canonical final-character check is not applied, because hostname normalization lowercases the code. Now read as addresses: - short bare v2 hosts, such as v2.dev, V2.io and v2.invite:8443 - a v2. label of any other length, including a truncated code, which now fails at lookup rather than inline The previous commit's accepted tradeoff is gone: an apex such as v2.<tld> typed without a scheme now works. The docs only say that a code on its own is refused, which still holds, so they are unchanged. Tests: - invite-link builds every bare-code variant from one minted-length code, adds v2.dev and V2.io accepts, and uses the code for the explicit-scheme accept and fallback-copy cases. - CommunityDialog drops the short v2.invite refusal and adds a v2.dev lookup to its lookup table, with the origin as a column. - CommunityLanding runs its bare-host lookup for v2.dev as well as relay.example. - The 4 new v2.dev/V2.io cases fail against the previous production code, and all other cases pass there. All pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (114 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…ssion Review of PR #2412 found that the destination step only stored a pasted invite link's code with setCode, which takes effect on the next render. The submission that parsed the link still read the previous code, normally empty. With a signed-in account whose consent covers the community and a readable profile answer with exists: false, the direct admission called admit with that empty code, so admitCommunity skipped policy acceptance and the invite claim and went on to profile setup without redeeming the link. With a denied first read, the stale code only sent the user through an unneeded access step. The destination step now computes the effective code as the parsed code or the entered one, uses it for the direct-admission decision and passes it to admit, which takes an optional code and defaults to the rendered state. The state update stays, so the access step and later retries still show and reuse the code. The access step's own admission is unchanged. Tests: the two signed-in-account CommunityDialog tests are parametrized over an opened invite and a pasted link. The consent test also covers a readable new-profile first read, and both assert the exact accept-policy and claim bodies carry v2.abc. The 3 pasted-link cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (133 passed), pnpm typecheck and pnpm lint. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of PR #2412 noted that the request mock's callback body and closing braces kept their old indentation when the consent test moved inside it.each, so the Lint and types job's format check failed. Formatted with Biome; whitespace only. Validation: biome format . reports no fixes, and the CommunityDialog unit tests pass (39). Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord / Guardians integration follow-up: no new actionable source findings.
Reviewed head 64a185d0c31734162b79b4c4096c218ed2962e56 against base/merge-base 9cc2881d63bd8d28fddf56065fc36d8dcbc7741e. This bounded pass retains the six feature patches and prior parser, effective-code admission and formatting repairs. Rocket checked request/viewer lifetimes and saved-membership routing, Mantis consent/code binding and stale continuations, Groot controlled-switcher and dialog recovery/focus paths, and Star Lord integrated the results.
Submitted joins reuse the canonical destination, viewer-filtered request IDs and existing one-shot dialog submission. Seeded invite codes and freshly pasted codes still reach the same admission owner; address edits clear the previous code and consent. Controlled switcher reopening resets the navigation focus target, with the shared guard preserving focus moved elsewhere. The previously reported inherited Cancel/admission concern remains unchanged and is not a new finding here.
The earlier test-formatting P3 is repaired; JavaScript (1/6) succeeds in the hosted snapshot. Required CI is still failed. The four failed jobs inspected—JavaScript (6/6), Chromium (4/9), WebKit (8/14 and 13/14)—stop during pnpm download with HTTP 504 before tests start (run). Other failed-job causes were not investigated; this review does not establish green CI.
Source-only: implementation and test sources inspected; no local tests, builds or apps run. The PR description was inspected and its earlier optional update remains relevant. Its attached recording was not visually inspected. Rendered focus, native behavior and real-relay joining remain unverified.
Join a community refused the invite link Buzz itself mints (https://<host>/invite/<code>). It also refused a bare host such as secondary.example, telling the user to enter a wss:// or https:// relay URL. The no-community landing accepted the link but refused bare hosts, while the native first-run field accepted both. Both fields now read input through parsePastedInvite: - Its final fallback is enteredRelayOrigin instead of communityDestination, so a bare host is read as wss:// and then passes the same secure-origin checks. - The dialog's destination step parses with it and keeps the communityDestination check on the parsed origin. An invite link's code fills Invite code, as the invite prop does. - An unreadable value gets the native field's copy, "Enter a community address or invite link." - ws://, http://, credentials, other paths, query and fragment are still rejected. - An origin supplied by an OS invite parses with no code, so it leaves the prefilled code alone. Accepted consequences: - Typed text on the landing no longer resolves BUZZ_COMMUNITY_ALIASES, matching the rule that typed addresses never resolve aliases. - A dotted bare code such as v2.invite now reads as https://v2.invite and fails at lookup rather than inline. - Edit connection shares the destination step, so it also takes bare hosts and invite links. - The native CommunityUrlField and the relay's invite URL format are unchanged. docs/communities.md now describes bare hosts and invite links, and the help guide says Join a community takes either. Tests: - invite-link adds bare-host accepts, moves v2.invite to the accept list, and adds rejects for the bare-host forms of the refused inputs. - CommunityDialog swaps not-a-relay for "not a relay" with the new copy, and adds invite-link and bare-host lookups. - CommunityLanding adds a bare-host lookup. - All 9 new or changed cases fail against main's production code and pass with the fix. Validation on this snapshot: - Unit tests for invite-link, CommunityDialog, CommunityLanding and CommunityUrlField: 92 passed, with CommunityUrlField unchanged. - tests/browser/dialog-actions.spec.mjs and navigation-join.spec.mjs: 4/4 in Chromium and 4/4 in WebKit. - pnpm typecheck and pnpm lint pass. After-fix recording check: PASSED. The note's flow, rec2-community-join-invite-link, asserts the old refusals, so it was re-run as a copy with the same stubs and steps and inverted expectations (Chromium 1280x800, browser build of this tree). - In Join a community, the minted link opens Your profile, and Back shows Invite code "fixture" under https://primary.example. - secondary.example is looked up as https://secondary.example with no Invite code field, and wss://secondary.example still works. - On the no-community landing, both secondary.example and the minted link reach Your profile, with no inline refusal. Not checked: - The native desktop build was not run. - The policy-message interaction with the unmerged relay trust policy PR depends on that PR keeping communityDestination in the destination step, which this change does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…a host Review of #2412 found that a pasted invite code alone, such as v2.<base64url>, was read as the bare host https://v2.<code>. Join a community then ran community discovery against that origin, sending the code to DNS and failing as a lookup, not pointing the user at the link. parsePastedInvite now refuses a scheme-less value that is the relay's v2. code prefix (buzz-core V2_PREFIX) followed by one base64url segment. It does this before the bare-host fallback, so no lookup starts. The no-community landing and the Join a community dialog both show "Paste the whole invite link, not only its code." via pastedInviteError and keep their existing copy for other unreadable input. Not refused: - a host with more labels, such as v2.relay.example - a port, a path, or an explicit scheme (wss://v2.invite) - full invite links, which are parsed earlier Accepted tradeoff: a domain apex typed as v2.<tld> with no scheme cannot be told apart from a code, so it is refused; wss://v2.<tld> still works. The native first-run CommunityUrlField is unchanged and still reads a bare code as a host, as on main. docs/communities.md and the help guide say a code on its own is refused. Tests: - invite-link moves v2.invite back to the reject list, and adds a realistic 32-byte v2 code plus accepts for v2.relay.example and wss://v2.invite. - CommunityDialog's inline-validation table adds both codes with the new copy and asserts no fetch. - CommunityLanding adds a bare-code refusal with no communityRequest. - The 5 new reject cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (98 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched the raw text, so a v2 code copied with a sentence-ending dot, a trailing slash, :443 or an uppercase V2. slipped past it. Address normalization then turned the code into an https://v2.<code> origin. The dialog and landing started discovery against it and showed the generic error, not the full-invite guidance. isBareInviteCode now normalizes scheme-less input with enteredRelayOrigin and tests the resulting hostname against the v2. prefix plus one base64url label. pastedInviteError calls the same helper, so every variant that is refused gets "Paste the whole invite link, not only its code." and no lookup starts. Still accepted: - explicit origins such as wss://v2.invite - hosts with more labels such as v2.relay.example Because the hostname is classified, a non-default port such as v2.invite:8443 is now also refused; wss://v2.invite:8443 still works. The docs already say a code on its own is refused, so they are unchanged. Tests: - invite-link rejects the trailing-dot code, V2.invite, v2.invite/, v2.invite:443 and v2.invite:8443. It also checks pastedInviteError copy for the variants and the fallback for other unreadable input. - CommunityDialog's no-fetch validation table and CommunityLanding's no-communityRequest refusal add the trailing-dot code. - The 10 new variant cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (112 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of #2412 found that the bare-code refusal matched `v2.` and any single base64url label on the normalized hostname. Short bare hosts such as v2.dev and v2.io were refused with "Paste the whole invite link, not only its code.", while wss://v2.dev worked. That contradicts the documented bare-host contract. The relay mints a code with encode_v2_code over a 32-byte (V2_SECRET_LEN) random secret: `v2.` plus unpadded base64url, which is 43 characters (buzz-core invite.rs, buzz-db relay_invite.rs). isBareInviteCode now requires exactly 43 base64url characters after `v2.`. It still tests the hostname that enteredRelayOrigin normalizes, so a minted code with a trailing dot, in uppercase, with a root slash, :443 or another port is still refused. The canonical final-character check is not applied, because hostname normalization lowercases the code. Now read as addresses: - short bare v2 hosts, such as v2.dev, V2.io and v2.invite:8443 - a v2. label of any other length, including a truncated code, which now fails at lookup rather than inline The previous commit's accepted tradeoff is gone: an apex such as v2.<tld> typed without a scheme now works. The docs only say that a code on its own is refused, which still holds, so they are unchanged. Tests: - invite-link builds every bare-code variant from one minted-length code, adds v2.dev and V2.io accepts, and uses the code for the explicit-scheme accept and fallback-copy cases. - CommunityDialog drops the short v2.invite refusal and adds a v2.dev lookup to its lookup table, with the origin as a column. - CommunityLanding runs its bare-host lookup for v2.dev as well as relay.example. - The 4 new v2.dev/V2.io cases fail against the previous production code, and all other cases pass there. All pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (114 passed), pnpm typecheck and pnpm lint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…ssion Review of PR #2412 found that the destination step only stored a pasted invite link's code with setCode, which takes effect on the next render. The submission that parsed the link still read the previous code, normally empty. With a signed-in account whose consent covers the community and a readable profile answer with exists: false, the direct admission called admit with that empty code, so admitCommunity skipped policy acceptance and the invite claim and went on to profile setup without redeeming the link. With a denied first read, the stale code only sent the user through an unneeded access step. The destination step now computes the effective code as the parsed code or the entered one, uses it for the direct-admission decision and passes it to admit, which takes an optional code and defaults to the rendered state. The state update stays, so the access step and later retries still show and reuse the code. The access step's own admission is unchanged. Tests: the two signed-in-account CommunityDialog tests are parametrized over an opened invite and a pasted link. The consent test also covers a readable new-profile first read, and both assert the exact accept-policy and claim bodies carry v2.abc. The 3 pasted-link cases fail against the previous production code and pass with this change. Validation: the invite-link, CommunityDialog, CommunityLanding and CommunityUrlField unit tests (133 passed), pnpm typecheck and pnpm lint. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Review of PR #2412 noted that the request mock's callback body and closing braces kept their old indentation when the consent test moved inside it.each, so the Lint and types job's format check failed. Formatted with Biome; whitespace only. Validation: biome format . reports no fixes, and the CommunityDialog unit tests pass (39). Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
64a185d to
8e615fb
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord / Guardians integration follow-up: no new actionable source findings.
Reviewed head 8e615fb9430cd77c6b3982b74bb574de76411ace against base/merge-base 40e6ccb597cf9d8b47f9692de8285458cc304017. All six feature source/test files remain identical to the previously reviewed 64a185d0; prior parser, bare-code refusal, effective-code admission and focus coverage is retained.
This bounded pass checked integration with the base's updated invite-sharing UI and native QR renderer. Copy and QR use the same minted URL; replaced mint/render results cannot replace the current link's QR. A QR rendering failure preserves the copyable link. The HTTPS invite-page parser and the reference page's buzz://join handoff preserve the destination/code pair into the existing admission flow. Rocket checked producer/receiver lifetimes, Mantis origin/code binding and stale-link isolation, Groot recovery/focus/documentation, and Star Lord integrated the source and test evidence. The previously reported inherited Cancel/admission concern is unchanged.
Source-only: no local tests, builds or apps run. The single hosted snapshot still had checks running, so this is not a completed CI verdict. Native scanning, browser-to-OS handoff, rendered focus and real-relay joining remain unverified. The PR description was inspected; its earlier optional correction remains relevant. The attached recording was not visually inspected.
Picks up c202642 (block#2837), which fixes the ThreadPanel catch-up test that fails on every run of a merge with the older main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * origin/main: (155 commits) deps: accept RUSTSEC-2026-0459 for libsqlite3-sys 0.38, move rmcp to 3.9.0 (block#3077) fix(communities): accept invite links and bare hosts in Join a community (block#2412) fix(builderlab): open a remote agent's Activity on the conversation it was opened from (block#3051) refactor(builderlab): keep capabilities fresh in the session (block#3003) feat(moderation): show invite links as a QR code (block#3029) Keep Create agent's Model row readable and give Goose a Thinking row (block#2673) Join a community right after creating it in Settings (block#2991) Let new Solo chats start temporary by default (block#2874) fix(messages): fill a screen that a head of folded agent activity leaves empty (block#3037) fix(search): search saved conversations while the community is offline or connecting (block#2724) test(agents): make version-history date assertion time-zone independent (block#3010) feat(relay): gate artifact writers on NIP-AR support (block#2735) fix(channels): open the dock's community switcher from Personal space (block#2806) fix(members): keep the list's place after a member change (block#2818) fix(channels): keep a launcher's side panel open on Close all tabs (block#2803) fix(activity): show a remote agent's Buzz send as its reply (block#3025) refactor(settings): move external identity authorization to security (block#3032) test(typeahead): serve the fixture pages as a build so cold loads fit (block#2793) feat(channels): list unread top-level mentions in sidebar Activity, and count only unread mentions on the Inbox card (block#2722) fix(solo): delete a removed workspace's settings after the groups write (block#2819) ... Signed-off-by: Andrew Harvard <aharvard@squareup.com>
* origin/main: deps: accept RUSTSEC-2026-0459 for libsqlite3-sys 0.38, move rmcp to 3.9.0 (#3077) fix(communities): accept invite links and bare hosts in Join a community (#2412) fix(builderlab): open a remote agent's Activity on the conversation it was opened from (#3051) refactor(builderlab): keep capabilities fresh in the session (#3003) feat(moderation): show invite links as a QR code (#3029) Keep Create agent's Model row readable and give Goose a Thinking row (#2673) Signed-off-by: Bradley Axen <baxen@squareup.com>
…pover-card-layout-7eeda1e0 * origin/main: (128 commits) deps: accept RUSTSEC-2026-0459 for libsqlite3-sys 0.38, move rmcp to 3.9.0 (#3077) fix(communities): accept invite links and bare hosts in Join a community (#2412) fix(builderlab): open a remote agent's Activity on the conversation it was opened from (#3051) refactor(builderlab): keep capabilities fresh in the session (#3003) feat(moderation): show invite links as a QR code (#3029) Keep Create agent's Model row readable and give Goose a Thinking row (#2673) Join a community right after creating it in Settings (#2991) Let new Solo chats start temporary by default (#2874) fix(messages): fill a screen that a head of folded agent activity leaves empty (#3037) fix(search): search saved conversations while the community is offline or connecting (#2724) test(agents): make version-history date assertion time-zone independent (#3010) feat(relay): gate artifact writers on NIP-AR support (#2735) fix(channels): open the dock's community switcher from Personal space (#2806) fix(members): keep the list's place after a member change (#2818) fix(channels): keep a launcher's side panel open on Close all tabs (#2803) fix(activity): show a remote agent's Buzz send as its reply (#3025) refactor(settings): move external identity authorization to security (#3032) test(typeahead): serve the fixture pages as a build so cold loads fit (#2793) feat(channels): list unread top-level mentions in sidebar Activity, and count only unread mentions on the Inbox card (#2722) fix(solo): delete a removed workspace's settings after the groups write (#2819) ...
Problem
Join a community refused the invite link Buzz mints itself (
https://<host>/invite/<code>). It also refused a bare host such assecondary.exampleand told the user to enter awss://orhttps://relay URL. The no-community landing (CommunityLanding) accepted the link but refused bare hosts. The native first-run field (CommunityUrlField) accepted both. So the same input was accepted in one place and refused in the other two.Fix
Both web fields now read input through
parsePastedInvite:enteredRelayOrigininstead ofcommunityDestination. A bare host is read aswss://and then goes through the same secure-origin checks.parsePastedInviteand still runscommunityDestinationon the parsed origin. A pasted invite link's code fills Invite code, the same way theinviteprop does.ws://,http://, credentials, other paths, query strings and fragments are still refused.The native
CommunityUrlFieldand the relay's invite URL format are unchanged.docs/communities.mdnow covers bare hosts and invite links. The help guide (docs/buzz-help.md) now says Join a community takes either one.Accepted consequences
BUZZ_COMMUNITY_ALIASES. This follows the existing rule that typed addresses never resolve aliases.v2.invite, now reads ashttps://v2.invite. It fails at lookup instead of inline.Recording (after the fix)
staged-image-a12a0e95-801b-427a-9bb5-68278bcd97eb.mp4
Browser build of this branch, Chromium at 1280×800, with stubbed relays. The owner mints an invite link. Join a community accepts it and opens Your profile, and Back shows Invite code
fixtureunderhttps://primary.example.secondary.exampleis looked up ashttps://secondary.examplewith no Invite code field. On the no-community landing, bothsecondary.exampleand the minted link reach Your profile.Tests
invite-link.test.ts: adds accepted bare hosts, movesv2.inviteto the accepted list, and adds refusals for the bare-host forms of the inputs that are still refused.CommunityDialog.test.tsx: replaces the not-a-relay case with a "not a relay" case using the new wording, and adds invite-link and bare-host lookups.CommunityLanding.test.tsx: adds a bare-host lookup.Validation on d1a91a1
invite-link,CommunityDialog,CommunityLandingandCommunityUrlField: 92 passed.CommunityUrlFieldis unchanged.tests/browser/dialog-actions.spec.mjsandnavigation-join.spec.mjs: 4/4 in Chromium and 4/4 in WebKit.pnpm typecheckandpnpm lintpass.Not checked
communityDestinationin the destination step, which this change does.Companion PR
Mobile regression tests are in block/buzz#9047. They lock in invite-page and bare-host entry, which the mobile app already handles.
🤖 Generated with Claude Code