adding peer/contactcard attributes
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
ycc
2026-03-03 10:46:25 +01:00
parent 8836d5c591
commit fab5818ec7
7 changed files with 546 additions and 194 deletions

View File

@@ -64,7 +64,7 @@ func CreateAndStoreUserMessage(message string, peer_uid string, replyToUid strin
} }
} }
usermessage.Status.Sent = uint64(time.Now().UTC().Unix()) usermessage.Status.Sent = uint64(time.Now().UTC().Unix())
usermessage.Status.AnswerToUuid = replyToUid usermessage.Status.ReplyToUuid = replyToUid
// Store message // Store message
err = peer.StoreMessage(usermessage, nil) err = peer.StoreMessage(usermessage, nil)

View File

@@ -26,6 +26,7 @@ type Peer struct {
MyIdentity *meowlib.KeyPair `json:"my_identity,omitempty"` MyIdentity *meowlib.KeyPair `json:"my_identity,omitempty"`
MyEncryptionKp *meowlib.KeyPair `json:"my_encryption_kp,omitempty"` MyEncryptionKp *meowlib.KeyPair `json:"my_encryption_kp,omitempty"`
MyLookupKp *meowlib.KeyPair `json:"my_lookup_kp,omitempty"` MyLookupKp *meowlib.KeyPair `json:"my_lookup_kp,omitempty"`
MySymKey string `json:"my_sym_key,omitempty"`
MyPullServers []string `json:"my_pull_servers,omitempty"` MyPullServers []string `json:"my_pull_servers,omitempty"`
// Peer keys and infos // Peer keys and infos
//Contact meowlib.ContactCard `json:"contact,omitempty"` // todo : remove //Contact meowlib.ContactCard `json:"contact,omitempty"` // todo : remove
@@ -40,6 +41,8 @@ type Peer struct {
LastMessage *InternalUserMessage `json:"last_message,omitempty"` LastMessage *InternalUserMessage `json:"last_message,omitempty"`
// Internal management attributes // Internal management attributes
Visible bool `json:"visible,omitempty"` Visible bool `json:"visible,omitempty"`
SendDeliveryAck bool `json:"send_delivery_ack,omitempty"`
SendProcessingAck bool `json:"send_processing_ack,omitempty"`
VisiblePassword string `json:"visible_password,omitempty"` VisiblePassword string `json:"visible_password,omitempty"`
PasswordType string `json:"password_type,omitempty"` PasswordType string `json:"password_type,omitempty"`
Blocked bool `json:"blocked,omitempty"` Blocked bool `json:"blocked,omitempty"`

View File

@@ -0,0 +1,7 @@
@startuml
ClientFdThread -> Lib : write poll job list
ClientFdThread -> ClientBgThread : notify job ?
ClientBgThread -> Lib : poll for servers
ClientBgThread -> ClientFdThread : notify message here
ClientFdThread -> Lib : Read redeived message and update db
@enduml

View File

@@ -0,0 +1,7 @@
@startuml
ClientFdThread -> Lib : write msg to db, encrypted msg for user to file, and job file
ClientFdThread -> ClientBgThread : notify job
ClientBgThread -> Lib : encrypt for server(s) and send including retries
ClientBgThread -> Lib: notify send result
ClientFdThread -> Lib : Read job report and update db
@enduml

View File

@@ -0,0 +1,314 @@
# Multi-Device Conversation Sync — Implementation Plan
## Context
meowlib already has scaffolding for multi-device sync:
| Existing artefact | Where |
|---|---|
| `Identity.Device *KeyPair` | `client/identity.go:35` |
| `Identity.OwnedDevices PeerList` | `client/identity.go:40` |
| `Peer.Type string` | `client/peer.go:52` |
| `ToServerMessage.device_messages` (field 10) | `pb/messages.proto:75` |
| `FromServerMessage.device_messages` (field 9) | `pb/messages.proto:99` |
| `BackgroundJob.Device *KeyPair` | `client/identity.go:334` |
The server (`server/router.go`) does **not** yet implement `device_messages` routing; it goes through `messages`/`Chat` today.
---
## Chosen Sync Scheme: Event-Driven Delta Sync over Existing Message Infrastructure
### Rationale
| Approach | Pros | Cons | Verdict |
|---|---|---|---|
| Full DB sync | Complete history | Huge payloads, merge conflicts, wasteful | ❌ |
| Inbox/outbox file sharing | Simple to reason about | File-level granularity, no dedup, breaks privacy model | ❌ |
| **Event-driven delta sync** | Minimal data, no merge needed, reuses existing crypto + server stack | Requires dedup table | ✅ |
Each message event (received, sent, status change) is forwarded immediately to sibling devices through the **same server infrastructure** as regular peer messages. Each device maintains its own complete local DB. Convergence is eventual; dedup via `ConversationStatus.Uuid`.
### Key Design Decisions
1. **Zero server changes required.** Device sync messages are addressed to the sibling device's lookup key and travel through the existing `msg:{lookup_key}` Redis sorted-set on the server, returned in `from_server.Chat` — identical to peer messages.
2. **Device peers reuse the `Peer` struct** with `Type = "device"`, stored in `Identity.OwnedDevices`. They have their own three keypairs (`MyIdentity`, `MyEncryptionKp`, `MyLookupKp`) and `MyPullServers`.
3. **A new proto message `DeviceSyncPayload`** is added to `messages.proto`. It is serialised and placed in `UserMessage.Appdata`; the parent `UserMessage.Type` is set to `"device_sync"`. This lets the client recognise sync messages without any server-side awareness.
4. **`GetRequestJobs()`** is extended to include device lookup keys alongside peer lookup keys for the appropriate servers, so the background poll thread picks up device sync messages without any extra call.
5. **Dedup** is handled by a small SQLite table `device_sync_seen` (one table per identity folder, not per peer) keyed on `DeviceSyncPayload.DedupId`.
---
## New Protobuf Message
Add to `pb/messages.proto` before re-generating:
```protobuf
// Payload carried inside UserMessage.appdata for device-to-device sync.
// The enclosing UserMessage.type MUST be "device_sync".
message DeviceSyncPayload {
string sync_type = 1; // "msg" | "status" | "peer_event"
string peer_uid = 2; // local UID of the peer conversation on the sending device
DbMessage db_message = 3; // the DbMessage to replicate
string dedup_id = 4; // globally unique ID (= DbMessage.status.uuid or generated)
}
```
Run `cd pb && ./protogen.sh` after adding this.
---
## Implementation Phases
### Phase 1 — Device Pairing
**Files to touch:** `client/identity.go`, `client/helpers/` (new file `deviceHelper.go`)
**Goal:** Allow two app instances owned by the same user to establish a shared keypair relationship, mirroring the peer invitation flow but flagging the peer as `Type = "device"`.
#### 1.1 `Identity.InitDevicePairing(myDeviceName string, serverUids []string) (*Peer, error)`
- Identical to `InvitePeer` but sets `peer.Type = "device"`.
- Stores the resulting peer in `Identity.OwnedDevices` (not `Peers`).
- Returns the peer so the caller can produce a `ContactCard` QR/file.
#### 1.2 `Identity.AnswerDevicePairing(myDeviceName string, receivedContact *meowlib.ContactCard) (*Peer, error)`
- Mirrors `AnswerInvitation`, stores in `OwnedDevices`.
#### 1.3 `Identity.FinalizeDevicePairing(receivedContact *meowlib.ContactCard) error`
- Mirrors `FinalizeInvitation`, operates on `OwnedDevices`.
#### 1.4 Helper functions (new file `client/helpers/deviceHelper.go`)
```go
// DevicePairingCreateMessage wraps an invitation step-1 for a device peer.
func DevicePairingCreateMessage(peer *client.Peer, serverUid string) ([]byte, string, error)
// DevicePairingAnswerMessage wraps invitation step-3 answer for a device peer.
func DevicePairingAnswerMessage(peer *client.Peer, serverUid string) ([]byte, string, error)
```
These reuse `invitationCreateHelper.go`/`invitationAnswerHelper.go` logic.
#### 1.5 Extend `PeerStorage` operations for OwnedDevices
`OwnedDevices` is currently a `PeerList` (in-memory slice). For scalability it should use the same Badger-backed `PeerStorage` mechanism as `Peers`. Consider adding a second `PeerStorage` field `DeviceStorage` to `Identity` with its own `DbFile`.
---
### Phase 2 — Sync Payload Helpers
**Files to touch:** `client/helpers/deviceHelper.go` (continued), `client/dbmessage.go`
#### 2.1 Build a sync message for one sibling device
```go
// BuildDeviceSyncMessage wraps a DbMessage into a UserMessage addressed to a
// sibling device peer. The caller then calls peer.ProcessOutboundUserMessage.
func BuildDeviceSyncMessage(
devicePeer *client.Peer,
syncType string, // "msg" | "status" | "peer_event"
peerUid string,
dbm *meowlib.DbMessage,
dedupId string,
) (*meowlib.UserMessage, error)
```
Implementation:
1. Serialise `DeviceSyncPayload{SyncType, PeerUid, DbMessage, DedupId}` with `proto.Marshal`.
2. Create a `UserMessage` with `Type = "device_sync"`, `Destination = devicePeer.ContactLookupKey`, `Appdata = serialisedPayload`.
3. Set `Status.Uuid = dedupId`.
#### 2.2 Dispatch sync to all sibling devices
```go
// DispatchSyncToDevices sends a DeviceSyncPayload to every device peer whose
// pull server list overlaps with the available servers.
// It enqueues a SendJob per device, reusing the existing bgSendHelper queue.
func DispatchSyncToDevices(
storagePath string,
syncType string,
peerUid string,
dbm *meowlib.DbMessage,
dedupId string,
) error
```
Iterates `identity.OwnedDevices`, builds and queues one `SendJob` per device (just like `CreateUserMessageAndSendJob` but using device peer keys and putting the message in `outbox/` with a recognisable prefix, e.g. `dev_{devPeerUid}_{dedupId}`).
The message is packed into `ToServerMessage.Messages` (same field as regular chat). No server changes needed.
---
### Phase 3 — Integrate Dispatch into Send/Receive Paths
**Files to touch:** `client/helpers/messageHelper.go`, `client/helpers/bgPollHelper.go`
#### 3.1 After outbound message stored (`CreateAndStoreUserMessage`)
At the end of `CreateAndStoreUserMessage` (after `peer.StoreMessage`), add:
```go
// Async: do not block the caller
go DispatchSyncToDevices(storagePath, "msg", peerUid, dbm, usermessage.Status.Uuid)
```
The `dbm` is obtained from `UserMessageToDbMessage(true, usermessage, nil)` (files are excluded from sync — they stay on the originating device or are re-requested).
#### 3.2 After inbound message stored (`ConsumeInboxFile`)
After `peer.StoreMessage(usermsg, filenames)` succeeds:
```go
dbm := client.UserMessageToDbMessage(false, usermsg, nil)
go DispatchSyncToDevices(storagePath, "msg", peer.Uid, dbm, usermsg.Status.Uuid)
```
#### 3.3 After ACK status update (`ReadAckMessageResponse` — currently a stub)
When status timestamps (received/processed) are updated in the DB, dispatch a `"status"` sync with the updated `DbMessage`.
---
### Phase 4 — Receive & Consume Device Sync Messages
**Files to touch:** `client/helpers/bgPollHelper.go`, new `client/helpers/deviceSyncHelper.go`
#### 4.1 Extend `GetRequestJobs()` to include device lookup keys
In `identity.go:GetRequestJobs()`, after the loop over `Peers`, add a similar loop over `OwnedDevices`:
```go
for _, devPeer := range id.OwnedDevices {
for _, server := range devPeer.MyPullServers {
if job, ok := srvs[server]; ok {
job.LookupKeys = append(job.LookupKeys, devPeer.MyLookupKp)
}
}
}
```
Device messages will now arrive inside `from_server.Chat` alongside regular peer messages. The next step distinguishes them.
#### 4.2 Distinguish device vs peer messages in `ConsumeInboxFile`
After `identity.Peers.GetFromMyLookupKey(packedUserMessage.Destination)` returns `nil`, try:
```go
devPeer := identity.OwnedDevices.GetFromMyLookupKey(packedUserMessage.Destination)
if devPeer != nil {
err := ConsumeDeviceSyncMessage(devPeer, packedUserMessage)
// continue to next message
continue
}
// original error path
```
#### 4.3 `ConsumeDeviceSyncMessage` (new file `client/helpers/deviceSyncHelper.go`)
```go
func ConsumeDeviceSyncMessage(
devPeer *client.Peer,
packed *meowlib.PackedUserMessage,
) error
```
Steps:
1. Decrypt with `devPeer.ProcessInboundUserMessage(packed.Payload, packed.Signature)`.
2. Check `usermsg.Type == "device_sync"`.
3. Deserialise `DeviceSyncPayload` from `usermsg.Appdata`.
4. Dedup check: call `IsDeviceSyncSeen(payload.DedupId)`. If yes, skip.
5. Mark seen: `MarkDeviceSyncSeen(payload.DedupId)`.
6. Dispatch by `payload.SyncType`:
- `"msg"`: find the local peer by `payload.PeerUid`, call `client.StoreDeviceSyncedMessage(peer, payload.DbMessage)`.
- `"status"`: update the status fields in the existing DB row matched by `payload.DbMessage.Status.Uuid`.
- `"peer_event"`: (future) update peer metadata.
#### 4.4 `StoreDeviceSyncedMessage` in `client/messagestorage.go`
A thin wrapper around `storeMessage` that:
- Marks the message as synced (a new bool field `Synced` in `DbMessage`, or use a naming convention in `DbMessage.Appdata`).
- Does **not** trigger a second round of sync dispatch (no re-broadcast).
- Handles absent file paths gracefully (files are not synced, only metadata).
---
### Phase 5 — Dedup Store
**Files to touch:** new `client/devicesyncdedup.go`
A single SQLite DB per identity folder: `{StoragePath}/{IdentityUuid}/devicesync.db`.
Schema:
```sql
CREATE TABLE IF NOT EXISTS seen (
id TEXT NOT NULL PRIMARY KEY,
seen_at INTEGER NOT NULL
);
```
Functions:
```go
func IsDeviceSyncSeen(storagePath, identityUuid, dedupId string) (bool, error)
func MarkDeviceSyncSeen(storagePath, identityUuid, dedupId string) error
func PruneDeviceSyncSeen(storagePath, identityUuid string, olderThan time.Duration) error
```
`PruneDeviceSyncSeen` is called periodically (e.g. weekly) from the background thread to remove entries older than 30 days.
---
## File Change Summary
| File | Change |
|---|---|
| `pb/messages.proto` | Add `DeviceSyncPayload` message |
| `pb/protogen.sh` → re-run | Regenerate `.pb.go` |
| `client/identity.go` | Add `InitDevicePairing`, `AnswerDevicePairing`, `FinalizeDevicePairing`; extend `GetRequestJobs()` |
| `client/peer.go` | No changes needed (Type field already exists) |
| `client/messagestorage.go` | Add `StoreDeviceSyncedMessage` |
| `client/devicesyncdedup.go` | **New** — dedup SQLite helpers |
| `client/helpers/deviceHelper.go` | **New**`BuildDeviceSyncMessage`, `DispatchSyncToDevices`, pairing message helpers |
| `client/helpers/deviceSyncHelper.go` | **New**`ConsumeDeviceSyncMessage` |
| `client/helpers/messageHelper.go` | Add `DispatchSyncToDevices` call after outbound store |
| `client/helpers/bgPollHelper.go` | Add device message detection in `ConsumeInboxFile` |
Server package: **no changes required**.
---
## Sync Scope
| Data | Synced | Notes |
|---|---|---|
| Message text / data | ✅ | In `DbMessage.Data` |
| Outbound flag | ✅ | In `DbMessage.Outbound` |
| Message UUID | ✅ | Via `ConversationStatus.Uuid` |
| Sent/received timestamps | ✅ | In `ConversationStatus` |
| File content | ❌ | Not synced; only `FilePaths` metadata synced |
| Peer metadata (keys, servers) | ❌ | Phase 2+ scope |
| Identity blob | ❌ | Out of scope; handled by manual export |
---
## Privacy Properties
- Device sync messages are end-to-end encrypted (same X25519 + PGP as peer messages).
- The server sees only the device lookup key as destination; it has no knowledge this is a sync vs a peer message.
- Including device lookup keys in batch pull requests does not leak which other device belongs to you (same privacy model as multiple peer lookup keys per request).
- `OwnedDevices` peers should be considered "hidden" (not shown in contact lists) and can optionally be stored in the hidden peer store.
---
## Testing Strategy
1. **Unit tests** for `DeviceSyncPayload` serialisation round-trip.
2. **Unit tests** for dedup store (seen/mark/prune lifecycle).
3. **Integration test** extending `TestEndToEnd`:
- Create identity, two device peers (DeviceA, DeviceB).
- Send a message on DeviceA.
- Verify DeviceB's DB contains the synced message after `ConsumeDeviceSyncMessage`.
- Resend the same dedup_id — verify no duplicate row created.
4. **Integration test** for inbound sync:
- DeviceA receives a peer message.
- Verify DeviceB gets the sync and stores it correctly.

View File

@@ -668,10 +668,10 @@ func (x *FromServerMessage) GetContactCard() []*ContactCard {
type MatriochkaServer struct { type MatriochkaServer struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // Server Url Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // Server Url
PublicKey string `protobuf:"bytes,2,opt,name=publicKey,proto3" json:"publicKey,omitempty"` // Server Public Key PublicKey string `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // Server Public Key
Uuid string `protobuf:"bytes,3,opt,name=uuid,proto3" json:"uuid,omitempty"` // Optional, uuid for delivery confirmation Uuid string `protobuf:"bytes,3,opt,name=uuid,proto3" json:"uuid,omitempty"` // Optional, uuid for delivery confirmation
Delay int32 `protobuf:"varint,4,opt,name=delay,proto3" json:"delay,omitempty"` // Max delay requested for message forwarding or delivery tracking Delay int32 `protobuf:"varint,4,opt,name=delay,proto3" json:"delay,omitempty"` // Max delay requested for message forwarding or delivery tracking
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -736,10 +736,10 @@ func (x *MatriochkaServer) GetDelay() int32 {
type Matriochka struct { type Matriochka struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
LookupKey string `protobuf:"bytes,1,opt,name=lookupKey,proto3" json:"lookupKey,omitempty"` // Optional, only if you want delivery tracking, less stealth LookupKey string `protobuf:"bytes,1,opt,name=lookup_key,json=lookupKey,proto3" json:"lookup_key,omitempty"` // Optional, only if you want delivery tracking, less stealth
Prev *MatriochkaServer `protobuf:"bytes,2,opt,name=prev,proto3" json:"prev,omitempty"` // Optional, like above Prev *MatriochkaServer `protobuf:"bytes,2,opt,name=prev,proto3" json:"prev,omitempty"` // Optional, like above
Next *MatriochkaServer `protobuf:"bytes,3,opt,name=next,proto3" json:"next,omitempty"` // Next server to deliver the message to Next *MatriochkaServer `protobuf:"bytes,3,opt,name=next,proto3" json:"next,omitempty"` // Next server to deliver the message to
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` // Matriochka data Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` // Matriochka data
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -902,10 +902,11 @@ type ContactCard struct {
ContactPublicKey string `protobuf:"bytes,2,opt,name=contact_public_key,json=contactPublicKey,proto3" json:"contact_public_key,omitempty"` // contact public key, will be used to authenticate her/his messages ContactPublicKey string `protobuf:"bytes,2,opt,name=contact_public_key,json=contactPublicKey,proto3" json:"contact_public_key,omitempty"` // contact public key, will be used to authenticate her/his messages
EncryptionPublicKey string `protobuf:"bytes,3,opt,name=encryption_public_key,json=encryptionPublicKey,proto3" json:"encryption_public_key,omitempty"` // public key you must use to to write encrypted messages to that contact EncryptionPublicKey string `protobuf:"bytes,3,opt,name=encryption_public_key,json=encryptionPublicKey,proto3" json:"encryption_public_key,omitempty"` // public key you must use to to write encrypted messages to that contact
LookupPublicKey string `protobuf:"bytes,4,opt,name=lookup_public_key,json=lookupPublicKey,proto3" json:"lookup_public_key,omitempty"` // public key you will use as "destination identifier" for her/him to lookup for your messages on the servers LookupPublicKey string `protobuf:"bytes,4,opt,name=lookup_public_key,json=lookupPublicKey,proto3" json:"lookup_public_key,omitempty"` // public key you will use as "destination identifier" for her/him to lookup for your messages on the servers
PullServers []*ServerCard `protobuf:"bytes,5,rep,name=pull_servers,json=pullServers,proto3" json:"pull_servers,omitempty"` // list the servers where the contact will look for messages from you SymetricKey string `protobuf:"bytes,5,opt,name=symetric_key,json=symetricKey,proto3" json:"symetric_key,omitempty"` // agreed key for payload symetric encryption
Version uint32 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` PullServers []*ServerCard `protobuf:"bytes,6,rep,name=pull_servers,json=pullServers,proto3" json:"pull_servers,omitempty"` // list the servers where the contact will look for messages from you
InvitationId string `protobuf:"bytes,7,opt,name=invitation_id,json=invitationId,proto3" json:"invitation_id,omitempty"` Version uint32 `protobuf:"varint,7,opt,name=version,proto3" json:"version,omitempty"`
InvitationMessage string `protobuf:"bytes,8,opt,name=invitation_message,json=invitationMessage,proto3" json:"invitation_message,omitempty"` InvitationId string `protobuf:"bytes,8,opt,name=invitation_id,json=invitationId,proto3" json:"invitation_id,omitempty"`
InvitationMessage string `protobuf:"bytes,9,opt,name=invitation_message,json=invitationMessage,proto3" json:"invitation_message,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -968,6 +969,13 @@ func (x *ContactCard) GetLookupPublicKey() string {
return "" return ""
} }
func (x *ContactCard) GetSymetricKey() string {
if x != nil {
return x.SymetricKey
}
return ""
}
func (x *ContactCard) GetPullServers() []*ServerCard { func (x *ContactCard) GetPullServers() []*ServerCard {
if x != nil { if x != nil {
return x.PullServers return x.PullServers
@@ -1002,7 +1010,7 @@ type PackedUserMessage struct {
Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` // the peer's current conversation lookup public key Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` // the peer's current conversation lookup public key
Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` // the message UserMessage encrypted with the destination peer's public key Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` // the message UserMessage encrypted with the destination peer's public key
Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` // the payload signature with the client identity private key Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` // the payload signature with the client identity private key
ServerTimestamp []int64 `protobuf:"varint,4,rep,packed,name=serverTimestamp,proto3" json:"serverTimestamp,omitempty"` // server time stamp, might be several in matriochka mode ServerTimestamp []int64 `protobuf:"varint,4,rep,packed,name=server_timestamp,json=serverTimestamp,proto3" json:"server_timestamp,omitempty"` // server time stamp, might be several in matriochka mode
ServerDeliveryUuid string `protobuf:"bytes,5,opt,name=server_delivery_uuid,json=serverDeliveryUuid,proto3" json:"server_delivery_uuid,omitempty"` // message uuid, for server delivery tracking, omitted if not delivery tracking desired ServerDeliveryUuid string `protobuf:"bytes,5,opt,name=server_delivery_uuid,json=serverDeliveryUuid,proto3" json:"server_delivery_uuid,omitempty"` // message uuid, for server delivery tracking, omitted if not delivery tracking desired
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -1075,14 +1083,15 @@ func (x *PackedUserMessage) GetServerDeliveryUuid() string {
type ConversationStatus struct { type ConversationStatus struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` // uuid of message, or uuid of related message if uuid_action is not empty
AnswerToUuid string `protobuf:"bytes,2,opt,name=answer_to_uuid,json=answerToUuid,proto3" json:"answer_to_uuid,omitempty"` // message is an answer to another one, specify uuid here UuidAction int32 `protobuf:"varint,2,opt,name=uuid_action,json=uuidAction,proto3" json:"uuid_action,omitempty"` // empty => normal message, 1: receivedack, 2: processedack, 3:reaction
LocalSequence uint64 `protobuf:"varint,3,opt,name=localSequence,proto3" json:"localSequence,omitempty"` // seq number in local conversation for custom reordering ReplyToUuid string `protobuf:"bytes,3,opt,name=reply_to_uuid,json=replyToUuid,proto3" json:"reply_to_uuid,omitempty"` // this message replies to the specified uuid
Sent uint64 `protobuf:"varint,4,opt,name=sent,proto3" json:"sent,omitempty"` // timestamp of the message sent LocalSequence uint64 `protobuf:"varint,4,opt,name=local_sequence,json=localSequence,proto3" json:"local_sequence,omitempty"` // seq number in local conversation for custom reordering
Received uint64 `protobuf:"varint,5,opt,name=received,proto3" json:"received,omitempty"` // timestamp of the message received Sent uint64 `protobuf:"varint,5,opt,name=sent,proto3" json:"sent,omitempty"` // timestamp of the message sent
Processed uint64 `protobuf:"varint,6,opt,name=processed,proto3" json:"processed,omitempty"` // timestamp of the message processed Received uint64 `protobuf:"varint,6,opt,name=received,proto3" json:"received,omitempty"` // timestamp of the message received
MyNextIdentity *ContactCard `protobuf:"bytes,7,opt,name=my_next_identity,json=myNextIdentity,proto3" json:"my_next_identity,omitempty"` Processed uint64 `protobuf:"varint,7,opt,name=processed,proto3" json:"processed,omitempty"` // timestamp of the message processed
PeerNextIdentityAck int32 `protobuf:"varint,8,opt,name=peer_next_identityAck,json=peerNextIdentityAck,proto3" json:"peer_next_identityAck,omitempty"` // version of the new peer accepted id MyNextIdentity *ContactCard `protobuf:"bytes,8,opt,name=my_next_identity,json=myNextIdentity,proto3" json:"my_next_identity,omitempty"`
PeerNextIdentityAck int32 `protobuf:"varint,9,opt,name=peer_next_identity_ack,json=peerNextIdentityAck,proto3" json:"peer_next_identity_ack,omitempty"` // version of the new peer accepted id
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -1124,9 +1133,16 @@ func (x *ConversationStatus) GetUuid() string {
return "" return ""
} }
func (x *ConversationStatus) GetAnswerToUuid() string { func (x *ConversationStatus) GetUuidAction() int32 {
if x != nil { if x != nil {
return x.AnswerToUuid return x.UuidAction
}
return 0
}
func (x *ConversationStatus) GetReplyToUuid() string {
if x != nil {
return x.ReplyToUuid
} }
return "" return ""
} }
@@ -1234,7 +1250,7 @@ type UserMessage struct {
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
Status *ConversationStatus `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` Status *ConversationStatus `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"`
Contact *ContactCard `protobuf:"bytes,6,opt,name=contact,proto3" json:"contact,omitempty"` Contact *ContactCard `protobuf:"bytes,6,opt,name=contact,proto3" json:"contact,omitempty"`
KnownServers *ServerCard `protobuf:"bytes,7,opt,name=knownServers,proto3" json:"knownServers,omitempty"` KnownServers *ServerCard `protobuf:"bytes,7,opt,name=known_servers,json=knownServers,proto3" json:"known_servers,omitempty"`
Group *Group `protobuf:"bytes,8,opt,name=group,proto3" json:"group,omitempty"` Group *Group `protobuf:"bytes,8,opt,name=group,proto3" json:"group,omitempty"`
Files []*File `protobuf:"bytes,9,rep,name=files,proto3" json:"files,omitempty"` Files []*File `protobuf:"bytes,9,rep,name=files,proto3" json:"files,omitempty"`
CurrentLocation *Location `protobuf:"bytes,10,opt,name=current_location,json=currentLocation,proto3" json:"current_location,omitempty"` CurrentLocation *Location `protobuf:"bytes,10,opt,name=current_location,json=currentLocation,proto3" json:"current_location,omitempty"`
@@ -1849,15 +1865,17 @@ const file_messages_proto_rawDesc = "" +
"\n" + "\n" +
"video_data\x18\n" + "video_data\x18\n" +
" \x01(\v2\x12.meowlib.VideoDataR\tvideoData\x127\n" + " \x01(\v2\x12.meowlib.VideoDataR\tvideoData\x127\n" +
"\fcontact_card\x18\v \x03(\v2\x14.meowlib.ContactCardR\vcontactCard\"l\n" + "\fcontact_card\x18\v \x03(\v2\x14.meowlib.ContactCardR\vcontactCard\"m\n" +
"\x10MatriochkaServer\x12\x10\n" + "\x10MatriochkaServer\x12\x10\n" +
"\x03url\x18\x01 \x01(\tR\x03url\x12\x1c\n" + "\x03url\x18\x01 \x01(\tR\x03url\x12\x1d\n" +
"\tpublicKey\x18\x02 \x01(\tR\tpublicKey\x12\x12\n" +
"\x04uuid\x18\x03 \x01(\tR\x04uuid\x12\x14\n" +
"\x05delay\x18\x04 \x01(\x05R\x05delay\"\x9c\x01\n" +
"\n" + "\n" +
"Matriochka\x12\x1c\n" + "public_key\x18\x02 \x01(\tR\tpublicKey\x12\x12\n" +
"\tlookupKey\x18\x01 \x01(\tR\tlookupKey\x12-\n" + "\x04uuid\x18\x03 \x01(\tR\x04uuid\x12\x14\n" +
"\x05delay\x18\x04 \x01(\x05R\x05delay\"\x9d\x01\n" +
"\n" +
"Matriochka\x12\x1d\n" +
"\n" +
"lookup_key\x18\x01 \x01(\tR\tlookupKey\x12-\n" +
"\x04prev\x18\x02 \x01(\v2\x19.meowlib.MatriochkaServerR\x04prev\x12-\n" + "\x04prev\x18\x02 \x01(\v2\x19.meowlib.MatriochkaServerR\x04prev\x12-\n" +
"\x04next\x18\x03 \x01(\v2\x19.meowlib.MatriochkaServerR\x04next\x12\x12\n" + "\x04next\x18\x03 \x01(\v2\x19.meowlib.MatriochkaServerR\x04next\x12\x12\n" +
"\x04data\x18\x04 \x01(\fR\x04data\"\xc3\x01\n" + "\x04data\x18\x04 \x01(\fR\x04data\"\xc3\x01\n" +
@@ -1870,42 +1888,45 @@ const file_messages_proto_rawDesc = "" +
"\x03url\x18\x04 \x01(\tR\x03url\x12\x14\n" + "\x03url\x18\x04 \x01(\tR\x03url\x12\x14\n" +
"\x05login\x18\x05 \x01(\tR\x05login\x12\x1a\n" + "\x05login\x18\x05 \x01(\tR\x05login\x12\x1a\n" +
"\bpassword\x18\x06 \x01(\tR\bpassword\x12\x1c\n" + "\bpassword\x18\x06 \x01(\tR\bpassword\x12\x1c\n" +
"\tsignature\x18\a \x01(\tR\tsignature\"\xd5\x02\n" + "\tsignature\x18\a \x01(\tR\tsignature\"\xf8\x02\n" +
"\vContactCard\x12\x12\n" + "\vContactCard\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12,\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12,\n" +
"\x12contact_public_key\x18\x02 \x01(\tR\x10contactPublicKey\x122\n" + "\x12contact_public_key\x18\x02 \x01(\tR\x10contactPublicKey\x122\n" +
"\x15encryption_public_key\x18\x03 \x01(\tR\x13encryptionPublicKey\x12*\n" + "\x15encryption_public_key\x18\x03 \x01(\tR\x13encryptionPublicKey\x12*\n" +
"\x11lookup_public_key\x18\x04 \x01(\tR\x0flookupPublicKey\x126\n" + "\x11lookup_public_key\x18\x04 \x01(\tR\x0flookupPublicKey\x12!\n" +
"\fpull_servers\x18\x05 \x03(\v2\x13.meowlib.ServerCardR\vpullServers\x12\x18\n" + "\fsymetric_key\x18\x05 \x01(\tR\vsymetricKey\x126\n" +
"\aversion\x18\x06 \x01(\rR\aversion\x12#\n" + "\fpull_servers\x18\x06 \x03(\v2\x13.meowlib.ServerCardR\vpullServers\x12\x18\n" +
"\rinvitation_id\x18\a \x01(\tR\finvitationId\x12-\n" + "\aversion\x18\a \x01(\rR\aversion\x12#\n" +
"\x12invitation_message\x18\b \x01(\tR\x11invitationMessage\"\xc9\x01\n" + "\rinvitation_id\x18\b \x01(\tR\finvitationId\x12-\n" +
"\x12invitation_message\x18\t \x01(\tR\x11invitationMessage\"\xca\x01\n" +
"\x11PackedUserMessage\x12 \n" + "\x11PackedUserMessage\x12 \n" +
"\vdestination\x18\x01 \x01(\tR\vdestination\x12\x18\n" + "\vdestination\x18\x01 \x01(\tR\vdestination\x12\x18\n" +
"\apayload\x18\x02 \x01(\fR\apayload\x12\x1c\n" + "\apayload\x18\x02 \x01(\fR\apayload\x12\x1c\n" +
"\tsignature\x18\x03 \x01(\fR\tsignature\x12(\n" + "\tsignature\x18\x03 \x01(\fR\tsignature\x12)\n" +
"\x0fserverTimestamp\x18\x04 \x03(\x03R\x0fserverTimestamp\x120\n" + "\x10server_timestamp\x18\x04 \x03(\x03R\x0fserverTimestamp\x120\n" +
"\x14server_delivery_uuid\x18\x05 \x01(\tR\x12serverDeliveryUuid\"\xb6\x02\n" + "\x14server_delivery_uuid\x18\x05 \x01(\tR\x12serverDeliveryUuid\"\xd7\x02\n" +
"\x12ConversationStatus\x12\x12\n" + "\x12ConversationStatus\x12\x12\n" +
"\x04uuid\x18\x01 \x01(\tR\x04uuid\x12$\n" + "\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x1f\n" +
"\x0eanswer_to_uuid\x18\x02 \x01(\tR\fanswerToUuid\x12$\n" + "\vuuid_action\x18\x02 \x01(\x05R\n" +
"\rlocalSequence\x18\x03 \x01(\x04R\rlocalSequence\x12\x12\n" + "uuidAction\x12\"\n" +
"\x04sent\x18\x04 \x01(\x04R\x04sent\x12\x1a\n" + "\rreply_to_uuid\x18\x03 \x01(\tR\vreplyToUuid\x12%\n" +
"\breceived\x18\x05 \x01(\x04R\breceived\x12\x1c\n" + "\x0elocal_sequence\x18\x04 \x01(\x04R\rlocalSequence\x12\x12\n" +
"\tprocessed\x18\x06 \x01(\x04R\tprocessed\x12>\n" + "\x04sent\x18\x05 \x01(\x04R\x04sent\x12\x1a\n" +
"\x10my_next_identity\x18\a \x01(\v2\x14.meowlib.ContactCardR\x0emyNextIdentity\x122\n" + "\breceived\x18\x06 \x01(\x04R\breceived\x12\x1c\n" +
"\x15peer_next_identityAck\x18\b \x01(\x05R\x13peerNextIdentityAck\"K\n" + "\tprocessed\x18\a \x01(\x04R\tprocessed\x12>\n" +
"\x10my_next_identity\x18\b \x01(\v2\x14.meowlib.ContactCardR\x0emyNextIdentity\x123\n" +
"\x16peer_next_identity_ack\x18\t \x01(\x05R\x13peerNextIdentityAck\"K\n" +
"\x05Group\x12\x12\n" + "\x05Group\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12.\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12.\n" +
"\amembers\x18\x02 \x03(\v2\x14.meowlib.ContactCardR\amembers\"\x94\x04\n" + "\amembers\x18\x02 \x03(\v2\x14.meowlib.ContactCardR\amembers\"\x95\x04\n" +
"\vUserMessage\x12 \n" + "\vUserMessage\x12 \n" +
"\vdestination\x18\x01 \x01(\tR\vdestination\x12\x12\n" + "\vdestination\x18\x01 \x01(\tR\vdestination\x12\x12\n" +
"\x04from\x18\x02 \x01(\tR\x04from\x12\x12\n" + "\x04from\x18\x02 \x01(\tR\x04from\x12\x12\n" +
"\x04type\x18\x03 \x01(\tR\x04type\x12\x12\n" + "\x04type\x18\x03 \x01(\tR\x04type\x12\x12\n" +
"\x04data\x18\x04 \x01(\fR\x04data\x123\n" + "\x04data\x18\x04 \x01(\fR\x04data\x123\n" +
"\x06status\x18\x05 \x01(\v2\x1b.meowlib.ConversationStatusR\x06status\x12.\n" + "\x06status\x18\x05 \x01(\v2\x1b.meowlib.ConversationStatusR\x06status\x12.\n" +
"\acontact\x18\x06 \x01(\v2\x14.meowlib.ContactCardR\acontact\x127\n" + "\acontact\x18\x06 \x01(\v2\x14.meowlib.ContactCardR\acontact\x128\n" +
"\fknownServers\x18\a \x01(\v2\x13.meowlib.ServerCardR\fknownServers\x12$\n" + "\rknown_servers\x18\a \x01(\v2\x13.meowlib.ServerCardR\fknownServers\x12$\n" +
"\x05group\x18\b \x01(\v2\x0e.meowlib.GroupR\x05group\x12#\n" + "\x05group\x18\b \x01(\v2\x0e.meowlib.GroupR\x05group\x12#\n" +
"\x05files\x18\t \x03(\v2\r.meowlib.FileR\x05files\x12<\n" + "\x05files\x18\t \x03(\v2\r.meowlib.FileR\x05files\x12<\n" +
"\x10current_location\x18\n" + "\x10current_location\x18\n" +
@@ -2015,7 +2036,7 @@ var file_messages_proto_depIdxs = []int32{
10, // 19: meowlib.Group.members:type_name -> meowlib.ContactCard 10, // 19: meowlib.Group.members:type_name -> meowlib.ContactCard
12, // 20: meowlib.UserMessage.status:type_name -> meowlib.ConversationStatus 12, // 20: meowlib.UserMessage.status:type_name -> meowlib.ConversationStatus
10, // 21: meowlib.UserMessage.contact:type_name -> meowlib.ContactCard 10, // 21: meowlib.UserMessage.contact:type_name -> meowlib.ContactCard
9, // 22: meowlib.UserMessage.knownServers:type_name -> meowlib.ServerCard 9, // 22: meowlib.UserMessage.known_servers:type_name -> meowlib.ServerCard
13, // 23: meowlib.UserMessage.group:type_name -> meowlib.Group 13, // 23: meowlib.UserMessage.group:type_name -> meowlib.Group
15, // 24: meowlib.UserMessage.files:type_name -> meowlib.File 15, // 24: meowlib.UserMessage.files:type_name -> meowlib.File
16, // 25: meowlib.UserMessage.current_location:type_name -> meowlib.Location 16, // 25: meowlib.UserMessage.current_location:type_name -> meowlib.Location

View File

@@ -1,13 +1,13 @@
// You should use the field numbers 1 through 15 // You should use the field numbers 1 through 15
// for the most-frequently-set fields. // for the most-frequently-set fields.
// Lower field number values take less space in the wire format. // Lower field number values take less space in the wire format.
// For example, field numbers in the range 1 through 15 take one byte to encode. // For example, field numbers in the range 1 through 15 take one byte to encode.
// Field numbers in the range 16 through 2047 take two bytes. // Field numbers in the range 16 through 2047 take two bytes.
/** /**
* Meow messages * Meow messages
* *
* This is the Meow protocol protobuf messages description. * This is the Meow protocol protobuf messages description.
* *
*/ */
syntax = "proto3"; syntax = "proto3";
package meowlib; package meowlib;
@@ -15,172 +15,173 @@ option go_package = "forge.redroom.link/yves/meowlib";
// structure definnig a message as received by a server in protobuf format // structure definnig a message as received by a server in protobuf format
message PackedServerMessage { message PackedServerMessage {
string from = 1; // The client public key for that server to get an answer string from = 1; // The client public key for that server to get an answer
bytes payload = 2; // The ToServerMessage encrypted with the server public key |or| symetrical encryption as agreed earlier bytes payload = 2; // The ToServerMessage encrypted with the server public key |or| symetrical encryption as agreed earlier
bytes signature = 3; // The message signature with the client public key |eo| the reference to teh symetrical key used bytes signature = 3; // The message signature with the client public key |eo| the reference to teh symetrical key used
} }
// structure to hold an invitation through a server // structure to hold an invitation through a server
message Invitation { message Invitation {
bytes payload = 1; // invitation payload, encrypted after step 2 bytes payload = 1; // invitation payload, encrypted after step 2
int32 timeout = 2; // how long do I want the invitation to remain available on the server int32 timeout = 2; // how long do I want the invitation to remain available on the server
int32 shortcodeLen = 3; // len of the shortcode you wish for short url transmission int32 shortcodeLen = 3; // len of the shortcode you wish for short url transmission
string shortcode = 4; // shortcode that the friend shall request to get the invitation string shortcode = 4; // shortcode that the friend shall request to get the invitation
string password = 5; // password to set for accessing invitation (optional) string password = 5; // password to set for accessing invitation (optional)
string uuid = 6; // id that the friend gave you, that you should include to your reply to get recognized string uuid = 6; // id that the friend gave you, that you should include to your reply to get recognized
int64 expiry = 7; // the server allowed expiry date, it may be samller than the requested timeout according to server policy int64 expiry = 7; // the server allowed expiry date, it may be samller than the requested timeout according to server policy
int32 step = 8; // progress in the inviattion process : 1=invite friend, 2=friend requests invitation, 3=friend's answer int32 step = 8; // progress in the inviattion process : 1=invite friend, 2=friend requests invitation, 3=friend's answer
string from=9; // used in step 3 the answer public key to check the signature in user message string from = 9; // used in step 3 the answer public key to check the signature in user message
} }
// structure for requesting incoming messages // structure for requesting incoming messages
message ConversationRequest { message ConversationRequest {
string lookup_key = 1; // lookup key for a conversation string lookup_key = 1; // lookup key for a conversation
bool delivery_request = 2; // look for for delivery tracking, key is implicit, "from" field is used bool delivery_request = 2; // look for for delivery tracking, key is implicit, "from" field is used
int64 send_timestamp = 3; int64 send_timestamp = 3;
string lookup_signature = 4; // prove that I own the private key by signing that block string lookup_signature = 4; // prove that I own the private key by signing that block
} }
message Meet { message Meet {
string public_status = 1; // Publish my online status, if the server is a meeting server string public_status = 1; // Publish my online status, if the server is a meeting server
ContactCard contact_card = 2; // mine or the requester ContactCard contact_card = 2; // mine or the requester
string message = 3; // short description string message = 3; // short description
} }
message Credentials { message Credentials {
string login = 1; // login string login = 1; // login
string password = 2; // password string password = 2; // password
string public_key = 3; // public key string public_key = 3; // public key
string private_key = 4; // private key string private_key = 4; // private key
} }
// structure defining a message for a server, that will be encrypted, then sent in a "packedmessage" payload // structure defining a message for a server, that will be encrypted, then sent in a "packedmessage" payload
message ToServerMessage { message ToServerMessage {
string type = 1; // Type 1 : final destination / 2 : forward string type = 1; // Type 1 : final destination / 2 : forward
string from = 2 ; // My pub key for the server to send me an encrypter answer string from = 2 ; // My pub key for the server to send me an encrypter answer
bytes payload = 3 ; // optional payload for server bytes payload = 3 ; // optional payload for server
repeated ConversationRequest pull_request = 4; repeated ConversationRequest pull_request = 4;
repeated PackedUserMessage messages = 5; repeated PackedUserMessage messages = 5;
repeated ServerCard known_servers = 6; repeated ServerCard known_servers = 6;
Matriochka matriochka_message = 7; Matriochka matriochka_message = 7;
string uuid = 8;
Invitation invitation = 9; // invitation for the 2 first steps of a "through server" invitation process string uuid = 8;
repeated PackedUserMessage device_messages = 10; // messages to another device belonging to the same user Invitation invitation = 9; // invitation for the 2 first steps of a "through server" invitation process
int64 timeout = 11; // timeout expected by the client for the server to answer (long polling) repeated PackedUserMessage device_messages = 10; // messages to another device belonging to the same user
VideoData video_data = 12; // video call data int64 timeout = 11; // timeout expected by the client for the server to answer (long polling)
Credentials credentials = 13; // credentials for a new user or mandatory server creds VideoData video_data = 12; // video call data
Credentials credentials = 13; // credentials for a new user or mandatory server creds
} }
// structure defining a from server receiver message decrypted from a "packedmessage" payload // structure defining a from server receiver message decrypted from a "packedmessage" payload
message FromServerMessage { message FromServerMessage {
string type = 1; // Type string type = 1; // Type
string server_public_key = 2 ; // Pub key from the server string server_public_key = 2 ; // Pub key from the server
bytes payload = 3 ; // bytes payload = 3 ; //
string uuid_ack = 4 ; // Ack for the last received ToServerMessage Uuid string uuid_ack = 4 ; // Ack for the last received ToServerMessage Uuid
string server_uuid = 5 ; // Provides the server uuid that replaced the client uuid string server_uuid = 5 ; // Provides the server uuid that replaced the client uuid
repeated PackedUserMessage chat = 6; repeated PackedUserMessage chat = 6;
repeated ServerCard known_servers = 7; repeated ServerCard known_servers = 7;
Invitation invitation = 8; // invitation answer, for the third steps of any invitation Invitation invitation = 8; // invitation answer, for the third steps of any invitation
repeated PackedUserMessage device_messages = 9; // messages from other devices belonging to the same user repeated PackedUserMessage device_messages = 9; // messages from other devices belonging to the same user
VideoData video_data = 10; // video call data VideoData video_data = 10; // video call data
repeated ContactCard contact_card = 11; // contact list for a personae repeated ContactCard contact_card = 11; // contact list for a personae
} }
message MatriochkaServer { message MatriochkaServer {
string url = 1; // Server Url string url = 1; // Server Url
string publicKey = 2; // Server Public Key string public_key = 2; // Server Public Key
string uuid = 3 ; // Optional, uuid for delivery confirmation string uuid = 3 ; // Optional, uuid for delivery confirmation
int32 delay = 4; // Max delay requested for message forwarding or delivery tracking int32 delay = 4; // Max delay requested for message forwarding or delivery tracking
} }
message Matriochka { message Matriochka {
string lookupKey = 1; // Optional, only if you want delivery tracking, less stealth string lookup_key = 1; // Optional, only if you want delivery tracking, less stealth
MatriochkaServer prev = 2; // Optional, like above MatriochkaServer prev = 2; // Optional, like above
MatriochkaServer next = 3; // Next server to deliver the message to MatriochkaServer next = 3; // Next server to deliver the message to
bytes data = 4; // Matriochka data bytes data = 4; // Matriochka data
} }
// structure describing required server attributes // structure describing required server attributes
message ServerCard { message ServerCard {
string name = 1; // friendly server name string name = 1; // friendly server name
string description=2; // description : owner type (company/private/university...), string description = 2; // description : owner type (company/private/university...),
string public_key = 3; // public key you must use to send encrypted messages to that server string public_key = 3; // public key you must use to send encrypted messages to that server
string url = 4; // meow server url string url = 4; // meow server url
string login = 5; // required login to access the server string login = 5; // required login to access the server
string password = 6; // password associated to the login string password = 6; // password associated to the login
string signature = 7; // signature of all previous fields by the server itself string signature = 7; // signature of all previous fields by the server itself
} }
// structure describing a user contact card ie the minimum set of attributes for exchanging identities // structure describing a user contact card ie the minimum set of attributes for exchanging identities
message ContactCard { message ContactCard {
string name=1; // contact nickname string name = 1; // contact nickname
string contact_public_key =2; // contact public key, will be used to authenticate her/his messages string contact_public_key = 2; // contact public key, will be used to authenticate her/his messages
string encryption_public_key= 3; // public key you must use to to write encrypted messages to that contact string encryption_public_key = 3; // public key you must use to to write encrypted messages to that contact
string lookup_public_key =4; // public key you will use as "destination identifier" for her/him to lookup for your messages on the servers string lookup_public_key = 4; // public key you will use as "destination identifier" for her/him to lookup for your messages on the servers
repeated ServerCard pull_servers =5; // list the servers where the contact will look for messages from you string symetric_key = 5; // agreed key for payload symetric encryption
uint32 version = 6; repeated ServerCard pull_servers = 6; // list the servers where the contact will look for messages from you
string invitation_id=7; uint32 version = 7;
string invitation_message=8; string invitation_id = 8;
string invitation_message = 9;
} }
// structure for sending a message to be forwarded to another user in protobuf format // structure for sending a message to be forwarded to another user in protobuf format
message PackedUserMessage { message PackedUserMessage {
string destination=1; // the peer's current conversation lookup public key string destination = 1; // the peer's current conversation lookup public key
bytes payload=2; // the message UserMessage encrypted with the destination peer's public key bytes payload = 2; // the message UserMessage encrypted with the destination peer's public key
bytes signature=3; // the payload signature with the client identity private key bytes signature = 3; // the payload signature with the client identity private key
repeated int64 serverTimestamp=4; // server time stamp, might be several in matriochka mode repeated int64 server_timestamp = 4; // server time stamp, might be several in matriochka mode
string server_delivery_uuid=5; // message uuid, for server delivery tracking, omitted if not delivery tracking desired string server_delivery_uuid = 5; // message uuid, for server delivery tracking, omitted if not delivery tracking desired
} }
message ConversationStatus { message ConversationStatus {
string uuid = 1; string uuid = 1; // uuid of message, or uuid of related message if uuid_action is not empty
string answer_to_uuid=2; // message is an answer to another one, specify uuid here int32 uuid_action = 2; // empty => normal message, 1: receivedack, 2: processedack, 3:reaction
uint64 localSequence = 3 ; // seq number in local conversation for custom reordering string reply_to_uuid = 3; // this message replies to the specified uuid
uint64 sent = 4 ; // timestamp of the message sent uint64 local_sequence = 4 ; // seq number in local conversation for custom reordering
uint64 received = 5; // timestamp of the message received uint64 sent = 5 ; // timestamp of the message sent
uint64 processed = 6; // timestamp of the message processed uint64 received = 6; // timestamp of the message received
ContactCard my_next_identity = 7; uint64 processed = 7; // timestamp of the message processed
int32 peer_next_identityAck = 8; // version of the new peer accepted id ContactCard my_next_identity = 8;
} int32 peer_next_identity_ack = 9; // version of the new peer accepted id
message Group{
string name=1;
repeated ContactCard members = 2;
} }
message Group{
string name = 1;
repeated ContactCard members = 2;
}
// structure defining information that might be exchanged between two peers. // structure defining information that might be exchanged between two peers.
message UserMessage { message UserMessage {
string destination = 1; // Lookupkey string destination = 1; // Lookupkey
string from = 2; // My public key for that contact string from = 2; // My public key for that contact
string type = 3; // Message type string type = 3; // Message type
bytes data = 4; bytes data = 4;
ConversationStatus status = 5; ConversationStatus status = 5;
ContactCard contact = 6; ContactCard contact = 6;
ServerCard knownServers = 7; ServerCard known_servers = 7;
Group group = 8; Group group = 8;
repeated File files = 9; repeated File files = 9;
Location current_location = 10; Location current_location = 10;
bytes appdata = 11; bytes appdata = 11;
Invitation invitation = 12; Invitation invitation = 12;
VideoData video_data = 13; VideoData video_data = 13;
} }
// UserMessage types : // UserMessage types :
@@ -190,47 +191,46 @@ message UserMessage {
// 4 : location request // 4 : location request
// 5 : location response // 5 : location response
message File { message File {
string filename=1; // the proposed filename string filename = 1; // the proposed filename
uint64 size=2; // the file size uint64 size = 2; // the file size
uint32 chunk=3; // the chunk counter if file is sent by chunks uint32 chunk = 3; // the chunk counter if file is sent by chunks
bytes data=4; // the file/chunk content bytes data = 4; // the file/chunk content
} }
message Location { message Location {
uint64 time=1; uint64 time = 1;
float latitude=2; float latitude = 2;
float longitude=3; float longitude = 3;
int32 altitude=4; int32 altitude = 4;
} }
message DbMessage { message DbMessage {
bool outbound = 1; // direction of the message bool outbound = 1; // direction of the message
string type = 2; string type = 2;
bytes data = 3; // text data bytes data = 3; // text data
ConversationStatus status = 4; ConversationStatus status = 4;
ContactCard contact = 5; ContactCard contact = 5;
Group group = 6; Group group = 6;
repeated string file_paths = 7; repeated string file_paths = 7;
Location current_location = 8; Location current_location = 8;
bytes appdata = 9; bytes appdata = 9;
Invitation invitation = 10; Invitation invitation = 10;
string from = 11; // source peer uid, used when storing group conversations with more than one peer string from = 11; // source peer uid, used when storing group conversations with more than one peer
string server_delivery_uuid = 12; // uuid returned by the server upon delivery string server_delivery_uuid = 12; // uuid returned by the server upon delivery
uint64 server_delivery_timestamp = 13; // timestamp of the server delivery uint64 server_delivery_timestamp = 13; // timestamp of the server delivery
} }
message VideoData { message VideoData {
string url = 1; string url = 1;
string room = 2; string room = 2;
uint64 duration = 3; uint64 duration = 3;
repeated VideoCredential credentials = 4; repeated VideoCredential credentials = 4;
repeated string media_query = 5; repeated string media_query = 5;
} }
message VideoCredential { message VideoCredential {
string username = 1; string username = 1;
string shared_key = 2; string shared_key = 2;
string token = 3; string token = 3;
} }