Qwisha Protocol

A lightweight overlay protocol for enhancing SMS with modern messaging features

Version 1.0 | Compact Header Format | Zero Internet Required

Protocol Overview

The Qwisha Protocol is an overlay protocol that extends standard SMS with advanced messaging features including message editing, deletion, replies, and search capabilities—all without requiring an internet connection.

Key Principles

  • Compact Headers: Minimal overhead to avoid SMS segmentation
  • Backward Compatible: Falls back to regular SMS for non-Qwisha users
  • Stateless: Each message is self-contained with all necessary metadata
  • Resilient: Handles out-of-order delivery and message loss gracefully
Protocol Stats

Header Size: ~20-30 bytes

Overhead: Minimal

Commands: 4 (s, r, e, d)

Format: ASCII-compatible

Header Format

@i=<MSGID>;c=<CMD>;r=<REFID> <CONTENT>

Format Breakdown

Field Description
@ Protocol identifier (must be first character)
i Message ID - Unique identifier for this message
c Command - Action type (s, r, e, d)
r Reference ID - ID of message being replied to/edited/deleted (optional)
CONTENT Message content (separated by space after header)

Header Characteristics

Example Header
@i=abc123;c=s;r=null Hello World
Compact Reply Header
@i=xyz789;c=r;r=abc123 Yes, I agree!
Edit Command
@i=edit456;c=e;r=abc123 Hello Updated World
Delete Command
@i=del789;c=d;r=abc123

Note: Delete commands may not include content

Message ID Format

Message IDs are short alphanumeric strings (typically 6-12 characters) generated using a collision-resistant algorithm.

// Example ID generation (pseudocode)
function generateShortId(): String {
    return base64(sha256(timestamp + random + deviceId)).substring(0, 8)
}

Protocol Commands

s

Send

Send a new message


Usage: @i=<MSGID>;c=s <CONTENT>

Reference ID: Optional (null or omitted)

Example
@i=msg001;c=s Hello, how are you?
r

Reply

Reply to a specific message


Usage: @i=<MSGID>;c=r;r=<REFID> <CONTENT>

Reference ID: Required (ID of message being replied to)

Example
@i=msg002;c=r;r=msg001 I'm doing great, thanks!
e

Edit

Edit an existing message


Usage: @i=<MSGID>;c=e;r=<REFID> <NEW_CONTENT>

Reference ID: Required (ID of message being edited)

Only the original sender can edit messages

Example
@i=edit001;c=e;r=msg001 Hello, how are you doing?
d

Delete

Delete a message from both devices


Usage: @i=<MSGID>;c=d;r=<REFID>

Reference ID: Required (ID of message being deleted)

Content: Optional (typically omitted)

Only the original sender can delete messages

Example
@i=del001;c=d;r=msg001

Message Flow

Send Message Flow
1. User composes message
2. Generate unique message ID
3. Construct header: @i=<ID>;c=s <CONTENT>
4. Send via SMS
5. Receiver parses header and extracts content
6. Store in local database with message ID
Edit Message Flow
1. User selects message to edit
2. Generate new message ID for edit command
3. Construct header: @i=<NEW_ID>;c=e;r=<ORIGINAL_ID> <NEW_CONTENT>
4. Send via SMS
5. Receiver identifies edit command and reference ID
6. Update message content in local database

Implementation Details

Parsing Algorithm

fun parseQwishaMessage(body: String): QwishaMessage? {
    // Check if message starts with protocol identifier
    if (!body.startsWith("@")) {
        return null // Regular SMS
    }
    
    // Find header end (first space)
    val headerEnd = body.indexOf(' ')
    if (headerEnd == -1) {
        return parseHeaderOnly(body) // Delete command
    }
    
    // Extract header and content
    val header = body.substring(1, headerEnd)
    val content = body.substring(headerEnd + 1)
    
    // Parse header fields
    val fields = header.split(";").associate { part ->
        val (key, value) = part.split("=", limit = 2)
        key to value
    }
    
    return QwishaMessage(
        id = fields["i"] ?: return null,
        command = fields["c"] ?: return null,
        refId = fields["r"]?.takeIf { it != "null" && it.isNotEmpty() },
        content = content
    )
}

Message Construction

fun buildQwishaMessage(
    msgId: String,
    command: String,
    refId: String? = null,
    content: String
): String {
    val header = buildString {
        append("@i=$msgId;c=$command")
        if (refId != null) {
            append(";r=$refId")
        }
    }
    return "$header $content"
}

// Examples:
// buildQwishaMessage("abc123", "s", null, "Hello") 
// → "@i=abc123;c=s Hello"
//
// buildQwishaMessage("xyz789", "r", "abc123", "Hi!")
// → "@i=xyz789;c=r;r=abc123 Hi!"

Database Schema

@Entity
data class Message(
    @PrimaryKey val id: String,           // Message ID from protocol
    val threadId: String,                  // Phone number
    val content: String,                   // Message content
    val outgoing: Boolean,                 // Sent vs received
    val replyTo: String?,                  // Reference ID for replies
    val status: String,                    // sent, delivered, read, failed
    val timestamp: Long,                   // Unix timestamp
    val hasOverlayHeader: Boolean = false  // Protocol message flag
)

Error Handling

Fallback Behavior
  • If header parsing fails, treat as regular SMS
  • If message ID is missing, generate a new one
  • If reference ID is invalid, ignore the reference
  • If edit/delete target doesn't exist, log and continue

Backward Compatibility

The protocol supports both new single-letter commands (s, r, e, d) and legacy full-word commands (send, reply, edit, delete) for backward compatibility.

// Both formats are accepted:
@i=abc123;c=s Hello // New format
@i=abc123;c=send Hello // Legacy format

Security Considerations

Current Limitations
  • No message authentication by default
  • Edit/delete commands can be spoofed
  • No end-to-end encryption
  • Message IDs are predictable
Future Enhancements
  • HMAC signatures for command verification
  • End-to-end encryption support
  • Cryptographically secure message IDs
  • Message authentication codes (MAC)