Skip to content

exploration: support legacy Matrix user IDs#14

Draft
wouter173 wants to merge 2 commits into
mainfrom
feat/issue-9-legacy-mxids
Draft

exploration: support legacy Matrix user IDs#14
wouter173 wants to merge 2 commits into
mainfrom
feat/issue-9-legacy-mxids

Conversation

@wouter173

Copy link
Copy Markdown
Owner

Closes #9

Summary

  • accept historical opaque Matrix user ID localparts when decoding server responses
  • retain the modern lowercase localpart grammar for UserId.make
  • expose UserId.creationSchema for outgoing creation boundaries
  • cover uppercase and otherwise historical localparts with regression tests

Verification

  • CI=true pnpm --filter mxfx test --run (73 tests passed)
  • CI=true pnpm --filter mxfx typecheck
  • CI=true pnpm fmt:check
  • git diff --check
  • targeted oxlint on changed files with type-aware checks disabled

Caveat

The repository-wide pnpm lint command currently fails before linting because .oxlintrc.json enables type-aware checking but the tsgolint executable was removed on main. The changed files pass the non-type-aware oxlint rules, and TypeScript typechecking passes.

Comment thread packages/mxfx/src/branded/user-id.ts Outdated
* grammar. Homeservers must continue accepting these IDs even though they may not
* create new IDs using that grammar.
*/
const schema = Schema.TemplateLiteral(['@', opaqueId, ':', ServerName.schema]).pipe(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Accept the historical empty localpart

The Matrix Historical User IDs rules explicitly require clients to accept an empty localpart, but opaqueId includes isMinLength(1), so Schema.decodeUnknownEffect(UserId.schema) rejects @:matrix.org. UserId.schema is used by inbound event and account response schemas, so one such legacy identifier can still reject the server payload that issue #9 is intended to handle. Please use a user-ID-specific historical localpart which allows empty, and cover @:matrix.org with a regression test. See https://spec.matrix.org/unstable/appendices/#historical-user-ids.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 362e6fa. UserId.schema now uses a dedicated historical localpart that permits the required empty localpart, while UserId.make still uses the strict modern non-empty grammar. Added inbound coverage for @:matrix.org; the existing outbound rejection remains covered.

Comment thread packages/mxfx/src/branded/user-id.ts Outdated
* create new IDs using that grammar.
*/
const schema = Schema.TemplateLiteral(['@', opaqueId, ':', ServerName.schema]).pipe(
Schema.check(Schema.isMaxLength(255)),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Enforce the MXID limit in UTF-8 bytes

Matrix limits the complete user ID to 255 bytes, whereas Effect Schema.isMaxLength checks JavaScript input.length, which counts UTF-16 code units. This becomes observable now that schema accepts Unicode: @ followed by 130 copies of é and :matrix.org is 272 UTF-8 bytes but only 142 code units, and UserId.schema accepts it. Please use a UTF-8 byte-length check for the historical schema and add a multibyte boundary test. See https://spec.matrix.org/unstable/appendices/#user-identifiers.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 362e6fa. The complete MXID is now checked with TextEncoder and capped at 255 UTF-8 bytes instead of UTF-16 code units. Regression coverage accepts an exact 255-byte multibyte ID and rejects the corresponding 256-byte ID.

Comment thread packages/mxfx/src/branded/user-id.ts Outdated
* grammar. Homeservers must continue accepting these IDs even though they may not
* create new IDs using that grammar.
*/
const schema = Schema.TemplateLiteral(['@', opaqueId, ':', ServerName.schema]).pipe(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Reject lone UTF-16 surrogates

The historical grammar permits legal non-surrogate Unicode code points, but the opaqueId regex only excludes NUL and colon. A JavaScript string containing a lone U+D800 therefore passes UserId.schema; I reproduced this locally. JSON parsing can produce such a string, and UTF-8 encoding later replaces it with U+FFFD, so the branded value is not stable. Please add an explicit well-formed-Unicode check to the historical user localpart and a regression test. See https://spec.matrix.org/unstable/appendices/#historical-user-ids.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 362e6fa. The historical localpart now rejects isolated UTF-16 surrogate code units while accepting valid paired astral code points. Added regressions for lone high/low surrogates, an accepted emoji, and preserved NUL rejection.

@wouter173 wouter173 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary: the inbound/outbound split is directionally sound, UserId.make remains strict, and the existing focused tests and typecheck pass. Before merging, UserId.schema should cover the full historical acceptance grammar and preserve the protocol invariants: accept an empty localpart, reject lone surrogates, and enforce the 255-byte UTF-8 limit. I left three inline comments with reproductions and suggested regression cases. No files or commits were changed during review.

@wouter173
wouter173 marked this pull request as draft July 17, 2026 09:00
@wouter173 wouter173 changed the title fix: support legacy Matrix user IDs [insipration] fix: support legacy Matrix user IDs Jul 17, 2026
@wouter173 wouter173 changed the title [insipration] fix: support legacy Matrix user IDs exploration: support legacy Matrix user IDs Jul 17, 2026
@wouter173 wouter173 added the exploration AI made pr and implementation prompted with limited knowledge to explore possible implementations label Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

exploration AI made pr and implementation prompted with limited knowledge to explore possible implementations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

How to deal with legacy mxids

1 participant