mgmt: v12 (domainless) room ids rejected by link shape-check; first-contact joins need a via hint #103

Closed
opened 2026-07-12 06:17:03 +00:00 by robocub · 1 comment
Collaborator

Found live 2026-07-12: @qwertyad:qwertyad.one's homeserver mints MSC4291-style domainless room ids (!HZxp_Dy3wHbASuHxk8Th0qhzGHl3Bxuj3t1IvMMH54U). Two problems:

  1. link shape-check rejected them — it required a :domain part (legacy !localpart:domain). The rejection was also reply-only (no log/audit), so triage was blind.
  2. First-contact federated joins of domainless rooms can't resolve: with no domain in the id, the homeserver needs an explicit via hint to find an unknown room — legacy ids carried it implicitly.

Fix (branch fix/103-v12-room-ids): plausible_room_id() accepts both generations (! + non-empty + no whitespace; the join/state probes are the real validation — ruma parses both fine, proven by the bot syncing and replying inside qwertyad's v12 E2EE DM all night); the shape rejection now logs; appservice_join_room gains an optional via (sent as both via and the legacy server_name spelling), threaded as the requester's MXID domain through the link probe and the DM-invite accept path; the ban/report room validators updated to the same predicate.

Found live 2026-07-12: `@qwertyad:qwertyad.one`'s homeserver mints MSC4291-style domainless room ids (`!HZxp_Dy3wHbASuHxk8Th0qhzGHl3Bxuj3t1IvMMH54U`). Two problems: 1. **`link` shape-check rejected them** — it required a `:domain` part (legacy `!localpart:domain`). The rejection was also reply-only (no log/audit), so triage was blind. 2. **First-contact federated joins of domainless rooms can't resolve**: with no domain in the id, the homeserver needs an explicit `via` hint to find an unknown room — legacy ids carried it implicitly. Fix (branch `fix/103-v12-room-ids`): `plausible_room_id()` accepts both generations (`!` + non-empty + no whitespace; the join/state probes are the real validation — ruma parses both fine, proven by the bot syncing and replying inside qwertyad's v12 E2EE DM all night); the shape rejection now logs; `appservice_join_room` gains an optional `via` (sent as both `via` and the legacy `server_name` spelling), threaded as the requester's MXID domain through the link probe and the DM-invite accept path; the ban/report `room` validators updated to the same predicate.
Author
Collaborator

Shipped in v0.3.0-beta.1, deployed to prod 2026-07-12 07:29 UTC. #97: invite dedupe + cooldown-stamp-after-join (alpha.25) + the server-side pending-invite reap gate (beta.1; the alpha.25 summary-count gate was proven lagging by the #99 drill — evidence on #100). Prod mgmt_cooldown_secs restored with the deploy; post-restart journal confirms failed joins no longer arm the cooldown. #103: both room-id generations accepted (link/ban/report), via hints on first-contact joins, shape rejections logged. Residual mode 2 tracked as #104 with a live known-fail scenario.

Shipped in **v0.3.0-beta.1**, deployed to prod 2026-07-12 07:29 UTC. #97: invite dedupe + cooldown-stamp-after-join (alpha.25) + the server-side pending-invite reap gate (beta.1; the alpha.25 summary-count gate was proven lagging by the #99 drill — evidence on #100). Prod `mgmt_cooldown_secs` restored with the deploy; post-restart journal confirms failed joins no longer arm the cooldown. #103: both room-id generations accepted (`link`/`ban`/`report`), via hints on first-contact joins, shape rejections logged. Residual mode 2 tracked as #104 with a live known-fail scenario.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dark/nether-voicebridge#103
No description provided.