From d51c500ce4a8a13ceb28d4c295309b89135bddb7 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Tue, 24 Jun 2025 15:49:19 -0700 Subject: [PATCH 01/40] Added base types (wip) --- proto/umbra/base.proto | 58 +++++++++++++++++ specs/encrypted.md | 145 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 proto/umbra/base.proto create mode 100644 specs/encrypted.md diff --git a/proto/umbra/base.proto b/proto/umbra/base.proto new file mode 100644 index 0000000..ee5bb87 --- /dev/null +++ b/proto/umbra/base.proto @@ -0,0 +1,58 @@ +syntax = "proto3"; + +package umbra.base; + + +/////////////////////////////////////////////////////////////////////////////// +// SDS Payloads +/////////////////////////////////////////////////////////////////////////////// + +message HistoryEntry { + string message_id = 1; // Unique identifier of the SDS message, as defined in `Message` + bytes retrieval_hint = 2; // Optional information to help remote parties retrieve this SDS + // message; For example, A Waku deterministic message hash or routing payload hash +} + +message ReliabilityInfo { + int32 lamport_timestamp = 10; + repeated HistoryEntry causal_history = 11; + bytes bloom_filter = 12; +} + +/////////////////////////////////////////////////////////////////////////////// +// Payload Framing Messages +/////////////////////////////////////////////////////////////////////////////// + + +// TODO: This also encompasses plaintexts, is there a better name? +// Alternatives: ??? + +message EncryptedBytes { + + oneof encryption { + bytes encrypted_bytes=1; + Plaintext plaintext = 2; + Ecies ecies = 3; + } + + + message Ecies { + bytes encrypted_bytes=1; + bytes ephemeral_pubkey = 2; + bytes tag = 3; + + } + + message Plaintext { + bytes payload=1; + } +} + + +message UmbraEnvelopeV1 { + + string conversation_hint = 1; + uint32 nonce = 2; + + EncryptedBytes encrypted_bytes = 100; +} \ No newline at end of file diff --git a/specs/encrypted.md b/specs/encrypted.md new file mode 100644 index 0000000..eaf7685 --- /dev/null +++ b/specs/encrypted.md @@ -0,0 +1,145 @@ +--- +title: BASE +name: Base type framework for the Umbra Protocol +category: Standards Track +status: raw +tags: chat +editor: Jazz Alyxzander +contributors: +--- +## Abstract + +This specification outlines the base message structure for the ChatSDK protocol. + +## Background / Rationale / Motivation +When a client detects a new encrypted message it must determine how to decrypt it. +It is desired that clients can determine if they are the intended recipient without inferring this from successful decryption. This leads to lost messages with no indication of a problem, such as an out of date client. + +One of the most difficult problems in decentralized messaging is managing breaking changes between independent clients. As clients are not always running the latest version they cannot always successfully parse new message types. + + +## Theory / Semantics + + +The generic structure of the protocol types follows the Conversations approach outlined [here](https://forum.vac.dev/t/chatsdk-conversations/509). +The intention is to provide consistent data encapsulation for a wide range of micro-protocols. + + +```mermaid +flowchart TD + A((On Receive)) --> E(Envelope) + + E --> EB(EncryptedBytes) + + EB -->|decrypt based on Envelope.Hint| CH{ConversationId?} + + CH --> PF + CH --> GF + PF(PrivateV1Frame) --> CO(Content) + PF --> Example:Reset + GF(GroupV1Frame) --> CO(Content) + GF --> Example:KeyRotation + +``` + + +### UmbraEnvelope + +The root of the parsing tree is the 'envelope'. +Its primary role is to aid with payload discovery, which provides a hint as to whether this payload is of interest to the client. + +One approach to do this privately is to include the conversation_id in a wrapping payload which is asymmetrically encrypted using the recipients public_key. This is undesirable for a few reasons: +- Decryption Inefficiency: The message contents are already encrypted, which results in clients having to decrypt twice. +- Encryption Failure Ambiguity: Clients cannot detect payload errors. This is because decryption faults are indistinguishable from messages intended for another client. +- Poor Scaling: Requiring messages be encrypted per client, conflicts with more efficient payload generation techniques like conversation based addressing. + +Another way is to provide a conversation hint in cleartext, however obscured to stop casual observers from attributing messages to conversations. +- Hash(Conversation_id + Salt) +- Hash stops casual observers from associating messages to a conversation +- Salt produces a different output for + +[TODO: Define Conversation hint] +[TODO: Highlight privacy impacts] + +The data contained here is public and has privacy implications, careful consideration is required. + + + +### EncryptedBytes + +The EncryptedBytes message is a self-describing wrapper for all encrypted payloads. As the protocol grows it will include potentially different encryption mechanisms. This message type makes no assumptions about the encryption used an allows new conversation types to use the same messaging framework. + + +[TODO: Why isn't this defined within the conversation frames like SDS?] + + + +## Wire Format Specification / Syntax +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, +“NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +This section SHOULD not contain explanations of semantics and focus on concisely defining the wire format. +Implementations SHOULD adhere to these exact formats to interoperate with other implementations. +It is fine, if parts of the previous section that touch on the wire format are repeated. +The purpose of this section is having a concise definition of what an implementation sends and accepts. +Parts that are not specified here are considered implementation details. +Implementors are free to decide on how to implement these details. + + +The wire format is specified using protocol buffers v3. + +```mermaid + +message UmbraEnvelopeV1 { + + string conversation_hint = 1; + uint32 nonce = 2; + + EncryptedBytes encrypted_bytes = 100; +} + +message EncryptedBytes { + + oneof encryption { + bytes encrypted_bytes=1; + Plaintext plaintext = 2; + Ecies ecies = 3; + } + + + message Ecies { + bytes encrypted_bytes=1; + bytes ephemeral_pubkey = 2; + bytes tag = 3; + + } + + message Plaintext { + bytes payload=1; + } +} + +``` + + + + + +## Implementation Suggestions (optional) +An optional *implementation suggestions* section may provide suggestions on how to approach implementation details, and, +if available, point to existing implementations for reference. + + +## (Further Optional Sections) + + +## Security/Privacy Considerations + + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +A list of references. \ No newline at end of file From 5fd32881e4bcdfdac5542539906c3a419bb3cade Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:12:50 -0700 Subject: [PATCH 02/40] removed manditory encryption header --- proto/umbra/base.proto | 27 +-------------------------- proto/umbra/encryption.proto | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 26 deletions(-) create mode 100644 proto/umbra/encryption.proto diff --git a/proto/umbra/base.proto b/proto/umbra/base.proto index ee5bb87..8b9d7de 100644 --- a/proto/umbra/base.proto +++ b/proto/umbra/base.proto @@ -24,35 +24,10 @@ message ReliabilityInfo { /////////////////////////////////////////////////////////////////////////////// -// TODO: This also encompasses plaintexts, is there a better name? -// Alternatives: ??? - -message EncryptedBytes { - - oneof encryption { - bytes encrypted_bytes=1; - Plaintext plaintext = 2; - Ecies ecies = 3; - } - - - message Ecies { - bytes encrypted_bytes=1; - bytes ephemeral_pubkey = 2; - bytes tag = 3; - - } - - message Plaintext { - bytes payload=1; - } -} - - message UmbraEnvelopeV1 { string conversation_hint = 1; uint32 nonce = 2; - EncryptedBytes encrypted_bytes = 100; + bytes encrypted_bytes = 100; } \ No newline at end of file diff --git a/proto/umbra/encryption.proto b/proto/umbra/encryption.proto new file mode 100644 index 0000000..80d96ca --- /dev/null +++ b/proto/umbra/encryption.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package umbra.encryption; + + +message Plaintext { + bytes payload=1; +} + +message Ecies { + bytes encrypted_bytes=1; + bytes ephemeral_pubkey = 2; + bytes tag = 3; +} + +// TODO: This also encompasses plaintexts, is there a better name? +// Alternatives: ??? +message EncryptedBytes { + + oneof encryption { + Plaintext plaintext = 1; + Ecies ecies = 2; + } +} From 93e69eee756d7e746e51251ae1307520fbf721e1 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:22:57 -0700 Subject: [PATCH 03/40] added placeholder types for inbox and private chat --- proto/umbra/conversations/inbox_v1.proto | 16 +++++++++++++ proto/umbra/conversations/private_v1.proto | 26 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 proto/umbra/conversations/inbox_v1.proto create mode 100644 proto/umbra/conversations/private_v1.proto diff --git a/proto/umbra/conversations/inbox_v1.proto b/proto/umbra/conversations/inbox_v1.proto new file mode 100644 index 0000000..cb6663c --- /dev/null +++ b/proto/umbra/conversations/inbox_v1.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package umbra.convos.inbox; + +import "proto/umbra/conversations/private_v1.proto"; + + +// Specification: [TODO] + +message InboxV1Frame { + string recipient = 1; + string conversation_type = 2; + oneof params { + convos.private_v1.ConversationInvite_PrivateV1 invite_private_v1 = 10; + } +} diff --git a/proto/umbra/conversations/private_v1.proto b/proto/umbra/conversations/private_v1.proto new file mode 100644 index 0000000..94410f9 --- /dev/null +++ b/proto/umbra/conversations/private_v1.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package umbra.convos.private_v1; + +import "proto/umbra/base.proto"; +import "proto/umbra/common_frames.proto"; + +message ConversationInvite_PrivateV1 { + // TODO: update placeholder fields + string participants = 1; +} + + +message PrivateV1Frame { + // SDS like information: Message ID and channel_id extracted for utility + string message_id = 2; + string conversation_id = 3; + + base.ReliabilityInfo reliability_info = 4; + + oneof frame_type { + common_frames.ContentFrame content = 10; + // .... + } +} + From ba3b90407f3561d6f22a74b4c96a2212e63ac34b Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:29:04 -0700 Subject: [PATCH 04/40] add proto generation for rust --- .gitignore | 1 + rust/.devcontainer/devcontainer.json | 12 + rust/.devcontainer/postcreate.sh | 2 + rust/.vscode/settings.json | 5 + rust/Cargo.lock | 593 +++++++++++++++++++++++++++ rust/cargo.toml | 18 + rust/umbra-types/Cargo.toml | 12 + rust/umbra-types/build.rs | 33 ++ rust/umbra-types/src/lib.rs | 1 + rust/umbra-types/src/payload.rs | 43 ++ 10 files changed, 720 insertions(+) create mode 100644 .gitignore create mode 100644 rust/.devcontainer/devcontainer.json create mode 100755 rust/.devcontainer/postcreate.sh create mode 100644 rust/.vscode/settings.json create mode 100644 rust/Cargo.lock create mode 100644 rust/cargo.toml create mode 100644 rust/umbra-types/Cargo.toml create mode 100644 rust/umbra-types/build.rs create mode 100644 rust/umbra-types/src/lib.rs create mode 100644 rust/umbra-types/src/payload.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fe5239 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/rust/target diff --git a/rust/.devcontainer/devcontainer.json b/rust/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c77db0e --- /dev/null +++ b/rust/.devcontainer/devcontainer.json @@ -0,0 +1,12 @@ +{ + "name": "Rust-UmbraTypes", + "image": "mcr.microsoft.com/devcontainers/rust:latest", + "customizations": { + "vscode": { + "extensions": [ + "pbkit.vscode-pbkit" + ] + } + }, + "postCreateCommand": "bash -i .devcontainer/postcreate.sh" +} \ No newline at end of file diff --git a/rust/.devcontainer/postcreate.sh b/rust/.devcontainer/postcreate.sh new file mode 100755 index 0000000..8bf99d5 --- /dev/null +++ b/rust/.devcontainer/postcreate.sh @@ -0,0 +1,2 @@ +sudo apt-get update +sudo apt-get install -y protobuf-compiler \ No newline at end of file diff --git a/rust/.vscode/settings.json b/rust/.vscode/settings.json new file mode 100644 index 0000000..cc90acf --- /dev/null +++ b/rust/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "[rust]": { + "editor.formatOnSave": true + } +} \ No newline at end of file diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..7187125 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,593 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "prettyplease" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes 1.10.1", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "protobuf-codegen" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" +dependencies = [ + "protobuf", +] + +[[package]] +name = "protoc" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0218039c514f9e14a5060742ecd50427f8ac4f85a6dc58f2ddb806e318c55ee" +dependencies = [ + "log", + "which", +] + +[[package]] +name = "protoc-rust" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f8a182bb17c485f20bdc4274a8c39000a61024cfe461c799b50fec77267838" +dependencies = [ + "protobuf", + "protobuf-codegen", + "protoc", + "tempfile", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix 1.0.7", + "windows-sys 0.59.0", +] + +[[package]] +name = "umbra-types" +version = "0.0.1-dev" +dependencies = [ + "bytes 0.4.12", + "prost", + "prost-build", + "protoc-rust", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] diff --git a/rust/cargo.toml b/rust/cargo.toml new file mode 100644 index 0000000..0a4bd17 --- /dev/null +++ b/rust/cargo.toml @@ -0,0 +1,18 @@ +[workspace] + +members = [ + "umbra-types" +] + +default-members = [ + "umbra-types" +] + +resolver = "3" + +[workspace.package] +version = "0.0.1-dev" + +[workspace.dependencies] + + diff --git a/rust/umbra-types/Cargo.toml b/rust/umbra-types/Cargo.toml new file mode 100644 index 0000000..420d659 --- /dev/null +++ b/rust/umbra-types/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "umbra-types" +edition = "2024" +version.workspace = true + +[dependencies] +prost = "0.13.5" +bytes = "0.4" + +[build-dependencies] +prost-build = "0.13.5" +protoc-rust = "2" diff --git a/rust/umbra-types/build.rs b/rust/umbra-types/build.rs new file mode 100644 index 0000000..a7731f5 --- /dev/null +++ b/rust/umbra-types/build.rs @@ -0,0 +1,33 @@ +extern crate protoc_rust; + +use std::env; +use std::path::PathBuf; + +fn get_proto_dir() -> PathBuf { + // protos are stored in ../../proto/ + let mut proto_root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + proto_root_dir.pop(); + proto_root_dir.pop(); + proto_root_dir +} + +fn main() { + let mut config = prost_build::Config::new(); + + // Create mod file to maintain import hierarchy + config.include_file("mod.rs"); + + + config.compile_protos( + &[ + "proto/umbra/base.proto", + "proto/umbra/common_frames.proto", + "proto/umbra/encryption.proto", + "proto/umbra/conversations/inbox_v1.proto", + "proto/umbra/conversations/private_v1.proto", + ], + // set proto_path + &[get_proto_dir().to_str().unwrap()], + ) + .unwrap(); +} diff --git a/rust/umbra-types/src/lib.rs b/rust/umbra-types/src/lib.rs new file mode 100644 index 0000000..fbb091f --- /dev/null +++ b/rust/umbra-types/src/lib.rs @@ -0,0 +1 @@ +pub mod payload; diff --git a/rust/umbra-types/src/payload.rs b/rust/umbra-types/src/payload.rs new file mode 100644 index 0000000..0ce769a --- /dev/null +++ b/rust/umbra-types/src/payload.rs @@ -0,0 +1,43 @@ +pub mod types { + include!(concat!(env!("OUT_DIR"), "/mod.rs")); +} + +pub use types::umbra::*; + +#[cfg(test)] +mod tests { + use super::*; + use prost::Message; + + #[test] + fn test_private_v1_roundtrip() { + let text = "Hello, World!".to_string(); + + let msg = convos::private_v1::PrivateV1Frame { + message_id: "messageId".to_string(), + conversation_id: "conversationId".to_string(), + reliability_info: Some(base::ReliabilityInfo { + lamport_timestamp: 0, + causal_history: vec![], + bloom_filter: vec![1, 2, 3, 4], + }), + frame_type: Some(convos::private_v1::private_v1_frame::FrameType::Content( + common_frames::ContentFrame { + domain: 0, + tag: 0, + bytes: text.encode_to_vec(), + }, + )), + }; + + let bytes = msg.encode_to_vec(); + + let msg_from_bytes = + convos::private_v1::PrivateV1Frame::decode(&*bytes).expect("Failed to decode message"); + + assert_eq!( + msg, msg_from_bytes, + "Encoded and decoded messages should match" + ); + } +} From 533bd31357eb76ac0070da1120cef94430bd1361 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:58:49 -0700 Subject: [PATCH 05/40] added logging to build --- rust/umbra-types/build.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rust/umbra-types/build.rs b/rust/umbra-types/build.rs index a7731f5..34a63d0 100644 --- a/rust/umbra-types/build.rs +++ b/rust/umbra-types/build.rs @@ -4,21 +4,27 @@ use std::env; use std::path::PathBuf; fn get_proto_dir() -> PathBuf { + println!( + "Manifest dir: {:?}", + env::var("CARGO_MANIFEST_DIR").unwrap() + ); // protos are stored in ../../proto/ let mut proto_root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); proto_root_dir.pop(); proto_root_dir.pop(); + + println!("proto_dir: {:?}", proto_root_dir); proto_root_dir } fn main() { let mut config = prost_build::Config::new(); - // Create mod file to maintain import hierarchy + // Create mod file to maintain import hierarchy config.include_file("mod.rs"); - - config.compile_protos( + config + .compile_protos( &[ "proto/umbra/base.proto", "proto/umbra/common_frames.proto", From 9a50d8e370b3df99505884a85d37b8f1f818210f Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:12:43 -0700 Subject: [PATCH 06/40] added common types --- proto/umbra/common_frames.proto | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 proto/umbra/common_frames.proto diff --git a/proto/umbra/common_frames.proto b/proto/umbra/common_frames.proto new file mode 100644 index 0000000..85dd08c --- /dev/null +++ b/proto/umbra/common_frames.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package umbra.common_frames; + +message ContentFrame { + uint32 domain = 1; + uint32 tag = 2; + bytes bytes = 3; +} \ No newline at end of file From dc4fc1ed30b16f58b44e48479cf238f280be280a Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:30:50 -0700 Subject: [PATCH 07/40] add re-export --- rust/umbra-types/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/umbra-types/src/lib.rs b/rust/umbra-types/src/lib.rs index fbb091f..1b37286 100644 --- a/rust/umbra-types/src/lib.rs +++ b/rust/umbra-types/src/lib.rs @@ -1 +1,3 @@ pub mod payload; + +pub use payload::types::umbra::*; From 3831bf727685a65f05993220fe8c6729f4c2e985 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:02:56 -0700 Subject: [PATCH 08/40] add explicit version --- rust/Cargo.lock | 2 +- rust/umbra-types/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7187125..fa61098 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -402,7 +402,7 @@ dependencies = [ [[package]] name = "umbra-types" -version = "0.0.1-dev" +version = "0.0.2-dev" dependencies = [ "bytes 0.4.12", "prost", diff --git a/rust/umbra-types/Cargo.toml b/rust/umbra-types/Cargo.toml index 420d659..4226c57 100644 --- a/rust/umbra-types/Cargo.toml +++ b/rust/umbra-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "umbra-types" edition = "2024" -version.workspace = true +version = "0.0.2-dev" [dependencies] prost = "0.13.5" From 270b97210a8fa2b538e846f4f4895ab744aa5688 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:38:07 -0700 Subject: [PATCH 09/40] simplify parsing --- proto/umbra/base.proto | 40 +++++++++---- proto/umbra/conversations/inbox_v1.proto | 4 +- proto/umbra/conversations/private_v1.proto | 15 +++-- rust/umbra-types/src/lib.rs | 21 +++++++ rust/umbra-types/src/payload.rs | 66 +++++++++++++++++++--- 5 files changed, 117 insertions(+), 29 deletions(-) diff --git a/proto/umbra/base.proto b/proto/umbra/base.proto index 8b9d7de..9e7c2d4 100644 --- a/proto/umbra/base.proto +++ b/proto/umbra/base.proto @@ -2,22 +2,42 @@ syntax = "proto3"; package umbra.base; +import "proto/umbra/encryption.proto"; /////////////////////////////////////////////////////////////////////////////// // SDS Payloads /////////////////////////////////////////////////////////////////////////////// message HistoryEntry { - string message_id = 1; // Unique identifier of the SDS message, as defined in `Message` - bytes retrieval_hint = 2; // Optional information to help remote parties retrieve this SDS - // message; For example, A Waku deterministic message hash or routing payload hash + string message_id = 1; // Unique identifier of the SDS message, as defined in `Message` + bytes retrieval_hint = 2; // Optional information to help remote parties retrieve this SDS + // message; For example, A Waku deterministic message hash or routing payload hash + } + + message ReliableBytes { + string message_id = 2; + string channel_id = 3; + int32 lamport_timestamp = 10; + repeated HistoryEntry causal_history = 11; + bytes bloom_filter = 12; + optional bytes content = 20; + } + + +/////////////////////////////////////////////////////////////////////////////// +// Encryption +/////////////////////////////////////////////////////////////////////////////// + +// TODO: This also encompasses plaintexts, is there a better name? +// Alternatives: ??? +message EncryptedBytes { + + oneof encryption { + encryption.Plaintext plaintext = 1; + encryption.Ecies ecies = 2; + } } -message ReliabilityInfo { - int32 lamport_timestamp = 10; - repeated HistoryEntry causal_history = 11; - bytes bloom_filter = 12; -} /////////////////////////////////////////////////////////////////////////////// // Payload Framing Messages @@ -27,7 +47,7 @@ message ReliabilityInfo { message UmbraEnvelopeV1 { string conversation_hint = 1; - uint32 nonce = 2; + uint64 salt = 2; - bytes encrypted_bytes = 100; + bytes payload = 5; } \ No newline at end of file diff --git a/proto/umbra/conversations/inbox_v1.proto b/proto/umbra/conversations/inbox_v1.proto index cb6663c..2418b82 100644 --- a/proto/umbra/conversations/inbox_v1.proto +++ b/proto/umbra/conversations/inbox_v1.proto @@ -9,8 +9,8 @@ import "proto/umbra/conversations/private_v1.proto"; message InboxV1Frame { string recipient = 1; - string conversation_type = 2; - oneof params { + // string conversation_type = 2; + oneof frame_type { convos.private_v1.ConversationInvite_PrivateV1 invite_private_v1 = 10; } } diff --git a/proto/umbra/conversations/private_v1.proto b/proto/umbra/conversations/private_v1.proto index 94410f9..ab73a64 100644 --- a/proto/umbra/conversations/private_v1.proto +++ b/proto/umbra/conversations/private_v1.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package umbra.convos.private_v1; -import "proto/umbra/base.proto"; import "proto/umbra/common_frames.proto"; message ConversationInvite_PrivateV1 { @@ -11,16 +10,16 @@ message ConversationInvite_PrivateV1 { } -message PrivateV1Frame { - // SDS like information: Message ID and channel_id extracted for utility - string message_id = 2; - string conversation_id = 3; - - base.ReliabilityInfo reliability_info = 4; +message Placeholder { + uint32 counter = 1; +} + +message PrivateV1Frame { + string conversation_id = 1; oneof frame_type { common_frames.ContentFrame content = 10; + Placeholder placeholder = 11; // .... } } - diff --git a/rust/umbra-types/src/lib.rs b/rust/umbra-types/src/lib.rs index 1b37286..e03d6c7 100644 --- a/rust/umbra-types/src/lib.rs +++ b/rust/umbra-types/src/lib.rs @@ -1,3 +1,24 @@ pub mod payload; pub use payload::types::umbra::*; + +#[cfg(test)] + +mod tests { + use super::*; + use prost::Message; + + #[test] + fn test_private_v1_roundtrip() { + convos::private_v1::PrivateV1Frame { + conversation_id: "conversationId".to_string(), + frame_type: Some(convos::private_v1::private_v1_frame::FrameType::Content( + common_frames::ContentFrame { + domain: 0, + tag: 0, + bytes: "Hello, World!".to_string().encode_to_vec(), + }, + )), + }; + } +} diff --git a/rust/umbra-types/src/payload.rs b/rust/umbra-types/src/payload.rs index 0ce769a..a3a703a 100644 --- a/rust/umbra-types/src/payload.rs +++ b/rust/umbra-types/src/payload.rs @@ -2,8 +2,50 @@ pub mod types { include!(concat!(env!("OUT_DIR"), "/mod.rs")); } +use prost::Message; pub use types::umbra::*; +use crate::{ + base::{EncryptedBytes, UmbraEnvelopeV1}, + convos::{ + inbox::{InboxV1Frame, inbox_v1_frame}, + private_v1::{PrivateV1Frame, private_v1_frame}, + }, +}; + +impl PrivateV1Frame { + pub fn new(conversation_id: String, frame: private_v1_frame::FrameType) -> Self { + PrivateV1Frame { + conversation_id, + frame_type: Some(frame), + } + } +} + +impl InboxV1Frame { + pub fn new(recipient: String, frame: inbox_v1_frame::FrameType) -> Self { + InboxV1Frame { + recipient: recipient, + // conversation_id, + frame_type: Some(frame), + } + } +} + +pub trait ToEnvelope { + fn to_envelope(self, conversation_id: String, salt: u64) -> UmbraEnvelopeV1; +} + +impl ToEnvelope for EncryptedBytes { + fn to_envelope(self, conversation_id: String, salt: u64) -> UmbraEnvelopeV1 { + UmbraEnvelopeV1 { + conversation_hint: conversation_id, // TODO + salt, + payload: self.encode_to_vec(), // Avoid allocation here? + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -13,14 +55,8 @@ mod tests { fn test_private_v1_roundtrip() { let text = "Hello, World!".to_string(); - let msg = convos::private_v1::PrivateV1Frame { - message_id: "messageId".to_string(), + let msg = PrivateV1Frame { conversation_id: "conversationId".to_string(), - reliability_info: Some(base::ReliabilityInfo { - lamport_timestamp: 0, - causal_history: vec![], - bloom_filter: vec![1, 2, 3, 4], - }), frame_type: Some(convos::private_v1::private_v1_frame::FrameType::Content( common_frames::ContentFrame { domain: 0, @@ -30,10 +66,22 @@ mod tests { )), }; - let bytes = msg.encode_to_vec(); + let reliable = base::ReliableBytes { + message_id: "msg_id".into(), + channel_id: msg.conversation_id.clone(), + lamport_timestamp: 0, + causal_history: vec![], + bloom_filter: vec![1, 2, 3, 4], + content: Some(msg.encode_to_vec()), + }; + + let buf = reliable.encode_to_vec(); + + let reliable_msg = base::ReliableBytes::decode(&*buf).unwrap(); let msg_from_bytes = - convos::private_v1::PrivateV1Frame::decode(&*bytes).expect("Failed to decode message"); + convos::private_v1::PrivateV1Frame::decode(&*reliable_msg.content.unwrap()) + .expect("Failed to decode message"); assert_eq!( msg, msg_from_bytes, From 16dbe448f5936bba5f102ffe8e4d3b0d2c941d3c Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:55:26 -0700 Subject: [PATCH 10/40] make inbox protocol part of core --- proto/umbra/base.proto | 15 +++ proto/umbra/conversations/inbox_v1.proto | 16 --- proto/umbra/invite.proto | 7 ++ rust/umbra-types/build.rs | 4 +- rust/umbra-types/src/payload.rs | 7 +- specs/conversations.md | 112 +++++++++++++++++ specs/encrypted.md | 145 ----------------------- specs/inbox.md | 120 +++++++++++++++++++ 8 files changed, 258 insertions(+), 168 deletions(-) delete mode 100644 proto/umbra/conversations/inbox_v1.proto create mode 100644 proto/umbra/invite.proto create mode 100644 specs/conversations.md delete mode 100644 specs/encrypted.md create mode 100644 specs/inbox.md diff --git a/proto/umbra/base.proto b/proto/umbra/base.proto index 9e7c2d4..554d636 100644 --- a/proto/umbra/base.proto +++ b/proto/umbra/base.proto @@ -3,6 +3,21 @@ syntax = "proto3"; package umbra.base; import "proto/umbra/encryption.proto"; +import "proto/umbra/invite.proto"; + + +/////////////////////////////////////////////////////////////////////////////// +// Protocol Frames +/////////////////////////////////////////////////////////////////////////////// + +message InboxV1Frame { + string recipient = 1; + // string conversation_type = 2; + oneof frame_type { + invite.InvitePrivateV1 invite_private_v1 = 10; + } +} + /////////////////////////////////////////////////////////////////////////////// // SDS Payloads diff --git a/proto/umbra/conversations/inbox_v1.proto b/proto/umbra/conversations/inbox_v1.proto deleted file mode 100644 index 2418b82..0000000 --- a/proto/umbra/conversations/inbox_v1.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -package umbra.convos.inbox; - -import "proto/umbra/conversations/private_v1.proto"; - - -// Specification: [TODO] - -message InboxV1Frame { - string recipient = 1; - // string conversation_type = 2; - oneof frame_type { - convos.private_v1.ConversationInvite_PrivateV1 invite_private_v1 = 10; - } -} diff --git a/proto/umbra/invite.proto b/proto/umbra/invite.proto new file mode 100644 index 0000000..cd592a7 --- /dev/null +++ b/proto/umbra/invite.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package umbra.invite; + +message InvitePrivateV1 { + string participants = 1; +} \ No newline at end of file diff --git a/rust/umbra-types/build.rs b/rust/umbra-types/build.rs index 34a63d0..0e4a5e5 100644 --- a/rust/umbra-types/build.rs +++ b/rust/umbra-types/build.rs @@ -28,9 +28,9 @@ fn main() { &[ "proto/umbra/base.proto", "proto/umbra/common_frames.proto", - "proto/umbra/encryption.proto", - "proto/umbra/conversations/inbox_v1.proto", "proto/umbra/conversations/private_v1.proto", + "proto/umbra/encryption.proto", + "proto/umbra/invite.proto", ], // set proto_path &[get_proto_dir().to_str().unwrap()], diff --git a/rust/umbra-types/src/payload.rs b/rust/umbra-types/src/payload.rs index a3a703a..9d13b0a 100644 --- a/rust/umbra-types/src/payload.rs +++ b/rust/umbra-types/src/payload.rs @@ -6,11 +6,8 @@ use prost::Message; pub use types::umbra::*; use crate::{ - base::{EncryptedBytes, UmbraEnvelopeV1}, - convos::{ - inbox::{InboxV1Frame, inbox_v1_frame}, - private_v1::{PrivateV1Frame, private_v1_frame}, - }, + base::{EncryptedBytes, InboxV1Frame, UmbraEnvelopeV1, inbox_v1_frame}, + convos::private_v1::{PrivateV1Frame, private_v1_frame}, }; impl PrivateV1Frame { diff --git a/specs/conversations.md b/specs/conversations.md new file mode 100644 index 0000000..171557b --- /dev/null +++ b/specs/conversations.md @@ -0,0 +1,112 @@ +--- +title: CONVERSATIONS +name: Conversation based communications +category: Standards Track +status: raw +tags: chat +editor: Jazz Alyxzander +contributors: +--- +## Abstract + +This specification outlines the base message structure for the Conversations protocol. + +## Background / Rationale / Motivation + +Modern communication systems require a reliable and flexible way to establish and manage conversations between participants. +While simple message-passing protocols exist, they lack the ability to manage state across a stream of messages. + +The Conversations Protocol addresses this gap by providing a clear and standardized way for participants to initiate, conversations in decentralized environments. + + + +## Theory / Semantics + +[TODO: remove this link, by sumamrizing and embedding in the spec so its standalone] +This is a lightweight framework for defining Conversation micro-protocols. +The high level Conversations approach outlined [here](https://forum.vac.dev/t/chatsdk-conversations/509). +The intention is to provide the minimal building blocks for a wide range of micro-protocols, while allowing ConversationTypes to remain as flexible as possible. + + +### Conversations +A conversation is responsible for defining its encryption, encoding, and message types which are necessary for operation. + +A ConversationType MUST define which content topics are valid places to receive messages. +A ConversationType MUST define which encryption scheme is used +A ConversationType MUST define which Frames are valid. +- Clients MUST be able to decode frames deterministically. + +A ConversationType SHOULD define membership requirements and limitations. + +Every Conversation instance MUST have a conversation_id. + + +**Decoding** +Clients determine how to decode a byte sequence by the `conversation_id`. + +[TODO: Should we add more guidance later? e,g ///] +`conversation_id's` must be uniquely identify which encryption state to. As there may be many ConversationTypes defined, specifications should be mindful of conflicts. + +Clients only accepts envelopes with known `conversation_id's`. All others can be discarded as there is insufficient information to properly decrypt/decode the messages. + + +**Framing** +To maintain a deterministic decoding tree, all frames on the wire MUST be wrapped with a `UmbraEnvelope`. +This provides a common type for all frames. + +Underneath UmbraEnvelope, ConversationTypes are free to wrap frames as desired, however it is recommended to use `EncryptedFrame` to ensure clients can decode messages regardless of software version. + + +**Conversation Hinting** +[TODO: Needs lots of work] +Conversation identifiers (conversation_id) have the potential to leak sensitive metadata if exposed in cleartext. Frames sharing the same conversation_id could allow observers to infer social graph relationships, user activity patterns, or conversation linkage, depending on how conversation_id values are used by the specific ConversationType. + +To mitigate this risk and provide a safer default for Conversation implementors, conversation_id values SHOULD be obscured in a way that prevents observers from linking frames belonging to the same conversation. + +[TODO: Ratcheting Private Identifiers] + + + +### Default Inbox +To provide a baseline for interaction, clients need a method to receive initial frames/invites. To achieve this all clients MUST implement a default Inbox with `inbox_address = HASH(client_address)`. [TODO: Define hash here, or embed as part of the Inbox spec] + +See [Inbox](./inbox.md) for more information. + + +## Wire Format Specification / Syntax + +The wire format is specified using protocol buffers v3. + +```mermaid + +message UmbraEnvelopeV1 { + + string conversation_hint = 1; + uint32 salt = 2; + + EncryptedBytes encrypted_bytes = 10; +} + + +``` + +## Implementation Suggestions (optional) +An optional *implementation suggestions* section may provide suggestions on how to approach implementation details, and, +if available, point to existing implementations for reference. + + +## (Further Optional Sections) + + +## Security/Privacy Considerations +[TODO: cover Conversation_id -> Message Types ] + + + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +A list of references. diff --git a/specs/encrypted.md b/specs/encrypted.md deleted file mode 100644 index eaf7685..0000000 --- a/specs/encrypted.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: BASE -name: Base type framework for the Umbra Protocol -category: Standards Track -status: raw -tags: chat -editor: Jazz Alyxzander -contributors: ---- -## Abstract - -This specification outlines the base message structure for the ChatSDK protocol. - -## Background / Rationale / Motivation -When a client detects a new encrypted message it must determine how to decrypt it. -It is desired that clients can determine if they are the intended recipient without inferring this from successful decryption. This leads to lost messages with no indication of a problem, such as an out of date client. - -One of the most difficult problems in decentralized messaging is managing breaking changes between independent clients. As clients are not always running the latest version they cannot always successfully parse new message types. - - -## Theory / Semantics - - -The generic structure of the protocol types follows the Conversations approach outlined [here](https://forum.vac.dev/t/chatsdk-conversations/509). -The intention is to provide consistent data encapsulation for a wide range of micro-protocols. - - -```mermaid -flowchart TD - A((On Receive)) --> E(Envelope) - - E --> EB(EncryptedBytes) - - EB -->|decrypt based on Envelope.Hint| CH{ConversationId?} - - CH --> PF - CH --> GF - PF(PrivateV1Frame) --> CO(Content) - PF --> Example:Reset - GF(GroupV1Frame) --> CO(Content) - GF --> Example:KeyRotation - -``` - - -### UmbraEnvelope - -The root of the parsing tree is the 'envelope'. -Its primary role is to aid with payload discovery, which provides a hint as to whether this payload is of interest to the client. - -One approach to do this privately is to include the conversation_id in a wrapping payload which is asymmetrically encrypted using the recipients public_key. This is undesirable for a few reasons: -- Decryption Inefficiency: The message contents are already encrypted, which results in clients having to decrypt twice. -- Encryption Failure Ambiguity: Clients cannot detect payload errors. This is because decryption faults are indistinguishable from messages intended for another client. -- Poor Scaling: Requiring messages be encrypted per client, conflicts with more efficient payload generation techniques like conversation based addressing. - -Another way is to provide a conversation hint in cleartext, however obscured to stop casual observers from attributing messages to conversations. -- Hash(Conversation_id + Salt) -- Hash stops casual observers from associating messages to a conversation -- Salt produces a different output for - -[TODO: Define Conversation hint] -[TODO: Highlight privacy impacts] - -The data contained here is public and has privacy implications, careful consideration is required. - - - -### EncryptedBytes - -The EncryptedBytes message is a self-describing wrapper for all encrypted payloads. As the protocol grows it will include potentially different encryption mechanisms. This message type makes no assumptions about the encryption used an allows new conversation types to use the same messaging framework. - - -[TODO: Why isn't this defined within the conversation frames like SDS?] - - - -## Wire Format Specification / Syntax -The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, -“NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). - -This section SHOULD not contain explanations of semantics and focus on concisely defining the wire format. -Implementations SHOULD adhere to these exact formats to interoperate with other implementations. -It is fine, if parts of the previous section that touch on the wire format are repeated. -The purpose of this section is having a concise definition of what an implementation sends and accepts. -Parts that are not specified here are considered implementation details. -Implementors are free to decide on how to implement these details. - - -The wire format is specified using protocol buffers v3. - -```mermaid - -message UmbraEnvelopeV1 { - - string conversation_hint = 1; - uint32 nonce = 2; - - EncryptedBytes encrypted_bytes = 100; -} - -message EncryptedBytes { - - oneof encryption { - bytes encrypted_bytes=1; - Plaintext plaintext = 2; - Ecies ecies = 3; - } - - - message Ecies { - bytes encrypted_bytes=1; - bytes ephemeral_pubkey = 2; - bytes tag = 3; - - } - - message Plaintext { - bytes payload=1; - } -} - -``` - - - - - -## Implementation Suggestions (optional) -An optional *implementation suggestions* section may provide suggestions on how to approach implementation details, and, -if available, point to existing implementations for reference. - - -## (Further Optional Sections) - - -## Security/Privacy Considerations - - -## Copyright - -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). - -## References - -A list of references. \ No newline at end of file diff --git a/specs/inbox.md b/specs/inbox.md new file mode 100644 index 0000000..ed695ca --- /dev/null +++ b/specs/inbox.md @@ -0,0 +1,120 @@ +--- +title: INBOX +name: Inbound messages +category: Standards Track +status: raw +tags: chat +editor: Jazz Alyxzander +contributors: +--- +## Abstract + + +## Background / Rationale / Motivation + + + +## Theory / Semantics + +Inboxes are inbound only conversation types, which allow a client to recieve messages from contacts. +An inbox does not have a defined set of participants, and is used to receive messages when there does not exist and established channel between contacts. +All messages to an inbox MUST be encrypted. [TODO: Reason] + +### Accepted types +Inboxes SHOULD attempt to handle all envelopes sent with the conversation_id `/inbox/v1/`. + +### Default Inbox + +[Differentiate between Inbox the place you recieve and the Protocol -- Is htere a difference] +An inbox is the default conversation type. +It is a catch-all for all messages which + + + +[TODO: is domain separation needed here or should we save some bytes?] + + +**Encryption** + +All Frames sent to the INBOX must be encrypted. + +Frames CAN be encrypted + +**Content Topic** + +// TODO: Inbox Topics will be defined in ContactBundles, allowing for dynamic topic usage + +All clients must listen for messages posted with the content topic `/inbox/` + +### Invites +Conversations are required to define their own Invites, which contain the required information to bootstrap new participants. + + +[TODO: InviteV1] + +### EncryptedBytes + +The EncryptedBytes message is a self-describing wrapper for all encrypted payloads. As the protocol grows it will include potentially different encryption mechanisms. This message type makes no assumptions about the encryption used an allows new conversation types to use the same messaging framework. + + +[TODO: Why isn't this defined within the conversation frames like SDS?] + + + +### Invites + +Individual Conversations are responsible for defining a payload which will be used to initialize remote participants. + +#### Invite Encryption + +Invite + + + + +## Wire Format Specification / Syntax +The wire format is specified using protocol buffers v3. + +```mermaid + +message EncryptedBytes { + + oneof encryption { + bytes encrypted_bytes=1; + Plaintext plaintext = 2; + Ecies ecies = 3; + } + + + message Ecies { + bytes encrypted_bytes=1; + bytes ephemeral_pubkey = 2; + bytes tag = 3; + + } + + message Plaintext { + bytes payload=1; + } +} + +``` + +## Implementation Suggestions (optional) +An optional *implementation suggestions* section may provide suggestions on how to approach implementation details, and, +if available, point to existing implementations for reference. + + +## (Further Optional Sections) + + +## Security/Privacy Considerations + + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +A list of references. From a986259416f2155dd969e38db2feca104fb12ec6 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Sun, 29 Jun 2025 13:25:14 -0700 Subject: [PATCH 11/40] private invite vectored participants --- proto/umbra/invite.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/umbra/invite.proto b/proto/umbra/invite.proto index cd592a7..38cfb76 100644 --- a/proto/umbra/invite.proto +++ b/proto/umbra/invite.proto @@ -3,5 +3,5 @@ syntax = "proto3"; package umbra.invite; message InvitePrivateV1 { - string participants = 1; + repeated string participants = 1; } \ No newline at end of file From 237f9a1d5ee86155bf5229a2e266bc38f7069d51 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 30 Jun 2025 16:48:02 -0700 Subject: [PATCH 12/40] remove duplicates --- proto/umbra/conversations/private_v1.proto | 4 ---- proto/umbra/encryption.proto | 8 -------- 2 files changed, 12 deletions(-) diff --git a/proto/umbra/conversations/private_v1.proto b/proto/umbra/conversations/private_v1.proto index ab73a64..87e315f 100644 --- a/proto/umbra/conversations/private_v1.proto +++ b/proto/umbra/conversations/private_v1.proto @@ -4,10 +4,6 @@ package umbra.convos.private_v1; import "proto/umbra/common_frames.proto"; -message ConversationInvite_PrivateV1 { - // TODO: update placeholder fields - string participants = 1; -} message Placeholder { diff --git a/proto/umbra/encryption.proto b/proto/umbra/encryption.proto index 80d96ca..52a20b7 100644 --- a/proto/umbra/encryption.proto +++ b/proto/umbra/encryption.proto @@ -13,12 +13,4 @@ message Ecies { bytes tag = 3; } -// TODO: This also encompasses plaintexts, is there a better name? -// Alternatives: ??? -message EncryptedBytes { - oneof encryption { - Plaintext plaintext = 1; - Ecies ecies = 2; - } -} From 3bfd702716002cc9078820e3e8f5697a13dbce75 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:03:40 -0700 Subject: [PATCH 13/40] change import path --- proto/umbra/base.proto | 4 ++-- proto/umbra/conversations/private_v1.proto | 3 ++- rust/umbra-types/build.rs | 12 +++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/proto/umbra/base.proto b/proto/umbra/base.proto index 554d636..3c7e98d 100644 --- a/proto/umbra/base.proto +++ b/proto/umbra/base.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package umbra.base; -import "proto/umbra/encryption.proto"; -import "proto/umbra/invite.proto"; +import "encryption.proto"; +import "invite.proto"; /////////////////////////////////////////////////////////////////////////////// diff --git a/proto/umbra/conversations/private_v1.proto b/proto/umbra/conversations/private_v1.proto index 87e315f..43a6a73 100644 --- a/proto/umbra/conversations/private_v1.proto +++ b/proto/umbra/conversations/private_v1.proto @@ -2,7 +2,8 @@ syntax = "proto3"; package umbra.convos.private_v1; -import "proto/umbra/common_frames.proto"; +import "common_frames.proto"; + diff --git a/rust/umbra-types/build.rs b/rust/umbra-types/build.rs index 0e4a5e5..2e98da9 100644 --- a/rust/umbra-types/build.rs +++ b/rust/umbra-types/build.rs @@ -12,6 +12,8 @@ fn get_proto_dir() -> PathBuf { let mut proto_root_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); proto_root_dir.pop(); proto_root_dir.pop(); + proto_root_dir.push("proto"); + proto_root_dir.push("umbra"); println!("proto_dir: {:?}", proto_root_dir); proto_root_dir @@ -26,11 +28,11 @@ fn main() { config .compile_protos( &[ - "proto/umbra/base.proto", - "proto/umbra/common_frames.proto", - "proto/umbra/conversations/private_v1.proto", - "proto/umbra/encryption.proto", - "proto/umbra/invite.proto", + "base.proto", + "common_frames.proto", + "conversations/private_v1.proto", + "encryption.proto", + "invite.proto", ], // set proto_path &[get_proto_dir().to_str().unwrap()], From 47dd29b414db756e9d7a4a8a158fb90f70bd471f Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:52:54 -0700 Subject: [PATCH 14/40] Add nim implementation --- nim/.devcontainer/devcontainer.json | 14 ++++++++++++++ nim/.devcontainer/post_create.sh | 1 + nim/.gitignore | 2 ++ nim/chat_types.nimble | 13 +++++++++++++ nim/config.nims | 4 ++++ nim/src/chat_types.nim | 9 +++++++++ nim/tests/config.nims | 1 + nim/tests/test_encoding.nim | 16 ++++++++++++++++ 8 files changed, 60 insertions(+) create mode 100644 nim/.devcontainer/devcontainer.json create mode 100755 nim/.devcontainer/post_create.sh create mode 100644 nim/.gitignore create mode 100644 nim/chat_types.nimble create mode 100644 nim/config.nims create mode 100644 nim/src/chat_types.nim create mode 100644 nim/tests/config.nims create mode 100644 nim/tests/test_encoding.nim diff --git a/nim/.devcontainer/devcontainer.json b/nim/.devcontainer/devcontainer.json new file mode 100644 index 0000000..6661940 --- /dev/null +++ b/nim/.devcontainer/devcontainer.json @@ -0,0 +1,14 @@ +{ + "name": "UmbraTypes", + "image": "nimlang/nim", + "customizations": { + "vscode": { + "extensions": [ + "NimLang.nimlang", //Nim lang extension + "vadimcn.vscode-lldb", //for Nim debug + "streetsidesoftware.code-spell-checker" //spell checker + ] + } + }, + "postCreateCommand": "./.devcontainer/post_create.sh" +} \ No newline at end of file diff --git a/nim/.devcontainer/post_create.sh b/nim/.devcontainer/post_create.sh new file mode 100755 index 0000000..06c8937 --- /dev/null +++ b/nim/.devcontainer/post_create.sh @@ -0,0 +1 @@ +nimble install nimlangserver \ No newline at end of file diff --git a/nim/.gitignore b/nim/.gitignore new file mode 100644 index 0000000..2ca3f1f --- /dev/null +++ b/nim/.gitignore @@ -0,0 +1,2 @@ +nimble.develop +nimble.paths diff --git a/nim/chat_types.nimble b/nim/chat_types.nimble new file mode 100644 index 0000000..6f93a8d --- /dev/null +++ b/nim/chat_types.nimble @@ -0,0 +1,13 @@ +# Package + +version = "0.1.0" +author = "jazzz" +description = "Type definitions for the chat protocol" +license = "MIT" +srcDir = "src" +bin = @["dev"] # Remove + +# Dependencies +requires "nim >= 2.0.14" +requires "chronicles" +requires "protobuf_serialization" diff --git a/nim/config.nims b/nim/config.nims new file mode 100644 index 0000000..8ee48d2 --- /dev/null +++ b/nim/config.nims @@ -0,0 +1,4 @@ +# begin Nimble config (version 2) +when withDir(thisDir(), system.fileExists("nimble.paths")): + include "nimble.paths" +# end Nimble config diff --git a/nim/src/chat_types.nim b/nim/src/chat_types.nim new file mode 100644 index 0000000..51cb1ad --- /dev/null +++ b/nim/src/chat_types.nim @@ -0,0 +1,9 @@ +import protobuf_serialization +import protobuf_serialization/proto_parser + +import_proto3 "../../proto/umbra/base.proto" + +export protobuf_serialization + +# TODO: Do the Objects have to be listed manually? +export HistoryEntry \ No newline at end of file diff --git a/nim/tests/config.nims b/nim/tests/config.nims new file mode 100644 index 0000000..3bb69f8 --- /dev/null +++ b/nim/tests/config.nims @@ -0,0 +1 @@ +switch("path", "$projectDir/../src") \ No newline at end of file diff --git a/nim/tests/test_encoding.nim b/nim/tests/test_encoding.nim new file mode 100644 index 0000000..22e2b82 --- /dev/null +++ b/nim/tests/test_encoding.nim @@ -0,0 +1,16 @@ +import unittest + +import chat_types + +suite "Type Encoding Tests": + test "HistoryEntry roundtrip": + let x = chat_types.HistoryEntry(message_id: "12345", retrieval_hint: @[1'u8, 2, 3, 255]) + let encoded = Protobuf.encode(x) + let decoded = Protobuf.decode(encoded, HistoryEntry) + + check x.message_id == decoded.message_id + check x.retrieval_hint == decoded.retrieval_hint + check x == decoded + + test "Multiplication works": + check(2 * 3 == 6) \ No newline at end of file From 83f9918988085bd1cbb4fc41818e6823fd2455bc Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 2 Jul 2025 13:46:46 -0700 Subject: [PATCH 15/40] remove dev files --- nim/chat_types.nimble | 1 - 1 file changed, 1 deletion(-) diff --git a/nim/chat_types.nimble b/nim/chat_types.nimble index 6f93a8d..caaac4d 100644 --- a/nim/chat_types.nimble +++ b/nim/chat_types.nimble @@ -5,7 +5,6 @@ author = "jazzz" description = "Type definitions for the chat protocol" license = "MIT" srcDir = "src" -bin = @["dev"] # Remove # Dependencies requires "nim >= 2.0.14" From 6fb75ac5e91bce4450dfdd863fe0eaaacf4d99c3 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:19:02 -0700 Subject: [PATCH 16/40] Standardize package name --- nim/{chat_types.nimble => chat_proto.nimble} | 1 - nim/src/{chat_types.nim => chat_proto.nim} | 0 2 files changed, 1 deletion(-) rename nim/{chat_types.nimble => chat_proto.nimble} (89%) rename nim/src/{chat_types.nim => chat_proto.nim} (100%) diff --git a/nim/chat_types.nimble b/nim/chat_proto.nimble similarity index 89% rename from nim/chat_types.nimble rename to nim/chat_proto.nimble index 6f93a8d..caaac4d 100644 --- a/nim/chat_types.nimble +++ b/nim/chat_proto.nimble @@ -5,7 +5,6 @@ author = "jazzz" description = "Type definitions for the chat protocol" license = "MIT" srcDir = "src" -bin = @["dev"] # Remove # Dependencies requires "nim >= 2.0.14" diff --git a/nim/src/chat_types.nim b/nim/src/chat_proto.nim similarity index 100% rename from nim/src/chat_types.nim rename to nim/src/chat_proto.nim From 2d289dbd8bd20a86fe00aec3086b26931cec8a54 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:22:12 -0700 Subject: [PATCH 17/40] Update packagename --- nim/chat_proto.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nim/chat_proto.nimble b/nim/chat_proto.nimble index caaac4d..60db5d6 100644 --- a/nim/chat_proto.nimble +++ b/nim/chat_proto.nimble @@ -1,5 +1,5 @@ # Package - +packageName = "chat_proto" version = "0.1.0" author = "jazzz" description = "Type definitions for the chat protocol" From 54ebf403202651afdbfd719f25a34f3429c5b680 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:10:34 -0700 Subject: [PATCH 18/40] update tests --- nim/tests/test_encoding.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nim/tests/test_encoding.nim b/nim/tests/test_encoding.nim index 22e2b82..6d6adfa 100644 --- a/nim/tests/test_encoding.nim +++ b/nim/tests/test_encoding.nim @@ -1,10 +1,10 @@ import unittest -import chat_types +import chat_proto suite "Type Encoding Tests": test "HistoryEntry roundtrip": - let x = chat_types.HistoryEntry(message_id: "12345", retrieval_hint: @[1'u8, 2, 3, 255]) + let x = chat_proto.HistoryEntry(message_id: "12345", retrieval_hint: @[1'u8, 2, 3, 255]) let encoded = Protobuf.encode(x) let decoded = Protobuf.decode(encoded, HistoryEntry) From 63dbac27b2c768ca7a643aac8b33070b3868ec03 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 3 Jul 2025 08:32:29 -0700 Subject: [PATCH 19/40] remove proto3:optional for compat --- proto/umbra/base.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/umbra/base.proto b/proto/umbra/base.proto index 3c7e98d..61fdf35 100644 --- a/proto/umbra/base.proto +++ b/proto/umbra/base.proto @@ -35,7 +35,7 @@ message HistoryEntry { int32 lamport_timestamp = 10; repeated HistoryEntry causal_history = 11; bytes bloom_filter = 12; - optional bytes content = 20; + bytes content = 20; } From b9a2fc8c6e7c6bebfe9253b7377554b2488b0307 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 3 Jul 2025 08:33:04 -0700 Subject: [PATCH 20/40] added symlink for nim protos --- nim/proto | 1 + nim/src/chat_proto.nim | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 120000 nim/proto diff --git a/nim/proto b/nim/proto new file mode 120000 index 0000000..9be3401 --- /dev/null +++ b/nim/proto @@ -0,0 +1 @@ +../proto/umbra \ No newline at end of file diff --git a/nim/src/chat_proto.nim b/nim/src/chat_proto.nim index 51cb1ad..bbbfcee 100644 --- a/nim/src/chat_proto.nim +++ b/nim/src/chat_proto.nim @@ -1,9 +1,10 @@ import protobuf_serialization import protobuf_serialization/proto_parser -import_proto3 "../../proto/umbra/base.proto" +import_proto3 "../proto/base.proto" export protobuf_serialization # TODO: Do the Objects have to be listed manually? -export HistoryEntry \ No newline at end of file +export HistoryEntry + From 1fb7f614073672c84fd2ab36929e9cbd70b561eb Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 3 Jul 2025 09:05:15 -0700 Subject: [PATCH 21/40] change folder structure --- nim/{src => }/chat_proto.nim | 2 +- nim/chat_proto.nimble | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) rename nim/{src => }/chat_proto.nim (83%) diff --git a/nim/src/chat_proto.nim b/nim/chat_proto.nim similarity index 83% rename from nim/src/chat_proto.nim rename to nim/chat_proto.nim index bbbfcee..a0da39d 100644 --- a/nim/src/chat_proto.nim +++ b/nim/chat_proto.nim @@ -1,7 +1,7 @@ import protobuf_serialization import protobuf_serialization/proto_parser -import_proto3 "../proto/base.proto" +import_proto3 "./proto/base.proto" export protobuf_serialization diff --git a/nim/chat_proto.nimble b/nim/chat_proto.nimble index 60db5d6..a30b2b1 100644 --- a/nim/chat_proto.nimble +++ b/nim/chat_proto.nimble @@ -4,9 +4,10 @@ version = "0.1.0" author = "jazzz" description = "Type definitions for the chat protocol" license = "MIT" -srcDir = "src" +bin = @["chat_proto"] # Dependencies requires "nim >= 2.0.14" requires "chronicles" requires "protobuf_serialization" +requires "protobuf" From 247323fd6c6d5ed939e67d780a59c1752a229c37 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:04:13 -0700 Subject: [PATCH 22/40] refactor protos --- proto/umbra/base.proto | 68 ----------------------------------- proto/umbra/encryption.proto | 10 ++++++ proto/umbra/envelope.proto | 16 +++++++++ proto/umbra/inbox.proto | 12 +++++++ proto/umbra/reliability.proto | 23 ++++++++++++ 5 files changed, 61 insertions(+), 68 deletions(-) delete mode 100644 proto/umbra/base.proto create mode 100644 proto/umbra/envelope.proto create mode 100644 proto/umbra/inbox.proto create mode 100644 proto/umbra/reliability.proto diff --git a/proto/umbra/base.proto b/proto/umbra/base.proto deleted file mode 100644 index 61fdf35..0000000 --- a/proto/umbra/base.proto +++ /dev/null @@ -1,68 +0,0 @@ -syntax = "proto3"; - -package umbra.base; - -import "encryption.proto"; -import "invite.proto"; - - -/////////////////////////////////////////////////////////////////////////////// -// Protocol Frames -/////////////////////////////////////////////////////////////////////////////// - -message InboxV1Frame { - string recipient = 1; - // string conversation_type = 2; - oneof frame_type { - invite.InvitePrivateV1 invite_private_v1 = 10; - } -} - - -/////////////////////////////////////////////////////////////////////////////// -// SDS Payloads -/////////////////////////////////////////////////////////////////////////////// - -message HistoryEntry { - string message_id = 1; // Unique identifier of the SDS message, as defined in `Message` - bytes retrieval_hint = 2; // Optional information to help remote parties retrieve this SDS - // message; For example, A Waku deterministic message hash or routing payload hash - } - - message ReliableBytes { - string message_id = 2; - string channel_id = 3; - int32 lamport_timestamp = 10; - repeated HistoryEntry causal_history = 11; - bytes bloom_filter = 12; - bytes content = 20; - } - - -/////////////////////////////////////////////////////////////////////////////// -// Encryption -/////////////////////////////////////////////////////////////////////////////// - -// TODO: This also encompasses plaintexts, is there a better name? -// Alternatives: ??? -message EncryptedBytes { - - oneof encryption { - encryption.Plaintext plaintext = 1; - encryption.Ecies ecies = 2; - } -} - - -/////////////////////////////////////////////////////////////////////////////// -// Payload Framing Messages -/////////////////////////////////////////////////////////////////////////////// - - -message UmbraEnvelopeV1 { - - string conversation_hint = 1; - uint64 salt = 2; - - bytes payload = 5; -} \ No newline at end of file diff --git a/proto/umbra/encryption.proto b/proto/umbra/encryption.proto index 52a20b7..f4a3c8c 100644 --- a/proto/umbra/encryption.proto +++ b/proto/umbra/encryption.proto @@ -3,6 +3,16 @@ syntax = "proto3"; package umbra.encryption; +// TODO: This also encompasses plaintexts, is there a better name? +// Alternatives: ??? +message EncryptedPayload { + + oneof encryption { + encryption.Plaintext plaintext = 1; + encryption.Ecies ecies = 2; + } +} + message Plaintext { bytes payload=1; } diff --git a/proto/umbra/envelope.proto b/proto/umbra/envelope.proto new file mode 100644 index 0000000..9bc8a6e --- /dev/null +++ b/proto/umbra/envelope.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package umbra.envelope; + + +/////////////////////////////////////////////////////////////////////////////// +// Payload Framing Messages +/////////////////////////////////////////////////////////////////////////////// + +message UmbraEnvelopeV1 { + + string conversation_hint = 1; + uint64 salt = 2; + + bytes payload = 5; +} \ No newline at end of file diff --git a/proto/umbra/inbox.proto b/proto/umbra/inbox.proto new file mode 100644 index 0000000..1707189 --- /dev/null +++ b/proto/umbra/inbox.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package umbra.inbox; + +import "invite.proto"; + +message InboxV1Frame { + string recipient = 1; + oneof frame_type { + invite.InvitePrivateV1 invite_private_v1 = 10; + } +} \ No newline at end of file diff --git a/proto/umbra/reliability.proto b/proto/umbra/reliability.proto new file mode 100644 index 0000000..3c433c3 --- /dev/null +++ b/proto/umbra/reliability.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package umbra.reliability; + +/////////////////////////////////////////////////////////////////////////////// +// SDS Payloads +/////////////////////////////////////////////////////////////////////////////// + +message HistoryEntry { + string message_id = 1; // Unique identifier of the SDS message, as defined in `Message` + bytes retrieval_hint = 2; // Optional information to help remote parties retrieve this SDS + // message; For example, A Waku deterministic message hash or routing payload hash + } + + message ReliablePayload { + string message_id = 2; + string channel_id = 3; + int32 lamport_timestamp = 10; + repeated HistoryEntry causal_history = 11; + bytes bloom_filter = 12; + // Optional field causes errors in nim protobuf generation. Removing for now as optional is implied anways. + bytes content = 20; + } From 2b6d4910d2cc47e3e036cd07aae70f97c484aeb1 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:04:34 -0700 Subject: [PATCH 23/40] update rust types --- rust/umbra-types/build.rs | 4 +++- rust/umbra-types/src/payload.rs | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/rust/umbra-types/build.rs b/rust/umbra-types/build.rs index 2e98da9..b2f5515 100644 --- a/rust/umbra-types/build.rs +++ b/rust/umbra-types/build.rs @@ -28,11 +28,13 @@ fn main() { config .compile_protos( &[ - "base.proto", + "envelope.proto", "common_frames.proto", "conversations/private_v1.proto", "encryption.proto", + "inbox.proto", "invite.proto", + "reliability.proto" ], // set proto_path &[get_proto_dir().to_str().unwrap()], diff --git a/rust/umbra-types/src/payload.rs b/rust/umbra-types/src/payload.rs index 9d13b0a..3b136f5 100644 --- a/rust/umbra-types/src/payload.rs +++ b/rust/umbra-types/src/payload.rs @@ -6,8 +6,10 @@ use prost::Message; pub use types::umbra::*; use crate::{ - base::{EncryptedBytes, InboxV1Frame, UmbraEnvelopeV1, inbox_v1_frame}, convos::private_v1::{PrivateV1Frame, private_v1_frame}, + encryption::EncryptedPayload, + envelope::UmbraEnvelopeV1, + inbox::{InboxV1Frame, inbox_v1_frame}, }; impl PrivateV1Frame { @@ -33,7 +35,7 @@ pub trait ToEnvelope { fn to_envelope(self, conversation_id: String, salt: u64) -> UmbraEnvelopeV1; } -impl ToEnvelope for EncryptedBytes { +impl ToEnvelope for EncryptedPayload { fn to_envelope(self, conversation_id: String, salt: u64) -> UmbraEnvelopeV1 { UmbraEnvelopeV1 { conversation_hint: conversation_id, // TODO @@ -48,14 +50,18 @@ mod tests { use super::*; use prost::Message; + use crate::reliability::ReliablePayload; + use crate::common_frames::ContentFrame; + + #[test] fn test_private_v1_roundtrip() { let text = "Hello, World!".to_string(); let msg = PrivateV1Frame { conversation_id: "conversationId".to_string(), - frame_type: Some(convos::private_v1::private_v1_frame::FrameType::Content( - common_frames::ContentFrame { + frame_type: Some(private_v1_frame::FrameType::Content( + ContentFrame { domain: 0, tag: 0, bytes: text.encode_to_vec(), @@ -63,21 +69,21 @@ mod tests { )), }; - let reliable = base::ReliableBytes { + let reliable = ReliablePayload { message_id: "msg_id".into(), channel_id: msg.conversation_id.clone(), lamport_timestamp: 0, causal_history: vec![], bloom_filter: vec![1, 2, 3, 4], - content: Some(msg.encode_to_vec()), + content: msg.encode_to_vec(), }; let buf = reliable.encode_to_vec(); - let reliable_msg = base::ReliableBytes::decode(&*buf).unwrap(); + let reliable_msg = ReliablePayload::decode(&*buf).unwrap(); let msg_from_bytes = - convos::private_v1::PrivateV1Frame::decode(&*reliable_msg.content.unwrap()) + PrivateV1Frame::decode(&*reliable_msg.content) .expect("Failed to decode message"); assert_eq!( From b413517c16024dbda47f17cd6bb6edb26c577766 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:46:28 -0700 Subject: [PATCH 24/40] update conversations.md --- specs/conversations.md | 71 ++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/specs/conversations.md b/specs/conversations.md index 171557b..5257279 100644 --- a/specs/conversations.md +++ b/specs/conversations.md @@ -7,58 +7,73 @@ tags: chat editor: Jazz Alyxzander contributors: --- -## Abstract +# Abstract This specification outlines the base message structure for the Conversations protocol. -## Background / Rationale / Motivation +# Background / Rationale / Motivation Modern communication systems require a reliable and flexible way to establish and manage conversations between participants. While simple message-passing protocols exist, they lack the ability to manage state across a stream of messages. -The Conversations Protocol addresses this gap by providing a clear and standardized way for participants to initiate, conversations in decentralized environments. +The Conversations Protocol addresses this gap by defining a mechanism to establish long term communication sessions in decentralized environments. +# Theory / Semantics -## Theory / Semantics - -[TODO: remove this link, by sumamrizing and embedding in the spec so its standalone] +[TODO: remove this link, by summarizing and embedding in the spec so its standalone] This is a lightweight framework for defining Conversation micro-protocols. The high level Conversations approach outlined [here](https://forum.vac.dev/t/chatsdk-conversations/509). The intention is to provide the minimal building blocks for a wide range of micro-protocols, while allowing ConversationTypes to remain as flexible as possible. +At a high level this Protocol defines what a ConversationType is, it's requirements, and a mechanism for initializing Conversations with others. -### Conversations -A conversation is responsible for defining its encryption, encoding, and message types which are necessary for operation. +**Frame**: + +## Conversations +A ConversationType is a specification which defines a method for clients to communicate. +Each ConversationType defines its own encryption, encoding, and message types which are necessary for operation. A ConversationType MUST define which content topics are valid places to receive messages. A ConversationType MUST define which encryption scheme is used A ConversationType MUST define which Frames are valid. - Clients MUST be able to decode frames deterministically. - A ConversationType SHOULD define membership requirements and limitations. -Every Conversation instance MUST have a conversation_id. +A Conversation is an instance of a particular ConversationType which contains the associated state such as membership, encryption parameters, names etc. + +### ConversationIdentifiers + +A Conversation instance MUST a conversationd_id and the `conversation_id` MUST uniquely identify the conversation. +[TODO: Should more guidance be added later? e,g ///] -**Decoding** -Clients determine how to decode a byte sequence by the `conversation_id`. - -[TODO: Should we add more guidance later? e,g ///] -`conversation_id's` must be uniquely identify which encryption state to. As there may be many ConversationTypes defined, specifications should be mindful of conflicts. - -Clients only accepts envelopes with known `conversation_id's`. All others can be discarded as there is insufficient information to properly decrypt/decode the messages. +## Default Inbox +The default inbox allows clients to discover new conversations asynchronously without prior coordination. By listening in a static location -**Framing** -To maintain a deterministic decoding tree, all frames on the wire MUST be wrapped with a `UmbraEnvelope`. -This provides a common type for all frames. - -Underneath UmbraEnvelope, ConversationTypes are free to wrap frames as desired, however it is recommended to use `EncryptedFrame` to ensure clients can decode messages regardless of software version. +To achieve this all clients MUST implement a default Inbox with `inbox_address = HASH(client_address)`. [TODO: Define hash here, or embed as part of the Inbox spec] +See [Inbox](./inbox.md) for more information. -**Conversation Hinting** +As the clients address is directly linked to the content_topic there is some metadata leakage, and this pathway SHOULD only be used as a last resort. + +## Framing +To disambiguate between different logical layers, payload types sent by a Conversation are referred to as `Frames`. + +Conversations are free to determine which frames are needed for their specific use cases, with these caveats: +- The outer most frame MUST be wrapped in an UmbraEnvelope. +- All frames MUST be able to be decoded deterministically. [TODO: Unambiguously a better choice?] + +Deterministic decoding means that clients can always classify a envelope as 1 of 3 states: Readable, BadlyFormed, Addressed to someone else. + + +### Conversation Hinting [TODO: Needs lots of work] + +UmbraEnvelopes enable deterministic decoding by containing a reference to the conversation which this message belongs. Clients only accepts envelopes with known `conversation_hints's`. All others can be discarded as there is insufficient information to properly decrypt/decode the messages. + + Conversation identifiers (conversation_id) have the potential to leak sensitive metadata if exposed in cleartext. Frames sharing the same conversation_id could allow observers to infer social graph relationships, user activity patterns, or conversation linkage, depending on how conversation_id values are used by the specific ConversationType. To mitigate this risk and provide a safer default for Conversation implementors, conversation_id values SHOULD be obscured in a way that prevents observers from linking frames belonging to the same conversation. @@ -67,12 +82,6 @@ To mitigate this risk and provide a safer default for Conversation implementors, -### Default Inbox -To provide a baseline for interaction, clients need a method to receive initial frames/invites. To achieve this all clients MUST implement a default Inbox with `inbox_address = HASH(client_address)`. [TODO: Define hash here, or embed as part of the Inbox spec] - -See [Inbox](./inbox.md) for more information. - - ## Wire Format Specification / Syntax The wire format is specified using protocol buffers v3. @@ -99,8 +108,10 @@ if available, point to existing implementations for reference. ## Security/Privacy Considerations -[TODO: cover Conversation_id -> Message Types ] +Messages sent to the default inbox are linkable to an client (as it is derived from the clients address). This means that if a target client address is known to an observer, they can determine if any messages were sent to the target using the default inbox. In this case the Envelopes contain no sender information, so this does not leak social graph information. + +Messages sent via different pathways would have their own privacy guarantees. ## Copyright From fa7a03dbe517d1bd42f9702edd2b3341d5d7cc74 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 3 Jul 2025 22:28:36 -0700 Subject: [PATCH 25/40] update inbox.md --- specs/inbox.md | 82 ++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/specs/inbox.md b/specs/inbox.md index ed695ca..3f399a8 100644 --- a/specs/inbox.md +++ b/specs/inbox.md @@ -1,75 +1,70 @@ --- title: INBOX -name: Inbound messages +name: Inbound message queues category: Standards Track status: raw tags: chat editor: Jazz Alyxzander contributors: --- -## Abstract +# Abstract -## Background / Rationale / Motivation +# Background / Rationale / Motivation +Clients must be able to receive frames before conversations can be initialized. While its possible to coordinate a content topic out of band, that becomes limiting factor to growth. +# Theory / Semantics -## Theory / Semantics - -Inboxes are inbound only conversation types, which allow a client to recieve messages from contacts. +Inboxes are inbound only conversation types, which allow a client to receive messages from contacts. An inbox does not have a defined set of participants, and is used to receive messages when there does not exist and established channel between contacts. -All messages to an inbox MUST be encrypted. [TODO: Reason] -### Accepted types -Inboxes SHOULD attempt to handle all envelopes sent with the conversation_id `/inbox/v1/`. - -### Default Inbox - -[Differentiate between Inbox the place you recieve and the Protocol -- Is htere a difference] -An inbox is the default conversation type. -It is a catch-all for all messages which +An inbox does not have an inherent keypair or identity associated with it - it's an agreed upon location to recieve messages. +## Creation -[TODO: is domain separation needed here or should we save some bytes?] +Inboxes do not need to be "created", and there is no required initialization. -**Encryption** - -All Frames sent to the INBOX must be encrypted. - -Frames CAN be encrypted - -**Content Topic** +## Content Topic // TODO: Inbox Topics will be defined in ContactBundles, allowing for dynamic topic usage - All clients must listen for messages posted with the content topic `/inbox/` -### Invites -Conversations are required to define their own Invites, which contain the required information to bootstrap new participants. -[TODO: InviteV1] +## Accepted types + + + +## Encryption + +All Frames sent to the Inbox MUST be encrypted to maintain message confidentiality. + +This protocol uses a [KN noise handshake](https://noiseexplorer.com/patterns/KN/) to secure inbound messages. + +Recipients S,E is sent out of band. Messages sent in this manner do not benefit from sender authentication. Contents need to be validated prior to being trusted. + + + +## Framing + +[TODO: Is there benefit to using SDS in this case? If all messages are invites and communication occurs else where, is this just wasting bytes?] +```mermaid +flowchart TD + UmbraEnvelopeV1 <--> EncryptedPayload + EncryptedPayload <--> D{En/Decrypt} + D <--> ReliableBytes + ReliableBytes --> InboxV1Frame + +``` ### EncryptedBytes The EncryptedBytes message is a self-describing wrapper for all encrypted payloads. As the protocol grows it will include potentially different encryption mechanisms. This message type makes no assumptions about the encryption used an allows new conversation types to use the same messaging framework. -[TODO: Why isn't this defined within the conversation frames like SDS?] - - - -### Invites - -Individual Conversations are responsible for defining a payload which will be used to initialize remote participants. - -#### Invite Encryption - -Invite - - ## Wire Format Specification / Syntax @@ -77,6 +72,13 @@ The wire format is specified using protocol buffers v3. ```mermaid +message InboxV1Frame { + string recipient = 1; + oneof frame_type { + ... supported invite types + } +} + message EncryptedBytes { oneof encryption { From 7b6c81f1bfaf6148b680145c976705383cf15729 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:32:59 -0700 Subject: [PATCH 26/40] updated nim types --- nim/chat_proto.nim | 11 ++++++++++- nim/chat_proto.nimble | 5 +++-- nim/proto | 1 - 3 files changed, 13 insertions(+), 4 deletions(-) delete mode 120000 nim/proto diff --git a/nim/chat_proto.nim b/nim/chat_proto.nim index a0da39d..740163e 100644 --- a/nim/chat_proto.nim +++ b/nim/chat_proto.nim @@ -1,10 +1,19 @@ import protobuf_serialization import protobuf_serialization/proto_parser -import_proto3 "./proto/base.proto" +import_proto3 "../proto/umbra/encryption.proto" +import_proto3 "../proto/umbra/envelope.proto" +import_proto3 "../proto/umbra/inbox.proto" +import_proto3 "../proto/umbra/reliability.proto" export protobuf_serialization # TODO: Do the Objects have to be listed manually? +export EncryptedPayload export HistoryEntry +export InboxV1Frame +export ReliablePayload +export UmbraEnvelopeV1 + + diff --git a/nim/chat_proto.nimble b/nim/chat_proto.nimble index a30b2b1..706c68e 100644 --- a/nim/chat_proto.nimble +++ b/nim/chat_proto.nimble @@ -4,10 +4,11 @@ version = "0.1.0" author = "jazzz" description = "Type definitions for the chat protocol" license = "MIT" -bin = @["chat_proto"] +# bin = @["chat_proto"] +# installDirs = @["../proto"] + # Dependencies requires "nim >= 2.0.14" requires "chronicles" requires "protobuf_serialization" -requires "protobuf" diff --git a/nim/proto b/nim/proto deleted file mode 120000 index 9be3401..0000000 --- a/nim/proto +++ /dev/null @@ -1 +0,0 @@ -../proto/umbra \ No newline at end of file From 90b3154c12df71f6194baf1ed9bf1e954858551b Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:36:36 -0700 Subject: [PATCH 27/40] newline cleanup --- nim/.devcontainer/post_create.sh | 2 +- nim/tests/config.nims | 2 +- proto/umbra/common_frames.proto | 2 +- proto/umbra/encryption.proto | 2 -- proto/umbra/envelope.proto | 2 +- proto/umbra/inbox.proto | 2 +- proto/umbra/invite.proto | 2 +- rust/.devcontainer/postcreate.sh | 2 +- 8 files changed, 7 insertions(+), 9 deletions(-) diff --git a/nim/.devcontainer/post_create.sh b/nim/.devcontainer/post_create.sh index 06c8937..8581b5a 100755 --- a/nim/.devcontainer/post_create.sh +++ b/nim/.devcontainer/post_create.sh @@ -1 +1 @@ -nimble install nimlangserver \ No newline at end of file +nimble install nimlangserver diff --git a/nim/tests/config.nims b/nim/tests/config.nims index 3bb69f8..80091ff 100644 --- a/nim/tests/config.nims +++ b/nim/tests/config.nims @@ -1 +1 @@ -switch("path", "$projectDir/../src") \ No newline at end of file +switch("path", "$projectDir/../src") diff --git a/proto/umbra/common_frames.proto b/proto/umbra/common_frames.proto index 85dd08c..453c00f 100644 --- a/proto/umbra/common_frames.proto +++ b/proto/umbra/common_frames.proto @@ -6,4 +6,4 @@ message ContentFrame { uint32 domain = 1; uint32 tag = 2; bytes bytes = 3; -} \ No newline at end of file +} diff --git a/proto/umbra/encryption.proto b/proto/umbra/encryption.proto index f4a3c8c..a579382 100644 --- a/proto/umbra/encryption.proto +++ b/proto/umbra/encryption.proto @@ -22,5 +22,3 @@ message Ecies { bytes ephemeral_pubkey = 2; bytes tag = 3; } - - diff --git a/proto/umbra/envelope.proto b/proto/umbra/envelope.proto index 9bc8a6e..0b0d2ea 100644 --- a/proto/umbra/envelope.proto +++ b/proto/umbra/envelope.proto @@ -13,4 +13,4 @@ message UmbraEnvelopeV1 { uint64 salt = 2; bytes payload = 5; -} \ No newline at end of file +} diff --git a/proto/umbra/inbox.proto b/proto/umbra/inbox.proto index 1707189..ad4fed4 100644 --- a/proto/umbra/inbox.proto +++ b/proto/umbra/inbox.proto @@ -9,4 +9,4 @@ message InboxV1Frame { oneof frame_type { invite.InvitePrivateV1 invite_private_v1 = 10; } -} \ No newline at end of file +} diff --git a/proto/umbra/invite.proto b/proto/umbra/invite.proto index 38cfb76..c94b442 100644 --- a/proto/umbra/invite.proto +++ b/proto/umbra/invite.proto @@ -4,4 +4,4 @@ package umbra.invite; message InvitePrivateV1 { repeated string participants = 1; -} \ No newline at end of file +} diff --git a/rust/.devcontainer/postcreate.sh b/rust/.devcontainer/postcreate.sh index 8bf99d5..0783b61 100755 --- a/rust/.devcontainer/postcreate.sh +++ b/rust/.devcontainer/postcreate.sh @@ -1,2 +1,2 @@ sudo apt-get update -sudo apt-get install -y protobuf-compiler \ No newline at end of file +sudo apt-get install -y protobuf-compiler From dc62e5ceaa9a3055718cd22c9ddfb83717a1a708 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:37:06 -0700 Subject: [PATCH 28/40] remove placeholder tests --- nim/tests/test_encoding.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/nim/tests/test_encoding.nim b/nim/tests/test_encoding.nim index 6d6adfa..a1ad7ba 100644 --- a/nim/tests/test_encoding.nim +++ b/nim/tests/test_encoding.nim @@ -11,6 +11,3 @@ suite "Type Encoding Tests": check x.message_id == decoded.message_id check x.retrieval_hint == decoded.retrieval_hint check x == decoded - - test "Multiplication works": - check(2 * 3 == 6) \ No newline at end of file From efb4635c57a7108f44a03c608c9b799fd4eaac9e Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Sat, 5 Jul 2025 15:21:16 -0700 Subject: [PATCH 29/40] Updated readme --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f12ed0..48999ec 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ -# chat_proto \ No newline at end of file +# chat_proto +![State](https://img.shields.io/badge/State-WIP-red) + + +This repository contains the core implementation of the $CHAT_PROTO types. + +To make them easy to use, implementations in Rust and Nim are provided developers can focus on building. + + +## Structure + +- **proto:** Protobuf definitions for Core and Conversation types +- **nim:** nimble package for generated types in nim +- **rust:** cargo crate for generated types in rust +- **specs:** current home of the specifications - these will likely be moved out of the repo. They are currently in lockstep with the type definitions which minimizes desync. + +## Related Repositories + +- [Nim POC](https://github.com/waku-org/nim-chat-poc/tree/jazzz/inbox) - This is a demo of the types being consumed in`nim` + - Importing packages from a monorepo appears to be broken in `nimble`, as a short term work around. The `.proto` files have been embedded in the POC, and will be removed once resolved + +- [Rust POC](https://github.com/jazzz/umbra) - This is demo of the types being consumed in `rust` + From 6e3e08b4f74cb0e1dc3d2d00ae84a580ddc616e7 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 7 Jul 2025 06:51:52 -0700 Subject: [PATCH 30/40] fix protobuf syntac highlighting --- specs/conversations.md | 4 ++-- specs/inbox.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/conversations.md b/specs/conversations.md index 5257279..9aed0e9 100644 --- a/specs/conversations.md +++ b/specs/conversations.md @@ -44,7 +44,7 @@ A Conversation is an instance of a particular ConversationType which contains th ### ConversationIdentifiers -A Conversation instance MUST a conversationd_id and the `conversation_id` MUST uniquely identify the conversation. +A Conversation instance MUST a conversation_id and the `conversation_id` MUST uniquely identify the conversation. [TODO: Should more guidance be added later? e,g ///] @@ -86,7 +86,7 @@ To mitigate this risk and provide a safer default for Conversation implementors, The wire format is specified using protocol buffers v3. -```mermaid +```protobuf message UmbraEnvelopeV1 { diff --git a/specs/inbox.md b/specs/inbox.md index 3f399a8..dd27813 100644 --- a/specs/inbox.md +++ b/specs/inbox.md @@ -70,7 +70,7 @@ The EncryptedBytes message is a self-describing wrapper for all encrypted payloa ## Wire Format Specification / Syntax The wire format is specified using protocol buffers v3. -```mermaid +```protobuf message InboxV1Frame { string recipient = 1; From 9ee46738a928408b3648b9270cf6b6030819f6e7 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:19:31 -0700 Subject: [PATCH 31/40] encryption definition --- specs/inbox.md | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/specs/inbox.md b/specs/inbox.md index dd27813..a7c1c91 100644 --- a/specs/inbox.md +++ b/specs/inbox.md @@ -17,12 +17,14 @@ Clients must be able to receive frames before conversations can be initialized. # Theory / Semantics Inboxes are inbound only conversation types, which allow a client to receive messages from contacts. -An inbox does not have a defined set of participants, and is used to receive messages when there does not exist and established channel between contacts. +An inbox does not have a defined set of participants, and is used to receive messages when there is not an established channel between contacts. An inbox does not have an inherent keypair or identity associated with it - it's an agreed upon location to recieve messages. -## Creation +## Inbox Identifiers + +Inboxes are Identified by a Inboxes do not need to be "created", and there is no required initialization. @@ -32,6 +34,8 @@ Inboxes do not need to be "created", and there is no required initialization. // TODO: Inbox Topics will be defined in ContactBundles, allowing for dynamic topic usage All clients must listen for messages posted with the content topic `/inbox/` +`lower_hex(hash(/inbox/)` + ## Accepted types @@ -42,11 +46,35 @@ All clients must listen for messages posted with the content topic `/inbox/ e, ee, es + ``` +In this case the responder provides both `s` and `e` out of band. +The handshakes primary purpose is to provide sender confidentiality, with some forward secrecy. The handshake is similar to a one way N handshake with a recipient side ephemeral key. + +Note this channel does not not provide sender authentication, and should only be used to implement a confidential message delivery with some forward secrecy. This limitation is intentional to maintain O-RTT encryption. As this is an inbound pathway further messages to establish mutual authentication with identity hiding would be wasteful. + +### Ciphersuite + +The noise handshake is implemented with the following functions: + +**DH:** X25519 +**cipher:** AEAD_CHACHA20_POLY1305 +**hash:** BLAKE2s + +The noise protocol name would then be `Noise_KNfallback_25519_ChaChaPoly_BLAKE2s` + +This protocol opts for 32bit variants to optimize for mobile and resource constrained environments. + +### Endianness +[TODO: The Noiseprotocol specification recommends BigEndian length fields - Need to define if this protocol will deviate] ## Framing @@ -111,7 +139,7 @@ if available, point to existing implementations for reference. ## Security/Privacy Considerations - +The encryption scheme used does not provide any sender authentication. Messages sent over this pathway need to validate the sender before trusting any of the contents. ## Copyright From 00e2c87c46ef20096e5be5447dc1e5e7bb509a12 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Tue, 8 Jul 2025 18:32:17 -0700 Subject: [PATCH 32/40] separated conversation requirements from spec --- specs/.md | 119 +++++++++++++++++++++++++++++++++++++++++ specs/conversations.md | 99 +++++++++++++--------------------- 2 files changed, 157 insertions(+), 61 deletions(-) create mode 100644 specs/.md diff --git a/specs/.md b/specs/.md new file mode 100644 index 0000000..4ae090b --- /dev/null +++ b/specs/.md @@ -0,0 +1,119 @@ +--- +title: > +name: A private decentralized messaging protocol for multiple usecases. +category: Standards Track +status: raw +tags: chat +editor: Jazz Alyxzander +contributors: +--- +# Abstract + +This specification defines a modular communication protocol designed to provide a privacy focused approach to common messaging patterns. The protocol employs a lean root layer that defers to independent sub-protocols for defining communication behavior, with versioning as a first-class concept for handling protocol evolution. + +# Background / Rationale / Motivation + +Traditionally communication protocols face several critical challenges as they grow. + +Different communication scenarios have vastly different requirements. A protocol optimized for high-volume public broadcasts performs poorly for intimate encrypted conversations, and vice versa. Monolithic protocols cannot optimize for these diverse use cases simultaneously. + +Once widely deployed, communication protocols become difficult to modify. Even minor changes can break compatibility across clients, leading to protocol stagnation and eventually ossification. + +When decentralized protocols do attempt to evolve, versioning becomes a complex negotiation problem. Clients must maintain backward compatibility with multiple protocol versions, leading to bloated implementations and coupling which increases complexity. + +At each stage protocols become increasingly difficult to modify, which slows down forward progress. + +A preferred approach would be to practice resiliency at the protocol level by focusing on versioning from the beginning. + +# Theory / Semantics + +This protocol is a lean coordination layer which provides the backbone for smaller independent "Conversation" sub-protocols. Conversation protocols completely define a pathway for communication. This root protocol provides common functionality to support a wide array of communication use cases. + +```mermaid +flowchart TD + P(%PROTO) --> C1(Conversation) + P --> C2(Conversation) + P --> C3(....) + style C3 fill:#FFFFFF,stroke:#,stroke-width:1px,stroke-dasharray: 5 1 +``` + +This protocol is intentionally light, as the work is deferred to the actual conversation protocols. + +## Versioning + +Incompatible versions of a conversation protocol result in distinct conversationTypes (E.g. PrivateV1 vs PrivateV3). Individual conversationTypes can implement functionality to migrate participants to a conversation. By making this a conversationType issue, different use cases can choose a system that works for them. + + +## Conversations +Conversations can be created permissionlessly within the %PROTO, as there is no required registration. Developers are free to use any given conversation type as long as all intended participants support it. + +ConversationTypes MUST adhere to the [Conversations](./conversations.md) specification in order to maintain compatibility, however network adoption is not q requirement. + + +## Default Inbox +There exists a circular dependency in initializing a Conversation. Conversations require an established conversation to send an invitation, yet at the beginning no channel exists between contacts. + +To resolve this all clients MUST implement a default [Inbox](./inbox.md) to receive initial messages. The default inbox allows clients to discover new conversations asynchronously without prior coordination. By listening in a static location. + + +The default inbox MUST be configured with the parameters: +- **inbox_addr:** `client_address` +- **discriminator:** "default" + +As the clients address is directly linked to the content_topic there is some metadata leakage, and this pathway SHOULD only be used as a last resort. + +## Envelopes +As clients can support multiple sub-protocols simultaneously, a mechanism is required to determine how decode messages. + +All payloads sent MUST be deterministically decodable. Deterministic decoding means that clients can always classify a envelope as 1 of 3 states: Readable, BadlyFormed, Addressed to someone else. To support this %PROTO uses an Envelope to link encrypted payloads with the information required to process them. As this payload is sent in the clear the payloads cannot contain any identifiable information. + + + +### Conversation Hinting +Conversation_ids are sufficient to allow clients to lookup the required encryption parameters and decrypt a message. However this would leak Conversation metadata. Instead a hint is used which allows clients to check if this message is of interest. + +Messages with an unknown hint can be safely disregarded. + +ConversationHints are computed by using a salted hash of the `conversationId`. specifically defined as `lowercase_hex(blake2s(conversation_id))` +[TODO: Should conversations define their own hinting?] + + + +## Wire Format Specification / Syntax + +The wire format is specified using protocol buffers v3. + +```protobuf + +message UmbraEnvelopeV1 { + + string conversation_hint = 1; + uint32 salt = 2; + + EncryptedBytes encrypted_bytes = 10; +} + + +``` + +## Implementation Suggestions (optional) + +### User level Conversations +A ConversationType defines how to send and receive messages. Developer SHOULD mask the conversationType from users. Developers should use an independent identifier in their apps for user-level "conversations" so that the protocols can update freely. + +## (Further Optional Sections) + + +## Security/Privacy Considerations + +Messages sent to the default inbox are linkable to an client (as it is derived from the clients address). This means that if a target client address is known to an observer, they can determine if any messages were sent to the target using the default inbox. In this case the Envelopes contain no sender information, so this does not leak social graph information. + +Messages inherit the privacy and security properties of the ConversationType used to send them. Please refer to the corresponding specifications when analyzing properties. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +## References + +A list of references. diff --git a/specs/conversations.md b/specs/conversations.md index 9aed0e9..8216a3c 100644 --- a/specs/conversations.md +++ b/specs/conversations.md @@ -9,7 +9,7 @@ contributors: --- # Abstract -This specification outlines the base message structure for the Conversations protocol. +This specification outlines the requirements for defining a ConversationType # Background / Rationale / Motivation @@ -21,83 +21,63 @@ The Conversations Protocol addresses this gap by defining a mechanism to establi # Theory / Semantics -[TODO: remove this link, by summarizing and embedding in the spec so its standalone] -This is a lightweight framework for defining Conversation micro-protocols. -The high level Conversations approach outlined [here](https://forum.vac.dev/t/chatsdk-conversations/509). -The intention is to provide the minimal building blocks for a wide range of micro-protocols, while allowing ConversationTypes to remain as flexible as possible. - -At a high level this Protocol defines what a ConversationType is, it's requirements, and a mechanism for initializing Conversations with others. - -**Frame**: - -## Conversations A ConversationType is a specification which defines a method for clients to communicate. Each ConversationType defines its own encryption, encoding, and message types which are necessary for operation. -A ConversationType MUST define which content topics are valid places to receive messages. -A ConversationType MUST define which encryption scheme is used -A ConversationType MUST define which Frames are valid. -- Clients MUST be able to decode frames deterministically. -A ConversationType SHOULD define membership requirements and limitations. - A Conversation is an instance of a particular ConversationType which contains the associated state such as membership, encryption parameters, names etc. -### ConversationIdentifiers +## Requirements +To be considered valid, every ConversationType specification is required to define the operation completely. -A Conversation instance MUST a conversation_id and the `conversation_id` MUST uniquely identify the conversation. -[TODO: Should more guidance be added later? e,g ///] +A ConversationType MUST define which encryption scheme is used +A ConversationType MUST define which Frames are used. +A ConversationType MUST define which encoding is used. +A ConversationType MUST define which content topics are valid places to receive messages. +A ConversationType MUST define how to generate conversation_ids + +A ConversationType SHOULD define membership requirements and limitations. +A ConversationType SHOULD define privacy and security guarantees. -## Default Inbox -The default inbox allows clients to discover new conversations asynchronously without prior coordination. By listening in a static location +## ConversationType Identifiers +ConversationTypes are identified by the title of the specification. This allows developers to lookup the associated specification. +[TODO: This doesn't make any sense, as its been mentioned that new versions of a conversationType are distinct types. Which is it? ] -To achieve this all clients MUST implement a default Inbox with `inbox_address = HASH(client_address)`. [TODO: Define hash here, or embed as part of the Inbox spec] -See [Inbox](./inbox.md) for more information. +E.g. inbox.md -> InboxV1 +## Conversation Identifiers + +conversation_ids allow for the efficient lookup of encryption state. + +Care should be taken to ensure that conversation_ids do not conflict. +[TODO: Should more guidance be added later? e,g mandating a format to ensure uniqueness between conversationTypes -- ///] + +[TODO: touch on the nuance of generating conversation_ids from participant lists?] -As the clients address is directly linked to the content_topic there is some metadata leakage, and this pathway SHOULD only be used as a last resort. ## Framing To disambiguate between different logical layers, payload types sent by a Conversation are referred to as `Frames`. +Conversations are free to determine which frames are needed for their specific use cases. -Conversations are free to determine which frames are needed for their specific use cases, with these caveats: -- The outer most frame MUST be wrapped in an UmbraEnvelope. -- All frames MUST be able to be decoded deterministically. [TODO: Unambiguously a better choice?] +ConversationTypes MUST define a section which defines all possible frames -Deterministic decoding means that clients can always classify a envelope as 1 of 3 states: Readable, BadlyFormed, Addressed to someone else. - - -### Conversation Hinting -[TODO: Needs lots of work] - -UmbraEnvelopes enable deterministic decoding by containing a reference to the conversation which this message belongs. Clients only accepts envelopes with known `conversation_hints's`. All others can be discarded as there is insufficient information to properly decrypt/decode the messages. - - -Conversation identifiers (conversation_id) have the potential to leak sensitive metadata if exposed in cleartext. Frames sharing the same conversation_id could allow observers to infer social graph relationships, user activity patterns, or conversation linkage, depending on how conversation_id values are used by the specific ConversationType. - -To mitigate this risk and provide a safer default for Conversation implementors, conversation_id values SHOULD be obscured in a way that prevents observers from linking frames belonging to the same conversation. - -[TODO: Ratcheting Private Identifiers] +ConversationTypes SHOULD maintain a deterministic decoding tree. - -## Wire Format Specification / Syntax - -The wire format is specified using protocol buffers v3. - -```protobuf - -message UmbraEnvelopeV1 { - - string conversation_hint = 1; - uint32 salt = 2; - - EncryptedBytes encrypted_bytes = 10; -} +## Encryption +Conversation types are free to choose which ever encryption mechanism works best for their application. +[TODO: Expand on recomendations] + + +## Content Topics +Content topics are how ConversationTypes define where an how messages are discovered by participants. + +When developing new ConversationTypes contributors should consider: +- Privacy impacts of the chosen topic policy. +- Channel binding and the impacts on message security. -``` ## Implementation Suggestions (optional) An optional *implementation suggestions* section may provide suggestions on how to approach implementation details, and, @@ -109,10 +89,7 @@ if available, point to existing implementations for reference. ## Security/Privacy Considerations -Messages sent to the default inbox are linkable to an client (as it is derived from the clients address). This means that if a target client address is known to an observer, they can determine if any messages were sent to the target using the default inbox. In this case the Envelopes contain no sender information, so this does not leak social graph information. - -Messages sent via different pathways would have their own privacy guarantees. - +This approach puts heavy requirements on ConversationTypes to build their own cryptography without providing much guidance. Finding mechanisms that provide safety while maintaining the flexibility should be prioritized in follow up work. ## Copyright From 8c0f192303610b6bc57ec8754f88cb9dc3b308b9 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 9 Jul 2025 10:06:24 -0700 Subject: [PATCH 33/40] update hash salt --- specs/.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/specs/.md b/specs/.md index 4ae090b..c99313b 100644 --- a/specs/.md +++ b/specs/.md @@ -17,17 +17,13 @@ Traditionally communication protocols face several critical challenges as they g Different communication scenarios have vastly different requirements. A protocol optimized for high-volume public broadcasts performs poorly for intimate encrypted conversations, and vice versa. Monolithic protocols cannot optimize for these diverse use cases simultaneously. -Once widely deployed, communication protocols become difficult to modify. Even minor changes can break compatibility across clients, leading to protocol stagnation and eventually ossification. +Once widely deployed, communication protocols become difficult to modify. Even minor changes can break compatibility across clients. As a result versioning becomes a complex negotiation problem, which makes deploying protocol updates difficult. At each stage protocols become increasingly difficult to modify, which slows down forward progress and eventually leads to ossification. -When decentralized protocols do attempt to evolve, versioning becomes a complex negotiation problem. Clients must maintain backward compatibility with multiple protocol versions, leading to bloated implementations and coupling which increases complexity. - -At each stage protocols become increasingly difficult to modify, which slows down forward progress. - -A preferred approach would be to practice resiliency at the protocol level by focusing on versioning from the beginning. +A preferred approach would be to practice resiliency at the protocol level by focusing on versioning from the beginning. # Theory / Semantics -This protocol is a lean coordination layer which provides the backbone for smaller independent "Conversation" sub-protocols. Conversation protocols completely define a pathway for communication. This root protocol provides common functionality to support a wide array of communication use cases. +This protocol is a lean coordination layer which provides the backbone for smaller independent "Conversation" sub-protocols. Conversation protocols completely define a pathway for communication. This root protocol provides common functionality to support a wide array of communication use cases, and the remaining functionality is deferred to Conversation protocols to define. ```mermaid flowchart TD @@ -37,17 +33,22 @@ flowchart TD style C3 fill:#FFFFFF,stroke:#,stroke-width:1px,stroke-dasharray: 5 1 ``` -This protocol is intentionally light, as the work is deferred to the actual conversation protocols. +## Conversations +ConversationTypes are standalone methods to send and receive messages. While a full service "chat" protocol needs to provide additional features such as identity management, message history, backups and versioning, conversations are more narrowly scoped. + +They can be created permissionlessly within the %PROTO, as there is no required registration. Developers are free to use any given conversation type as long as all intended participants support it. + +ConversationTypes MUST adhere to the [Conversations](./conversations.md) specification in order to maintain compatibility, however network adoption is not a requirement. + + ## Versioning -Incompatible versions of a conversation protocol result in distinct conversationTypes (E.g. PrivateV1 vs PrivateV3). Individual conversationTypes can implement functionality to migrate participants to a conversation. By making this a conversationType issue, different use cases can choose a system that works for them. +Incompatible versions of a conversation protocol result in distinct conversationTypes (E.g. PrivateV1 vs PrivateV3). The opinionated strategy is to allow multiplexing at the conversation level, rather than requiring the entire network of clients to run the same version. +Individual conversationTypes can implement functionality to migrate participants to a conversation, but that is deferred to contributors. By making this a conversationType issue, different use cases can choose a system that works for them. -## Conversations -Conversations can be created permissionlessly within the %PROTO, as there is no required registration. Developers are free to use any given conversation type as long as all intended participants support it. - -ConversationTypes MUST adhere to the [Conversations](./conversations.md) specification in order to maintain compatibility, however network adoption is not q requirement. +Developers wishing to interop with other projects will need to ensure they have overlapping support of ConversationTypes. ## Default Inbox From 76c8f9e626ac27203436a0310386d29dc9c9083b Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 9 Jul 2025 10:06:58 -0700 Subject: [PATCH 34/40] proto.md fixups --- specs/.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/specs/.md b/specs/.md index c99313b..15b40bc 100644 --- a/specs/.md +++ b/specs/.md @@ -75,7 +75,7 @@ Conversation_ids are sufficient to allow clients to lookup the required encrypti Messages with an unknown hint can be safely disregarded. -ConversationHints are computed by using a salted hash of the `conversationId`. specifically defined as `lowercase_hex(blake2s(conversation_id))` +ConversationHints are computed by using a salted hash of the `conversationId`. specifically defined as `lowercase_hex(blake2s(salt || conversation_id))` [TODO: Should conversations define their own hinting?] @@ -91,7 +91,7 @@ message UmbraEnvelopeV1 { string conversation_hint = 1; uint32 salt = 2; - EncryptedBytes encrypted_bytes = 10; + EncryptedBytes encrypted_bytes = 3; } @@ -100,17 +100,20 @@ message UmbraEnvelopeV1 { ## Implementation Suggestions (optional) ### User level Conversations -A ConversationType defines how to send and receive messages. Developer SHOULD mask the conversationType from users. Developers should use an independent identifier in their apps for user-level "conversations" so that the protocols can update freely. + +Application developers should maintain standalone identifiers for user-level conversations that are separate from the protocol-level conversation_id. A single logical conversation from the user's perspective may utilize multiple ConversationTypes over time. As versions are considered different types, the underlying relationship is many to one. By maintaining application-level conversation identifiers, developers can provide users with consistent conversation continuity while the underlying protocol mechanisms handle version transitions and security upgrades transparently. + ## (Further Optional Sections) ## Security/Privacy Considerations -Messages sent to the default inbox are linkable to an client (as it is derived from the clients address). This means that if a target client address is known to an observer, they can determine if any messages were sent to the target using the default inbox. In this case the Envelopes contain no sender information, so this does not leak social graph information. - Messages inherit the privacy and security properties of the ConversationType used to send them. Please refer to the corresponding specifications when analyzing properties. +### Default Inbox Privacy +Messages sent to the default inbox are linkable to an client (as it is derived from the clients address). This means that if a target client address is known to an observer, they can determine if any messages were sent to the target using the default inbox. In this case the Envelopes contain no sender information, so this does not leak social graph information. + ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 655719fdbdc04475edd3028113b3721d2ace69ba Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 9 Jul 2025 11:43:49 -0700 Subject: [PATCH 35/40] remove redundant requirements --- specs/conversations.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/specs/conversations.md b/specs/conversations.md index 8216a3c..bbaf3c1 100644 --- a/specs/conversations.md +++ b/specs/conversations.md @@ -37,7 +37,7 @@ A ConversationType MUST define how to generate conversation_ids A ConversationType SHOULD define membership requirements and limitations. A ConversationType SHOULD define privacy and security guarantees. - +A ConversationType SHOULD maintain a deterministic decoding tree. ## ConversationType Identifiers ConversationTypes are identified by the title of the specification. This allows developers to lookup the associated specification. @@ -60,9 +60,7 @@ Care should be taken to ensure that conversation_ids do not conflict. To disambiguate between different logical layers, payload types sent by a Conversation are referred to as `Frames`. Conversations are free to determine which frames are needed for their specific use cases. -ConversationTypes MUST define a section which defines all possible frames -ConversationTypes SHOULD maintain a deterministic decoding tree. ## Encryption @@ -78,15 +76,6 @@ When developing new ConversationTypes contributors should consider: - Channel binding and the impacts on message security. - -## Implementation Suggestions (optional) -An optional *implementation suggestions* section may provide suggestions on how to approach implementation details, and, -if available, point to existing implementations for reference. - - -## (Further Optional Sections) - - ## Security/Privacy Considerations This approach puts heavy requirements on ConversationTypes to build their own cryptography without providing much guidance. Finding mechanisms that provide safety while maintaining the flexibility should be prioritized in follow up work. From ed5d8efa8d12e25f2240d234d64ec93bdba7e5db Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 9 Jul 2025 11:44:16 -0700 Subject: [PATCH 36/40] update inbox.md --- specs/inbox.md | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/specs/inbox.md b/specs/inbox.md index a7c1c91..221f55d 100644 --- a/specs/inbox.md +++ b/specs/inbox.md @@ -19,8 +19,16 @@ Clients must be able to receive frames before conversations can be initialized. Inboxes are inbound only conversation types, which allow a client to receive messages from contacts. An inbox does not have a defined set of participants, and is used to receive messages when there is not an established channel between contacts. -An inbox does not have an inherent keypair or identity associated with it - it's an agreed upon location to recieve messages. +An inbox does not have an inherent keypair or identity associated with it - it's an agreed upon location to receive messages. +Inboxes are not exclusive to a single account, and can be used by many different accounts. It is expected + + +## Invitations / Initialization + +Inboxes do not require coordination with other clients because they are inbound only. + +However to receive messages at this inbox, remote clients must know this is a valid place to receive messages. This can be statically defined in a conversation protocol or communicated out of band. ## Inbox Identifiers @@ -29,7 +37,7 @@ Inboxes are Identified by a Inboxes do not need to be "created", and there is no required initialization. -## Content Topic +## Content Topic Usage // TODO: Inbox Topics will be defined in ContactBundles, allowing for dynamic topic usage All clients must listen for messages posted with the content topic `/inbox/` @@ -90,9 +98,9 @@ flowchart TD ### EncryptedBytes -The EncryptedBytes message is a self-describing wrapper for all encrypted payloads. As the protocol grows it will include potentially different encryption mechanisms. This message type makes no assumptions about the encryption used an allows new conversation types to use the same messaging framework. - +The EncryptedBytes message is a self-describing wrapper for all encrypted payloads. This message type makes no assumptions about the encryption used an allows new conversation types to use the same messaging framework. +As this protocol uses the KN noise handshake, the encoding wrapper uses the corresponding type. ## Wire Format Specification / Syntax @@ -107,39 +115,28 @@ message InboxV1Frame { } } -message EncryptedBytes { +message EncryptedPayload { oneof encryption { - bytes encrypted_bytes=1; - Plaintext plaintext = 2; - Ecies ecies = 3; + NoiseKN noise_KN = 3; } - - message Ecies { - bytes encrypted_bytes=1; + message NoiseKN { + bytes encrypted_bytes = 1; bytes ephemeral_pubkey = 2; - bytes tag = 3; - - } - - message Plaintext { - bytes payload=1; } } ``` -## Implementation Suggestions (optional) -An optional *implementation suggestions* section may provide suggestions on how to approach implementation details, and, -if available, point to existing implementations for reference. - - -## (Further Optional Sections) - ## Security/Privacy Considerations -The encryption scheme used does not provide any sender authentication. Messages sent over this pathway need to validate the sender before trusting any of the contents. + +### Sender Auth +The encryption scheme used does not provide any sender authentication. Messages sent over this pathway need to validate the sender before trusting any of the contents. + +### EncryptedPayload metadata leakage +Encrypted bytes themselves are not encrypted so its fields are visible to all observers. Through analytical means observers can determine the type of message being sent, by looking at what fields are present, and the relative size of the payload. This is true regardless of whether the encrypted bytes are wrapped in a EncryptedPayload object. Wrapping the payload allows for better support into the future without meaningful changing the metadata leakage. ## Copyright From 84d3fb9f50d66f9384ed4fafcd67d19698dba780 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Wed, 9 Jul 2025 16:39:11 -0700 Subject: [PATCH 37/40] Inboxes cleanup --- specs/inbox.md | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/specs/inbox.md b/specs/inbox.md index 221f55d..b1673c4 100644 --- a/specs/inbox.md +++ b/specs/inbox.md @@ -16,13 +16,16 @@ Clients must be able to receive frames before conversations can be initialized. # Theory / Semantics -Inboxes are inbound only conversation types, which allow a client to receive messages from contacts. +Inboxes are inbound only conversation types, which allow a client to receive messages from contacts asynchronously. An inbox does not have a defined set of participants, and is used to receive messages when there is not an established channel between contacts. An inbox does not have an inherent keypair or identity associated with it - it's an agreed upon location to receive messages. Inboxes are not exclusive to a single account, and can be used by many different accounts. It is expected +## Parameters + +- **inbox_address:** uniquely identifies the inbox. ## Invitations / Initialization @@ -30,21 +33,13 @@ Inboxes do not require coordination with other clients because they are inbound However to receive messages at this inbox, remote clients must know this is a valid place to receive messages. This can be statically defined in a conversation protocol or communicated out of band. -## Inbox Identifiers - -Inboxes are Identified by a - -Inboxes do not need to be "created", and there is no required initialization. - ## Content Topic Usage -// TODO: Inbox Topics will be defined in ContactBundles, allowing for dynamic topic usage -All clients must listen for messages posted with the content topic `/inbox/` - -`lower_hex(hash(/inbox/)` - +All clients must listen for messages posted with the content topic. The content topic that is used is defined by `lower_hex(blake2s("/inbox/"))`. +## Conversation Id +Messages sent to the inbox MUST use the conversation_id = `/inbox/v1/` ## Accepted types From c8f754bcd0e8c89fbcb99c0aaa73801d0a14c80b Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 10 Jul 2025 15:54:09 -0700 Subject: [PATCH 38/40] Migrated inbox to waku-specs --- specs/inbox.md | 143 +------------------------------------------------ 1 file changed, 1 insertion(+), 142 deletions(-) diff --git a/specs/inbox.md b/specs/inbox.md index b1673c4..adb400f 100644 --- a/specs/inbox.md +++ b/specs/inbox.md @@ -1,142 +1 @@ ---- -title: INBOX -name: Inbound message queues -category: Standards Track -status: raw -tags: chat -editor: Jazz Alyxzander -contributors: ---- -# Abstract - - -# Background / Rationale / Motivation -Clients must be able to receive frames before conversations can be initialized. While its possible to coordinate a content topic out of band, that becomes limiting factor to growth. - - -# Theory / Semantics - -Inboxes are inbound only conversation types, which allow a client to receive messages from contacts asynchronously. -An inbox does not have a defined set of participants, and is used to receive messages when there is not an established channel between contacts. - -An inbox does not have an inherent keypair or identity associated with it - it's an agreed upon location to receive messages. - -Inboxes are not exclusive to a single account, and can be used by many different accounts. It is expected - -## Parameters - -- **inbox_address:** uniquely identifies the inbox. - -## Invitations / Initialization - -Inboxes do not require coordination with other clients because they are inbound only. - -However to receive messages at this inbox, remote clients must know this is a valid place to receive messages. This can be statically defined in a conversation protocol or communicated out of band. - - -## Content Topic Usage - -All clients must listen for messages posted with the content topic. The content topic that is used is defined by `lower_hex(blake2s("/inbox/"))`. - -## Conversation Id -Messages sent to the inbox MUST use the conversation_id = `/inbox/v1/` - -## Accepted types - - - -## Encryption - -All Frames sent to the Inbox MUST be encrypted to maintain message confidentiality. - -This protocol uses a reversed variant of the [KN noise handshake](https://noiseexplorer.com/patterns/KN/) to secure inbound messages. - - ```noise -KNfallback: - <- e, s - ... - -> e, ee, es - ``` - -In this case the responder provides both `s` and `e` out of band. - -The handshakes primary purpose is to provide sender confidentiality, with some forward secrecy. The handshake is similar to a one way N handshake with a recipient side ephemeral key. - -Note this channel does not not provide sender authentication, and should only be used to implement a confidential message delivery with some forward secrecy. This limitation is intentional to maintain O-RTT encryption. As this is an inbound pathway further messages to establish mutual authentication with identity hiding would be wasteful. - -### Ciphersuite - -The noise handshake is implemented with the following functions: - -**DH:** X25519 -**cipher:** AEAD_CHACHA20_POLY1305 -**hash:** BLAKE2s - -The noise protocol name would then be `Noise_KNfallback_25519_ChaChaPoly_BLAKE2s` - -This protocol opts for 32bit variants to optimize for mobile and resource constrained environments. - -### Endianness -[TODO: The Noiseprotocol specification recommends BigEndian length fields - Need to define if this protocol will deviate] - -## Framing - -[TODO: Is there benefit to using SDS in this case? If all messages are invites and communication occurs else where, is this just wasting bytes?] -```mermaid -flowchart TD - UmbraEnvelopeV1 <--> EncryptedPayload - EncryptedPayload <--> D{En/Decrypt} - D <--> ReliableBytes - ReliableBytes --> InboxV1Frame - -``` - -### EncryptedBytes - -The EncryptedBytes message is a self-describing wrapper for all encrypted payloads. This message type makes no assumptions about the encryption used an allows new conversation types to use the same messaging framework. - -As this protocol uses the KN noise handshake, the encoding wrapper uses the corresponding type. - - -## Wire Format Specification / Syntax -The wire format is specified using protocol buffers v3. - -```protobuf - -message InboxV1Frame { - string recipient = 1; - oneof frame_type { - ... supported invite types - } -} - -message EncryptedPayload { - - oneof encryption { - NoiseKN noise_KN = 3; - } - - message NoiseKN { - bytes encrypted_bytes = 1; - bytes ephemeral_pubkey = 2; - } -} - -``` - - -## Security/Privacy Considerations - -### Sender Auth -The encryption scheme used does not provide any sender authentication. Messages sent over this pathway need to validate the sender before trusting any of the contents. - -### EncryptedPayload metadata leakage -Encrypted bytes themselves are not encrypted so its fields are visible to all observers. Through analytical means observers can determine the type of message being sent, by looking at what fields are present, and the relative size of the payload. This is true regardless of whether the encrypted bytes are wrapped in a EncryptedPayload object. Wrapping the payload allows for better support into the future without meaningful changing the metadata leakage. - -## Copyright - -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). - -## References - -A list of references. +Moved to WakuSpecs - https://github.com/waku-org/specs/pull/72 From e7b9b4a7b5fba89cbaccd702a22f9177e8ff7689 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Thu, 10 Jul 2025 18:14:16 -0700 Subject: [PATCH 39/40] Bring inline with inbox.md spec --- proto/umbra/encryption.proto | 15 +++------------ proto/umbra/envelope.proto | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/proto/umbra/encryption.proto b/proto/umbra/encryption.proto index a579382..5bb740d 100644 --- a/proto/umbra/encryption.proto +++ b/proto/umbra/encryption.proto @@ -3,22 +3,13 @@ syntax = "proto3"; package umbra.encryption; -// TODO: This also encompasses plaintexts, is there a better name? -// Alternatives: ??? message EncryptedPayload { - oneof encryption { - encryption.Plaintext plaintext = 1; - encryption.Ecies ecies = 2; + NoiseKN noise_KN = 3; } } -message Plaintext { - bytes payload=1; -} - -message Ecies { - bytes encrypted_bytes=1; +message NoiseKN { + bytes encrypted_bytes = 1; bytes ephemeral_pubkey = 2; - bytes tag = 3; } diff --git a/proto/umbra/envelope.proto b/proto/umbra/envelope.proto index 0b0d2ea..0ee50bc 100644 --- a/proto/umbra/envelope.proto +++ b/proto/umbra/envelope.proto @@ -7,7 +7,7 @@ package umbra.envelope; // Payload Framing Messages /////////////////////////////////////////////////////////////////////////////// -message UmbraEnvelopeV1 { +message WapEnvelopeV1 { string conversation_hint = 1; uint64 salt = 2; From b8d83fc5f015476f90ff73a7178cd73b4dfd1ccd Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 11 Jul 2025 13:49:33 -0700 Subject: [PATCH 40/40] Remove umbra references --- nim/.devcontainer/devcontainer.json | 2 +- proto/{umbra => wap}/common_frames.proto | 2 +- .../{umbra => wap}/conversations/private_v1.proto | 2 +- proto/{umbra => wap}/encryption.proto | 2 +- proto/{umbra => wap}/envelope.proto | 2 +- proto/{umbra => wap}/inbox.proto | 2 +- proto/{umbra => wap}/invite.proto | 2 +- proto/{umbra => wap}/reliability.proto | 2 +- rust/.devcontainer/devcontainer.json | 2 +- rust/Cargo.lock | 14 +++++++------- rust/cargo.toml | 4 ++-- rust/{umbra-types => wap-types}/Cargo.toml | 2 +- rust/{umbra-types => wap-types}/build.rs | 2 +- rust/{umbra-types => wap-types}/src/lib.rs | 2 +- rust/{umbra-types => wap-types}/src/payload.rs | 10 +++++----- specs/.md | 5 ++--- 16 files changed, 28 insertions(+), 29 deletions(-) rename proto/{umbra => wap}/common_frames.proto (79%) rename proto/{umbra => wap}/conversations/private_v1.proto (90%) rename proto/{umbra => wap}/encryption.proto (87%) rename proto/{umbra => wap}/envelope.proto (93%) rename proto/{umbra => wap}/inbox.proto (89%) rename proto/{umbra => wap}/invite.proto (79%) rename proto/{umbra => wap}/reliability.proto (97%) rename rust/{umbra-types => wap-types}/Cargo.toml (88%) rename rust/{umbra-types => wap-types}/build.rs (96%) rename rust/{umbra-types => wap-types}/src/lib.rs (94%) rename rust/{umbra-types => wap-types}/src/payload.rs (95%) diff --git a/nim/.devcontainer/devcontainer.json b/nim/.devcontainer/devcontainer.json index 6661940..8a86859 100644 --- a/nim/.devcontainer/devcontainer.json +++ b/nim/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "UmbraTypes", + "name": "WapTypes", "image": "nimlang/nim", "customizations": { "vscode": { diff --git a/proto/umbra/common_frames.proto b/proto/wap/common_frames.proto similarity index 79% rename from proto/umbra/common_frames.proto rename to proto/wap/common_frames.proto index 453c00f..972f58e 100644 --- a/proto/umbra/common_frames.proto +++ b/proto/wap/common_frames.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package umbra.common_frames; +package wap.common_frames; message ContentFrame { uint32 domain = 1; diff --git a/proto/umbra/conversations/private_v1.proto b/proto/wap/conversations/private_v1.proto similarity index 90% rename from proto/umbra/conversations/private_v1.proto rename to proto/wap/conversations/private_v1.proto index 43a6a73..d2022a2 100644 --- a/proto/umbra/conversations/private_v1.proto +++ b/proto/wap/conversations/private_v1.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package umbra.convos.private_v1; +package wap.convos.private_v1; import "common_frames.proto"; diff --git a/proto/umbra/encryption.proto b/proto/wap/encryption.proto similarity index 87% rename from proto/umbra/encryption.proto rename to proto/wap/encryption.proto index 5bb740d..1d42123 100644 --- a/proto/umbra/encryption.proto +++ b/proto/wap/encryption.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package umbra.encryption; +package wap.encryption; message EncryptedPayload { diff --git a/proto/umbra/envelope.proto b/proto/wap/envelope.proto similarity index 93% rename from proto/umbra/envelope.proto rename to proto/wap/envelope.proto index 0ee50bc..6a961d4 100644 --- a/proto/umbra/envelope.proto +++ b/proto/wap/envelope.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package umbra.envelope; +package wap.envelope; /////////////////////////////////////////////////////////////////////////////// diff --git a/proto/umbra/inbox.proto b/proto/wap/inbox.proto similarity index 89% rename from proto/umbra/inbox.proto rename to proto/wap/inbox.proto index ad4fed4..6f85db5 100644 --- a/proto/umbra/inbox.proto +++ b/proto/wap/inbox.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package umbra.inbox; +package wap.inbox; import "invite.proto"; diff --git a/proto/umbra/invite.proto b/proto/wap/invite.proto similarity index 79% rename from proto/umbra/invite.proto rename to proto/wap/invite.proto index c94b442..d0e7fd3 100644 --- a/proto/umbra/invite.proto +++ b/proto/wap/invite.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package umbra.invite; +package wap.invite; message InvitePrivateV1 { repeated string participants = 1; diff --git a/proto/umbra/reliability.proto b/proto/wap/reliability.proto similarity index 97% rename from proto/umbra/reliability.proto rename to proto/wap/reliability.proto index 3c433c3..f71f9b2 100644 --- a/proto/umbra/reliability.proto +++ b/proto/wap/reliability.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package umbra.reliability; +package wap.reliability; /////////////////////////////////////////////////////////////////////////////// // SDS Payloads diff --git a/rust/.devcontainer/devcontainer.json b/rust/.devcontainer/devcontainer.json index c77db0e..b53d567 100644 --- a/rust/.devcontainer/devcontainer.json +++ b/rust/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "Rust-UmbraTypes", + "name": "Rust-WapTypes", "image": "mcr.microsoft.com/devcontainers/rust:latest", "customizations": { "vscode": { diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fa61098..a2181ec 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -401,7 +401,13 @@ dependencies = [ ] [[package]] -name = "umbra-types" +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wap-types" version = "0.0.2-dev" dependencies = [ "bytes 0.4.12", @@ -410,12 +416,6 @@ dependencies = [ "protoc-rust", ] -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" diff --git a/rust/cargo.toml b/rust/cargo.toml index 0a4bd17..b42facc 100644 --- a/rust/cargo.toml +++ b/rust/cargo.toml @@ -1,11 +1,11 @@ [workspace] members = [ - "umbra-types" + "wap-types" ] default-members = [ - "umbra-types" + "wap-types" ] resolver = "3" diff --git a/rust/umbra-types/Cargo.toml b/rust/wap-types/Cargo.toml similarity index 88% rename from rust/umbra-types/Cargo.toml rename to rust/wap-types/Cargo.toml index 4226c57..798e686 100644 --- a/rust/umbra-types/Cargo.toml +++ b/rust/wap-types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "umbra-types" +name = "wap-types" edition = "2024" version = "0.0.2-dev" diff --git a/rust/umbra-types/build.rs b/rust/wap-types/build.rs similarity index 96% rename from rust/umbra-types/build.rs rename to rust/wap-types/build.rs index b2f5515..55883a9 100644 --- a/rust/umbra-types/build.rs +++ b/rust/wap-types/build.rs @@ -13,7 +13,7 @@ fn get_proto_dir() -> PathBuf { proto_root_dir.pop(); proto_root_dir.pop(); proto_root_dir.push("proto"); - proto_root_dir.push("umbra"); + proto_root_dir.push("wap"); println!("proto_dir: {:?}", proto_root_dir); proto_root_dir diff --git a/rust/umbra-types/src/lib.rs b/rust/wap-types/src/lib.rs similarity index 94% rename from rust/umbra-types/src/lib.rs rename to rust/wap-types/src/lib.rs index e03d6c7..6145d84 100644 --- a/rust/umbra-types/src/lib.rs +++ b/rust/wap-types/src/lib.rs @@ -1,6 +1,6 @@ pub mod payload; -pub use payload::types::umbra::*; +pub use payload::types::wap::*; #[cfg(test)] diff --git a/rust/umbra-types/src/payload.rs b/rust/wap-types/src/payload.rs similarity index 95% rename from rust/umbra-types/src/payload.rs rename to rust/wap-types/src/payload.rs index 3b136f5..453f118 100644 --- a/rust/umbra-types/src/payload.rs +++ b/rust/wap-types/src/payload.rs @@ -3,12 +3,12 @@ pub mod types { } use prost::Message; -pub use types::umbra::*; +pub use types::wap::*; use crate::{ convos::private_v1::{PrivateV1Frame, private_v1_frame}, encryption::EncryptedPayload, - envelope::UmbraEnvelopeV1, + envelope::WapEnvelopeV1, inbox::{InboxV1Frame, inbox_v1_frame}, }; @@ -32,12 +32,12 @@ impl InboxV1Frame { } pub trait ToEnvelope { - fn to_envelope(self, conversation_id: String, salt: u64) -> UmbraEnvelopeV1; + fn to_envelope(self, conversation_id: String, salt: u64) -> WapEnvelopeV1; } impl ToEnvelope for EncryptedPayload { - fn to_envelope(self, conversation_id: String, salt: u64) -> UmbraEnvelopeV1 { - UmbraEnvelopeV1 { + fn to_envelope(self, conversation_id: String, salt: u64) -> WapEnvelopeV1 { + WapEnvelopeV1 { conversation_hint: conversation_id, // TODO salt, payload: self.encode_to_vec(), // Avoid allocation here? diff --git a/specs/.md b/specs/.md index 15b40bc..6bb1414 100644 --- a/specs/.md +++ b/specs/.md @@ -1,5 +1,5 @@ --- -title: > +title: Waku Application Protocol name: A private decentralized messaging protocol for multiple usecases. category: Standards Track status: raw @@ -56,7 +56,6 @@ There exists a circular dependency in initializing a Conversation. Conversations To resolve this all clients MUST implement a default [Inbox](./inbox.md) to receive initial messages. The default inbox allows clients to discover new conversations asynchronously without prior coordination. By listening in a static location. - The default inbox MUST be configured with the parameters: - **inbox_addr:** `client_address` - **discriminator:** "default" @@ -86,7 +85,7 @@ The wire format is specified using protocol buffers v3. ```protobuf -message UmbraEnvelopeV1 { +message WapEnvelopeV1 { string conversation_hint = 1; uint32 salt = 2;