diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml index 592a8fdae..99e1a2f40 100644 --- a/.github/workflows/on-push.yml +++ b/.github/workflows/on-push.yml @@ -49,120 +49,119 @@ jobs: name: lib-schema path: packages/lib/**/schema/ backend-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-backend' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-backend' + package-pat: ${{ secrets.PACKAGE_PAT }} integration-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-integration-tests' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-integration-tests' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-crypto-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-crypto' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-crypto' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-delivery-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-delivery' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-delivery' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-messaging-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-messaging' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-messaging' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-profile-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-profile' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-profile' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-shared-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-shared' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-shared' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-storage-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-storage' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-storage' + package-pat: ${{ secrets.PACKAGE_PAT }} offchain-resolver-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-offchain-resolver' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-offchain-resolver' + package-pat: ${{ secrets.PACKAGE_PAT }} diff --git a/.yarn/versions/3dae3775.yml b/.yarn/versions/3dae3775.yml index 854760c12..7c3817114 100644 --- a/.yarn/versions/3dae3775.yml +++ b/.yarn/versions/3dae3775.yml @@ -1,16 +1,16 @@ undecided: - - dm3-backend - - dm3-billboard-client - - dm3-billboard-widget - - dm3-integration-tests - - dm3-lib-billboard-client-api - - dm3-lib-crypto - - dm3-lib-delivery - - dm3-lib-delivery-api - - dm3-lib-messaging - - dm3-lib-offchain-resolver-api - - dm3-lib-profile - - dm3-lib-shared - - dm3-lib-storage - - dm3-offchain-resolver - - dm3-react + - dm3-backend + - dm3-billboard-client + - dm3-billboard-widget + - dm3-integration-tests + - dm3-lib-billboard-client-api + - dm3-lib-crypto + - dm3-lib-delivery + - dm3-lib-delivery-api + - dm3-lib-messaging + - dm3-lib-offchain-resolver-api + - dm3-lib-profile + - dm3-lib-shared + - dm3-lib-storage + - dm3-offchain-resolver + - dm3-react diff --git a/.yarnrc.yml b/.yarnrc.yml index b29a1efeb..774c08021 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,17 +1,17 @@ nodeLinker: node-modules npmScopes: - testscope: - npmPublishRegistry: "http://localhost:4873" - npmRegistryServer: "http://localhost:4873" + testscope: + npmPublishRegistry: 'http://localhost:4873' + npmRegistryServer: 'http://localhost:4873' plugins: - - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs - spec: "@yarnpkg/plugin-workspace-tools" - - path: .yarn/plugins/@yarnpkg/plugin-version.cjs - spec: "@yarnpkg/plugin-version" + - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs + spec: '@yarnpkg/plugin-workspace-tools' + - path: .yarn/plugins/@yarnpkg/plugin-version.cjs + spec: '@yarnpkg/plugin-version' unsafeHttpWhitelist: - - localhost + - localhost yarnPath: .yarn/releases/yarn-3.5.1.cjs diff --git a/README.md b/README.md index f638a297a..82fe14f10 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,47 @@ # Packages -* [dm3-react](packages/react): dm3 UI components -* [dm3-web](packages/web): dm3 web app -* [dm3-backend](packages/backend): The delivery and storage service -* [dm3-lib](packages/lib): Basic dm3 functionality + +- [dm3-react](packages/react): dm3 UI components +- [dm3-web](packages/web): dm3 web app +- [dm3-backend](packages/backend): The delivery and storage service +- [dm3-lib](packages/lib): Basic dm3 functionality # Protocol + ## Simple Summary + dm3 Protocol enables decentral, open, and secure messaging based on established web3 services like ENS and IPFS. ## Specification + https://dm3.readthedocs.io/en/doc-latest/ -## Principles -* **Decentral**: An dm3 client must be realizable as a real decentral application and the messages must also be stored in a decentral way. -* **Open**: All parts of dm3 are open source and the protocol is permissionless. Everyone should be able to write an dm3 client. -* **Secure**: All messages are end-to-end encrypted and the encryption keys are only under the control of the user. +## Principles + +- **Decentral**: An dm3 client must be realizable as a real decentral application and the messages must also be stored in a decentral way. +- **Open**: All parts of dm3 are open source and the protocol is permissionless. Everyone should be able to write an dm3 client. +- **Secure**: All messages are end-to-end encrypted and the encryption keys are only under the control of the user. ## Terminology -* **Ethereum Account Key**: The private key linked to an [Externally Owned Account](https://ethereum.org/en/whitepaper/#ethereum-accounts). -* **Message Encryption Key Pair**: The key pair used to encrypt/decrypt messages. -* **Signing Key Pair**: The key pair used to sign/verify messages. -* **Storage Encryption Key**: Synchronous key to encrypt the user storage. `keccak256(personalSignWithEthAccount(salt))` -* **Delivery Service**: The service that buffers messages until they are delivered. -* **Registry**: A decentral service (e.g. ENS) mapping Ethereum accounts to profile registry entry URLs (e.g. using text records). -* **Profile Registry Entry**: A resource containing properties linked to an Ethereum account that is using dm3. E.g. public keys, the delivery service URL and spam filter configuration. + +- **Ethereum Account Key**: The private key linked to an [Externally Owned Account](https://ethereum.org/en/whitepaper/#ethereum-accounts). +- **Message Encryption Key Pair**: The key pair used to encrypt/decrypt messages. +- **Signing Key Pair**: The key pair used to sign/verify messages. +- **Storage Encryption Key**: Synchronous key to encrypt the user storage. `keccak256(personalSignWithEthAccount(salt))` +- **Delivery Service**: The service that buffers messages until they are delivered. +- **Registry**: A decentral service (e.g. ENS) mapping Ethereum accounts to profile registry entry URLs (e.g. using text records). +- **Profile Registry Entry**: A resource containing properties linked to an Ethereum account that is using dm3. E.g. public keys, the delivery service URL and spam filter configuration. ## Integration Guide -This guide explains how to make a messenger dm3 compatible. + +This guide explains how to make a messenger dm3 compatible. The integration test package contains executable integration examples: -- [Basic example](packages/integration-tests/src/lib/CreateAndSend.test.ts) -- [Example with a message proxy](packages/integration-tests/src/lib/CreateAndSendOverProxy.test.ts) + +- [Basic example](packages/integration-tests/src/lib/CreateAndSend.test.ts) +- [Example with a message proxy](packages/integration-tests/src/lib/CreateAndSendOverProxy.test.ts) + ### Delivery Service Setup + The following function creates a delivery service profile and the corresponding keys. `http://a` is the URL pointing to the delivery service endpoint. ```Typescript @@ -50,10 +60,12 @@ const profileJsonDataUri = createJsonDataUri(deliveryServiceProfileA); The data URI of the profile must be published on-chain as ENS `network.dm3.deliveryService` text record on the delivery service ENS domain. ### Creating and Publishing User Profiles + Every dm3 user needs to create and publish a profile containing: -- the public signing key, -- the public encryption key, -- and a list of the ENS names referencing the delivery service the user subscribed to + +- the public signing key, +- the public encryption key, +- and a list of the ENS names referencing the delivery service the user subscribed to The following function call will return the user profile, the keys, and the nonce. @@ -69,45 +81,50 @@ The user profiles need to be transformed into a data URI before they can be publ ```Typescript const profileJsonDataUriAlice = createJsonDataUri(aliceProfile); - ``` +``` The profiles must be published on-chain or made available via CCIP. Therefore the ENS `network.dm3.profile` text record needs to be set to the data URI containing the user profile. ### Sending and Receiving Messages + ```Mermaid flowchart LR s(Sender App) --> rds[Receiver Delivery Service] --> ra(Receiver App) ``` + To receive messages the dm3 clients can subscribe to a "new message" event via a WebSocket that is provided by the delivery service. The messages could also be fetched via a REST request. In the following example, Bob subscribes to a push service. The callback function is called by the delivery service if there is a message for Bob. The message has to be decrypted and the message signature must be checked. - ```Typescript -onMessage('bob.eth', async (encryptedEnvelop: EncryptionEnvelop) => { - - // The client will decrypt a received message - const envelop = await decryptEnvelop( - encryptedEnvelop, - bobKeys.encryptionKeyPair, - ); - // The client must check the signature of a received message - const valid = await checkMessageSignature( - envelop.message, - aliceProfile.profile.publicSigningKey, - 'alice.eth', - ); +```Typescript +onMessage('bob.eth', async (encryptedEnvelop: EncryptionEnvelop) => { - if (!valid) { - throw Error('Signature check failed'); - } + // The client will decrypt a received message + const envelop = await decryptEnvelop( + encryptedEnvelop, + bobKeys.encryptionKeyPair, + ); + + // The client must check the signature of a received message + const valid = await checkMessageSignature( + envelop.message, + aliceProfile.profile.publicSigningKey, + 'alice.eth', + ); + + if (!valid) { + throw Error('Signature check failed'); + } }); ``` + **Note:** The communication between the receiving delivery service and receiving client app is not part of the dm3 Message Transport Protocol. The following code example will create a message with the content 'Test Message'. + ```Typescript const messageAliceToBob = await createMessage( 'bob.eth', @@ -116,22 +133,25 @@ const messageAliceToBob = await createMessage( aliceKeys.signingKeyPair.privateKey, ); ``` + The message must be encrypted and put into an envelope containing the meta information needed for the delivery. The `createEnvelop` function will return an encrypted and unencrypted version of the envelope. The unencrypted version could be used for storing it with a chunk of other received and send messages in a message store. The message store could be encrypted with the generated symmetrical key. **Note**: The storage of sent or received messages is not part of the dm3 Message Transport Protocol. The following function returns a dm3 submit message JSON RPC Request. + ```Typescript const jsonRpcRequest = createJsonRpcCallSubmitMessage( encyptedEnvelopAliceToBob, ); ``` -This request needs to be submitted to the URL specified in the receiver delivery service profile. +This request needs to be submitted to the URL specified in the receiver delivery service profile. #### Message Proxy -Some messenger providers may prefer that their clients interact with a message proxy instead of directly sending messages to the receiver delivery service. +Some messenger providers may prefer that their clients interact with a message proxy instead of directly sending messages to the receiver delivery service. + ```Mermaid flowchart LR s(Sender App) --> m[Message Proxy] --> rds[Receiver Delivery Service] --> ra(Receiver App) @@ -149,6 +169,7 @@ const proxyEnvelope = await createProxyEnvelop( ``` The message proxy forwards the message using the `sendOverMessageProxy` function + ```Typescript await sendOverMessageProxy({ getRessource, @@ -167,6 +188,7 @@ await sendOverMessageProxy({ The function`handleMessageOnDeliveryService` will decrypt the delivery information and add a postmark (incoming timestamp) to the incoming envelope. + ```Typescript const processedEnvelop = await handleMessageOnDeliveryService( encryptedEnvelop, @@ -176,4 +198,3 @@ and add a postmark (incoming timestamp) to the incoming envelope. ``` After processing the envelope, the delivery service forwards the message to the receiver app. - diff --git a/packages/backend/README.md b/packages/backend/README.md index 968443b2e..0a20dc02c 100644 --- a/packages/backend/README.md +++ b/packages/backend/README.md @@ -1,4 +1,5 @@ # dm3-backend + ## Getting Started ### Build @@ -7,16 +8,16 @@ cd ../../ && yarn build ``` - ### Usage - yarn + ``` yarn start ``` -npm +npm + ``` npm start -``` \ No newline at end of file +``` diff --git a/packages/backend/docker-compose.yml b/packages/backend/docker-compose.yml index c0304fae8..c54a08503 100644 --- a/packages/backend/docker-compose.yml +++ b/packages/backend/docker-compose.yml @@ -2,10 +2,9 @@ version: '3.6' # The containers that compose the project services: - db: - image: redis - restart: always - container_name: redis - ports: - - '6379:6379' - + db: + image: redis + restart: always + container_name: redis + ports: + - '6379:6379' diff --git a/packages/billboard-client/README.md b/packages/billboard-client/README.md index baf10cb0b..17aa29b8b 100644 --- a/packages/billboard-client/README.md +++ b/packages/billboard-client/README.md @@ -1,4 +1,5 @@ # dm3-backend + ## Getting Started ### Build @@ -7,16 +8,16 @@ cd ../../ && yarn build ``` - ### Usage - yarn + ``` yarn start ``` -npm +npm + ``` yarn start -``` \ No newline at end of file +``` diff --git a/packages/billboard-client/docker-compose.yml b/packages/billboard-client/docker-compose.yml index 1f12f41bf..c558c6882 100644 --- a/packages/billboard-client/docker-compose.yml +++ b/packages/billboard-client/docker-compose.yml @@ -2,10 +2,9 @@ version: '3.6' # The containers that compose the project services: - db: - image: redis - restart: always - container_name: redis_billboard-client - ports: - - '6368:6368' - + db: + image: redis + restart: always + container_name: redis_billboard-client + ports: + - '6368:6368' diff --git a/packages/billboard-widget/.eslintrc.cjs b/packages/billboard-widget/.eslintrc.cjs index 4020bcbf4..f11621f51 100644 --- a/packages/billboard-widget/.eslintrc.cjs +++ b/packages/billboard-widget/.eslintrc.cjs @@ -1,14 +1,14 @@ module.exports = { - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - parser: '@typescript-eslint/parser', - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': 'warn', - }, -} + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': 'warn', + }, +}; diff --git a/packages/billboard-widget/README.md b/packages/billboard-widget/README.md index 58abbf9c6..4294b70d1 100644 --- a/packages/billboard-widget/README.md +++ b/packages/billboard-widget/README.md @@ -85,17 +85,17 @@ Common billboard widget options: - `className`: A custom CSS class that will be added to the main div of the widget. This can be used for custom styling. -- `avatarSrc`: A custom URL for all user avatars, that takes the user's identifier (hash) and returns a URL. This function will be used to generate avatar images for users. +- `avatarSrc`: A custom URL for all user avatars, that takes the user's identifier (hash) and returns a URL. This function will be used to generate avatar images for users. - `userNameResolver`: A custom user name resolver function that takes the user identifier and returns a new string or a promise resolving to a string. - `dateFormat`: Formatting string for the date of each message, using date-fns formatter function. - - See: https://date-fns.org/v2.30.0/docs/format + - See: https://date-fns.org/v2.30.0/docs/format - - The default is `P` which stands for _Long localized date_. + - The default is `P` which stands for _Long localized date_. -- `relativeDate`: Display a relative date for recent dates. Default is `true`. +- `relativeDate`: Display a relative date for recent dates. Default is `true`. ### clientOptions diff --git a/packages/billboard-widget/index.html b/packages/billboard-widget/index.html index e0d1c8408..81aefabd3 100644 --- a/packages/billboard-widget/index.html +++ b/packages/billboard-widget/index.html @@ -1,13 +1,13 @@ -
- - - -