UI & Customization
Custom Emojis & Metadata Budgets
Discord-like syntax formatting, WebP serialization, tombstone deletions, and pad-relative limits.
Overview
Wiltkey supports user-generated custom emojis, invoked in chats using the :name: format. Emojis are encoded as base64-encoded WebP bytes and synced to peers over standard message lanes. Because keystream bytes cannot be reused or reclaimed, custom emojis are restricted by a soft metadata budget, and deleting an emoji results in a cryptographic tombstone.
How it Works
-
Inline Syncing:
Custom emojis are not sent via a side-channel. Defining an emoji writes an
emoji_defmessage to our outgoing lane. Deleting an emoji writes anemoji_deletetombstone message. These consume pad bytes and propagate via standard resync sweeps. Upon receipt, they are parsed and merged into the localCustomEmojiStorerather than rendered as chat bubbles. -
Pad-Relative Metadata Budget:
To prevent custom emojis from exhausting a device's storage, the client computes a soft metadata budget proportional to the total pad size:
budgetBytes = totalPadBytes * 0.02 (Capped at 1MB)If a pad is too small to yield at least 50KB (
customEmojiThreshold), custom emojis are disabled. -
Tombstone Deletion ("Junk Data"):
When deleting an emoji, the image bytes are cleared in memory, but a dead slot marked
deleted = trueremains. The size of the deleted image is stashed inreservedBytes:approxBytes = deleted ? reservedBytes : imageB64.lengthThis stashed size continues to count against the metadata budget. The tombstone's creation timestamp is bumped to the deletion time, ensuring it wins the union-merge and propagates to the peer. -
Union-Merge & Trimming:
During sync,
mergeAllmerges incoming emoji lists. If the merged list exceeds the metadata budget, the oldest emojis (sorted bycreatedAtMs) are permanently trimmed until the list fits the budget.
Key Files & Symbols
| File Path | Symbol Name | Description |
|---|---|---|
lib/core/custom_emoji.dart |
CustomEmoji |
Represents an emoji name, WebP string, timestamp, and tombstone state. |
lib/core/custom_emoji.dart |
CustomEmojiStore |
Handles local storage (SharedPreferences) and union-merges. |
lib/core/chat_metadata.dart |
ChatMetaStore.budgetFor() |
Computes the pad-relative metadata budget: 2% of the pad, capped at 1MB. |
lib/core/state_emoji.dart |
defineEmoji() / deleteChatEmoji() |
Writes emoji creation/tombstone messages into the outgoing lane. |
Gotchas & Edge Cases
🛑 TOMBSTONES DO NOT RECLAIM SPACE
Because the keystream bytes utilized to transmit the WebP image cannot be reused, deleting a custom emoji does not reclaim metadata budget bytes. It freezes the cost in place as a tombstone. This prevents a malicious peer from continuously adding and deleting emojis to exhaust the receiver's budget.
Because the keystream bytes utilized to transmit the WebP image cannot be reused, deleting a custom emoji does not reclaim metadata budget bytes. It freezes the cost in place as a tombstone. This prevents a malicious peer from continuously adding and deleting emojis to exhaust the receiver's budget.