---
# System prepended metadata

title: Direct Messaging Zome

---

# Direct Messaging Zome

## User Stories
- As a user, I want to send a message to a contact so that I can deliver textual information to that user
- As a user, I want to reply to a specific message so that it would be clear to which message i'm replying to.
- As a user, I want to be able to search messages in a chat using a keyword so that I can easily find previously sent messages
- As a user, I want to be able to search messages in a chat using a keyword so that I can easily find previously sent messages
- As a user, I want to see the status of my messages so that I know if my message has been sent or if my messages has been seen (sending, sent, seen, error)
- As a user, I want to search messages by date so that I can pinpoint specific messages
- As a user, I want to delete my own message so that I can remove wrong sent messages.
- As a user, I want to edit a message I sent so that I can update information.

## Asynchronous node-to-node E2E messaging (with Holo)
- This pattern is inspired from [mailbox](https://forum.holochain.org/t/mailbox/1084) and [async node2node messaging](https://forum.holochain.org/t/asynchronous-private-messaging/1085) pattern
- We still need to implement E2E scheme for this. Currently, the de facto standard encryption protocol for synchronous/asynchronous messaging is the signal protocol. Simplified diagram can be found [here](https://hackmd.io/@tatssato/rk6ZZ5dNP)
- This will be fully functional with a Public Key Infrastructre like [DeepKey](https://developer.holochain.org/docs/guide/dpki/)
- TODO: Work on update/delete
- can private entries be linked in RSM? (test this)
- How to effectively segregate private messages entries in a single source chain?
- Is there any risk in leaving encrypted message stay on DHT forever?

```mermaid
sequenceDiagram
    participant AUI as Alice_UI
    participant ALD as Alice_Lobby_DNA
    participant BLD as Bobby_Lobby_DNA
    participant BUI as Bobby_UI
    participant LDHT as Lobby_DHT
    
    AUI-->>AUI: Signs the message (chaperone)
    AUI-->>ALD: call send_message
    ALD-->>ALD: commit the message in source chain (flag: not_received)
    alt DPKI available
        ALD-->>LDHT: get PubKey of Bobby through DPKI
        LDHT-->>ALD: send PubKey
    else DPKI not available
        alt online
            ALD-->>BLD: ask for PubKey
            BLD-->>ALD: send PubKey
        else offline
            ALD-->>AUI: Recipient offline
            AUI-->>AUI: prompt resend
        end
    end
    ALD-->>ALD: encrypt payload with bobby PubKey
    ALD-->>BLD: call_remote to call receive_message (check CAP token?)
    alt online
        BLD-->>BLD: check the provenance of payload against the author field
        alt provenance match
            BLD-->>BLD: commit the payload in the source chain.
            BLD-->>BUI: Send the encrypted entry
            BLD-->>ALD: send received flag: true
            ALD-->>ALD: link received flag to message payload (tentative)
            BUI-->>BUI: decrypt the payload (chaperone)
            BUI-->>BUI: render message
        else provenance mismatch
            BLD-->>ALD: provenance mismatch
            ALD-->>AUI: send failed (provenance mismatch)
            AUI-->>AUI: prompt resend
        end
    else offline
        BLD-->>ALD: recepient offline
        ALD-->>LDHT: commit encrypted message payload and link to Bobby Agent ID
        BUI-->>BLD: call fetch_messages when back online
        BLD-->>LDHT: get messages
        LDHT-->>BLD: messages
        BLD-->>BLD: commit messages to source chain
        BLD-->>LDHT: remove links from agent_id to message (Is this enough?)
        BLD-->>BUI: messages
        BUI-->>BUI: render message
    end
```



## Entry Structure
```rust
// This could be considered as the genesis of a conversation between 2 agents.
// This will only be committed synchronously to the receving agent
// with call_remote to avoid leaving traces of social connection in DHT.
// If the agent's source chain is hosted on Holo, then there is at least a 98.6%
// for synchronous remote call to work.
// Counter signing this entry might be helpful to ensure authenticity.
// QUESTION: Should we have this? Or just have have genesis field
// in incoming and outgoing message?
#[hdk_entry(id="conversation", visbility="private")]
struct Conversation {
    creator: AgentPubKey,
    conversant: AgentPubKey,
    id: String,
    first_message_address: Option<EntryHash>
    timestamp: Timestamp,
}

let conversation = Conversation::new();
let outgoing_message = OugoingMessage::new();
create_link!(conversation, outgoing_message);

1. if conv exist {
 let link = get_link(hash_entry!(&conversation));
 let first_msg = get!(link.target);
 let first_msg_hash = hash!(first_msg)
 let new_message = IncomingMessage {
     previous_message_address: Some(first_msg_hash)
 }
 create_link!(first_msg_hash, new_message_hash)
 create_link!(conversation, new_message, 1);
}

2. 

// This will be an entry that will be committed to the DHT in case the
// receiver is offline.
// This entry is going to be encrypted and signed with 
// frequently rotating receiver's signing key and encryption key respectively.
#[hdk_entry(id="outgoing_message", visibility="public")]
struct OutgoingMessage {
    conversation_address: EntryHash,
    receiver: AgentPubKey,
    // if None, then this is the first message.
    previous_message_address: Option<EntryHash>,
    conversation_address: Option<EntryHash>
    // will be true if call_remote works,
    // if committed to DHT then it will be false
    // synchronous: bool,
    payload: String,
    timestamp: Timestamp,
}
Links: {
    // gets deleted once fetch is done
    receiving_agent_pubkey->outgoing_message|mailbox|
}

/* IGNORE
// This will be the entry that will be replicated from 
// OutgoingMessage and committed privately
// in the receiver's source chain.
// For every IncomingMessage there should be an OutgoingMessage.
*/

#[hdk_entry(id="incoming_message", visibility="private")]
struct IncomingMessage {
    conversation_address: EntryHash,
    sender: AgentPubKey,
    // Will be None if received synchronously
    // if None then synchronous should be true
    // outgoing_message_address: Option<EntryHash>,
    previous_message_address: Option<EntryHash>,
    conversation_address: Option<EntryHash>,
    // Should be false if outgoing_message_address is true 
    // synchronous: bool,
    timestamp: Timestamp,
    payload: String,
}
```

## Entry Relationship Diagram
```mermaid
graph TD
    subgraph dm zome
      subgraph agents
        alice_pubkey
        bobby_pubkey
      end
      subgraph conversation
        alice_bobby_conversation -.->|creator| alice_pubkey
        alice_bobby_conversation -.->|conversant| bobby_pubkey
      end
      subgraph outgoing_message
        outgoing_message_to_bobby_#1 -.-> alice_bobby_conversation
        outgoing_message_to_bobby_#1 -.-> alice_pubkey
        outgoing_message_to_bobby_#1 -.-> bobby_pubkey
        bobby_pubkey --> |mailbox - deleted once fetch|outgoing_message_to_bobby_#1
      end
      subgraph incoming_message
        incoming_message_from_alice_#1 -.-> alice_pubkey
        incoming_message_from_alice_#1 -.-> bobby_pubkey
        incoming_message_from_alice_#1 -.-> alice_bobby_conversation
        incoming_message_from_alice_#1 -.-> |counterpart|outgoing_message_to_bobby_#1
        incoming_message_from_bobby_#2 -.-> alice_pubkey
        incoming_message_from_bobby_#2 -.-> bobby_pubkey
        incoming_message_from_bobby_#2 -.-> |conversation|alice_bobby_conversation
        incoming_message_from_bobby_#2 -.-> |previous_message|outgoing_message_to_bobby_#1
        incoming_message_from_bobby_#2 -.-> incoming_message_from_alice_#1
      end
    end
```
## Validation
### Entries
### Links 