Skip to main content
← Back to list
01Issue
BugShippedSwamp ClubPublic
Assigneeskeeb

Relationships

#1474 Plus-addressed emails stripped on invite acceptance, breaking collective join and quest reward

Opened by stack72 · 7/29/2026· Shipped 8/14/2026

Description

When inviting a user with a plus-addressed email (e.g. [REDACTED-EMAIL]), the invitation email arrives correctly with the full address. However, when the invited user accepts the invite, the system strips the +swamp subaddress portion from the email.

Impact

This causes two failures:

  1. Collective membership not granted — the invited user is not added to the collective because the email on their account ([REDACTED-EMAIL]) no longer matches the invitation email ([REDACTED-EMAIL]), so claimPendingInvitations never matches.
  2. Invite quest reward not issued — the inviter does not receive the swamp quest payout for the invitation, since the invite is never successfully claimed.

Steps to Reproduce

  1. As a collective owner, invite [REDACTED-EMAIL] to your collective
  2. The invitation email arrives at the correct plus-addressed email
  3. Click the invite link in the email to accept
  4. Sign up / sign in with the invited email address
  5. Observe: the +swamp portion is stripped from the email, the user is not added to the collective, and the inviter receives no quest reward

Expected Behavior

Plus-addressed emails (RFC 5233 subaddressing) should be preserved throughout the invitation flow. The user should be added to the collective and the inviter should receive their quest reward.

Environment

  • Platform: swamp.club web app
  • Observed: 2026-07-30
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 4 MOREPR_MERGED+ 1 MORENOTIFICATION_SKIPPED

Shipped

8/14/2026, 7:44:58 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
keeb assigned keeb8/14/2026, 6:17:42 PM
Editable. Press Enter to edit.

keeb commented 8/14/2026, 7:25:25 PM

Triage: the subaddress is not being stripped by swamp-club

Reproduced this end to end against a local stack, and the mechanism turns out to be somewhere other than where the report puts it. Writing it up because the difference changes what the fix can safely be.

What the reproduction shows. Driving the real endpoints with a plus-addressed test account:

  1. POST /api/auth/sign-up/email stored user.email with the +swamp intact — verbatim.
  2. POST /api/v1/collectives/SLUG/invite stored invitation.email verbatim too.
  3. Signing in as the invitee auto-claimed the invite: member row written, invitation flipped to accepted.

So on the email/password path the plus survives every hop. A repo-wide search for subaddress-stripping patterns finds nothing — every hop does trim().toLowerCase() and no more (collective-commands.ts, claim-pending-invitations.ts, signup.tsx, invite-to-platform.ts), and the emailed accept link carries only the invitation id, never the address.

What does change the address. The one participant swamp-club does not control: the OAuth provider. Google Workspace treats a plus alias as the same mailbox and returns the canonical address on sign-in. Because accountLinking.disableImplicitLinking is true, that mints a NEW operative rather than linking to an existing one — so the invitation, which is keyed to the subaddressed form, can never match it. Membership is not granted, the inviter quest payout never fires, and from the invitee seat that is indistinguishable from the system stripping the +swamp.

The signup page made this worse than it needed to be: /signup?invitation=ID prefills AND locks the invited address in the email field, then renders four social buttons directly underneath that ignore the lock completely.

One question, since the reproduction cannot answer it: which provider did you use to sign up — Google, GitHub, Microsoft, Discord, or email/password? If it was email/password, the diagnosis above is wrong and I would like to re-derive it.

What is shipping

  • The post-auth destination now survives the OAuth round trip, so an invitee who signs up with a provider lands back on /o/SLUG/accept-invite and sees its existing WRONG_ACCOUNT state — which names the account they are signed in as and says the invitation went elsewhere — instead of being dropped on /profile with no signal at all. That routing gap was the still-live half of #1204.
  • The signup page now warns BEFORE the click, naming the address the provider has to return, wired to the social buttons with aria-describedby.
  • The old help text under the locked field claimed a social login would use the invited address. It said the opposite of what happens, so it is gone.

What is deliberately not shipping

Matching the canonical address to an invitation addressed to the plus form. RFC 5233 subaddressing is a mail-server convention, not a guarantee — on an arbitrary domain those can be two different people. Treating them as equal would grant collective membership, and with it read access to a private collective, on an inference about somebody else mail configuration. So the fix here is legibility rather than a looser match: the failure is now visible to the invitee at the moment it happens, and the remedy is one click for the owner (re-invite the canonical address).

The reasoning is recorded in CLAUDE.md so it does not get quietly reversed later — the natural reading of this issue title is an instruction to implement exactly that normalization.

Still open, filed separately: the collective OWNER gets no signal either way. After this change the invitation simply stays pending, indistinguishable from one nobody has opened yet.

Sign in to post a ripple.