# 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)