Ponder

Messaging Feature

Thread, message, encryption, polling, and chat_write behavior.

Purpose

Messaging provides 1:1 threads between accepted friends. It supports text messages and article-share messages with inline article previews.

Tables

Primary tables:

Supporting tables:

Thread Model

ThreadModel reads:

If pairKey is absent, the model derives it by sorting and joining participantIds with :.

Thread creation writes:

Message Model

ThreadMessageModel reads:

If typed article columns are absent, it can parse legacy article messages from body values beginning with __ponder_article__: followed by content ID and optional caption.

Encryption

FriendsService uses ThreadMessageCipher before writes and after reads:

ThreadMessageCipher uses AES-GCM with 256-bit keys and HKDF/HMAC-SHA256. Encrypted values are prefixed with __ponder_encrypted_v1__: and store base64-url encoded nonce, ciphertext, and MAC bytes. The derived key context is based on sorted participant IDs, with a legacy thread-ID context fallback during decryption.

Function Write Path

chat_write is the preferred write path for:

The function validates:

It writes row permissions for participants and authors where row security applies.

Read Path

ThreadScreen watches threadMessagesProvider(threadId).

threadMessagesProvider:

  1. Fetches messages immediately.
  2. Emits the message list.
  3. Waits for threadMessagesPollingIntervalProvider, currently two seconds.
  4. Repeats until disposed.

Message reads use FriendsService.getThreadMessages, which queries thread_messages by threadId, orders by sentAt ascending, fetches the thread, decrypts rows, and maps them to ThreadMessageModel.

UI

ThreadScreen:

Compatibility

The code intentionally supports current and legacy schemas. Current rows use pairKey, type, and contentId; legacy fallbacks use contentId thread lookup and encoded article-message bodies.