Messaging

Message lifecycle from compose to display, with privacy at every step.

Message Lifecycle


  COMPOSE → SERIALIZE → COMPRESS → ENCRYPT → SEAL → PAD → BATCH → ROUTE → DELIVER
                                                                              |
  DISPLAY ← STORE ← DECOMPRESS ← DECRYPT ← VERIFY ← UNSEAL ← UNPAD ← RECEIVE
  

Compose

The user types a message in the UI. The message is held in application memory as a UTF-8 string. No data is written to disk until the message is encrypted and stored in the local database.

Serialize

Message protobuf schema:

message RvntMessage {
    bytes   id          = 1;    // UUID v4 (16 bytes)
    uint64  timestamp   = 2;    // Unix epoch milliseconds
    string  content     = 3;    // UTF-8 text body
    bytes   reply_to    = 4;    // Optional: UUID of quoted message
    uint32  flags       = 5;    // Bit flags (see below)
    repeated Attachment attachments = 6;
}

message Attachment {
    bytes   hash        = 1;    // BLAKE3 hash of original file (32 bytes)
    string  filename    = 2;    // Original filename
    string  mime_type   = 3;    // MIME type
    uint64  size        = 4;    // File size in bytes
    bytes   thumbnail   = 5;    // Optional: encrypted thumbnail
    bytes   key         = 6;    // Per-file encryption key (32 bytes)
}

Message flags (bit field):
  0x01  EPHEMERAL      Message auto-deletes after read
  0x02  PRIORITY       High-priority (push notification)
  0x04  SILENT         No notification on receipt
  0x08  EDIT           This message edits a previous message (reply_to = original)
  0x10  DELETE         This message deletes a previous message (reply_to = target)

Compress, Encrypt, Seal, Pad, Route

See How It Works for the detailed pipeline. The serialized protobuf is compressed (zstd), encrypted (Double Ratchet / AES-256-GCM), wrapped in a sealed sender envelope, padded to a fixed size, optionally batched (mixnet), and routed through Tor.

Message Status System

RVNT tracks message status through a state machine. Each transition generates a status update that is sent back through the same encrypted pipeline.

Message status state machine:

  QUEUED → SENT → DELIVERED → READ
    │        │         │
    │        │         └── Recipient opened conversation
    │        └── Recipient device received envelope
    └── Message encrypted and queued for transmission

  Error states:
  QUEUED → FAILED (network error, peer offline)
  SENT → EXPIRED (no delivery acknowledgment within 24h)

Status transitions:
  QUEUED:    Message encrypted, added to outbound queue
  SENT:      Tor circuit confirmed transmission to peer's address
  DELIVERED: Recipient device sent encrypted ACK
  READ:      Recipient opened the conversation containing this message
  FAILED:    Transmission failed after retry attempts exhausted
  EXPIRED:   No delivery ACK received within TTL (default 24 hours)

Delivery Acknowledgments

When a recipient's device receives and successfully decrypts a message,
it sends a delivery acknowledgment back to the sender:

DeliveryAck {
    message_id:  UUID of acknowledged message     [16 bytes]
    status:      DELIVERED (0x01) or READ (0x02)   [1 byte]
    timestamp:   When the status changed            [8 bytes]
}

The ACK is:
  1. Encrypted with the same Double Ratchet session
  2. Wrapped in a sealed sender envelope
  3. Routed through Tor
  4. Indistinguishable from a regular message to an observer

The ACK does NOT reveal:
  - Which message was acknowledged (encrypted)
  - Who sent the ACK (sealed sender)
  - That it IS an ACK vs a regular message (same format + padding)

Typing Indicators

Typing indicators tell your contact that you are currently composing a message. This is a convenience feature with privacy implications.

Typing indicator behavior:
  - Sent when user begins typing (debounced: 3 seconds)
  - Sent when user stops typing (after 5 seconds of inactivity)
  - NOT sent for every keystroke
  - Rate limited: maximum 1 typing event per 3 seconds

TypingIndicator {
    action: START (0x01) or STOP (0x02)     [1 byte]
}

Privacy options:
  - Global disable: Settings > Privacy > Typing Indicators > Off
  - Per-conversation disable: Conversation settings > Typing > Off
  - Disabling typing indicators also stops receiving them
    (reciprocal: if you don't send, you don't receive)

Security note:
  Typing indicators are encrypted and sealed like regular messages.
  An observer cannot distinguish a typing indicator from a message.
  However, the timing pattern of typing indicators can reveal
  conversation activity patterns. Disable if this is a concern.

Online Status

RVNT can show whether a contact is currently online. This feature is opt-in and disabled by default.

Online status modes:
  OFF (default):  Your online status is never shared
  CONTACTS:       Only mutual contacts see your status
  EVERYONE:       Any RVNT user can see your status

Implementation:
  - Presence is communicated via the libp2p gossipsub protocol
  - Encrypted presence announcements to subscribed contacts
  - No server stores online status
  - Status is ephemeral: 60-second TTL, must be refreshed

Privacy considerations:
  - Online status reveals when you are using RVNT
  - This can be used to build activity patterns
  - Disabled by default for this reason
  - Even when enabled, status is only visible to selected contacts

Read Receipts

Read receipts tell the sender that the recipient has opened the conversation and presumably read the message.

Read receipt behavior:
  - Triggered when the recipient opens a conversation with unread messages
  - NOT triggered for individual messages (batch: all unread marked as read)
  - Sent as a READ status update (same as delivery ACK format)

Privacy options:
  - Global disable: Settings > Privacy > Read Receipts > Off
  - Per-conversation disable: Conversation settings > Read Receipts > Off
  - Reciprocal: disabling read receipts also prevents you from seeing
    whether your contacts have read your messages

Default: ENABLED (can be changed during onboarding)

Ephemeral Messages

Ephemeral messages auto-delete after being read. The deletion is local: the message is removed from the recipient's database after a configurable timer.

Ephemeral message timers:
  5 seconds   (view once)
  30 seconds
  5 minutes
  1 hour
  24 hours
  7 days

Implementation:
  1. Sender sets EPHEMERAL flag + timer duration
  2. Message is sent through normal pipeline
  3. Recipient decrypts and displays message
  4. Timer starts when message is displayed on screen
  5. When timer expires:
     a. Message deleted from SQLCipher database
     b. Any associated media deleted from cache
     c. Thumbnail removed
     d. Deletion is irreversible
  6. Sender receives a DELETED status update

Limitations:
  - The recipient can screenshot before the timer expires
  - The recipient can photograph the screen
  - RVNT does NOT attempt screenshot detection (unreliable)
  - Ephemeral messages are a usability feature, not a security guarantee

Message Editing and Deletion

Edit:
  - Send a new message with EDIT flag set
  - reply_to field contains the UUID of the original message
  - Recipient's UI replaces the original message content
  - Edit history is stored locally (recipient can view edits)
  - Edits are encrypted the same as regular messages

Delete (for everyone):
  - Send a new message with DELETE flag set
  - reply_to field contains the UUID of the message to delete
  - Recipient's UI removes the message
  - A "message deleted" placeholder may be shown
  - The actual ciphertext is deleted from the recipient's database
  - Works within 24 hours of original send (after that, local-only delete)

Further Reading

Last updated: 2026-04-12

RVNT Documentation — Post-quantum encrypted communications