Emojis and stickers get separate tables on purpose: an emoji sits inside a
sentence and a sticker is the whole message, so their sizes, limits and render
paths differ.
Emoji names are unique per space and restricted to letters, digits and
underscore — :name: has to resolve to one image, and a space or colon inside
the name would make the reference impossible to delimit. Sticker names are
labels picked from a grid, so they accept anything.
Rendering reuses the existing markdown pipeline by rewriting :name: into image
syntax, which needs no plugin. Code spans and fences are left alone: text
someone wrapped in backticks was meant to be shown literally, and turning it
into an image would destroy what they wrote.
Messages carry stickerId with ON DELETE set null — removing a sticker from the
space must not delete the history of everyone who used it. The server refuses a
sticker from another space, which would otherwise leak images between unrelated
servers.
Expressions load once per space and stay in memory: the message renderer
consults the map for every :name: it finds, and a request per lookup would turn
each message into a cascade.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>