---
# System prepended metadata

title: DWN Sync Proposal

---

# DWN Sync Proposal

Synchronizing a tenant's data across more than 1 DWN can be achieved by introducing three mechanisms:
* An append-only event log
* The ability to get events from event log
* the ability to get a message by CID

using the above mechanisms in addition to existing ones we can break sync into 2 phases:
* **Push**: here are all the messages i have for you
* **Pull**: give me all the messages i haven't already gotten from you

## Event Log
Within the context of a DWN, An append-only event log is simply a ordered K/V store where the key is an ordinal number and the value is the CID of a message. 

Each DWN would maintain it's own individual event log. There's no requirement for an individual DWN's event log to be in the same order as another DWN

Whenever a message is stored by a DWN, the CID of that message is added to its event log.

## `EventsGet`

message type used to get events from a DWN

### Message Contents

| property name   | type              | required (y/n) | description                                                        |
| --------------- | ----------------- | -------------- | ------------------------------------------------------------------ |
| `method`        | const `EventsGet` | Y              |                                                                    |
| `watermark`     | string            | N              | providing no watermark returns all events for a given tenant       |
| `authorization` | GeneralJws        | Y              | same expected authorization format required by other DWN messages. |


### Response
all events that occurred between the watermark and the time at which the message is processed
[`MessageReply`](https://github.com/TBD54566975/dwn-sdk-js/blob/main/src/core/message-reply.ts#L13) where `entries` is an array of event objects

### Notes
* Should not be usable in protocol definitions


## `MessagesGet`

message typed used to get a message by CID from a DWN

### Message Contents

| property name   | type                | required (y/n) | description                                                        |
| --------------- | ------------------- | -------------- | ------------------------------------------------------------------ |
| `method`        | const `MessagesGet` | Y              |                                                                    |
| `cid`           | string              | Y              | the CID of the message you want                                    | 
| `authorization` | GeneralJws          | Y              | same expected authorization format required by other DWN messages. |

## Pull
Introducing `EventsGet` and `MessagesGet` makes it possible to pull all messages from a given DWN

```mermaid
sequenceDiagram
autonumber

participant A as DWN A
participant B as DWN B

A->>A: grab pull-specific watermark for DWN B
A->>B: EventsGet{ watermark, tenant_sig }
B->>B: fetch all events between watermark and now
B->>A: [event_id:cid, event_id:cid, ...]
loop each event
    A->>A: message = MessagesGet{ cid, tenant_sig }
    alt if message
    else else
        A->>B: MessagesGet{ cid, tenant_sig }
        B->>B: inflate message
        B->>A: message
        A->>A: store message
    end
    
    A->>A: update watermark
end
```

## Push
introducing `EventsGet` and `MessagesGet` allows a DWN to _push_ messages to another DWN

```mermaid
sequenceDiagram
autonumber

participant A as DWN A
participant B as DWN B

A->>A: grab push-specific watermark for DWN B
A->>A: EventsGet{watermark, tenant_sig}
loop each event
    A->>A: MessagesGet{ cid, tenant_sig }
    A->>B: message
    B->>B: process message
    A->>A: update watermark
end
```

## Considerations
* the only DWNs that can be synced (aka _pushed to_ and _pulled from_) are DWNs listed in a DID Doc. 
* This approach requires that a syncing DWN maintain a push-watermark and pull-watermark for each DWN it wants to push to and pull from
* There's a "sync echo" that inevitably occurs with the above approach. Imagine a scenario where there's DWN A and DWN B. They've never synced before. DWN B is empty. DWN A has 1 message in it. 
    * DWN A _pushes_ the message it has to DWN B
    * DWN B receives the message. stores it. event is added to event log
    * DWN A _pulls_ from DWN B. DWN B only has 1 message and it happens to be the message that was sent by DWN A. 
* introducing 2 additional message types: `SyncPush` and `SyncPull` as convenience messages that facilitate pushing and pulling introduces challenges:
    * both Push and Pull necessitate `EventsGet` and `MessagesGet` messages, both of which need to be signed which implies that keys need to be present
        * _Counter Argument_: I suppose the `authorization` JWS of `SyncPush` and `SyncPull` could could have a wrapped signature that could be used for any resulting `EventsGet` and `MessagesGet` messages. haven't fully thought this through yet
    * probably not much benefit of _sending_ `SyncPush` and `SyncPull` messages to your other DWNs. I guess it'd be a way to manually trigger your addressable DWNs to _push_ or _pull_
    * would have to decide whether the SDK wants to take on the responsibility of storing watermarks
        * or make `watermark` a required property
* If `SyncPush` and `SyncPull` don't make sense as new message types, we could always implement them as methods that are surfaced for consumption by `dwn-sdk-js`

## PoC
the chat-app demo in web5-labs is driven by a PoC of this proposed approach. 
* [message-store-level-v2](https://github.com/tbdeng/web5-labs/tree/main/message-store-level-v2) implements an event log. This message-store is used in [web5-wallet](https://github.com/tbdeng/web5-labs/blob/main/web5-wallet/background/dwn.js) and [addressable-dwn](https://github.com/tbdeng/web5-labs/blob/main/addressable-dwn/src/dwn.js#L2)
    * this would get implemented into [`MessageStoreLevel`](https://github.com/TBD54566975/dwn-sdk-js/blob/main/src/store/message-store-level.ts) in dwn-sdk-js
* [addressable-dwn](https://github.com/tbdeng/web5-labs/blob/main/addressable-dwn/src/app.js#L19-L34) exposes an HTTP endpoint `/dwn/event-log` as a workaround for `EventsGet` not existing. 
    * * this PoC endpoint **does not** have any authn/authz verification as it's just a PoC
* [addressable-dwn](https://github.com/tbdeng/web5-labs/blob/main/addressable-dwn/src/app.js#L36-L60) exposes an HTTP endpoint `/dwn/messages/:cid` as a workarond for `MessagesGet` not existing
    * this PoC endpoint **does not** have any authn/authz verification as it's just a PoC
* [`push`](https://github.com/tbdeng/web5-labs/blob/main/web5-wallet/background/alarm-handlers/push-dwn-messages.js) and [`pull`](https://github.com/tbdeng/web5-labs/blob/main/web5-wallet/background/alarm-handlers/pull-dwn-messages.js) are implemented in `web5-wallet`. they run once every minute (frequency is limited by browser extension constraints)