From c8a5a16fe7b807ee1abd77a8469b6e21ddde12a7 Mon Sep 17 00:00:00 2001
From: Zdravko Iliev <zdravko.iliev@vereign.com>
Date: Tue, 13 Jun 2023 07:44:31 +0000
Subject: [PATCH] feat: implement agent events for basic messages

---
 apps/agent/deployment/Dockerfile              |   1 +
 .../src/app/app.controller.ts                 |  39 +++-
 .../src/app/app.controller.ts                 |   2 +-
 .../app/managers/attestation.controller.ts    |  36 ++++
 compose/docker-compose.simple.yml             |  40 +++++
 compose/env/holder.simple.env                 |  34 ++++
 compose/env/issuer.simple.env                 |  34 ++++
 .../src/asker-nats/agent.consumer.service.ts  | 166 ++++++++++--------
 libs/asker/src/asker-rest/rest.controller.ts  |   8 +-
 .../src/asker/agent.event.listener.servce.ts  |  63 +++++++
 libs/asker/src/asker/agent.service.ts         |  16 ++
 libs/asker/src/asker/asker.module.ts          |   3 +-
 libs/asker/src/asker/asker.service.ts         |   1 -
 libs/clients/src/lib/gateway.client.ts        |   1 -
 .../make.basic.message.request.dto.ts         |  11 ++
 .../make.basic.message.response.dto.ts        |  11 ++
 libs/dtos/src/events/dtoToEventTransformer.ts |  14 +-
 libs/dtos/src/events/types.ts                 |   6 +
 libs/dtos/src/index.ts                        |   2 +
 package.json                                  |   4 +-
 20 files changed, 411 insertions(+), 81 deletions(-)
 create mode 100644 compose/docker-compose.simple.yml
 create mode 100644 compose/env/holder.simple.env
 create mode 100644 compose/env/issuer.simple.env
 create mode 100644 libs/asker/src/asker/agent.event.listener.servce.ts
 create mode 100644 libs/dtos/src/dtos/requests/make.basic.message.request.dto.ts
 create mode 100644 libs/dtos/src/dtos/responses/make.basic.message.response.dto.ts

diff --git a/apps/agent/deployment/Dockerfile b/apps/agent/deployment/Dockerfile
index 30cfa847..63745abb 100644
--- a/apps/agent/deployment/Dockerfile
+++ b/apps/agent/deployment/Dockerfile
@@ -12,5 +12,6 @@ RUN yarn install
 
 EXPOSE 8080
 EXPOSE 8001
+EXPOSE 6001
 
 CMD ["node", "main.js"]
diff --git a/apps/attestation-manager/src/app/app.controller.ts b/apps/attestation-manager/src/app/app.controller.ts
index 66740705..84bdeef5 100644
--- a/apps/attestation-manager/src/app/app.controller.ts
+++ b/apps/attestation-manager/src/app/app.controller.ts
@@ -2,12 +2,14 @@ import { Body, Controller, Logger } from "@nestjs/common";
 
 import { MessagePattern, RpcException } from "@nestjs/microservices";
 import {
+  BasicMessageEvent,
   CloudEventDto,
   CreateCredentialDefinitionRequsetDto,
   CreateSchemaRequestDto,
   CredentialEvent,
   GatewayAcceptedResponseDto,
   IssueCredentialRequestDto,
+  MakeBasicMessageRequestDto,
   makeEvent,
   SchemaEvent,
 } from "@ocm-engine/dtos";
@@ -27,7 +29,7 @@ export class AppController {
       type: SchemaEvent;
       source: string;
     },
-  ): Promise<{ id: string }> {
+  ): Promise<GatewayAcceptedResponseDto> {
     this.logger.debug(JSON.stringify(payload, null, 2));
 
     try {
@@ -63,7 +65,40 @@ export class AppController {
       type: CredentialEvent;
       source: string;
     },
-  ): Promise<{ id: string }> {
+  ): Promise<GatewayAcceptedResponseDto> {
+    this.logger.debug(JSON.stringify(payload, null, 2));
+
+    try {
+      const event = makeEvent(payload);
+      this.logger.debug(JSON.stringify(event, null, 2));
+      await this.producerService.publish<typeof payload.data>(
+        payload.type,
+        event as CloudEventDto<typeof payload.data>,
+      );
+
+      const response = new GatewayAcceptedResponseDto();
+      response.id = event.id;
+
+      return response;
+    } catch (e) {
+      this.logger.debug(JSON.stringify(e, null, 2));
+      if (e instanceof Error) {
+        throw new RpcException(e.message);
+      }
+
+      throw new RpcException("Internal server error");
+    }
+  }
+
+  @MessagePattern("messages")
+  async sendMeesage(
+    @Body()
+    payload: {
+      data: MakeBasicMessageRequestDto;
+      type: BasicMessageEvent;
+      source: string;
+    },
+  ): Promise<GatewayAcceptedResponseDto> {
     this.logger.debug(JSON.stringify(payload, null, 2));
 
     try {
diff --git a/apps/connection-manager/src/app/app.controller.ts b/apps/connection-manager/src/app/app.controller.ts
index 8001d35a..a8c5c05c 100644
--- a/apps/connection-manager/src/app/app.controller.ts
+++ b/apps/connection-manager/src/app/app.controller.ts
@@ -25,7 +25,7 @@ export class AppController {
       type: ConnectionEvent;
       source: string;
     },
-  ): Promise<{ id: string }> {
+  ): Promise<GatewayAcceptedResponseDto> {
     this.logger.debug(JSON.stringify(payload, null, 2));
 
     try {
diff --git a/apps/gateway/src/app/managers/attestation.controller.ts b/apps/gateway/src/app/managers/attestation.controller.ts
index c1e290fb..707c2ae2 100644
--- a/apps/gateway/src/app/managers/attestation.controller.ts
+++ b/apps/gateway/src/app/managers/attestation.controller.ts
@@ -21,6 +21,8 @@ import {
   GatewayAcceptedResponseDto,
   GetSchemaRequestDto,
   IssueCredentialRequestDto,
+  MakeBasicMessageRequestDto,
+  MESSAGE_MAKE,
   SCHEMA_CREATE,
   SCHEMA_GET,
   SCHEMA_LIST,
@@ -312,4 +314,38 @@ export class AttestationController {
       },
     });
   }
+
+  @Post("/messages")
+  @ApiResponse({
+    status: 200,
+    description:
+      "Request is accepted for execution, the response id will match the event id received from the web socket",
+    type: GatewayAcceptedResponseDto,
+  })
+  @ApiBadRequestResponse({
+    status: 400,
+    description:
+      "Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.",
+    type: BadRequestException,
+  })
+  @ApiInternalServerErrorResponse({
+    status: 500,
+    description: "Unknown error",
+  })
+  @ApiOperation({
+    summary: "Send basic message",
+    description:
+      "Method will send basic message to a connection. The id of the response will be matched when you receive event from the websocket",
+    tags: ["Credentials Offers"],
+  })
+  sendMeesage(@Body() message: MakeBasicMessageRequestDto) {
+    return this.amClient.sendPayload({
+      pattern: "messages",
+      payload: {
+        source: "messages",
+        data: message,
+        type: MESSAGE_MAKE,
+      },
+    });
+  }
 }
diff --git a/compose/docker-compose.simple.yml b/compose/docker-compose.simple.yml
new file mode 100644
index 00000000..0d8d9ad9
--- /dev/null
+++ b/compose/docker-compose.simple.yml
@@ -0,0 +1,40 @@
+version: '3.8'
+
+services:
+  pg_db:
+    image: 'postgres:latest'
+    ports:
+      - '5432:5432'
+    environment:
+      POSTGRES_USER: postgres
+      POSTGRES_PASSWORD: postgres
+    volumes:
+      - ./data/db-simple/:/var/lib/postgresql/data/
+
+  agent-issuer-simple:
+    container_name: agent-issuer-simple
+    build:
+      context: "../"
+      dockerfile: "./apps/agent/deployment/Dockerfile"
+    env_file:
+      - ./env/issuer.simple.env
+    ports:
+      - "8080:8080"
+      - "8001:8001"
+    depends_on:
+      pg_db:
+        condition: service_started
+
+  agent-holder-simple:
+    container_name: agent-holder-simple
+    build:
+      context: "../"
+      dockerfile: "./apps/agent/deployment/Dockerfile"
+    env_file:
+      - ./env/holder.simple.env
+    ports:
+      - "8081:8080"
+      - "6001:6001"
+    depends_on:
+      pg_db:
+        condition: service_started
diff --git a/compose/env/holder.simple.env b/compose/env/holder.simple.env
new file mode 100644
index 00000000..0db552de
--- /dev/null
+++ b/compose/env/holder.simple.env
@@ -0,0 +1,34 @@
+LEDGERS="BCOVRIN_TEST"
+IDUNION_KEY=
+
+AGENT_PEER_URL="http://agent-holder:6001"
+AGENT_NAME=DEV_SIMPLE_AGENT_HOLDER_OCM # this should be changed to company name
+AGENT_KEY=DEV_SIMPLE_AGENT_HOLDER_OCM #example random string
+AGENT_DID_SEED=200000000000000000000000TCuste21xh #did private key seed min lenght 32
+AGENT_DB_HOST=pg_db:5432
+AGENT_DB_USER=postgres
+AGENT_DB_PASS=postgres
+AGENT_PORT=8081
+AGENT_CONSUMER_NAME=agent_1
+AGENT_IS_REST=true
+AGENT_MAX_MESSAGES=10
+AGENT_RETE_LIMIT=5
+
+NATS_SERVERS=broker-holder:4222
+NATS_STREAM_NAME=ssi_holder_stream
+NATS_SUBJECTS="connections.*,proofs.*,credentials.*,schemas.*"
+
+GATEWAY_HTTP_PORT=8081
+GATEWAY_TCP_PORT=8881
+GATEWAY_SOCKET_EVENT_NAME=message
+GATEWAY_MESSAGE_PATTERN=webhook
+GATEWAY_HOST=gateway-holder
+
+CONNECTION_SERVICE_TCP_PORT=8882
+CONNECTION_SERVICE_HOST=cm-holder
+
+ATTESTATION_SERVICE_TCP_PORT=8883
+ATTESTATION_SERVICE_HOST=ap-holder
+
+PROOF_SERVICE_TCP_PORT=8884
+PROOF_SERVICE_HOST=pm-holder
diff --git a/compose/env/issuer.simple.env b/compose/env/issuer.simple.env
new file mode 100644
index 00000000..22c8de61
--- /dev/null
+++ b/compose/env/issuer.simple.env
@@ -0,0 +1,34 @@
+LEDGERS="BCOVRIN_TEST"
+IDUNION_KEY=
+
+AGENT_PEER_URL="http://agent-issuer:8001"
+AGENT_NAME=DEV_SIMPLE_AGENT_ISSUER_OCM # this should be changed to company name
+AGENT_KEY=DEV_SIMPLE_AGENT_ISSUER_OCM #example random string
+AGENT_DID_SEED=200000000000000000000000TCuste21js #did private key seed min lenght 32
+AGENT_DB_HOST=pg_db:5432
+AGENT_DB_USER=postgres
+AGENT_DB_PASS=postgres
+AGENT_PORT=8081
+AGENT_CONSUMER_NAME=agent_1
+AGENT_IS_REST=true
+AGENT_MAX_MESSAGES=10
+AGENT_RETE_LIMIT=5
+
+NATS_SERVERS=broker-issuer:4222
+NATS_STREAM_NAME=ssi_issuer_stream
+NATS_SUBJECTS="connections.*,proofs.*,credentials.*,schemas.*"
+
+GATEWAY_HTTP_PORT=8081
+GATEWAY_TCP_PORT=8881
+GATEWAY_SOCKET_EVENT_NAME=message
+GATEWAY_MESSAGE_PATTERN=webhook
+GATEWAY_HOST=gateway-issuer
+
+CONNECTION_SERVICE_TCP_PORT=8882
+CONNECTION_SERVICE_HOST=cm-issuer
+
+ATTESTATION_SERVICE_TCP_PORT=8883
+ATTESTATION_SERVICE_HOST=ap-issuer
+
+PROOF_SERVICE_TCP_PORT=8884
+PROOF_SERVICE_HOST=pm-issuer
diff --git a/libs/asker/src/asker-nats/agent.consumer.service.ts b/libs/asker/src/asker-nats/agent.consumer.service.ts
index ee23e028..6833e885 100644
--- a/libs/asker/src/asker-nats/agent.consumer.service.ts
+++ b/libs/asker/src/asker-nats/agent.consumer.service.ts
@@ -28,6 +28,8 @@ import {
   GetSchemaRequestDto,
   IssueCredentialRequestDto,
   IssueProofRequestDto,
+  MakeBasicMessageRequestDto,
+  MESSAGE_MAKE,
   PROOF_ACCEPT,
   PROOF_ISSUE,
   PROOF_LIST,
@@ -35,6 +37,7 @@ import {
   SCHEMA_GET,
   SCHEMA_LIST,
 } from "@ocm-engine/dtos";
+import asyncRetry from "async-retry";
 
 @Injectable()
 export class AgentConsumerService implements OnModuleInit, OnModuleDestroy {
@@ -61,77 +64,98 @@ export class AgentConsumerService implements OnModuleInit, OnModuleDestroy {
       let data;
       let dto;
 
-      switch (event.type) {
-        case CONNECTION_CREATE:
-          data = await this.agentService.createInvitation();
-          break;
-        case CONNECTION_ACCEPT:
-          dto = event.data as CreateInvitationResponseDto;
-          data = await this.agentService.acceptInvitation(dto.invitationUrl);
-          break;
-
-        case CONNECTION_LIST:
-          data = await this.agentService.fetchConnections();
-          break;
-
-        case CONNECTION_GET:
-          dto = event.data as GetConnectionRequestDto;
-          data = await this.agentService.getConnectionById(dto.connectionId);
-          break;
-
-        case SCHEMA_CREATE:
-          dto = event.data as CreateSchemaRequestDto;
-          data = await this.agentService.createSchema(dto);
-          break;
-        case SCHEMA_LIST:
-          data = await this.agentService.fetchSchemas();
-          break;
-
-        case SCHEMA_GET:
-          dto = event.data as GetSchemaRequestDto;
-          data = await this.agentService.getSchemaById(dto.schemaId);
-          break;
-        case CRED_DEF_CREATE:
-          data = await this.agentService.createCredentialDefinition(
-            event.data as CreateCredentialDefinitionRequsetDto,
-          );
-          break;
-
-        case CRED_ISSUE:
-          data = await this.agentService.issueCredential(
-            event.data as IssueCredentialRequestDto,
-          );
-          break;
-
-        case CRED_LIST:
-          data = await this.agentService.credentials();
-          break;
-
-        case CRED_OFFER_LIST:
-          data = await this.agentService.credentialByStatedOfferReceived();
-          break;
-
-        case CRED_OFFER_ACCEPT:
-          dto = event.data as AcceptCredentialOfferRequestDto;
-          data = await this.agentService.acceptCredential(
-            dto.credentialRecordId,
-          );
-          break;
-
-        case PROOF_ISSUE:
-          dto = event.data as IssueProofRequestDto;
-          data = await this.agentService.issueProof(dto);
-          break;
-
-        case PROOF_LIST:
-          data = await this.agentService.proofs();
-          break;
-
-        case PROOF_ACCEPT:
-          dto = event.data as AcceptProofRequestDto;
-          data = await this.agentService.acceptProof(dto.proofRecordId);
-          break;
-      }
+      await asyncRetry(
+        async () => {
+          switch (event.type) {
+            case CONNECTION_CREATE:
+              data = await this.agentService.createInvitation();
+              break;
+            case CONNECTION_ACCEPT:
+              dto = event.data as CreateInvitationResponseDto;
+              data = await this.agentService.acceptInvitation(
+                dto.invitationUrl,
+              );
+              break;
+
+            case CONNECTION_LIST:
+              data = await this.agentService.fetchConnections();
+              break;
+
+            case CONNECTION_GET:
+              dto = event.data as GetConnectionRequestDto;
+              data = await this.agentService.getConnectionById(
+                dto.connectionId,
+              );
+              break;
+
+            case SCHEMA_CREATE:
+              dto = event.data as CreateSchemaRequestDto;
+              data = await this.agentService.createSchema(dto);
+              break;
+            case SCHEMA_LIST:
+              data = await this.agentService.fetchSchemas();
+              break;
+
+            case SCHEMA_GET:
+              dto = event.data as GetSchemaRequestDto;
+              data = await this.agentService.getSchemaById(dto.schemaId);
+              break;
+            case CRED_DEF_CREATE:
+              data = await this.agentService.createCredentialDefinition(
+                event.data as CreateCredentialDefinitionRequsetDto,
+              );
+              break;
+
+            case CRED_ISSUE:
+              data = await this.agentService.issueCredential(
+                event.data as IssueCredentialRequestDto,
+              );
+              break;
+
+            case CRED_LIST:
+              data = await this.agentService.credentials();
+              break;
+
+            case CRED_OFFER_LIST:
+              data = await this.agentService.credentialByStatedOfferReceived();
+              break;
+
+            case CRED_OFFER_ACCEPT:
+              dto = event.data as AcceptCredentialOfferRequestDto;
+              data = await this.agentService.acceptCredential(
+                dto.credentialRecordId,
+              );
+              break;
+
+            case PROOF_ISSUE:
+              dto = event.data as IssueProofRequestDto;
+              data = await this.agentService.issueProof(dto);
+              break;
+
+            case PROOF_LIST:
+              data = await this.agentService.proofs();
+              break;
+
+            case PROOF_ACCEPT:
+              dto = event.data as AcceptProofRequestDto;
+              data = await this.agentService.acceptProof(dto.proofRecordId);
+              break;
+
+            case MESSAGE_MAKE:
+              dto = event.data as MakeBasicMessageRequestDto;
+              data = await this.agentService.sendMessage(dto);
+              break;
+          }
+        },
+        {
+          retries: 5,
+          maxTimeout: 5000,
+          onRetry: (error) => {
+            this.logger.log(JSON.stringify(error, null, 2));
+            this.logger.error(`Fail...${error.message}`);
+          },
+        },
+      );
 
       event.data = data;
       this.logger.debug(`before sending ${JSON.stringify(event, null, 2)}`);
diff --git a/libs/asker/src/asker-rest/rest.controller.ts b/libs/asker/src/asker-rest/rest.controller.ts
index b47f29b3..f4b3aafb 100644
--- a/libs/asker/src/asker-rest/rest.controller.ts
+++ b/libs/asker/src/asker-rest/rest.controller.ts
@@ -6,11 +6,12 @@ import {
   IssueCredentialRequestDto,
   IssueProofRequestDto,
   CreateSchemaRequestDto,
+  MakeBasicMessageRequestDto,
 } from "@ocm-engine/dtos";
 import { AllExceptionsHandler } from "./exception.handler";
 
 @UseFilters(AllExceptionsHandler)
-@Controller()
+@Controller("v1")
 export class RestController {
   constructor(private readonly agentService: AgentService) {}
 
@@ -84,4 +85,9 @@ export class RestController {
   resolve(@Body("did") did: string) {
     return this.agentService.resolve(did);
   }
+
+  @Post("/messages")
+  sendMeesage(@Body() message: MakeBasicMessageRequestDto) {
+    return this.agentService.sendMessage(message);
+  }
 }
diff --git a/libs/asker/src/asker/agent.event.listener.servce.ts b/libs/asker/src/asker/agent.event.listener.servce.ts
new file mode 100644
index 00000000..8b9a0f56
--- /dev/null
+++ b/libs/asker/src/asker/agent.event.listener.servce.ts
@@ -0,0 +1,63 @@
+import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
+import { GatewayClient } from "@ocm-engine/clients";
+import { AskerService } from "./asker.service";
+import {
+  BasicMessageEventTypes,
+  BasicMessageRole,
+  BasicMessageStateChangedEvent,
+} from "@aries-framework/core";
+import {
+  MakeBasicMessageResponseDto,
+  makeEvent,
+  MESSAGE_MAKE,
+} from "@ocm-engine/dtos";
+import { IConfAgent } from "@ocm-engine/config";
+import { ConfigService } from "@nestjs/config";
+
+@Injectable()
+export class AgentEventListenerServce implements OnModuleInit {
+  private agentConfig: IConfAgent;
+  private readonly logger: Logger = new Logger(AgentEventListenerServce.name);
+
+  constructor(
+    private readonly gatewayClient: GatewayClient,
+    private readonly asker: AskerService,
+    private readonly configService: ConfigService,
+  ) {}
+
+  onModuleInit(): void {
+    this.logger.debug("Agent is listening for AFJ events");
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    this.agentConfig = this.configService.get<IConfAgent>("agent")!;
+
+    this.asker.agent.events.on(
+      BasicMessageEventTypes.BasicMessageStateChanged,
+      async (ev: BasicMessageStateChangedEvent) => {
+        if (ev.payload.basicMessageRecord.role === BasicMessageRole.Receiver) {
+          this.logger.debug(JSON.stringify(ev, null, 2));
+
+          const dto = new MakeBasicMessageResponseDto();
+          dto.message = ev.payload.basicMessageRecord.content;
+          dto.id = ev.payload.basicMessageRecord.id;
+
+          if (this.agentConfig.agentIsRest) {
+            this.logger.debug(
+              "agent is configured as rest, webhook still not implemented",
+            );
+
+            return;
+          }
+
+          const event = makeEvent({
+            data: dto,
+            type: MESSAGE_MAKE,
+            source: "agent-basic-message-afj",
+          });
+
+          this.logger.debug("Sending message event to gateway");
+          this.gatewayClient.sendPayload(event);
+        }
+      },
+    );
+  }
+}
diff --git a/libs/asker/src/asker/agent.service.ts b/libs/asker/src/asker/agent.service.ts
index d311a3ea..0fdf814a 100644
--- a/libs/asker/src/asker/agent.service.ts
+++ b/libs/asker/src/asker/agent.service.ts
@@ -13,6 +13,8 @@ import {
   IssueCredentialResponseDto,
   IssueProofRequestDto,
   IssueProofResponseDto,
+  MakeBasicMessageRequestDto,
+  MakeBasicMessageResponseDto,
   SchemaNotCreatedError,
 } from "@ocm-engine/dtos";
 import {
@@ -309,4 +311,18 @@ export class AgentService {
   resolve = async (did: string) => {
     return this.asker.agent.dids.resolve(did);
   };
+
+  sendMessage = async (message: MakeBasicMessageRequestDto) => {
+    const response = new MakeBasicMessageResponseDto();
+
+    const m = await this.asker.agent.basicMessages.sendMessage(
+      message.connectionId,
+      message.message,
+    );
+
+    response.id = m.id;
+    response.message = m.content;
+
+    return response;
+  };
 }
diff --git a/libs/asker/src/asker/asker.module.ts b/libs/asker/src/asker/asker.module.ts
index 254adfdf..90dea640 100644
--- a/libs/asker/src/asker/asker.module.ts
+++ b/libs/asker/src/asker/asker.module.ts
@@ -3,11 +3,12 @@ import { AskerService } from "./asker.service";
 import { AgentService } from "./agent.service";
 import { ConfigModule } from "@nestjs/config";
 import { LedgersModule } from "@ocm-engine/ledgers";
+import { AgentEventListenerServce } from "./agent.event.listener.servce";
 
 @Global()
 @Module({
   imports: [ConfigModule, LedgersModule],
-  providers: [AgentService, AskerService],
+  providers: [AgentService, AskerService, AgentEventListenerServce],
   exports: [AgentService, AskerService],
 })
 export class AskerModule {}
diff --git a/libs/asker/src/asker/asker.service.ts b/libs/asker/src/asker/asker.service.ts
index 6bf5e577..b3a391c0 100644
--- a/libs/asker/src/asker/asker.service.ts
+++ b/libs/asker/src/asker/asker.service.ts
@@ -10,7 +10,6 @@ import {
   ConsoleLogger,
   HttpOutboundTransport,
   InitConfig,
-  KeyDerivationMethod,
   LogLevel,
   TypedArrayEncoder,
   WsOutboundTransport,
diff --git a/libs/clients/src/lib/gateway.client.ts b/libs/clients/src/lib/gateway.client.ts
index c32b0b76..c7e9564d 100644
--- a/libs/clients/src/lib/gateway.client.ts
+++ b/libs/clients/src/lib/gateway.client.ts
@@ -16,7 +16,6 @@ export class GatewayClient {
 
   constructor(configService: ConfigService) {
     this.gatewayConf = configService.get<IGateway>("gateway")!;
-    console.log(this.gatewayConf);
 
     this.client = ClientProxyFactory.create({
       transport: Transport.TCP,
diff --git a/libs/dtos/src/dtos/requests/make.basic.message.request.dto.ts b/libs/dtos/src/dtos/requests/make.basic.message.request.dto.ts
new file mode 100644
index 00000000..33f2130e
--- /dev/null
+++ b/libs/dtos/src/dtos/requests/make.basic.message.request.dto.ts
@@ -0,0 +1,11 @@
+import { IsNotEmpty, IsString } from "class-validator";
+
+export class MakeBasicMessageRequestDto {
+  @IsNotEmpty()
+  @IsString()
+  connectionId: string;
+
+  @IsNotEmpty()
+  @IsString()
+  message: string;
+}
diff --git a/libs/dtos/src/dtos/responses/make.basic.message.response.dto.ts b/libs/dtos/src/dtos/responses/make.basic.message.response.dto.ts
new file mode 100644
index 00000000..684b4d31
--- /dev/null
+++ b/libs/dtos/src/dtos/responses/make.basic.message.response.dto.ts
@@ -0,0 +1,11 @@
+import { IsNotEmpty, IsString } from "class-validator";
+
+export class MakeBasicMessageResponseDto {
+  @IsNotEmpty()
+  @IsString()
+  id: string;
+
+  @IsNotEmpty()
+  @IsString()
+  message: string;
+}
diff --git a/libs/dtos/src/events/dtoToEventTransformer.ts b/libs/dtos/src/events/dtoToEventTransformer.ts
index 0bb08689..e0c2c304 100644
--- a/libs/dtos/src/events/dtoToEventTransformer.ts
+++ b/libs/dtos/src/events/dtoToEventTransformer.ts
@@ -1,5 +1,6 @@
 import {
   ALL_EVENTS,
+  BasicMessageEvent,
   ConnectionEvent,
   CredentialEvent,
   ProofEvent,
@@ -14,6 +15,8 @@ import { GetConnectionRequestDto } from "../dtos/requests/get.connection.request
 import { IssueCredentialRequestDto } from "../dtos/requests/issue.credential.request.dto";
 import { IssueProofRequestDto } from "../dtos/requests/issue.proof.request.dto";
 import { AcceptCredentialOfferRequestDto } from "../dtos/requests/accept.credential.offer.request.dto";
+import { MakeBasicMessageResponseDto } from "../dtos/responses/make.basic.message.response.dto";
+import { MakeBasicMessageRequestDto } from "../dtos/requests/make.basic.message.request.dto";
 
 export const makeEvent = (payload: {
   data:
@@ -25,8 +28,15 @@ export const makeEvent = (payload: {
     | CreateSchemaRequestDto
     | CreateSchemaRequestDto
     | CreateCredentialDefinitionRequsetDto
-    | IssueCredentialRequestDto;
-  type: SchemaEvent | CredentialEvent | ProofEvent | ConnectionEvent;
+    | IssueCredentialRequestDto
+    | MakeBasicMessageResponseDto
+    | MakeBasicMessageRequestDto;
+  type:
+    | SchemaEvent
+    | CredentialEvent
+    | ProofEvent
+    | ConnectionEvent
+    | BasicMessageEvent;
   source: string;
 }) => {
   if (ALL_EVENTS.includes(payload.type)) {
diff --git a/libs/dtos/src/events/types.ts b/libs/dtos/src/events/types.ts
index e4fc183d..f19e1b6c 100644
--- a/libs/dtos/src/events/types.ts
+++ b/libs/dtos/src/events/types.ts
@@ -60,9 +60,15 @@ export const PROOF_EVENTS: ProofEvent[] = [
   PROOF_ISSUE,
 ];
 
+export type BasicMessageEvent = "messages.make";
+export const MESSAGE_MAKE = "messages.make";
+
+export const DIDCOMM_EVENTS: BasicMessageEvent[] = [MESSAGE_MAKE];
+
 export const ALL_EVENTS = [
   ...SCHEMA_EVENTS,
   ...CRED_EVENTS,
   ...PROOF_EVENTS,
   ...CONNECTION_EVENTS,
+  ...DIDCOMM_EVENTS,
 ];
diff --git a/libs/dtos/src/index.ts b/libs/dtos/src/index.ts
index 1cb5460a..e699d9fe 100644
--- a/libs/dtos/src/index.ts
+++ b/libs/dtos/src/index.ts
@@ -7,6 +7,7 @@ export * from "./dtos/requests/get.connection.request.dto";
 export * from "./dtos/requests/get.schema.request.dto";
 export * from "./dtos/requests/accept.credential.offer.request.dto";
 export * from "./dtos/requests/accept.proof.request.dto";
+export * from "./dtos/requests/make.basic.message.request.dto";
 
 export * from "./dtos/responses/create.invitation.response.dto";
 export * from "./dtos/responses/accept.invitation.response.dto";
@@ -15,6 +16,7 @@ export * from "./dtos/responses/create.credential.definition.response.dto";
 export * from "./dtos/responses/issue.credential.response.dto";
 export * from "./dtos/responses/issue.proof.response.dto";
 export * from "./dtos/responses/gateway.accepted.response.dto";
+export * from "./dtos/responses/make.basic.message.response.dto";
 
 export * from "./errors/connection.not.found.error";
 export * from "./errors/schema.not.created.error";
diff --git a/package.json b/package.json
index 92245c61..ef466b42 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,9 @@
     "infra:issuer:stop": "cd compose && docker-compose --profile issuer stop",
     "infra:holder:stop": "cd compose && docker-compose --profile holder stop",
     "infra:local": "cd compose && docker-compose -f=docker-compose.infra.yml up -d",
-    "infra:local:stop": "cd compose && docker-compose -f=docker-compose.infra.yml stop"
+    "infra:local:stop": "cd compose && docker-compose -f=docker-compose.infra.yml stop",
+
+    "infra:simple": "cd compose && docker-compose -f docker-compose.simple.yml up -d"
   },
   "private": true,
   "dependencies": {
-- 
GitLab