gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet] branch master updated: -added core functionality of the messeng


From: gnunet
Subject: [gnunet] branch master updated: -added core functionality of the messenger service
Date: Sat, 06 Mar 2021 01:47:08 +0100

This is an automated email from the git hooks/post-receive script.

thejackimonster pushed a commit to branch master
in repository gnunet.

The following commit(s) were added to refs/heads/master by this push:
     new 2925310d6 -added core functionality of the messenger service
2925310d6 is described below

commit 2925310d67483aca6e055e1ce0593c6463cd6c72
Author: TheJackiMonster <thejackimonster@gmail.com>
AuthorDate: Sun Nov 1 22:57:28 2020 +0100

    -added core functionality of the messenger service
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    formatting messenger code
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -completed core functionality of messenger service
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -code cleanup and reuse
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    +added structure to memberships of rooms
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -implemented member permission checks and deletion of messages
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -moved solving requests out of updating last messages (also forward before 
update)
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -reduced complexity of permisson check and changed load/save of rooms
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -added save/load for accessed keys and basement peers of a room
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -implemented save/load for members with their history and session
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -abstracted management of egos and contacts
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fix warning
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -abstracted management of members
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed and adjusted test case
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -separated handling of direct and anonymous contacts
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -messenger added member-sessions which fix multiple edge cases, also 
additional cleanup
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -updated docs and fixed memory leak
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -changed info messages and added protocol version exchange
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -adjusted client api to use contacts from sessions
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -added more logging and fixed wrong session usage
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -adjusted comm0 test case and removed adding members from checking messages
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed test-case for peer exchange
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -getting multiple peers connected in test-case with cadet
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed wrong assert and added tunnel version check
    
    -simplified handling and forwarding
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed merging last messages and cycling info messages
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -automated adding sessions and members
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -corrected use of identity keys and signatures
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -adding local joining on entering external room
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed test-case comm0
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -added more test-cases with generic setup
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed multiple simultaneous channels blocking each other
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -making test-cases more precise
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -added check of members in test-cases, reduced merge messages
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -unified delayed operations: requests, deletions and merges
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -finished handling of operations
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed member session historystorage, added request permission check and 
padding for transmission
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -improved padding and removed automatic recursive requests
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -implemented filter for sending messages and added private messages to API 
level
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -wrong setups fixed with proper ego lookups
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -fixed problem with anonymous ego and changed to discrete-level padding
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -added links to replace deleted messages, added local deletion and fixed 
anonymous id changing
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
    
    -added session completion and removal through completion process
    
    Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
---
 po/POTFILES.in                                     |    2 +
 src/include/gnunet_messenger_service.h             |  240 ++++-
 src/messenger/.gitignore                           |   10 +
 src/messenger/Makefile.am                          |  136 ++-
 src/messenger/gnunet-messenger.c                   |  132 ++-
 src/messenger/gnunet-service-messenger.c           |  148 ++-
 src/messenger/gnunet-service-messenger.h           |   20 +-
 src/messenger/gnunet-service-messenger_basement.c  |    4 +-
 src/messenger/gnunet-service-messenger_basement.h  |   24 +-
 src/messenger/gnunet-service-messenger_contact.c   |   96 --
 src/messenger/gnunet-service-messenger_contact.h   |  112 --
 src/messenger/gnunet-service-messenger_ego_store.c |  295 ++++++
 src/messenger/gnunet-service-messenger_ego_store.h |  152 +++
 src/messenger/gnunet-service-messenger_handle.c    |  449 +++++---
 src/messenger/gnunet-service-messenger_handle.h    |  120 ++-
 .../gnunet-service-messenger_list_handles.c        |   16 +-
 .../gnunet-service-messenger_list_handles.h        |   42 +-
 .../gnunet-service-messenger_list_messages.c       |   76 +-
 .../gnunet-service-messenger_list_messages.h       |   42 +-
 src/messenger/gnunet-service-messenger_member.c    |  379 +++++++
 src/messenger/gnunet-service-messenger_member.h    |  170 +++
 .../gnunet-service-messenger_member_session.c      |  707 +++++++++++++
 .../gnunet-service-messenger_member_session.h      |  275 +++++
 .../gnunet-service-messenger_member_store.c        |  250 +++++
 .../gnunet-service-messenger_member_store.h        |  151 +++
 .../gnunet-service-messenger_message_handle.c      |  108 +-
 .../gnunet-service-messenger_message_handle.h      |  100 +-
 .../gnunet-service-messenger_message_kind.c        |   69 +-
 .../gnunet-service-messenger_message_kind.h        |   58 +-
 .../gnunet-service-messenger_message_recv.c        |  206 ++--
 .../gnunet-service-messenger_message_recv.h        |  128 +--
 .../gnunet-service-messenger_message_send.c        |   89 +-
 .../gnunet-service-messenger_message_send.h        |  121 +--
 .../gnunet-service-messenger_message_store.c       |  360 ++++++-
 .../gnunet-service-messenger_message_store.h       |   92 +-
 src/messenger/gnunet-service-messenger_operation.c |  214 ++++
 src/messenger/gnunet-service-messenger_operation.h |  129 +++
 .../gnunet-service-messenger_operation_store.c     |  224 ++++
 .../gnunet-service-messenger_operation_store.h     |  131 +++
 src/messenger/gnunet-service-messenger_room.c      | 1092 ++++++++++----------
 src/messenger/gnunet-service-messenger_room.h      |  265 ++---
 src/messenger/gnunet-service-messenger_service.c   |  306 +-----
 src/messenger/gnunet-service-messenger_service.h   |  162 +--
 src/messenger/gnunet-service-messenger_tunnel.c    |  233 +++--
 src/messenger/gnunet-service-messenger_tunnel.h    |  104 +-
 src/messenger/messenger_api.c                      |  264 +++--
 src/messenger/messenger_api_contact.c              |   44 +-
 src/messenger/messenger_api_contact.h              |   45 +-
 src/messenger/messenger_api_contact_store.c        |  182 ++++
 src/messenger/messenger_api_contact_store.h        |  122 +++
 src/messenger/messenger_api_ego.h                  |    2 +-
 src/messenger/messenger_api_handle.c               |   99 +-
 src/messenger/messenger_api_handle.h               |   69 +-
 src/messenger/messenger_api_list_tunnels.c         |   74 +-
 src/messenger/messenger_api_list_tunnels.h         |   44 +-
 src/messenger/messenger_api_message.c              |  423 ++++++--
 src/messenger/messenger_api_message.h              |  119 ++-
 src/messenger/messenger_api_room.c                 |  214 +++-
 src/messenger/messenger_api_room.h                 |   54 +-
 ...rvice-messenger_util.c => messenger_api_util.c} |   30 +-
 ...rvice-messenger_util.h => messenger_api_util.h} |   27 +-
 src/messenger/test_messenger.c                     |   40 +-
 ...{messenger_api_ego.h => test_messenger_adapt.c} |   35 +-
 src/messenger/test_messenger_anonymous.c           |   34 +-
 src/messenger/test_messenger_api.conf              |   32 +-
 ...ger_api_ego.h => test_messenger_async_client.c} |   35 +-
 ...senger_api_ego.h => test_messenger_async_p2p.c} |   35 +-
 src/messenger/test_messenger_comm0.c               |  252 -----
 ...messenger_api_ego.h => test_messenger_growth.c} |   35 +-
 .../{messenger_api_ego.h => test_messenger_ring.c} |   35 +-
 ...messenger_api_ego.h => test_messenger_server.c} |   35 +-
 ...nger_api_ego.h => test_messenger_sync_client.c} |   35 +-
 ...ssenger_api_ego.h => test_messenger_sync_p2p.c} |   35 +-
 ...ger_api_ego.h => test_messenger_worst_client.c} |   35 +-
 ...senger_api_ego.h => test_messenger_worst_p2p.c} |   35 +-
 src/messenger/testing_messenger_barrier.c          |  170 +++
 src/messenger/testing_messenger_barrier.h          |  131 +++
 src/messenger/testing_messenger_setup.c            |  528 ++++++++++
 ...ssenger_api_ego.h => testing_messenger_setup.h} |   25 +-
 79 files changed, 8377 insertions(+), 3236 deletions(-)

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 51ada43b3..b2fa88d73 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -550,5 +550,7 @@ src/vpn/vpn_api.c
 src/zonemaster/gnunet-service-zonemaster-monitor.c
 src/zonemaster/gnunet-service-zonemaster.c
 src/fs/fs_api.h
+src/include/gnunet_identity_service.h
+src/include/gnunet_messenger_service.h
 src/testbed/testbed_api.h
 src/testbed/testbed_api_operations.h
diff --git a/src/include/gnunet_messenger_service.h 
b/src/include/gnunet_messenger_service.h
index 389b6b8fd..99d4cf267 100644
--- a/src/include/gnunet_messenger_service.h
+++ b/src/include/gnunet_messenger_service.h
@@ -48,6 +48,14 @@ extern "C" {
 #include "gnunet_scheduler_lib.h"
 #include "gnunet_time_lib.h"
 
+/**
+ * Version number of GNUnet Messenger API.
+ */
+#define GNUNET_MESSENGER_VERSION 0x00000001
+
+/**
+ * Identifier of GNUnet MESSENGER Service.
+ */
 #define GNUNET_MESSENGER_SERVICE_NAME "messenger"
 
 /**
@@ -140,12 +148,19 @@ enum GNUNET_MESSENGER_MessageKind
    */
   GNUNET_MESSENGER_KIND_PRIVATE = 14,
 
+  /**
+   * The delete kind. The message contains a #GNUNET_MESSENGER_MessageDelete 
body.
+   */
+  GNUNET_MESSENGER_KIND_DELETE = 15,
+
   /**
    * The unknown kind. The message contains an unknown body.
    */
   GNUNET_MESSENGER_KIND_UNKNOWN = 0
 };
 
+#define GNUNET_MESSENGER_KIND_MAX (GNUNET_MESSENGER_KIND_DELETE)
+
 /**
  * Get the name of a message <i>kind</i>.
  *
@@ -157,6 +172,9 @@ GNUNET_MESSENGER_name_of_kind (enum 
GNUNET_MESSENGER_MessageKind kind);
 
 /**
  * The header of a #GNUNET_MESSENGER_Message.
+ * This allows authentification of the sender, temporal ordering and finding 
potentially missed messages.
+ *
+ * Message-header-size: 40+ bytes
  */
 struct GNUNET_MESSENGER_MessageHeader
 {
@@ -188,6 +206,9 @@ struct GNUNET_MESSENGER_MessageHeader
 
 /**
  * An info message body.
+ * This allows ensuring member ids are unique and this first message can be 
verified.
+ *
+ * Message-body-size: 8+ bytes
  */
 struct GNUNET_MESSENGER_MessageInfo
 {
@@ -197,13 +218,16 @@ struct GNUNET_MESSENGER_MessageInfo
   struct GNUNET_IDENTITY_PublicKey host_key;
 
   /**
-   * The new unique id for the receiver in a room.
+   * The version of GNUnet Messenger API.
    */
-  struct GNUNET_ShortHashCode unique_id;
+  uint32_t messenger_version;
 };
 
 /**
  * A join message body.
+ * This allows informing others about joining the room with a given key pair.
+ *
+ * Message-body-size: 4+ bytes
  */
 struct GNUNET_MESSENGER_MessageJoin
 {
@@ -215,6 +239,9 @@ struct GNUNET_MESSENGER_MessageJoin
 
 /**
  * A leave message body.
+ * This allows informing others about leaving the room.
+ *
+ * Message-body-size: 0 bytes
  */
 struct GNUNET_MESSENGER_MessageLeave
 {
@@ -222,6 +249,9 @@ struct GNUNET_MESSENGER_MessageLeave
 
 /**
  * A name message body.
+ * This allows replacing the current name with another one.
+ *
+ * Message-body-size: 0+ bytes
  */
 struct GNUNET_MESSENGER_MessageName
 {
@@ -233,6 +263,9 @@ struct GNUNET_MESSENGER_MessageName
 
 /**
  * A key message body.
+ * This allows replacing the current key pair with another one.
+ *
+ * Message-body-size: 4+ bytes
  */
 struct GNUNET_MESSENGER_MessageKey
 {
@@ -244,6 +277,9 @@ struct GNUNET_MESSENGER_MessageKey
 
 /**
  * A peer message body.
+ * This allows informing others to open a peer as a door to the current room.
+ *
+ * Message-body-size: 32 bytes
  */
 struct GNUNET_MESSENGER_MessagePeer
 {
@@ -255,6 +291,9 @@ struct GNUNET_MESSENGER_MessagePeer
 
 /**
  * An id message body.
+ * This allows replacing the member id with a newly unique generated one.
+ *
+ * Message-body-size: 8 bytes
  */
 struct GNUNET_MESSENGER_MessageId
 {
@@ -266,6 +305,9 @@ struct GNUNET_MESSENGER_MessageId
 
 /**
  * A miss message body.
+ * This allows informing others about a disconnection of any door.
+ *
+ * Message-body-size: 32 bytes
  */
 struct GNUNET_MESSENGER_MessageMiss
 {
@@ -277,6 +319,9 @@ struct GNUNET_MESSENGER_MessageMiss
 
 /**
  * A merge message body.
+ * This allows merging message history branches together.
+ *
+ * Message-body-size: 16 bytes
  */
 struct GNUNET_MESSENGER_MessageMerge
 {
@@ -288,6 +333,9 @@ struct GNUNET_MESSENGER_MessageMerge
 
 /**
  * A request message body.
+ * This allows requesting the content of a specific message which is currently 
missing.
+ *
+ * Message-body-size: 16 bytes
  */
 struct GNUNET_MESSENGER_MessageRequest
 {
@@ -299,6 +347,9 @@ struct GNUNET_MESSENGER_MessageRequest
 
 /**
  * An invite message body.
+ * This allows sharing information about other rooms in form of an invitation.
+ *
+ * Message-body-size: 48 bytes
  */
 struct GNUNET_MESSENGER_MessageInvite
 {
@@ -315,6 +366,9 @@ struct GNUNET_MESSENGER_MessageInvite
 
 /**
  * A text message body.
+ * This allows general communication in text form.
+ *
+ * Message-body-size: 0+ bytes
  */
 struct GNUNET_MESSENGER_MessageText
 {
@@ -326,6 +380,9 @@ struct GNUNET_MESSENGER_MessageText
 
 /**
  * A file message body.
+ * This allows sending necessary details about an uploaded encrypted file to 
allow access to it.
+ *
+ * Message-body-size: 335+ bytes
  */
 struct GNUNET_MESSENGER_MessageFile
 {
@@ -352,6 +409,9 @@ struct GNUNET_MESSENGER_MessageFile
 
 /**
  * A private message body.
+ * This allows to encapsulate any message to be encrypted for only one 
specific member to receive in a room.
+ *
+ * Message-body-size: 32+ bytes
  */
 struct GNUNET_MESSENGER_MessagePrivate
 {
@@ -371,6 +431,25 @@ struct GNUNET_MESSENGER_MessagePrivate
   char *data;
 };
 
+/**
+ * A delete message body
+ * This allows deletion of an own previous message with any custom automatic 
delay.
+ *
+ * Message-body-size: 24 bytes
+ */
+struct GNUNET_MESSENGER_MessageDelete
+{
+  /**
+   * The hash of the message to delete.
+   */
+  struct GNUNET_HashCode hash;
+
+  /**
+   * The delay of the delete operation to get processed.
+   */
+  struct GNUNET_TIME_RelativeNBO delay;
+};
+
 /**
  * The unified body of a #GNUNET_MESSENGER_Message.
  */
@@ -392,6 +471,7 @@ struct GNUNET_MESSENGER_MessageBody
     struct GNUNET_MESSENGER_MessageText text;
     struct GNUNET_MESSENGER_MessageFile file;
     struct GNUNET_MESSENGER_MessagePrivate private;
+    struct GNUNET_MESSENGER_MessageDelete delete;
   };
 };
 
@@ -411,12 +491,28 @@ struct GNUNET_MESSENGER_Message
   struct GNUNET_MESSENGER_MessageBody body;
 };
 
+/**
+ * Enum for the different supported flags used by message handling
+ */
+enum GNUNET_MESSENGER_MessageFlags
+{
+  /**
+   * The none flag. The flag indicates that the message is not affected by any 
special context.
+   */
+  GNUNET_MESSENGER_FLAG_NONE = 0,
+
+  /**
+   * The private flag. The flag indicates that the message was privately 
encrypted.
+   */
+  GNUNET_MESSENGER_FLAG_PRIVATE = 1,
+};
+
 /**
  * Method called whenever the EGO of a <i>handle</i> changes or if the first 
connection fails
  * to load a valid EGO and the anonymous key pair will be used instead.
  *
- * @param cls Closure from <i>GNUNET_MESSENGER_connect</i>
- * @param handle Messenger handle
+ * @param[in/out] cls Closure from #GNUNET_MESSENGER_connect
+ * @param[in/out] handle Messenger handle
  */
 typedef void
 (*GNUNET_MESSENGER_IdentityCallback) (void *cls, struct 
GNUNET_MESSENGER_Handle *handle);
@@ -424,25 +520,45 @@ typedef void
 /**
  * Method called whenever a message is sent or received from a <i>room</i>.
  *
- * @param cls Closure from <i>GNUNET_MESSENGER_connect</i>
- * @param room Room handle
- * @param message Newly received or sent message
- * @param hash Hash identifying the message
+ * The flag <i>private_message</i> will be #GNUNET_YES if a message was
+ * received privately, otherwise #GNUNET_NO.
+ *
+ * @param[in/out] cls Closure from #GNUNET_MESSENGER_connect
+ * @param[in] room Room handle
+ * @param[in] sender Sender of message
+ * @param[in] message Newly received or sent message
+ * @param[in] hash Hash identifying the message
+ * @param[in] flags Flags of the message
  */
 typedef void
-(*GNUNET_MESSENGER_MessageCallback) (void *cls, const struct 
GNUNET_MESSENGER_Room *room,
-                                     const struct GNUNET_MESSENGER_Message 
*message, const struct GNUNET_HashCode *hash);
+(*GNUNET_MESSENGER_MessageCallback) (void *cls, struct GNUNET_MESSENGER_Room 
*room,
+                                     const struct GNUNET_MESSENGER_Contact 
*sender,
+                                     const struct GNUNET_MESSENGER_Message 
*message,
+                                     const struct GNUNET_HashCode *hash,
+                                     enum GNUNET_MESSENGER_MessageFlags flags);
+
+/**
+ * Method called for each member in a <i>room</i> during iteration. If the 
method returns
+ * #GNUNET_YES the iteration continues, otherwise it will quit the iteration.
+ *
+ * @param[in/out] cls Closure from #GNUNET_MESSENGER_iterate_members
+ * @param[in] room Room handle
+ * @param[in] contact Contact handle
+ */
+typedef int
+(*GNUNET_MESSENGER_MemberCallback) (void* cls, struct GNUNET_MESSENGER_Room 
*room,
+                                    const struct GNUNET_MESSENGER_Contact 
*contact);
 
 /**
  * Set up a handle for the messenger related functions and connects to all 
necessary services. It will look up the ego
  * key identified by its <i>name</i> and use it for signing all messages from 
the handle.
  *
- * @param cfg Configuration to use
- * @param name Name to look up an ego or NULL to stay anonymous
- * @param identity_callback Function called when the EGO of the handle changes
- * @param identity_cls Closure for the <i>identity_callback</i> handler
- * @param msg_callback Function called when a new message is sent or received
- * @param msg_cls Closure for the <i>msg_callback</i> handler
+ * @param[in] cfg Configuration to use
+ * @param[in] name Name to look up an ego or NULL to stay anonymous
+ * @param[in] identity_callback Function called when the EGO of the handle 
changes
+ * @param[in/out] identity_cls Closure for the <i>identity_callback</i> handler
+ * @param[in] msg_callback Function called when a new message is sent or 
received
+ * @param[in/out] msg_cls Closure for the <i>msg_callback</i> handler
  * @return Messenger handle to use, NULL on error
  */
 struct GNUNET_MESSENGER_Handle*
@@ -458,8 +574,8 @@ GNUNET_MESSENGER_connect (const struct 
GNUNET_CONFIGURATION_Handle *cfg, const c
  * Keep in mind that this will fully delete the old ego key (if any is used) 
even if any other service wants to use it
  * as default.
  *
- * @param handle Messenger handle to use
- * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ * @param[in/out] handle Messenger handle to use
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
  */
 int
 GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle);
@@ -467,7 +583,7 @@ GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle 
*handle);
 /**
  * Disconnect all of the messengers used services and clears up its used 
memory.
  *
- * @param handle Messenger handle to use
+ * @param[in/out] handle Messenger handle to use
  */
 void
 GNUNET_MESSENGER_disconnect (struct GNUNET_MESSENGER_Handle *handle);
@@ -475,7 +591,7 @@ GNUNET_MESSENGER_disconnect (struct GNUNET_MESSENGER_Handle 
*handle);
 /**
  * Get the name (if specified, otherwise NULL) used by the messenger.
  *
- * @param handle Messenger handle to use
+ * @param[in] handle Messenger handle to use
  * @return Name used by the messenger or NULL
  */
 const char*
@@ -483,21 +599,21 @@ GNUNET_MESSENGER_get_name (const struct 
GNUNET_MESSENGER_Handle *handle);
 
 /**
  * Set the name for the messenger. This will rename the currently used ego and 
move all stored files related to the current
- * name to its new directory. If anything fails during this process the 
function returns GNUNET_NO and the name for
+ * name to its new directory. If anything fails during this process the 
function returns #GNUNET_NO and the name for
  * the messenger won't change as specified.
  *
- * @param handle Messenger handle to use
- * @param name Name for the messenger to change to
- * @return GNUNET_YES on success, GNUNET_NO on failure and GNUNET_SYSERR if 
<i>handle</i> is NULL
+ * @param[in/out] handle Messenger handle to use
+ * @param[in] name Name for the messenger to change to
+ * @return #GNUNET_YES on success, #GNUNET_NO on failure and #GNUNET_SYSERR if 
<i>handle</i> is NULL
  */
 int
 GNUNET_MESSENGER_set_name (struct GNUNET_MESSENGER_Handle *handle, const char 
*name);
 
 /**
- * Get the public key used by the messenger.
+ * Get the public key used by the messenger or NULL if the anonymous key was 
used.
  *
- * @param handle Messenger handle to use
- * @return Used ego's public key
+ * @param[in] handle Messenger handle to use
+ * @return Used ego's public key or NULL
  */
 const struct GNUNET_IDENTITY_PublicKey*
 GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle);
@@ -509,38 +625,38 @@ GNUNET_MESSENGER_get_key (const struct 
GNUNET_MESSENGER_Handle *handle);
  * Notice that there can only be one room related to a specific <i>key</i>. So 
trying to open two rooms with the same
  * <i>key</i> will result in opening the room once but returning the handle 
both times because the room stays open.
  *
- * You can also open a room after entering it through a <b>door</b> using 
<i>GNUNET_MESSENGER_entry_room(...)</i>. This
+ * You can also open a room after entering it through a <b>door</b> using 
#GNUNET_MESSENGER_enter_room. This
  * will notify all entered <b>doors</b> to list you as new <b>door</b>.
  *
  * ( All <b>doors</b> form a ring structured network to shorten the latency 
sending and receiving messages. )
  *
- * @param handle Messenger handle to use
- * @param key Hash identifying the port
+ * @param[in/out] handle Messenger handle to use
+ * @param[in] key Hash identifying the port
  * @return Room handle, NULL on error
  */
 struct GNUNET_MESSENGER_Room*
 GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_HashCode *key);
 
 /**
- * Enter a room to send and receive messages through a <b>door</b> opened 
using <i>GNUNET_MESSENGER_open_room(...)</i>.
+ * Enter a room to send and receive messages through a <b>door</b> opened 
using #GNUNET_MESSENGER_open_room.
  *
  * Notice that there can only be one room related to a specific <i>key</i>. So 
trying to enter two rooms with the same
  * <i>key</i> will result in entering the room once but returning the handle 
both times because the room stays entered.
  * You can however enter a room through multiple <b>doors</b> in parallel 
which results in connecting both ends. But
  * entering the room through the same <b>door</b> won't have any effect after 
the first time.
  *
- * You can also enter a room through a <b>door</b> after opening it using 
<i>GNUNET_MESSENGER_open_room(...)</i>. But the
+ * You can also enter a room through a <b>door</b> after opening it using 
#GNUNET_MESSENGER_open_room. But the
  * <b>door</b> may not be your own peer identity.
  *
  * ( All <b>doors</b> form a ring structured network to shorten the latency 
sending and receiving messages. )
  *
- * @param handle Messenger handle to use
- * @param door Peer identity of an open <b>door</b>
- * @param key Hash identifying the port
+ * @param[in/out] handle Messenger handle to use
+ * @param[in] door Peer identity of an open <b>door</b>
+ * @param[in] key Hash identifying the port
  * @return Room handle, NULL on error
  */
 struct GNUNET_MESSENGER_Room*
-GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_PeerIdentity *door,
+GNUNET_MESSENGER_enter_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_PeerIdentity *door,
                              const struct GNUNET_HashCode *key);
 
 /**
@@ -550,37 +666,37 @@ GNUNET_MESSENGER_entry_room (struct 
GNUNET_MESSENGER_Handle *handle, const struc
  * ( After a member closes a <b>door</b>, all members entered through that 
specific <b>door</b> have to use another one
  * or open the room on their own. )
  *
- * @param room Room handle
+ * @param[in/out] room Room handle
  */
 void
 GNUNET_MESSENGER_close_room (struct GNUNET_MESSENGER_Room *room);
 
 /**
- * Get the contact of a member in a <i>room</i> identified by their <i>id</i>.
+ * Get the contact of a member in a <i>room</i> which sent a specific message 
identified with a given <i>hash</i>.
  *
  * Notice that contacts are independent of rooms but will be removed if all 
rooms containing these contacts get closed.
  *
- * @param room Room handle
- * @param id Hash identifying a member
- * @return Contact handle, NULL if <i>id</i> is not in use
+ * @param[in] room Room handle
+ * @param[in] hash Hash identifying a message
+ * @return Contact handle, NULL otherwise
  */
 struct GNUNET_MESSENGER_Contact*
-GNUNET_MESSENGER_get_member (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_ShortHashCode *id);
+GNUNET_MESSENGER_get_sender (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Get the name used by the <i>contact</i>.
  *
- * @param contact Contact handle
+ * @param[in] contact Contact handle
  * @return Name of <i>contact</i> or NULL
  */
 const char*
 GNUNET_MESSENGER_contact_get_name (const struct GNUNET_MESSENGER_Contact 
*contact);
 
 /**
- * Get the public key used by the <i>contact</i>.
+ * Get the public key used by the <i>contact</i> or NULL if the anonymous key 
was used.
  *
- * @param contact Contact handle
- * @return Public key of the ego used by <i>contact</i>
+ * @param[in] contact Contact handle
+ * @return Public key of the ego used by <i>contact</i> or NULL
  */
 const struct GNUNET_IDENTITY_PublicKey*
 GNUNET_MESSENGER_contact_get_key (const struct GNUNET_MESSENGER_Contact 
*contact);
@@ -593,22 +709,44 @@ GNUNET_MESSENGER_contact_get_key (const struct 
GNUNET_MESSENGER_Contact *contact
  *
  * Notice that all messages sent and received are also stored and can be 
propagated to new members entering the room.
  *
- * @param room Room handle
- * @param message New message to send
+ * If you provide a specific <i>contact</i> as receiver of the given message, 
the message will automatically be
+ * encrypted and sent as a private message (see 
#GNUNET_MESSENGER_MessagePrivate). Therefore the selected contact
+ * will be the only member receiving the actual message.
+ *
+ * Sending a message to all members in a given room can be done by providing 
NULL as contact.
+ *
+ * @param[in/out] room Room handle
+ * @param[in] message New message to send
+ * @param[in] contact Contact or NULL
  */
 void
-GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_MESSENGER_Message *message);
+GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_MESSENGER_Message *message,
+                               const struct GNUNET_MESSENGER_Contact* contact);
 
 /**
  * Get the message in a <i>room</i> identified by its <i>hash</i>.
  *
- * @param room Room handle
- * @param hash Hash identifying a message
+ * @param[in] room Room handle
+ * @param[in] hash Hash identifying a message
  * @return Message struct or NULL if no message with that hash is known
  */
 const struct GNUNET_MESSENGER_Message*
 GNUNET_MESSENGER_get_message (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_HashCode *hash);
 
+/**
+ * Iterates through all members of a given <i>room</i> and calls a selected 
<i>callback</i>
+ * for each of them with a provided closure. The callback will receive the 
contact of each
+ * member. The function returns the amount of members iterated with the given 
callback.
+ *
+ * @param[in] room Room handle
+ * @param[in] callback Function called for each member
+ * @param[in] cls Closure for the <i>callback</i> handler
+ * @return Amount of members iterated
+ */
+int
+GNUNET_MESSENGER_iterate_members (struct GNUNET_MESSENGER_Room *room, 
GNUNET_MESSENGER_MemberCallback callback,
+                                  void* cls);
+
 #if 0 /* keep Emacsens' auto-indent happy */
 {
 #endif
diff --git a/src/messenger/.gitignore b/src/messenger/.gitignore
index 9de3fb304..ed78c5562 100644
--- a/src/messenger/.gitignore
+++ b/src/messenger/.gitignore
@@ -2,3 +2,13 @@ gnunet-service-messenger
 gnunet-messenger
 test_messenger_api
 test_messenger_anonymous
+test_messenger_sync_client
+test_messenger_async_client
+test_messenger_worst_client
+test_messenger_sync_p2p
+test_messenger_async_p2p
+test_messenger_worst_p2p
+test_messenger_server
+test_messenger_growth
+test_messenger_ring
+test_messenger_adapt
diff --git a/src/messenger/Makefile.am b/src/messenger/Makefile.am
index d9694420b..1ebfbe5ed 100644
--- a/src/messenger/Makefile.am
+++ b/src/messenger/Makefile.am
@@ -31,10 +31,14 @@ lib_LTLIBRARIES = \
 
 libgnunetmessenger_common_la_SOURCES = \
   messenger_api_ego.h \
+  messenger_api_contact.c messenger_api_contact.h \
+  messenger_api_contact_store.c messenger_api_contact_store.h \
   messenger_api_message.c messenger_api_message.h \
-  messenger_api_list_tunnels.c messenger_api_list_tunnels.h
+  messenger_api_list_tunnels.c messenger_api_list_tunnels.h \
+  messenger_api_util.c messenger_api_util.h
 libgnunetmessenger_common_la_LIBADD = \
   $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/cadet/libgnunetcadet.la \
   $(top_builddir)/src/identity/libgnunetidentity.la \
   $(XLIB) \
   $(LTLIBINTL)
@@ -44,7 +48,6 @@ libgnunetmessenger_common_la_LDFLAGS = \
 
 libgnunetmessenger_la_SOURCES = \
   messenger_api.c \
-  messenger_api_contact.c messenger_api_contact.h \
   messenger_api_handle.c messenger_api_handle.h \
   messenger_api_room.c messenger_api_room.h
 libgnunetmessenger_la_LIBADD = \
@@ -72,17 +75,21 @@ gnunet_service_messenger_SOURCES = \
   gnunet-service-messenger_service.c gnunet-service-messenger_service.h \
   gnunet-service-messenger_list_handles.c 
gnunet-service-messenger_list_handles.h \
   gnunet-service-messenger_list_messages.c 
gnunet-service-messenger_list_messages.h \
+  gnunet-service-messenger_member_session.c 
gnunet-service-messenger_member_session.h \
+  gnunet-service-messenger_member.c gnunet-service-messenger_member.h \
+  gnunet-service-messenger_member_store.c 
gnunet-service-messenger_member_store.h \
   gnunet-service-messenger_message_handle.c 
gnunet-service-messenger_message_handle.h \
   gnunet-service-messenger_message_kind.c 
gnunet-service-messenger_message_kind.h \
   gnunet-service-messenger_message_recv.c 
gnunet-service-messenger_message_recv.h \
   gnunet-service-messenger_message_send.c 
gnunet-service-messenger_message_send.h \
   gnunet-service-messenger_message_store.c 
gnunet-service-messenger_message_store.h \
+  gnunet-service-messenger_operation_store.c 
gnunet-service-messenger_operation_store.h \
+  gnunet-service-messenger_operation.c gnunet-service-messenger_operation.h \
   gnunet-service-messenger_basement.c gnunet-service-messenger_basement.h \
-  gnunet-service-messenger_contact.c gnunet-service-messenger_contact.h \
+  gnunet-service-messenger_ego_store.c gnunet-service-messenger_ego_store.h \
   gnunet-service-messenger_handle.c gnunet-service-messenger_handle.h \
   gnunet-service-messenger_room.c gnunet-service-messenger_room.h \
-  gnunet-service-messenger_tunnel.c gnunet-service-messenger_tunnel.h \
-  gnunet-service-messenger_util.c gnunet-service-messenger_util.h
+  gnunet-service-messenger_tunnel.c gnunet-service-messenger_tunnel.h
 gnunet_service_messenger_LDADD = \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(top_builddir)/src/cadet/libgnunetcadet.la \
@@ -94,7 +101,16 @@ gnunet_service_messenger_LDADD = \
 check_PROGRAMS = \
   test_messenger_api \
   test_messenger_anonymous \
-  test_messenger_comm0
+  test_messenger_sync_client \
+  test_messenger_async_client \
+  test_messenger_worst_client \
+  test_messenger_sync_p2p \
+  test_messenger_async_p2p \
+  test_messenger_worst_p2p \
+  test_messenger_server \
+  test_messenger_growth \
+  test_messenger_ring \
+  test_messenger_adapt
 
 if ENABLE_TEST_RUN
 AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export 
PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset 
XDG_CONFIG_HOME;
@@ -118,13 +134,113 @@ test_messenger_anonymous_LDADD = \
   $(top_builddir)/src/testing/libgnunettesting.la \
   $(top_builddir)/src/util/libgnunetutil.la
 
-test_messenger_comm0_SOURCES = \
-  test_messenger_comm0.c
-test_messenger_comm0_LDADD = \
+test_messenger_sync_client_SOURCES = \
+  test_messenger_sync_client.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_sync_client_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+  
+test_messenger_async_client_SOURCES = \
+  test_messenger_async_client.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_async_client_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+  
+test_messenger_worst_client_SOURCES = \
+  test_messenger_worst_client.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_worst_client_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_messenger_sync_p2p_SOURCES = \
+  test_messenger_sync_p2p.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_sync_p2p_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+  
+test_messenger_async_p2p_SOURCES = \
+  test_messenger_async_p2p.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_async_p2p_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_messenger_worst_p2p_SOURCES = \
+  test_messenger_worst_p2p.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_worst_p2p_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+  
+test_messenger_server_SOURCES = \
+  test_messenger_server.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_server_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+  
+test_messenger_growth_SOURCES = \
+  test_messenger_growth.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_growth_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+  
+test_messenger_ring_SOURCES = \
+  test_messenger_ring.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_ring_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+  
+test_messenger_adapt_SOURCES = \
+  test_messenger_adapt.c \
+  testing_messenger_barrier.c testing_messenger_barrier.h \
+  testing_messenger_setup.c testing_messenger_setup.h
+test_messenger_adapt_LDADD = \
   libgnunetmessenger_common.la \
   libgnunetmessenger.la \
   $(top_builddir)/src/testbed/libgnunettestbed.la \
-  $(top_builddir)/src/testbed-logger/libgnunettestbedlogger.la \
   $(top_builddir)/src/testing/libgnunettesting.la \
   $(top_builddir)/src/util/libgnunetutil.la
 
diff --git a/src/messenger/gnunet-messenger.c b/src/messenger/gnunet-messenger.c
index 579e5c3ad..737bb83c8 100644
--- a/src/messenger/gnunet-messenger.c
+++ b/src/messenger/gnunet-messenger.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -34,29 +34,38 @@ struct GNUNET_MESSENGER_Handle *messenger;
 /**
  * Function called whenever a message is received or sent.
  *
- * @param cls Closure
- * @param room Room
- * @param message Message
- * @param hash Hash of message
+ * @param[in/out] cls Closure
+ * @param[in] room Room
+ * @param[in] sender Sender of message
+ * @param[in] message Message
+ * @param[in] hash Hash of message
+ * @param[in] flags Flags of message
  */
 void
-on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-            const struct GNUNET_HashCode *hash)
+on_message (void *cls, struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Contact *sender,
+            const struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash,
+            enum GNUNET_MESSENGER_MessageFlags flags)
 {
-  struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_member (room, 
&(message->header.sender_id));
-
   const char *sender_name = GNUNET_MESSENGER_contact_get_name (sender);
 
   if (!sender_name)
     sender_name = "anonymous";
 
+  printf ("[%s] ", GNUNET_sh2s(&(message->header.sender_id)));
+
+  if (flags & GNUNET_MESSENGER_FLAG_PRIVATE)
+    printf ("*");
+
   switch (message->header.kind)
   {
   case GNUNET_MESSENGER_KIND_JOIN:
     {
-      printf ("* '%s' joined the room! [ %u %u %u %u ]\n", sender_name, 
message->body.join.key.ecdsa_key.q_y[0],
-              message->body.join.key.ecdsa_key.q_y[1], 
message->body.join.key.ecdsa_key.q_y[2],
-              message->body.join.key.ecdsa_key.q_y[3]);
+      printf ("* '%s' joined the room!\n", sender_name);
+      break;
+    }
+  case GNUNET_MESSENGER_KIND_NAME:
+    {
+      printf ("* '%s' gets renamed to '%s'\n", sender_name, 
message->body.name.name);
       break;
     }
   case GNUNET_MESSENGER_KIND_LEAVE:
@@ -76,6 +85,7 @@ on_message (void *cls, const struct GNUNET_MESSENGER_Room 
*room, const struct GN
     }
   default:
     {
+      printf ("~ message: %s\n", 
GNUNET_MESSENGER_name_of_kind(message->header.kind));
       break;
     }
   }
@@ -86,7 +96,7 @@ struct GNUNET_SCHEDULER_Task *read_task;
 /**
  * Task to shut down this application.
  *
- * @param cls Closure
+ * @param[in/out] cls Closure
  */
 static void
 shutdown_hook (void *cls)
@@ -108,10 +118,24 @@ listen_stdio (void *cls);
 
 #define MAX_BUFFER_SIZE 60000
 
+static int
+iterate_send_private_message (void *cls, struct GNUNET_MESSENGER_Room *room,
+                              const struct GNUNET_MESSENGER_Contact *contact)
+{
+  struct GNUNET_MESSENGER_Message *message = cls;
+
+  if (GNUNET_MESSENGER_contact_get_key(contact))
+    GNUNET_MESSENGER_send_message (room, message, contact);
+
+  return GNUNET_YES;
+}
+
+int private_mode;
+
 /**
  * Task run in stdio mode, after some data is available at stdin.
  *
- * @param cls Closure
+ * @param[in/out] cls Closure
  */
 static void
 read_stdio (void *cls)
@@ -140,7 +164,10 @@ read_stdio (void *cls)
   message.header.kind = GNUNET_MESSENGER_KIND_TEXT;
   message.body.text.text = buffer;
 
-  GNUNET_MESSENGER_send_message (room, &message);
+  if (GNUNET_YES == private_mode)
+    GNUNET_MESSENGER_iterate_members(room, iterate_send_private_message, 
&message);
+  else
+    GNUNET_MESSENGER_send_message (room, &message, NULL);
 
   read_task = GNUNET_SCHEDULER_add_now (listen_stdio, cls);
 }
@@ -148,7 +175,7 @@ read_stdio (void *cls)
 /**
  * Wait for input on STDIO and send it out over the #ch.
  *
- * @param cls Closure
+ * @param[in/out] cls Closure
  */
 static void
 listen_stdio (void *cls)
@@ -160,10 +187,8 @@ listen_stdio (void *cls)
   GNUNET_NETWORK_fdset_set_native (rs, 0);
 
   read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-  GNUNET_TIME_UNIT_FOREVER_REL,
-                                           rs,
-                                           NULL,
-                                           &read_stdio, cls);
+                                           GNUNET_TIME_UNIT_FOREVER_REL, rs,
+                                           NULL, &read_stdio, cls);
 
   GNUNET_NETWORK_fdset_destroy (rs);
 }
@@ -171,7 +196,7 @@ listen_stdio (void *cls)
 /**
  * Initial task to startup application.
  *
- * @param cls Closure
+ * @param[in/out] cls Closure
  */
 static void
 idle (void *cls)
@@ -192,8 +217,8 @@ struct GNUNET_SCHEDULER_Task *shutdown_task;
 /**
  * Function called when an identity is retrieved.
  *
- * @param cls Closure
- * @param handle Handle of messenger service
+ * @param[in/out] cls Closure
+ * @param[in/out] handle Handle of messenger service
  */
 static void
 on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle)
@@ -230,7 +255,7 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
   {
     printf ("* You try to entry a room...\n");
 
-    room = GNUNET_MESSENGER_entry_room (messenger, door, &key);
+    room = GNUNET_MESSENGER_enter_room (messenger, door, &key);
   }
   else
   {
@@ -246,17 +271,26 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
   if (!room)
     GNUNET_SCHEDULER_shutdown ();
   else
+  {
+    struct GNUNET_MESSENGER_Message message;
+    message.header.kind = GNUNET_MESSENGER_KIND_NAME;
+    message.body.name.name = GNUNET_strdup(name);
+
+    GNUNET_MESSENGER_send_message (room, &message, NULL);
+    GNUNET_free(message.body.name.name);
+
     GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_zero_ 
(), GNUNET_SCHEDULER_PRIORITY_IDLE, idle,
                                                 room);
+  }
 }
 
 /**
  * Main function that will be run by the scheduler.
  *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
- * @param cfg configuration
+ * @param[in/out] cls closure
+ * @param[in] args remaining command-line arguments
+ * @param[in] cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param[in] cfg configuration
  */
 static void
 run (void *cls, char *const*args, const char *cfgfile, const struct 
GNUNET_CONFIGURATION_Handle *cfg)
@@ -269,38 +303,24 @@ run (void *cls, char *const*args, const char *cfgfile, 
const struct GNUNET_CONFI
 /**
  * The main function to obtain messenger information.
  *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
+ * @param[in] argc number of arguments from the command line
+ * @param[in] argv command line arguments
+ * @return #EXIT_SUCCESS ok, #EXIT_FAILURE on error
  */
 int
 main (int argc, char **argv)
 {
   const char *description = "Open and connect to rooms using the MESSENGER to 
chat.";
 
-  struct GNUNET_GETOPT_CommandLineOption options[] = {
-    GNUNET_GETOPT_option_string ('d',
-                                 "door",
-                                 "PEERIDENTITY",
-                                 "peer identity to entry into the room",
-                                 &door_id),
-    GNUNET_GETOPT_option_string ('e',
-                                 "ego",
-                                 "IDENTITY",
-                                 "identity to use for messaging",
-                                 &ego_name),
-    GNUNET_GETOPT_option_string ('r',
-                                 "room",
-                                 "ROOMKEY",
-                                 "key of the room to connect to",
-                                 &room_key),
-    GNUNET_GETOPT_OPTION_END };
-
-  return (GNUNET_OK == GNUNET_PROGRAM_run (argc,
-                                           argv,
-                                           "gnunet-messenger\0",
-                                           gettext_noop(description),
-                                           options,
-                                           &run,
-                                           NULL) ? EXIT_SUCCESS : 
EXIT_FAILURE);
+  struct GNUNET_GETOPT_CommandLineOption options[] =
+  {
+    GNUNET_GETOPT_option_string ('d', "door", "PEERIDENTITY", "peer identity 
to entry into the room", &door_id),
+    GNUNET_GETOPT_option_string ('e', "ego", "IDENTITY", "identity to use for 
messaging", &ego_name),
+    GNUNET_GETOPT_option_string ('r', "room", "ROOMKEY", "key of the room to 
connect to", &room_key),
+    GNUNET_GETOPT_option_flag ('p', "private", "flag to enable private mode", 
&private_mode),
+    GNUNET_GETOPT_OPTION_END
+  };
+
+  return (GNUNET_OK == GNUNET_PROGRAM_run (argc, argv, "gnunet-messenger\0", 
gettext_noop(description), options, &run,
+  NULL) ? EXIT_SUCCESS : EXIT_FAILURE);
 }
diff --git a/src/messenger/gnunet-service-messenger.c 
b/src/messenger/gnunet-service-messenger.c
index 2c92305c4..187b65ed5 100644
--- a/src/messenger/gnunet-service-messenger.c
+++ b/src/messenger/gnunet-service-messenger.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -52,7 +52,7 @@ handle_create (void *cls, const struct 
GNUNET_MESSENGER_CreateMessage *msg)
 
   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handle created with name: %s\n", name);
 
-  setup_handle_name (msg_client->handle, strlen (name) > 0? name : NULL);
+  setup_handle_name (msg_client->handle, strlen (name) > 0 ? name : NULL);
 
   GNUNET_SERVICE_client_continue (msg_client->client);
 }
@@ -62,8 +62,7 @@ handle_update (void *cls, const struct 
GNUNET_MESSENGER_UpdateMessage *msg)
 {
   struct GNUNET_MESSENGER_Client *msg_client = cls;
 
-  if (GNUNET_OK != update_handle (msg_client->handle))
-    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Name is required to update key!\n");
+  update_handle (msg_client->handle);
 
   GNUNET_SERVICE_client_continue (msg_client->client);
 }
@@ -92,8 +91,7 @@ handle_set_name (void *cls, const struct 
GNUNET_MESSENGER_NameMessage *msg)
 
   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handles name is now: %s\n", name);
 
-  if (GNUNET_YES != set_handle_name (msg_client->handle, name))
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No valid name: %s\n", name);
+  set_handle_name (msg_client->handle, name);
 
   GNUNET_SERVICE_client_continue (msg_client->client);
 }
@@ -103,15 +101,13 @@ handle_room_open (void *cls, const struct 
GNUNET_MESSENGER_RoomMessage *msg)
 {
   struct GNUNET_MESSENGER_Client *msg_client = cls;
 
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room: %s\n",
-             GNUNET_h2s (&(msg->key)));
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room: %s\n", GNUNET_h2s 
(&(msg->key)));
 
   if (GNUNET_YES == open_handle_room (msg_client->handle, &(msg->key)))
   {
-    const struct GNUNET_ShortHashCode* member_id = 
get_handle_member_id(msg_client->handle, &(msg->key));
+    const struct GNUNET_ShortHashCode *member_id = get_handle_member_id 
(msg_client->handle, &(msg->key));
 
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room with member id: %s\n",
-               GNUNET_sh2s (member_id));
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room with member id: %s\n", 
GNUNET_sh2s (member_id));
 
     struct GNUNET_MESSENGER_RoomMessage *response;
     struct GNUNET_MQ_Envelope *env;
@@ -121,8 +117,7 @@ handle_room_open (void *cls, const struct 
GNUNET_MESSENGER_RoomMessage *msg)
     GNUNET_MQ_send (msg_client->handle->mq, env);
   }
   else
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Opening room failed: %s\n",
-               GNUNET_h2s (&(msg->key)));
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Opening room failed: %s\n", 
GNUNET_h2s (&(msg->key)));
 
   GNUNET_SERVICE_client_continue (msg_client->client);
 }
@@ -132,15 +127,13 @@ handle_room_entry (void *cls, const struct 
GNUNET_MESSENGER_RoomMessage *msg)
 {
   struct GNUNET_MESSENGER_Client *msg_client = cls;
 
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room: %s, %s\n",
-             GNUNET_h2s (&(msg->key)), GNUNET_i2s (&(msg->door)));
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room: %s, %s\n", GNUNET_h2s 
(&(msg->key)), GNUNET_i2s (&(msg->door)));
 
   if (GNUNET_YES == entry_handle_room (msg_client->handle, &(msg->door), 
&(msg->key)))
   {
-    const struct GNUNET_ShortHashCode* member_id = 
get_handle_member_id(msg_client->handle, &(msg->key));
+    const struct GNUNET_ShortHashCode *member_id = get_handle_member_id 
(msg_client->handle, &(msg->key));
 
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room with member id: %s\n",
-               GNUNET_sh2s (member_id));
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room with member id: %s\n", 
GNUNET_sh2s (member_id));
 
     struct GNUNET_MESSENGER_RoomMessage *response;
     struct GNUNET_MQ_Envelope *env;
@@ -151,8 +144,8 @@ handle_room_entry (void *cls, const struct 
GNUNET_MESSENGER_RoomMessage *msg)
     GNUNET_MQ_send (msg_client->handle->mq, env);
   }
   else
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Entrance into room failed: %s, %s\n",
-               GNUNET_h2s (&(msg->key)), GNUNET_i2s (&(msg->door)));
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Entrance into room failed: %s, %s\n", 
GNUNET_h2s (&(msg->key)),
+               GNUNET_i2s (&(msg->door)));
 
   GNUNET_SERVICE_client_continue (msg_client->client);
 }
@@ -166,10 +159,9 @@ handle_room_close (void *cls, const struct 
GNUNET_MESSENGER_RoomMessage *msg)
 
   if (GNUNET_YES == close_handle_room (msg_client->handle, &(msg->key)))
   {
-    const struct GNUNET_ShortHashCode* member_id = 
get_handle_member_id(msg_client->handle, &(msg->key));
+    const struct GNUNET_ShortHashCode *member_id = get_handle_member_id 
(msg_client->handle, &(msg->key));
 
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closing room with member id: %s\n",
-               GNUNET_sh2s (member_id));
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closing room with member id: %s\n", 
GNUNET_sh2s (member_id));
 
     struct GNUNET_MESSENGER_RoomMessage *response;
     struct GNUNET_MQ_Envelope *env;
@@ -187,17 +179,40 @@ handle_room_close (void *cls, const struct 
GNUNET_MESSENGER_RoomMessage *msg)
 static int
 check_send_message (void *cls, const struct GNUNET_MESSENGER_SendMessage *msg)
 {
-  const uint16_t full_length = ntohs (msg->header.size) - sizeof(msg->header);
+  const uint16_t full_length = ntohs (msg->header.size);
 
-  if (full_length < sizeof(msg->key))
+  if (full_length < sizeof(*msg))
     return GNUNET_NO;
 
-  const uint16_t length = full_length - sizeof(msg->key);
+  const enum GNUNET_MESSENGER_MessageFlags flags = (
+      (enum GNUNET_MESSENGER_MessageFlags) (msg->flags)
+  );
+
+  const uint16_t length = full_length - sizeof(*msg);
   const char *buffer = ((const char*) msg) + sizeof(*msg);
 
+  uint16_t key_length = 0;
+
+  if (!(flags & GNUNET_MESSENGER_FLAG_PRIVATE))
+    goto check_for_message;
+
+  struct GNUNET_IDENTITY_PublicKey public_key;
+
+  key_length = GNUNET_IDENTITY_read_key_from_buffer(&public_key, buffer, 
length);
+
+check_for_message:
+  if (key_length < 0)
+    return GNUNET_NO;
+
+  const uint16_t msg_length = length - key_length;
+  const char* msg_buffer = buffer + key_length;
+
   struct GNUNET_MESSENGER_Message message;
 
-  if (GNUNET_YES != decode_message (&message, length, buffer))
+  if (GNUNET_YES != decode_message (&message, msg_length, msg_buffer, 
GNUNET_NO, NULL))
+    return GNUNET_NO;
+
+  if (GNUNET_YES != filter_message_sending(&message))
     return GNUNET_NO;
 
   return GNUNET_OK;
@@ -208,42 +223,85 @@ handle_send_message (void *cls, const struct 
GNUNET_MESSENGER_SendMessage *msg)
 {
   struct GNUNET_MESSENGER_Client *msg_client = cls;
 
+  const enum GNUNET_MESSENGER_MessageFlags flags = (
+      (enum GNUNET_MESSENGER_MessageFlags) (msg->flags)
+  );
+
   const struct GNUNET_HashCode *key = &(msg->key);
   const char *buffer = ((const char*) msg) + sizeof(*msg);
 
   const uint16_t length = ntohs (msg->header.size) - sizeof(*msg);
+  uint16_t key_length = 0;
+
+  struct GNUNET_IDENTITY_PublicKey public_key;
+
+  if (flags & GNUNET_MESSENGER_FLAG_PRIVATE)
+    key_length = GNUNET_IDENTITY_read_key_from_buffer(
+        &public_key, buffer, length
+    );
+
+  const uint16_t msg_length = length - key_length;
+  const char* msg_buffer = buffer + key_length;
 
   struct GNUNET_MESSENGER_Message message;
-  decode_message (&message, length, buffer);
+  decode_message (&message, msg_length, msg_buffer, GNUNET_NO, NULL);
+
+  if ((flags & GNUNET_MESSENGER_FLAG_PRIVATE) &&
+      (GNUNET_YES != encrypt_message(&message, &public_key)))
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Encrypting message failed: Message 
got dropped!\n");
+
+    goto end_handling;
+  }
 
   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending message: %s to %s\n",
-             GNUNET_MESSENGER_name_of_kind (message.header.kind),
-             GNUNET_h2s (key));
+             GNUNET_MESSENGER_name_of_kind (message.header.kind), GNUNET_h2s 
(key));
 
   if (GNUNET_YES != send_handle_message (msg_client->handle, key, &message))
     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sending message failed: %s to %s\n",
-               GNUNET_MESSENGER_name_of_kind (message.header.kind),
-               GNUNET_h2s (key));
+               GNUNET_MESSENGER_name_of_kind (message.header.kind), GNUNET_h2s 
(key));
 
+end_handling:
   GNUNET_SERVICE_client_continue (msg_client->client);
 }
 
 static void
-handle_get_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg)
+handle_get_message (void *cls, const struct GNUNET_MESSENGER_GetMessage *msg)
 {
   struct GNUNET_MESSENGER_Client *msg_client = cls;
 
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Requesting message from room: %s\n",
-             GNUNET_h2s (&(msg->key)));
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Requesting message from room: %s\n", 
GNUNET_h2s (&(msg->key)));
 
   struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (messenger, 
&(msg->key));
 
-  if (room)
-    get_room_message (room, msg_client->handle, &(msg->hash), GNUNET_YES);
-  else
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Room not found: %s\n",
-               GNUNET_h2s (&(msg->key)));
+  if (!room)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Room not found: %s\n", GNUNET_h2s 
(&(msg->key)));
+    goto end_handling;
+  }
+
+  const struct GNUNET_MESSENGER_Message *message = get_room_message (room, 
msg_client->handle, &(msg->hash),
+                                                                     
GNUNET_YES);
+
+  if (!message)
+    goto end_handling;
+
+  struct GNUNET_MESSENGER_MemberStore *store = get_room_member_store(room);
+
+  struct GNUNET_MESSENGER_Member *member = get_store_member_of(store, message);
+
+  if (!member)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sender of message (%s) unknown!\n", 
GNUNET_h2s (&(msg->hash)));
+    goto end_handling;
+  }
+
+  struct GNUNET_MESSENGER_MemberSession *session = 
get_member_session_of(member, message, &(msg->hash));
+
+  if (session)
+    notify_handle_message (msg_client->handle, get_room_key(room), session, 
message, &(msg->hash));
 
+end_handling:
   GNUNET_SERVICE_client_continue (msg_client->client);
 }
 
@@ -271,16 +329,16 @@ callback_client_disconnect (void *cls, struct 
GNUNET_SERVICE_Client *client, voi
 /**
  * Setup MESSENGER internals.
  *
- * @param cls closure
- * @param config configuration to use
- * @param service the initialized service
+ * @param[in/out] cls closure
+ * @param[in] config configuration to use
+ * @param[in/out] service the initialized service
  */
 static void
 run (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, struct 
GNUNET_SERVICE_Handle *service)
 {
   messenger = create_service (config, service);
 
-  if ((!messenger) || (!messenger->cadet) || (!messenger->identity))
+  if (!messenger)
     GNUNET_SCHEDULER_shutdown ();
 }
 
@@ -302,5 +360,5 @@ GNUNET_SERVICE_MAIN(
     GNUNET_MQ_hd_fixed_size( room_entry, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY, struct GNUNET_MESSENGER_RoomMessage, 
NULL ),
     GNUNET_MQ_hd_fixed_size( room_close, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE, struct GNUNET_MESSENGER_RoomMessage, 
NULL ),
     GNUNET_MQ_hd_var_size( send_message, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE, struct 
GNUNET_MESSENGER_SendMessage, NULL ),
-    GNUNET_MQ_hd_fixed_size( get_message, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE, struct 
GNUNET_MESSENGER_RecvMessage, NULL ),
+    GNUNET_MQ_hd_fixed_size( get_message, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE, struct 
GNUNET_MESSENGER_GetMessage, NULL ),
     GNUNET_MQ_handler_end());
diff --git a/src/messenger/gnunet-service-messenger.h 
b/src/messenger/gnunet-service-messenger.h
index 85a1d2549..253fbaadb 100644
--- a/src/messenger/gnunet-service-messenger.h
+++ b/src/messenger/gnunet-service-messenger.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -74,7 +74,6 @@ struct GNUNET_MESSENGER_NameMessage
 struct GNUNET_MESSENGER_KeyMessage
 {
   struct GNUNET_MessageHeader header;
-  struct GNUNET_IDENTITY_PublicKey pubkey;
 };
 
 /**
@@ -105,7 +104,20 @@ struct GNUNET_MESSENGER_MemberMessage
 struct GNUNET_MESSENGER_SendMessage
 {
   struct GNUNET_MessageHeader header;
+
   struct GNUNET_HashCode key;
+  uint32_t flags;
+};
+
+/**
+ * Message to request something from a room
+ */
+struct GNUNET_MESSENGER_GetMessage
+{
+  struct GNUNET_MessageHeader header;
+
+  struct GNUNET_HashCode key;
+  struct GNUNET_HashCode hash;
 };
 
 /**
@@ -114,8 +126,12 @@ struct GNUNET_MESSENGER_SendMessage
 struct GNUNET_MESSENGER_RecvMessage
 {
   struct GNUNET_MessageHeader header;
+
   struct GNUNET_HashCode key;
+  struct GNUNET_HashCode sender;
+  struct GNUNET_HashCode context;
   struct GNUNET_HashCode hash;
+  uint32_t flags;
 };
 
 #endif //GNUNET_SERVICE_MESSENGER_H
diff --git a/src/messenger/gnunet-service-messenger_basement.c 
b/src/messenger/gnunet-service-messenger_basement.c
index 190cf2de5..f302c8d66 100644
--- a/src/messenger/gnunet-service-messenger_basement.c
+++ b/src/messenger/gnunet-service-messenger_basement.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -28,6 +28,8 @@
 size_t
 count_of_tunnels (const struct GNUNET_MESSENGER_ListTunnels *tunnels)
 {
+  GNUNET_assert(tunnels);
+
   const struct GNUNET_MESSENGER_ListTunnel *element;
   size_t count = 0;
 
diff --git a/src/messenger/gnunet-service-messenger_basement.h 
b/src/messenger/gnunet-service-messenger_basement.h
index 0a1a9b126..b19aec405 100644
--- a/src/messenger/gnunet-service-messenger_basement.h
+++ b/src/messenger/gnunet-service-messenger_basement.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -31,34 +31,34 @@
 /**
  * Returns the count of peers in a list (typically from the basement of a 
room).
  *
- * @param tunnels List of peer identities
+ * @param[in] tunnels List of peer identities
  * @return Count of the entries in the list
  */
 size_t
 count_of_tunnels (const struct GNUNET_MESSENGER_ListTunnels *tunnels);
 
 /**
- * Returns GNUNET_YES or GNUNET_NO to determine if the peer at index 
<i>src</i> should
+ * Returns #GNUNET_YES or #GNUNET_NO to determine if the peer at index 
<i>src</i> should
  * or should not connect outgoing to the peer at index <i>dst</i> to construct 
a complete
  * basement with a given <i>count</i> of peers.
  *
- * @param count Count of peers
- * @param src Source index
- * @param dst Destination index
- * @return GNUNET_YES or GNUNET_NO based on topologic requirement
+ * @param[in] count Count of peers
+ * @param[in] src Source index
+ * @param[in] dst Destination index
+ * @return #GNUNET_YES or #GNUNET_NO based on topologic requirement
  */
 int
 should_connect_tunnel_to (size_t count, size_t src, size_t dst);
 
 /**
- * Returns GNUNET_YES or GNUNET_NO to determine if the peers of index 
<i>src</i> and
+ * Returns #GNUNET_YES or #GNUNET_NO to determine if the peers of index 
<i>src</i> and
  * index <i>dst</i> should be connected in any direction to construct a 
complete
  * basement with a given <i>count</i> of peers.
  *
- * @param count Count of peers
- * @param src Source index
- * @param dst Destination index
- * @return GNUNET_YES or GNUNET_NO based on topologic requirement
+ * @param[in] count Count of peers
+ * @param[in] src Source index
+ * @param[in] dst Destination index
+ * @return #GNUNET_YES or #GNUNET_NO based on topologic requirement
  */
 int
 required_connection_between (size_t count, size_t src, size_t dst);
diff --git a/src/messenger/gnunet-service-messenger_contact.c 
b/src/messenger/gnunet-service-messenger_contact.c
deleted file mode 100644
index 1ec125402..000000000
--- a/src/messenger/gnunet-service-messenger_contact.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-   This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
-
-   GNUnet is free software: you can redistribute it and/or modify it
-   under the terms of the GNU Affero General Public License as published
-   by the Free Software Foundation, either version 3 of the License,
-   or (at your option) any later version.
-
-   GNUnet is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Affero General Public License for more details.
-
-   You should have received a copy of the GNU Affero General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-   SPDX-License-Identifier: AGPL3.0-or-later
- */
-/**
- * @author Tobias Frisch
- * @file src/messenger/gnunet-service-messenger_contact.c
- * @brief GNUnet MESSENGER service
- */
-
-#include "gnunet-service-messenger_contact.h"
-
-struct GNUNET_MESSENGER_SrvContact*
-create_contact (const struct GNUNET_IDENTITY_PublicKey *key)
-{
-  struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_new(struct 
GNUNET_MESSENGER_SrvContact);
-
-  contact->name = NULL;
-  contact->rc = 0;
-
-  GNUNET_memcpy(&(contact->public_key), key, sizeof(contact->public_key));
-
-  return contact;
-}
-
-void
-destroy_contact (struct GNUNET_MESSENGER_SrvContact *contact)
-{
-  if (contact->name)
-    GNUNET_free(contact->name);
-
-  GNUNET_free(contact);
-}
-
-const char*
-get_contact_name (const struct GNUNET_MESSENGER_SrvContact *contact)
-{
-  return contact->name;
-}
-
-void
-set_contact_name (struct GNUNET_MESSENGER_SrvContact *contact, const char 
*name)
-{
-  GNUNET_assert(name);
-
-  if (contact->name)
-    GNUNET_free(contact->name);
-
-  contact->name = GNUNET_strdup(name);
-}
-
-const struct GNUNET_IDENTITY_PublicKey*
-get_contact_key (const struct GNUNET_MESSENGER_SrvContact *contact)
-{
-  return &(contact->public_key);
-}
-
-void
-increase_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact)
-{
-  contact->rc++;
-}
-
-int
-decrease_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact)
-{
-  if (contact->rc > 0)
-    contact->rc--;
-
-  return contact->rc ? GNUNET_NO : GNUNET_YES;
-}
-
-const struct GNUNET_HashCode*
-get_contact_id_from_key (const struct GNUNET_MESSENGER_SrvContact *contact)
-{
-  static struct GNUNET_HashCode id;
-
-  GNUNET_CRYPTO_hash (&(contact->public_key), sizeof(contact->public_key), 
&id);
-
-  return &id;
-}
diff --git a/src/messenger/gnunet-service-messenger_contact.h 
b/src/messenger/gnunet-service-messenger_contact.h
deleted file mode 100644
index 4a4f8bf0f..000000000
--- a/src/messenger/gnunet-service-messenger_contact.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-   This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
-
-   GNUnet is free software: you can redistribute it and/or modify it
-   under the terms of the GNU Affero General Public License as published
-   by the Free Software Foundation, either version 3 of the License,
-   or (at your option) any later version.
-
-   GNUnet is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Affero General Public License for more details.
-
-   You should have received a copy of the GNU Affero General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-   SPDX-License-Identifier: AGPL3.0-or-later
- */
-/**
- * @author Tobias Frisch
- * @file src/messenger/gnunet-service-messenger_contact.h
- * @brief GNUnet MESSENGER service
- */
-
-#ifndef GNUNET_SERVICE_MESSENGER_CONTACT_H
-#define GNUNET_SERVICE_MESSENGER_CONTACT_H
-
-#include "platform.h"
-#include "gnunet_crypto_lib.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_SrvContact
-{
-  char *name;
-  size_t rc;
-
-  struct GNUNET_IDENTITY_PublicKey public_key;
-};
-
-/**
- * Creates and allocates a new contact with a given public <i>key</i> from an 
EGO.
- *
- * @param key Public key
- * @return New contact
- */
-struct GNUNET_MESSENGER_SrvContact*
-create_contact (const struct GNUNET_IDENTITY_PublicKey *key);
-
-/**
- * Destroys a contact and frees its memory fully.
- *
- * @param contact Contact
- */
-void
-destroy_contact (struct GNUNET_MESSENGER_SrvContact *contact);
-
-/**
- * Returns the current name of a given <i>contact</i> or NULL if no valid name 
was assigned yet.
- *
- * @param contact Contact
- * @return Name of the contact or NULL
- */
-const char*
-get_contact_name (const struct GNUNET_MESSENGER_SrvContact *contact);
-
-/**
- * Changes the current name of a given <i>contact</i> by copying it from the 
parameter <i>name</i>.
- *
- * @param contact Contact
- * @param name Valid name (may not be NULL!)
- */
-void
-set_contact_name (struct GNUNET_MESSENGER_SrvContact *contact, const char 
*name);
-
-/**
- * Returns the public key of a given <i>contact</i>.
- *
- * @param contact Contact
- * @return Public key of the contact
- */
-const struct GNUNET_IDENTITY_PublicKey*
-get_contact_key (const struct GNUNET_MESSENGER_SrvContact *contact);
-
-/**
- * Increases the reference counter of a given <i>contact</i> which is zero as 
default.
- *
- * @param contact Contact
- */
-void
-increase_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact);
-
-/**
- * Decreases the reference counter if possible (can not underflow!) of a given 
<i>contact</i>
- * and returns GNUNET_YES if the counter is equal to zero, otherwise GNUNET_NO.
- *
- * @param contact Contact
- * @return GNUNET_YES or GNUNET_NO depending on the reference counter
- */
-int
-decrease_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact);
-
-/**
- * Returns the resulting hashcode of the public key from a given 
<i>contact</i>.
- *
- * @param contact Contact
- * @return Hash of the contacts public key
- */
-const struct GNUNET_HashCode*
-get_contact_id_from_key (const struct GNUNET_MESSENGER_SrvContact *contact);
-
-#endif //GNUNET_SERVICE_MESSENGER_CONTACT_H
diff --git a/src/messenger/gnunet-service-messenger_ego_store.c 
b/src/messenger/gnunet-service-messenger_ego_store.c
new file mode 100644
index 000000000..3b069fcf5
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_ego_store.c
@@ -0,0 +1,295 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_ego_store.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_ego_store.h"
+
+#include "gnunet-service-messenger_handle.h"
+
+static void
+callback_update_ego (void *cls, struct GNUNET_IDENTITY_Ego *ego, void **ctx, 
const char *identifier)
+{
+  if ((!ego) || (!identifier))
+    return;
+
+  struct GNUNET_MESSENGER_EgoStore *store = cls;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "New ego in use: '%s'\n", identifier);
+
+  update_store_ego (store, identifier, GNUNET_IDENTITY_ego_get_private_key 
(ego));
+}
+
+void
+init_ego_store(struct GNUNET_MESSENGER_EgoStore *store, const struct 
GNUNET_CONFIGURATION_Handle *config)
+{
+  GNUNET_assert ((store) && (config));
+
+  store->cfg = config;
+  store->identity = GNUNET_IDENTITY_connect (config, &callback_update_ego, 
store);
+  store->egos = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  store->lu_start = NULL;
+  store->lu_end = NULL;
+
+  store->op_start = NULL;
+  store->op_end = NULL;
+}
+
+
+static int
+iterate_destroy_egos (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Ego *ego = value;
+  GNUNET_free(ego);
+  return GNUNET_YES;
+}
+
+void
+clear_ego_store(struct GNUNET_MESSENGER_EgoStore *store)
+{
+  GNUNET_assert (store);
+
+  struct GNUNET_MESSENGER_EgoOperation *op;
+
+  while (store->op_start)
+  {
+    op = store->op_start;
+
+    GNUNET_IDENTITY_cancel (op->operation);
+    GNUNET_CONTAINER_DLL_remove (store->op_start, store->op_end, op);
+
+    if (op->identifier)
+      GNUNET_free (op->identifier);
+
+    GNUNET_free (op);
+  }
+
+  struct GNUNET_MESSENGER_EgoLookup *lu;
+
+  while (store->lu_start)
+  {
+    lu = store->lu_start;
+
+    GNUNET_IDENTITY_ego_lookup_cancel(lu->lookup);
+    GNUNET_CONTAINER_DLL_remove (store->lu_start, store->lu_end, lu);
+
+    if (lu->identifier)
+      GNUNET_free(lu->identifier);
+
+    GNUNET_free (lu);
+  }
+
+  GNUNET_CONTAINER_multihashmap_iterate (store->egos, iterate_destroy_egos, 
NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (store->egos);
+
+  if (store->identity)
+  {
+    GNUNET_IDENTITY_disconnect (store->identity);
+
+    store->identity = NULL;
+  }
+}
+
+static void
+callback_ego_create (void *cls, const struct GNUNET_IDENTITY_PrivateKey *key, 
const char *emsg)
+{
+  struct GNUNET_MESSENGER_EgoOperation *element = cls;
+  struct GNUNET_MESSENGER_EgoStore *store = element->store;
+
+  GNUNET_assert(element->identifier);
+
+  if (emsg)
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg);
+
+  if (key)
+  {
+    struct GNUNET_MESSENGER_SrvHandle *handle = element->handle;
+
+    struct GNUNET_MESSENGER_Ego *msg_ego = update_store_ego (store, 
element->identifier, key);
+
+    set_handle_ego (handle, msg_ego);
+  }
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Creating ego failed!\n");
+
+  GNUNET_CONTAINER_DLL_remove (store->op_start, store->op_end, element);
+  GNUNET_free (element->identifier);
+  GNUNET_free (element);
+}
+
+void
+create_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char 
*identifier,
+                  void *handle)
+{
+  GNUNET_assert ((store) && (identifier));
+
+  struct GNUNET_MESSENGER_EgoOperation *element = GNUNET_new (struct 
GNUNET_MESSENGER_EgoOperation);
+
+  element->store = store;
+  element->handle = handle;
+
+  element->identifier = GNUNET_strdup (identifier);
+
+  element->operation = GNUNET_IDENTITY_create (store->identity, identifier, 
NULL,
+                                               GNUNET_IDENTITY_TYPE_ECDSA, 
callback_ego_create, element);
+
+  GNUNET_CONTAINER_DLL_insert (store->op_start, store->op_end, element);
+}
+
+static void
+callback_ego_lookup (void *cls, struct GNUNET_IDENTITY_Ego *ego)
+{
+  struct GNUNET_MESSENGER_EgoLookup *element = cls;
+  struct GNUNET_MESSENGER_EgoStore *store = element->store;
+
+  GNUNET_assert(element->identifier);
+
+  struct GNUNET_MESSENGER_Ego *msg_ego;
+
+  if (ego)
+    msg_ego = update_store_ego (
+        store, element->identifier, GNUNET_IDENTITY_ego_get_private_key(ego)
+    );
+  else
+    msg_ego = NULL;
+
+  if (element->cb)
+    element->cb(element->cls, element->identifier, msg_ego);
+
+  GNUNET_CONTAINER_DLL_remove (store->lu_start, store->lu_end, element);
+  GNUNET_free (element->identifier);
+  GNUNET_free (element);
+}
+
+void
+lookup_store_ego(struct GNUNET_MESSENGER_EgoStore *store, const char 
*identifier,
+                 GNUNET_MESSENGER_EgoLookupCallback lookup, void *cls)
+{
+  GNUNET_assert (store);
+
+  if (!identifier)
+  {
+    lookup(cls, identifier, NULL);
+    return;
+  }
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash (identifier, strlen (identifier), &hash);
+
+  struct GNUNET_MESSENGER_Ego *ego = GNUNET_CONTAINER_multihashmap_get 
(store->egos, &hash);
+
+  if (ego)
+    lookup(cls, identifier, ego);
+  else
+  {
+    struct GNUNET_MESSENGER_EgoLookup *element = GNUNET_new (struct 
GNUNET_MESSENGER_EgoLookup);
+
+    element->store = store;
+
+    element->cb = lookup;
+    element->cls = cls;
+
+    element->identifier = GNUNET_strdup (identifier);
+
+    element->lookup = GNUNET_IDENTITY_ego_lookup(store->cfg, identifier, 
callback_ego_lookup, element);
+
+    GNUNET_CONTAINER_DLL_insert (store->lu_start, store->lu_end, element);
+  }
+}
+
+struct GNUNET_MESSENGER_Ego*
+update_store_ego(struct GNUNET_MESSENGER_EgoStore *store, const char 
*identifier,
+                 const struct GNUNET_IDENTITY_PrivateKey *key)
+{
+  GNUNET_assert ((store) && (identifier) && (key));
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash (identifier, strlen (identifier), &hash);
+
+  struct GNUNET_MESSENGER_Ego *ego = GNUNET_CONTAINER_multihashmap_get 
(store->egos, &hash);
+
+  if (!ego)
+  {
+    ego = GNUNET_new(struct GNUNET_MESSENGER_Ego);
+    GNUNET_CONTAINER_multihashmap_put (store->egos, &hash, ego, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+
+  GNUNET_memcpy(&(ego->priv), key, sizeof(*key));
+
+  if (GNUNET_OK != GNUNET_IDENTITY_key_get_public (key, &(ego->pub)))
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Updating invalid ego key 
failed!\n");
+
+  return ego;
+}
+
+static void
+callback_ego_rename (void *cls, const char *emsg)
+{
+  struct GNUNET_MESSENGER_EgoOperation *element = cls;
+  struct GNUNET_MESSENGER_EgoStore *store = element->store;
+
+  GNUNET_assert(element->identifier);
+
+  if (emsg)
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg);
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash (element->identifier, strlen (element->identifier), 
&hash);
+
+  struct GNUNET_MESSENGER_Ego *ego = GNUNET_CONTAINER_multihashmap_get 
(store->egos, &hash);
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->egos, &hash, 
ego))
+  {
+    GNUNET_CRYPTO_hash ((char*) element->handle, strlen ((char*) 
element->handle), &hash);
+
+    GNUNET_CONTAINER_multihashmap_put (store->egos, &hash, ego,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Renaming ego failed!\n");
+
+  GNUNET_free (element->handle);
+
+  GNUNET_CONTAINER_DLL_remove (store->op_start, store->op_end, element);
+  GNUNET_free (element->identifier);
+  GNUNET_free (element);
+}
+
+void
+rename_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char 
*old_identifier,
+                  const char *new_identifier)
+{
+  GNUNET_assert ((store) && (old_identifier) && (new_identifier));
+
+  struct GNUNET_MESSENGER_EgoOperation *element = GNUNET_new (struct 
GNUNET_MESSENGER_EgoOperation);
+
+  element->store = store;
+  element->handle = GNUNET_strdup (new_identifier);
+
+  element->identifier = GNUNET_strdup (old_identifier);
+
+  element->operation = GNUNET_IDENTITY_rename (store->identity, 
old_identifier, new_identifier, callback_ego_rename, element);
+
+  GNUNET_CONTAINER_DLL_insert (store->op_start, store->op_end, element);
+}
diff --git a/src/messenger/gnunet-service-messenger_ego_store.h 
b/src/messenger/gnunet-service-messenger_ego_store.h
new file mode 100644
index 000000000..41f14fff2
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_ego_store.h
@@ -0,0 +1,152 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_ego_store.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_EGO_STORE_H
+#define GNUNET_SERVICE_MESSENGER_EGO_STORE_H
+
+#include "platform.h"
+#include "gnunet_container_lib.h"
+
+#include "messenger_api_ego.h"
+
+struct GNUNET_MESSENGER_Ego;
+struct GNUNET_MESSENGER_EgoStore;
+
+typedef void
+(*GNUNET_MESSENGER_EgoLookupCallback) (void *cls, const char *identifier,
+                                       const struct GNUNET_MESSENGER_Ego *ego);
+
+struct GNUNET_MESSENGER_EgoLookup
+{
+  struct GNUNET_MESSENGER_EgoLookup *prev;
+  struct GNUNET_MESSENGER_EgoLookup *next;
+
+  struct GNUNET_IDENTITY_EgoLookup *lookup;
+
+  struct GNUNET_MESSENGER_EgoStore *store;
+
+  GNUNET_MESSENGER_EgoLookupCallback cb;
+  void *cls;
+
+  char *identifier;
+};
+
+struct GNUNET_MESSENGER_EgoOperation
+{
+  struct GNUNET_MESSENGER_EgoOperation *prev;
+  struct GNUNET_MESSENGER_EgoOperation *next;
+
+  struct GNUNET_IDENTITY_Operation *operation;
+
+  struct GNUNET_MESSENGER_EgoStore *store;
+  void *handle;
+
+  char *identifier;
+};
+
+struct GNUNET_MESSENGER_EgoStore
+{
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  struct GNUNET_IDENTITY_Handle *identity;
+  struct GNUNET_CONTAINER_MultiHashMap *egos;
+
+  struct GNUNET_MESSENGER_EgoLookup *lu_start;
+  struct GNUNET_MESSENGER_EgoLookup *lu_end;
+
+  struct GNUNET_MESSENGER_EgoOperation *op_start;
+  struct GNUNET_MESSENGER_EgoOperation *op_end;
+};
+
+/**
+ * Initializes an EGO-store as fully empty.
+ *
+ * @param[out] store EGO-store
+ * @param[in] config Configuration handle
+ */
+void
+init_ego_store (struct GNUNET_MESSENGER_EgoStore *store, const struct 
GNUNET_CONFIGURATION_Handle *config);
+
+/**
+ * Clears an EGO-store, wipes its content and deallocates its memory.
+ *
+ * @param[in/out] store EGO-store
+ */
+void
+clear_ego_store (struct GNUNET_MESSENGER_EgoStore *store);
+
+/**
+ * Creates a new EGO which will be registered to a <i>store</i> under
+ * a specific <i>identifier</i>. A given <i>handle</i> will be informed
+ * about the creation and changes its EGO accordingly.
+ *
+ * @param[in/out] store EGO-store
+ * @param[in] identifier Identifier string
+ * @param[in/out] handle Handle or NULL
+ */
+void
+create_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char 
*identifier,
+                  void *handle);
+
+/**
+ * Lookups an EGO which was registered to a <i>store</i> under
+ * a specific <i>identifier</i>.
+ *
+ * @param[in/out] store EGO-store
+ * @param[in] identifier Identifier string
+ * @param[in] lookup Lookup callback (non-NULL)
+ * @param[in] cls Closure
+ */
+void
+lookup_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char 
*identifier,
+                  GNUNET_MESSENGER_EgoLookupCallback lookup, void *cls);
+
+/**
+ * Updates the registration of an EGO to a <i>store</i> under
+ * a specific <i>identifier</i> with a new <i>key</i>.
+ *
+ * @param[in/out] store EGO-store
+ * @param[in] identifier Identifier string
+ * @param[in] key Private EGO key
+ * @return Updated EGO
+ */
+struct GNUNET_MESSENGER_Ego*
+update_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char 
*identifier,
+                  const struct GNUNET_IDENTITY_PrivateKey *key);
+
+/**
+ * Updates the location of a registered EGO in a <i>store</i> to
+ * a different one under a specific <i>new_identifier<i> replacing
+ * its old one.
+ *
+ * @param[in/out] store EGO-store
+ * @param[in] old_identifier Old identifier string
+ * @param[in] new_identifier New identifier string
+ */
+void
+rename_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char 
*old_identifier,
+                  const char *new_identifier);
+
+#endif //GNUNET_SERVICE_MESSENGER_EGO_STORE_H
diff --git a/src/messenger/gnunet-service-messenger_handle.c 
b/src/messenger/gnunet-service-messenger_handle.c
index 38ad6fbb4..4d2318d62 100644
--- a/src/messenger/gnunet-service-messenger_handle.c
+++ b/src/messenger/gnunet-service-messenger_handle.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -28,18 +28,19 @@
 #include "gnunet-service-messenger.h"
 #include "gnunet-service-messenger_message_kind.h"
 
+#include "messenger_api_util.h"
+
 struct GNUNET_MESSENGER_SrvHandle*
 create_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MQ_Handle *mq)
 {
+  GNUNET_assert((service) && (mq));
+
   struct GNUNET_MESSENGER_SrvHandle *handle = GNUNET_new(struct 
GNUNET_MESSENGER_SrvHandle);
 
   handle->service = service;
   handle->mq = mq;
 
   handle->name = NULL;
-
-  handle->operation = NULL;
-
   handle->ego = NULL;
 
   handle->member_ids = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
@@ -58,11 +59,10 @@ iterate_free_member_ids (void *cls, const struct 
GNUNET_HashCode *key, void *val
 void
 destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle)
 {
-  if (handle->service->dir)
-   save_handle_configuration(handle);
+  GNUNET_assert(handle);
 
-  if (handle->operation)
-    GNUNET_IDENTITY_cancel (handle->operation);
+  if (handle->service->dir)
+    save_handle_configuration (handle);
 
   if (handle->name)
     GNUNET_free(handle->name);
@@ -74,8 +74,10 @@ destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle)
 }
 
 void
-get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name, char **dir)
+get_handle_data_subdir (const struct GNUNET_MESSENGER_SrvHandle *handle, const 
char *name, char **dir)
 {
+  GNUNET_assert((handle) && (dir));
+
   if (name)
     GNUNET_asprintf (dir, "%s%s%c%s%c", handle->service->dir, "identities",
                      DIR_SEPARATOR, name, DIR_SEPARATOR);
@@ -87,11 +89,15 @@ get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle 
*handle, const char *n
 static int
 create_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, 
const struct GNUNET_HashCode *key)
 {
-  struct GNUNET_ShortHashCode *random_id = generate_service_new_member_id 
(handle->service, key);
+  GNUNET_assert((handle) && (key));
+
+  struct GNUNET_ShortHashCode *random_id = GNUNET_new(struct 
GNUNET_ShortHashCode);
 
   if (!random_id)
     return GNUNET_NO;
 
+  generate_free_member_id (random_id, NULL);
+
   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->member_ids, key, 
random_id,
                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   {
@@ -99,8 +105,8 @@ create_handle_member_id (const struct 
GNUNET_MESSENGER_SrvHandle *handle, const
     return GNUNET_NO;
   }
 
-  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Created a new member id (%s) for room: 
%s\n",
-             GNUNET_sh2s(random_id), GNUNET_h2s(key));
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Created a new member id (%s) for room: 
%s\n", GNUNET_sh2s (random_id),
+             GNUNET_h2s (key));
 
   return GNUNET_YES;
 }
@@ -108,48 +114,61 @@ create_handle_member_id (const struct 
GNUNET_MESSENGER_SrvHandle *handle, const
 const struct GNUNET_ShortHashCode*
 get_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (key));
+
   return GNUNET_CONTAINER_multihashmap_get (handle->member_ids, key);
 }
 
-void
+int
 change_handle_member_id (struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key,
                          const struct GNUNET_ShortHashCode *unique_id)
 {
-  struct GNUNET_ShortHashCode *member_id = GNUNET_CONTAINER_multihashmap_get 
(handle->member_ids, key);
-
-  if (member_id)
-  {
-    GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Changed a member id (%s) for room (%s) 
",
-               GNUNET_sh2s(member_id), GNUNET_h2s(key));
-    GNUNET_log(GNUNET_ERROR_TYPE_INFO, "into (%s).\n",
-               GNUNET_sh2s(unique_id));
-
-    GNUNET_memcpy(member_id, unique_id, sizeof(*unique_id));
-
-    struct GNUNET_MESSENGER_MemberMessage *msg;
-    struct GNUNET_MQ_Envelope *env;
+  GNUNET_assert((handle) && (key) && (unique_id));
 
-    env = GNUNET_MQ_msg(msg, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID);
-
-    GNUNET_memcpy(&(msg->key), key, sizeof(*key));
-    GNUNET_memcpy(&(msg->id), member_id, sizeof(*member_id));
+  struct GNUNET_ShortHashCode *member_id = GNUNET_CONTAINER_multihashmap_get 
(handle->member_ids, key);
 
-    GNUNET_MQ_send (handle->mq, env);
-  }
-  else
+  if (!member_id)
   {
     member_id = GNUNET_new(struct GNUNET_ShortHashCode);
     GNUNET_memcpy(member_id, unique_id, sizeof(*member_id));
 
     if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->member_ids, 
key, member_id,
                                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    {
       GNUNET_free(member_id);
+      return GNUNET_SYSERR;
+    }
   }
+
+  if (0 == GNUNET_memcmp(unique_id, member_id))
+    goto send_message_to_client;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Change a member id (%s) for room 
(%s).\n", GNUNET_sh2s (member_id),
+             GNUNET_h2s (key));
+
+  GNUNET_memcpy(member_id, unique_id, sizeof(*unique_id));
+
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Member id changed to (%s).\n", 
GNUNET_sh2s (unique_id));
+
+  struct GNUNET_MESSENGER_MemberMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+send_message_to_client:
+
+  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID);
+
+  GNUNET_memcpy(&(msg->key), key, sizeof(*key));
+  GNUNET_memcpy(&(msg->id), member_id, sizeof(*member_id));
+
+  GNUNET_MQ_send (handle->mq, env);
+  return GNUNET_OK;
 }
 
 static void
 change_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name)
 {
+  GNUNET_assert(handle);
+
   if (handle->name)
     GNUNET_free(handle->name);
 
@@ -173,25 +192,67 @@ change_handle_name (struct GNUNET_MESSENGER_SrvHandle 
*handle, const char *name)
 }
 
 static void
-change_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, struct 
GNUNET_MESSENGER_Ego *ego)
+change_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_MESSENGER_Ego *ego)
 {
+  GNUNET_assert(handle);
+
   handle->ego = ego;
 
-  ego = get_handle_ego(handle);
+  ego = get_handle_ego (handle);
+
+  const uint16_t length = GNUNET_IDENTITY_key_get_length(&(ego->pub));
 
   struct GNUNET_MESSENGER_KeyMessage *msg;
   struct GNUNET_MQ_Envelope *env;
 
-  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY);
+  env = GNUNET_MQ_msg_extra(msg, length, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY);
 
-  GNUNET_memcpy(&(msg->pubkey), &(ego->pub), sizeof(ego->pub));
+  char *extra = ((char*) msg) + sizeof(*msg);
+
+  if (GNUNET_IDENTITY_write_key_to_buffer(&(ego->pub), extra, length) < 0)
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Could not write key to buffer.\n");
 
   GNUNET_MQ_send (handle->mq, env);
 }
 
-struct GNUNET_MESSENGER_Ego*
-get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle)
+struct GNUNET_MESSENGER_MessageHandle
+{
+  struct GNUNET_MESSENGER_SrvHandle *handle;
+  struct GNUNET_MESSENGER_Message *message;
+};
+
+static int
+iterate_send_message (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_MessageHandle *msg_handle = cls;
+
+  send_handle_message (msg_handle->handle, key, msg_handle->message);
+
+  return GNUNET_YES;
+}
+
+void
+set_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_MESSENGER_Ego *ego)
+{
+  GNUNET_assert((handle) && (ego));
+
+  struct GNUNET_MESSENGER_MessageHandle msg_handle;
+
+  msg_handle.handle = handle;
+  msg_handle.message = create_message_key (&(ego->priv));
+
+  GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, 
iterate_send_message, &msg_handle);
+
+  destroy_message (msg_handle.message);
+
+  change_handle_ego (handle, ego);
+}
+
+const struct GNUNET_MESSENGER_Ego*
+get_handle_ego (const struct GNUNET_MESSENGER_SrvHandle *handle)
 {
+  GNUNET_assert(handle);
+
   static struct GNUNET_MESSENGER_Ego anonymous;
   static int read_keys = 0;
 
@@ -200,99 +261,86 @@ get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle)
 
   if (!read_keys)
   {
-    struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous ();
-    GNUNET_memcpy(&(anonymous.priv), GNUNET_IDENTITY_ego_get_private_key(ego), 
sizeof(anonymous.priv));
-    GNUNET_IDENTITY_ego_get_public_key(ego, &(anonymous.pub));
+    struct GNUNET_IDENTITY_Ego *ego = GNUNET_IDENTITY_ego_get_anonymous ();
+    GNUNET_memcpy(&(anonymous.priv), GNUNET_IDENTITY_ego_get_private_key 
(ego), sizeof(anonymous.priv));
+    GNUNET_IDENTITY_ego_get_public_key (ego, &(anonymous.pub));
     read_keys = 1;
   }
 
   return &anonymous;
 }
 
-void
-setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name)
+static void
+callback_setup_handle_name (void *cls, const char *name, const struct 
GNUNET_MESSENGER_Ego *ego)
 {
+  struct GNUNET_MESSENGER_SrvHandle *handle = cls;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Setting up handle...\n");
+
   change_handle_name (handle, name);
-  change_handle_ego (handle, handle->name? lookup_service_ego(handle->service, 
handle->name) : NULL);
+  change_handle_ego (handle, ego);
 
   if (handle->service->dir)
-   load_handle_configuration(handle);
+    load_handle_configuration (handle);
 }
 
-struct GNUNET_MESSENGER_MessageHandle
-{
-  struct GNUNET_MESSENGER_SrvHandle *handle;
-  struct GNUNET_MESSENGER_Message *message;
-};
-
-static int
-iterate_send_message (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+void
+setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name)
 {
-  struct GNUNET_MESSENGER_MessageHandle *msg_handle = cls;
+  GNUNET_assert(handle);
 
-  send_handle_message (msg_handle->handle, key, msg_handle->message);
+  struct GNUNET_MESSENGER_EgoStore *store = 
get_service_ego_store(handle->service);
 
-  return GNUNET_YES;
+  lookup_store_ego (store, name, callback_setup_handle_name, handle);
 }
 
 static void
-callback_ego_create (void *cls, const struct GNUNET_IDENTITY_PrivateKey *key, 
const char *emsg)
+callback_update_handle (void *cls, const char *name, const struct 
GNUNET_MESSENGER_Ego *ego)
 {
   struct GNUNET_MESSENGER_SrvHandle *handle = cls;
 
-  handle->operation = NULL;
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Updating handle...\n");
 
-  if (emsg)
-    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg);
+  struct GNUNET_MESSENGER_EgoStore *store = 
get_service_ego_store(handle->service);
 
-  if (key)
-  {
-    struct GNUNET_MESSENGER_MessageHandle msg_handle;
-
-    msg_handle.handle = handle;
-    msg_handle.message = create_message_key (key);
-
-    GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, 
iterate_send_message, &msg_handle);
-
-    destroy_message (msg_handle.message);
-
-    update_service_ego(handle->service, handle->name, key);
-
-    change_handle_ego (handle, lookup_service_ego(handle->service, 
handle->name));
-  }
+  if (!ego)
+    create_store_ego(store, handle->name, handle);
+  else
+    change_handle_ego (handle, ego);
 }
 
-int
+void
 update_handle (struct GNUNET_MESSENGER_SrvHandle *handle)
 {
   GNUNET_assert(handle);
 
   if (!handle->name)
-    return GNUNET_SYSERR;
-
-  struct GNUNET_MESSENGER_Ego *ego = lookup_service_ego(handle->service, 
handle->name);
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Updating handle failed: Name is 
required!\n");
+    return;
+  }
 
-  if (!ego)
-    handle->operation = GNUNET_IDENTITY_create (handle->service->identity, 
handle->name, NULL,
-                                                GNUNET_IDENTITY_TYPE_ECDSA, 
callback_ego_create, handle);
-  else
-    change_handle_ego (handle, ego);
+  struct GNUNET_MESSENGER_EgoStore *store = 
get_service_ego_store(handle->service);
 
-  return GNUNET_OK;
+  lookup_store_ego (store, handle->name, callback_update_handle, handle);
 }
 
-int
-set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name)
+static void
+callback_set_handle_name (void *cls, const char *name, const struct 
GNUNET_MESSENGER_Ego *ego)
 {
-  GNUNET_assert(handle);
+  struct GNUNET_MESSENGER_SrvHandle *handle = cls;
 
-  if ((name) && (lookup_service_ego(handle->service, name)))
-    return GNUNET_NO;
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Renaming handle...\n");
+
+  if (ego)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Renaming handle failed: Name is 
occupied! (%s)\n", name);
+    return;
+  }
 
-  struct GNUNET_IDENTITY_Operation *operation = handle->operation;
+  struct GNUNET_MESSENGER_EgoStore *store = 
get_service_ego_store(handle->service);
 
-  if (handle->name)
-    handle->operation = GNUNET_IDENTITY_rename (handle->service->identity, 
handle->name, name, NULL, NULL);
+  int rename_ego_in_store = handle->ego? GNUNET_YES : GNUNET_NO;
 
   char *old_dir;
   get_handle_data_subdir (handle, handle->name, &old_dir);
@@ -323,29 +371,42 @@ set_handle_name (struct GNUNET_MESSENGER_SrvHandle 
*handle, const char *name)
     destroy_message (msg_handle.message);
 
     change_handle_name (handle, name);
-
-    if (operation)
-      GNUNET_IDENTITY_cancel (operation);
   }
   else
+    rename_ego_in_store = GNUNET_NO;
+
+  GNUNET_free(old_dir);
+  GNUNET_free(new_dir);
+
+  if (GNUNET_YES == rename_ego_in_store)
+    rename_store_ego(store, handle->name, name);
+}
+
+void
+set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name)
+{
+  GNUNET_assert(handle);
+
+  if (!name)
   {
-    if (handle->operation)
-    {
-      GNUNET_IDENTITY_cancel (handle->operation);
+    if (handle->ego)
+      GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Renaming handle failed: Name is 
required!\n");
+    else
+      change_handle_name (handle, name);
 
-      handle->operation = operation;
-    }
+    return;
   }
 
-  GNUNET_free(old_dir);
-  GNUNET_free(new_dir);
+  struct GNUNET_MESSENGER_EgoStore *store = 
get_service_ego_store(handle->service);
 
-  return (result == 0 ? GNUNET_OK : GNUNET_NO);
+  lookup_store_ego (store, name, callback_set_handle_name, handle);
 }
 
 int
 open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (key));
+
   if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != 
create_handle_member_id (handle, key)))
     return GNUNET_NO;
 
@@ -356,6 +417,8 @@ int
 entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_PeerIdentity *door,
                    const struct GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (door) && (key));
+
   if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != 
create_handle_member_id (handle, key)))
     return GNUNET_NO;
 
@@ -365,6 +428,8 @@ entry_handle_room (struct GNUNET_MESSENGER_SrvHandle 
*handle, const struct GNUNE
 int
 close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (key));
+
   if (!get_handle_member_id (handle, key))
     return GNUNET_NO;
 
@@ -373,8 +438,10 @@ close_handle_room (struct GNUNET_MESSENGER_SrvHandle 
*handle, const struct GNUNE
 
 int
 send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key,
-                     struct GNUNET_MESSENGER_Message *message)
+                     const struct GNUNET_MESSENGER_Message *message)
 {
+  GNUNET_assert((handle) && (key) && (message));
+
   const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key);
 
   if (!id)
@@ -391,45 +458,120 @@ send_handle_message (struct GNUNET_MESSENGER_SrvHandle 
*handle, const struct GNU
     return GNUNET_NO;
   }
 
-  struct GNUNET_HashCode hash;
+  struct GNUNET_MESSENGER_Message *msg = copy_message(message);
 
-  GNUNET_memcpy(&(message->header.sender_id), id, sizeof(*id));
+  GNUNET_memcpy(&(msg->header.sender_id), id, sizeof(*id));
 
-  send_room_message (room, handle, message, &hash);
-  return GNUNET_YES;
+  return send_room_message (room, handle, msg);
+}
+
+static const struct GNUNET_HashCode*
+get_next_member_session_contect(const struct GNUNET_MESSENGER_MemberSession 
*session)
+{
+  if (session->next)
+    return get_next_member_session_contect (session->next);
+  else
+    return get_member_session_context(session);
+}
+
+void
+notify_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key,
+                       const struct GNUNET_MESSENGER_MemberSession *session,
+                       const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((handle) && (key) && (session) && (message) && (hash));
+
+  if ((!handle->mq) || (!get_handle_member_id (handle, key)))
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Notifying client about message 
requires membership!\n");
+    return;
+  }
+
+  const struct GNUNET_IDENTITY_PublicKey *pubkey = 
get_contact_key(session->contact);
+
+  struct GNUNET_HashCode sender;
+  GNUNET_CRYPTO_hash(pubkey, sizeof(*pubkey), &sender);
+
+  const struct GNUNET_HashCode *context = get_next_member_session_contect 
(session);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Notifying client about message: %s\n", 
GNUNET_h2s (hash));
+
+  struct GNUNET_MESSENGER_Message *private_message = NULL;
+
+  if (GNUNET_MESSENGER_KIND_PRIVATE == message->header.kind)
+  {
+    private_message = copy_message(message);
+
+    if (GNUNET_YES != decrypt_message(private_message, 
&(get_handle_ego(handle)->priv)))
+    {
+      destroy_message(private_message);
+      private_message = NULL;
+    }
+    else
+      message = private_message;
+  }
+
+  struct GNUNET_MESSENGER_RecvMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  uint16_t length = get_message_size (message, GNUNET_YES);
+
+  env = GNUNET_MQ_msg_extra(msg, length, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE);
+
+  GNUNET_memcpy(&(msg->key), key, sizeof(msg->key));
+  GNUNET_memcpy(&(msg->sender), &sender, sizeof(msg->sender));
+  GNUNET_memcpy(&(msg->context), context, sizeof(msg->context));
+  GNUNET_memcpy(&(msg->hash), hash, sizeof(msg->hash));
+
+  msg->flags = (uint32_t) (
+      private_message? GNUNET_MESSENGER_FLAG_PRIVATE : 
GNUNET_MESSENGER_FLAG_NONE
+  );
+
+  char *buffer = ((char*) msg) + sizeof(*msg);
+  encode_message (message, length, buffer, GNUNET_YES);
+
+  if (private_message)
+    destroy_message(private_message);
+
+  GNUNET_MQ_send (handle->mq, env);
 }
 
-static int callback_scan_for_rooms(void* cls, const char *filename) {
-  struct GNUNET_MESSENGER_SrvHandle* handle = cls;
+static int
+callback_scan_for_rooms (void *cls, const char *filename)
+{
+  struct GNUNET_MESSENGER_SrvHandle *handle = cls;
 
-  struct GNUNET_CONFIGURATION_Handle* cfg = GNUNET_CONFIGURATION_create();
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
 
-  if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) &&
-      (GNUNET_OK == GNUNET_CONFIGURATION_parse(cfg, filename)))
+  if ((GNUNET_YES == GNUNET_DISK_file_test (filename)) && (GNUNET_OK == 
GNUNET_CONFIGURATION_parse (cfg, filename)))
   {
     struct GNUNET_HashCode key;
     struct GNUNET_ShortHashCode member_id;
 
-    if ((GNUNET_OK == GNUNET_CONFIGURATION_get_data(cfg, "room", "key", &key, 
sizeof(key))) &&
-        (GNUNET_OK == GNUNET_CONFIGURATION_get_data(cfg, "room", "member_id", 
&member_id, sizeof(member_id))))
-      change_handle_member_id(handle, &key, &member_id);
+    if ((GNUNET_OK == GNUNET_CONFIGURATION_get_data (cfg, "room", "key", &key, 
sizeof(key))) &&
+        (GNUNET_OK == GNUNET_CONFIGURATION_get_data (cfg, "room", "member_id", 
&member_id, sizeof(member_id))))
+      change_handle_member_id (handle, &key, &member_id);
   }
 
-  GNUNET_CONFIGURATION_destroy(cfg);
+  GNUNET_CONFIGURATION_destroy (cfg);
   return GNUNET_OK;
 }
 
-void load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle) {
-  char* id_dir;
-  get_handle_data_subdir(handle, handle->name, &id_dir);
+void
+load_handle_configuration (struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+  GNUNET_assert(handle);
 
-  if (GNUNET_YES == GNUNET_DISK_directory_test(id_dir, GNUNET_YES))
+  char *id_dir;
+  get_handle_data_subdir (handle, handle->name, &id_dir);
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (id_dir, GNUNET_YES))
   {
-    char* scan_dir;
-    GNUNET_asprintf(&scan_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR);
+    char *scan_dir;
+    GNUNET_asprintf (&scan_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR);
 
-    if (GNUNET_OK == GNUNET_DISK_directory_test(scan_dir, GNUNET_YES))
-      GNUNET_DISK_directory_scan(scan_dir, callback_scan_for_rooms, handle);
+    if (GNUNET_OK == GNUNET_DISK_directory_test (scan_dir, GNUNET_YES))
+      GNUNET_DISK_directory_scan (scan_dir, callback_scan_for_rooms, handle);
 
     GNUNET_free(scan_dir);
   }
@@ -438,63 +580,64 @@ void load_handle_configuration(struct 
GNUNET_MESSENGER_SrvHandle *handle) {
 }
 
 static int
-iterate_save_rooms(void* cls, const struct GNUNET_HashCode* key, void* value)
+iterate_save_rooms (void *cls, const struct GNUNET_HashCode *key, void *value)
 {
-  struct GNUNET_MESSENGER_SrvHandle* handle = cls;
-  struct GNUNET_ShortHashCode* member_id = value;
+  struct GNUNET_MESSENGER_SrvHandle *handle = cls;
+  struct GNUNET_ShortHashCode *member_id = value;
 
-  char* id_dir;
-  get_handle_data_subdir(handle, handle->name, &id_dir);
+  char *id_dir;
+  get_handle_data_subdir (handle, handle->name, &id_dir);
 
-  char* filename;
-  GNUNET_asprintf(&filename, "%s%s%c%s.cfg",
-                  id_dir, "rooms", DIR_SEPARATOR,
-                  GNUNET_h2s(key));
+  char *filename;
+  GNUNET_asprintf (&filename, "%s%s%c%s.cfg", id_dir, "rooms", DIR_SEPARATOR, 
GNUNET_h2s (key));
 
   GNUNET_free(id_dir);
 
-  struct GNUNET_CONFIGURATION_Handle* cfg = GNUNET_CONFIGURATION_create();
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
 
-  char* key_data = GNUNET_STRINGS_data_to_string_alloc(key, sizeof(*key));
+  char *key_data = GNUNET_STRINGS_data_to_string_alloc (key, sizeof(*key));
 
   if (key_data)
   {
-    GNUNET_CONFIGURATION_set_value_string(cfg, "room", "key", key_data);
+    GNUNET_CONFIGURATION_set_value_string (cfg, "room", "key", key_data);
 
     GNUNET_free(key_data);
   }
 
-  char* member_id_data = GNUNET_STRINGS_data_to_string_alloc(member_id, 
sizeof(*member_id));
+  char *member_id_data = GNUNET_STRINGS_data_to_string_alloc (member_id, 
sizeof(*member_id));
 
   if (member_id_data)
   {
-    GNUNET_CONFIGURATION_set_value_string(cfg, "room", "member_id", 
member_id_data);
+    GNUNET_CONFIGURATION_set_value_string (cfg, "room", "member_id", 
member_id_data);
 
     GNUNET_free(member_id_data);
   }
 
-  GNUNET_CONFIGURATION_write(cfg, filename);
-  GNUNET_CONFIGURATION_destroy(cfg);
+  GNUNET_CONFIGURATION_write (cfg, filename);
+  GNUNET_CONFIGURATION_destroy (cfg);
 
   GNUNET_free(filename);
 
   return GNUNET_YES;
 }
 
-void save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle)
+void
+save_handle_configuration (struct GNUNET_MESSENGER_SrvHandle *handle)
 {
-  char* id_dir;
-  get_handle_data_subdir(handle, handle->name, &id_dir);
+  GNUNET_assert(handle);
+
+  char *id_dir;
+  get_handle_data_subdir (handle, handle->name, &id_dir);
 
-  if ((GNUNET_YES == GNUNET_DISK_directory_test(id_dir, GNUNET_NO)) ||
-      (GNUNET_OK == GNUNET_DISK_directory_create(id_dir)))
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (id_dir, GNUNET_NO)) || 
(GNUNET_OK
+      == GNUNET_DISK_directory_create (id_dir)))
   {
-    char* save_dir;
-    GNUNET_asprintf(&save_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR);
+    char *save_dir;
+    GNUNET_asprintf (&save_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR);
 
-    if ((GNUNET_YES == GNUNET_DISK_directory_test(save_dir, GNUNET_NO)) ||
-        (GNUNET_OK == GNUNET_DISK_directory_create(save_dir)))
-      GNUNET_CONTAINER_multihashmap_iterate(handle->member_ids, 
iterate_save_rooms, handle);
+    if ((GNUNET_YES == GNUNET_DISK_directory_test (save_dir, GNUNET_NO)) ||
+        (GNUNET_OK == GNUNET_DISK_directory_create (save_dir)))
+      GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, 
iterate_save_rooms, handle);
 
     GNUNET_free(save_dir);
   }
diff --git a/src/messenger/gnunet-service-messenger_handle.h 
b/src/messenger/gnunet-service-messenger_handle.h
index 81cf377a8..70b2cac6d 100644
--- a/src/messenger/gnunet-service-messenger_handle.h
+++ b/src/messenger/gnunet-service-messenger_handle.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -35,6 +35,7 @@
 #include "gnunet_mq_lib.h"
 
 #include "gnunet-service-messenger_service.h"
+#include "gnunet-service-messenger_member_session.h"
 
 #include "messenger_api_ego.h"
 #include "messenger_api_message.h"
@@ -46,9 +47,7 @@ struct GNUNET_MESSENGER_SrvHandle
 
   char *name;
 
-  struct GNUNET_IDENTITY_Operation *operation;
-
-  struct GNUNET_MESSENGER_Ego *ego;
+  const struct GNUNET_MESSENGER_Ego *ego;
 
   struct GNUNET_CONTAINER_MultiHashMap *member_ids;
 };
@@ -56,8 +55,8 @@ struct GNUNET_MESSENGER_SrvHandle
 /**
  * Creates and allocates a new handle related to a <i>service</i> and using a 
given <i>mq</i> (message queue).
  *
- * @param service MESSENGER Service
- * @param mq Message queue
+ * @param[in/out] service MESSENGER Service
+ * @param[in/out] mq Message queue
  * @return New handle
  */
 struct GNUNET_MESSENGER_SrvHandle*
@@ -66,7 +65,7 @@ create_handle (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MQ_Handle
 /**
  * Destroys a handle and frees its memory fully.
  *
- * @param handle Handle
+ * @param[in/out] handle Handle
  */
 void
 destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle);
@@ -75,52 +74,63 @@ destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle);
  * Writes the path of the directory for a given <i>handle</i> using a specific 
<i>name</i> to the parameter
  * <i>dir</i>. This directory will be used to store data regarding the handle 
and its messages.
  *
- * @param handle Handle
- * @param name Potential name of the handle
- * @param dir[out] Path to store data
+ * @param[in] handle Handle
+ * @param[in] name Potential name of the handle
+ * @param[out] dir Path to store data
  */
 void
-get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name, char **dir);
+get_handle_data_subdir (const struct GNUNET_MESSENGER_SrvHandle *handle, const 
char *name, char **dir);
 
 /**
  * Returns the member id of a given <i>handle</i> in a specific <i>room</i>.
  *
  * If the handle is not a member of the specific <i>room</i>, NULL gets 
returned.
  *
- * @param handle Handle
- * @param key Key of a room
+ * @param[in] handle Handle
+ * @param[in] key Key of a room
  * @return Member id or NULL
  */
 const struct GNUNET_ShortHashCode*
 get_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key);
 
 /**
- * Changes the member id of a given <i>handle</i> in a specific <i>room</i> to 
match a <i>unique_id</i>.
+ * Changes the member id of a given <i>handle</i> in a specific <i>room</i> to 
match a <i>unique_id</i>
+ * and returns GNUNET_OK on success.
  *
  * The client connected to the <i>handle</i> will be informed afterwards 
automatically.
  *
- * @param handle Handle
- * @param key Key of a room
- * @param unique_id Unique member id
+ * @param[in/out] handle Handle
+ * @param[in] key Key of a room
+ * @param[in] unique_id Unique member id
+ * @return GNUNET_OK on success, otherwise GNUNET_SYSERR
  */
-void
+int
 change_handle_member_id (struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key,
                          const struct GNUNET_ShortHashCode *unique_id);
 
+/**
+ * Sets the EGO used by a given <i>handle</i>.
+ *
+ * @param[in/out] handle Handle
+ * @param[in] ego EGO keypair
+ */
+void
+set_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_MESSENGER_Ego *ego);
+
 /**
  * Returns the EGO used by a given <i>handle</i>.
  *
- * @param handle Handle
+ * @param[in] handle Handle
  * @return EGO keypair
  */
-struct GNUNET_MESSENGER_Ego*
-get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle);
+const struct GNUNET_MESSENGER_Ego*
+get_handle_ego (const struct GNUNET_MESSENGER_SrvHandle *handle);
 
 /**
  * Tries to set the name and EGO key of a <i>handle</i> initially by looking 
up a specific <i>name</i>.
  *
- * @param handle Handle
- * @param name Name (optionally: valid EGO name)
+ * @param[in/out] handle Handle
+ * @param[in] name Name (optionally: valid EGO name)
  */
 void
 setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name);
@@ -129,10 +139,9 @@ setup_handle_name (struct GNUNET_MESSENGER_SrvHandle 
*handle, const char *name);
  * Tries to change the keypair of an EGO of a <i>handle</i> under the same 
name and informs all rooms
  * about the change automatically.
  *
- * @param handle Handle
- * @return GNUNET_OK on success, otherwise GNUNET_SYSERR
+ * @param[in/out] handle Handle
  */
-int
+void
 update_handle (struct GNUNET_MESSENGER_SrvHandle *handle);
 
 /**
@@ -141,20 +150,19 @@ update_handle (struct GNUNET_MESSENGER_SrvHandle *handle);
  *
  * The client connected to the <i>handle</i> will be informed afterwards 
automatically.
  *
- * @param handle Handle
- * @param name New name
- * @return GNUNET_OK on success, otherwise GNUNET_NO
+ * @param[in/out] handle Handle
+ * @param[in] name New name
  */
-int
+void
 set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name);
 
 /**
  * Makes a given <i>handle</i> a member of the room using a specific 
<i>key</i> and opens the
  * room from the handles service.
  *
- * @param handle Handle
- * @param key Key of a room
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] handle Handle
+ * @param[in] key Key of a room
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key);
@@ -163,10 +171,10 @@ open_handle_room (struct GNUNET_MESSENGER_SrvHandle 
*handle, const struct GNUNET
  * Makes a given <i>handle</i> a member of the room using a specific 
<i>key</i> and enters the room
  * through a tunnel to a peer identified by a given <i>door</i> (peer 
identity).
  *
- * @param handle Handle
- * @param door Peer identity
- * @param key Key of a room
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] handle Handle
+ * @param[in] door Peer identity
+ * @param[in] key Key of a room
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_PeerIdentity *door,
@@ -176,9 +184,9 @@ entry_handle_room (struct GNUNET_MESSENGER_SrvHandle 
*handle, const struct GNUNE
  * Removes the membership of the room using a specific <i>key</i> and closes 
it if no other handle
  * from this service is still a member of it.
  *
- * @param handle Handle
- * @param key Key of a room
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] handle Handle
+ * @param[in] key Key of a room
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key);
@@ -186,31 +194,45 @@ close_handle_room (struct GNUNET_MESSENGER_SrvHandle 
*handle, const struct GNUNE
 /**
  * Sends a <i>message</i> from a given <i>handle</i> to the room using a 
specific <i>key</i>.
  *
- * @param handle Handle
- * @param key Key of a room
- * @param message Message
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] handle Handle
+ * @param[in] key Key of a room
+ * @param[in] message Message
+ * @return #GNUNET_YES on success, #GNUNET_NO or #GNUNET_SYSERR otherwise.
  */
 int
 send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key,
-                     struct GNUNET_MESSENGER_Message *message);
+                     const struct GNUNET_MESSENGER_Message *message);
+
+/**
+ * Notifies the handle that a new message was received or sent.
+ *
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
+ * @param[in] session Member session
+ * @param[in] message Message
+ * @param[in] hash Hash of message
+ */
+void
+notify_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key,
+                       const struct GNUNET_MESSENGER_MemberSession *session,
+                       const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Loads member ids and other potential configuration from a given 
<i>handle</i> which
  * depends on the given name the <i>handle</i> uses.
  *
- * @param handle Handle
+ * @param[out] handle Handle
  */
 void
-load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle);
+load_handle_configuration (struct GNUNET_MESSENGER_SrvHandle *handle);
 
 /**
  * Saves member ids and other potential configuration from a given 
<i>handle</i> which
  * depends on the given name the <i>handle</i> uses.
  *
- * @param handle Handle
+ * @param[in] handle Handle
  */
 void
-save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle);
+save_handle_configuration (struct GNUNET_MESSENGER_SrvHandle *handle);
 
 #endif //GNUNET_SERVICE_MESSENGER_HANDLE_H
diff --git a/src/messenger/gnunet-service-messenger_list_handles.c 
b/src/messenger/gnunet-service-messenger_list_handles.c
index 16a160dea..adcbf6a42 100644
--- a/src/messenger/gnunet-service-messenger_list_handles.c
+++ b/src/messenger/gnunet-service-messenger_list_handles.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -55,8 +55,10 @@ clear_list_handles (struct GNUNET_MESSENGER_ListHandles 
*handles)
 }
 
 void
-add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle)
+add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct 
GNUNET_MESSENGER_SrvHandle *handle)
 {
+  GNUNET_assert((handles) && (handle));
+
   struct GNUNET_MESSENGER_ListHandle *element = GNUNET_new(struct 
GNUNET_MESSENGER_ListHandle);
 
   element->handle = handle;
@@ -65,8 +67,10 @@ add_list_handle (struct GNUNET_MESSENGER_ListHandles 
*handles, void *handle)
 }
 
 int
-remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle)
+remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct 
GNUNET_MESSENGER_SrvHandle *handle)
 {
+  GNUNET_assert((handles) && (handle));
+
   struct GNUNET_MESSENGER_ListHandle *element;
 
   for (element = handles->head; element; element = element->next)
@@ -82,9 +86,11 @@ remove_list_handle (struct GNUNET_MESSENGER_ListHandles 
*handles, void *handle)
   return GNUNET_YES;
 }
 
-void*
-find_list_handle_by_member (struct GNUNET_MESSENGER_ListHandles *handles, 
const struct GNUNET_HashCode *key)
+struct GNUNET_MESSENGER_SrvHandle*
+find_list_handle_by_member (const struct GNUNET_MESSENGER_ListHandles 
*handles, const struct GNUNET_HashCode *key)
 {
+  GNUNET_assert((handles) && (key));
+
   struct GNUNET_MESSENGER_ListHandle *element;
 
   for (element = handles->head; element; element = element->next)
diff --git a/src/messenger/gnunet-service-messenger_list_handles.h 
b/src/messenger/gnunet-service-messenger_list_handles.h
index fe92cc58a..9f7ca725f 100644
--- a/src/messenger/gnunet-service-messenger_list_handles.h
+++ b/src/messenger/gnunet-service-messenger_list_handles.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -30,12 +30,14 @@
 #include "gnunet_crypto_lib.h"
 #include "gnunet_container_lib.h"
 
+struct GNUNET_MESSENGER_SrvHandle;
+
 struct GNUNET_MESSENGER_ListHandle
 {
   struct GNUNET_MESSENGER_ListHandle *prev;
   struct GNUNET_MESSENGER_ListHandle *next;
 
-  void *handle;
+  struct GNUNET_MESSENGER_SrvHandle *handle;
 };
 
 struct GNUNET_MESSENGER_ListHandles
@@ -45,17 +47,17 @@ struct GNUNET_MESSENGER_ListHandles
 };
 
 /**
- * Initializes list of handles as empty list.
+ * Initializes list of <i>handles</i> as empty list.
  *
- * @param handles List of handles
+ * @param[out] handles List of handles
  */
 void
 init_list_handles (struct GNUNET_MESSENGER_ListHandles *handles);
 
 /**
- * Destroys remaining handles and clears the list.
+ * Destroys remaining <i>handles</i> and clears the list.
  *
- * @param handles List of handles
+ * @param[in/out] handles List of handles
  */
 void
 clear_list_handles (struct GNUNET_MESSENGER_ListHandles *handles);
@@ -63,34 +65,34 @@ clear_list_handles (struct GNUNET_MESSENGER_ListHandles 
*handles);
 /**
  * Adds a specific <i>handle</i> to the end of the list.
  *
- * @param handles List of handles
- * @param handle Handle
+ * @param[in/out] handles List of handles
+ * @param[in/out] handle Handle
  */
 void
-add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle);
+add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct 
GNUNET_MESSENGER_SrvHandle *handle);
 
 /**
- * Removes the first entry matching with a specific <i>handle</i> from the 
list and
- * returns GNUNET_YES on success or GNUNET_NO on failure.
+ * Removes the first entry matching with a specific <i>handle</i> from the 
list of
+ * <i>handles</i> and returns #GNUNET_YES on success or #GNUNET_NO on failure.
  *
- * @param handles List of handles
- * @param handle Handle
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] handles List of handles
+ * @param[in/out] handle Handle
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
-remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void 
*handle);
+remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct 
GNUNET_MESSENGER_SrvHandle *handle);
 
 /**
- * Searches linearly through the list of handles for members of a specific room
+ * Searches linearly through the list of <i>handles</i> for members of a 
specific room
  * which is identified by a given <i>key</i>.
  *
  * If no handle is found which is a current member, NULL gets returned.
  *
- * @param handles List of handles
- * @param key Common key of a room
+ * @param[in] handles List of handles
+ * @param[in] key Common key of a room
  * @return First handle which is a current member
  */
-void*
-find_list_handle_by_member (struct GNUNET_MESSENGER_ListHandles *handles, 
const struct GNUNET_HashCode *key);
+struct GNUNET_MESSENGER_SrvHandle*
+find_list_handle_by_member (const struct GNUNET_MESSENGER_ListHandles 
*handles, const struct GNUNET_HashCode *key);
 
 #endif //GNUNET_SERVICE_MESSENGER_LIST_HANDLES_H
diff --git a/src/messenger/gnunet-service-messenger_list_messages.c 
b/src/messenger/gnunet-service-messenger_list_messages.c
index c4f1f7043..bb6086e41 100644
--- a/src/messenger/gnunet-service-messenger_list_messages.c
+++ b/src/messenger/gnunet-service-messenger_list_messages.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -54,6 +54,8 @@ clear_list_messages (struct GNUNET_MESSENGER_ListMessages 
*messages)
 void
 add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const 
struct GNUNET_HashCode *hash)
 {
+  GNUNET_assert((messages) && (hash));
+
   struct GNUNET_MESSENGER_ListMessage *element = GNUNET_new(struct 
GNUNET_MESSENGER_ListMessage);
 
   GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode));
@@ -61,9 +63,22 @@ add_to_list_messages (struct GNUNET_MESSENGER_ListMessages 
*messages, const stru
   GNUNET_CONTAINER_DLL_insert_tail(messages->head, messages->tail, element);
 }
 
+void
+copy_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const 
struct GNUNET_MESSENGER_ListMessages *origin)
+{
+  GNUNET_assert((messages) && (origin));
+
+  struct GNUNET_MESSENGER_ListMessage *element;
+
+  for (element = origin->head; element; element = element->next)
+    add_to_list_messages (messages, &(element->hash));
+}
+
 void
 remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, 
const struct GNUNET_HashCode *hash)
 {
+  GNUNET_assert((messages) && (hash));
+
   struct GNUNET_MESSENGER_ListMessage *element;
 
   for (element = messages->head; element; element = element->next)
@@ -74,3 +89,62 @@ remove_from_list_messages (struct 
GNUNET_MESSENGER_ListMessages *messages, const
       break;
     }
 }
+
+void
+load_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char 
*path)
+{
+  GNUNET_assert((messages) && (path));
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (path))
+    return;
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
+      path, GNUNET_DISK_OPEN_READ, permission
+  );
+
+  if (!handle)
+    return;
+
+  GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
+
+  struct GNUNET_HashCode hash;
+  ssize_t len;
+
+  do {
+    len = GNUNET_DISK_file_read(handle, &hash, sizeof(hash));
+
+    if (len != sizeof(hash))
+      break;
+
+    add_to_list_messages(messages, &hash);
+  } while (len == sizeof(hash));
+
+  GNUNET_DISK_file_close(handle);
+}
+
+void
+save_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char 
*path)
+{
+  GNUNET_assert((messages) && (path));
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
+      path, GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE, permission
+  );
+
+  if (!handle)
+    return;
+
+  GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
+
+  struct GNUNET_MESSENGER_ListMessage *element;
+
+  for (element = messages->head; element; element = element->next)
+    GNUNET_DISK_file_write(handle, &(element->hash), sizeof(element->hash));
+
+  GNUNET_DISK_file_sync(handle);
+  GNUNET_DISK_file_close(handle);
+}
diff --git a/src/messenger/gnunet-service-messenger_list_messages.h 
b/src/messenger/gnunet-service-messenger_list_messages.h
index 266c30ec6..9ace84cbf 100644
--- a/src/messenger/gnunet-service-messenger_list_messages.h
+++ b/src/messenger/gnunet-service-messenger_list_messages.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -29,6 +29,7 @@
 #include "platform.h"
 #include "gnunet_crypto_lib.h"
 #include "gnunet_container_lib.h"
+#include "gnunet_disk_lib.h"
 
 struct GNUNET_MESSENGER_ListMessage
 {
@@ -47,7 +48,7 @@ struct GNUNET_MESSENGER_ListMessages
 /**
  * Initializes list of message hashes as empty list.
  *
- * @param messages List of hashes
+ * @param[out] messages List of hashes
  */
 void
 init_list_messages (struct GNUNET_MESSENGER_ListMessages *messages);
@@ -55,7 +56,7 @@ init_list_messages (struct GNUNET_MESSENGER_ListMessages 
*messages);
 /**
  * Clears the list of message hashes.
  *
- * @param messages List of hashes
+ * @param[in/out] messages List of hashes
  */
 void
 clear_list_messages (struct GNUNET_MESSENGER_ListMessages *messages);
@@ -63,19 +64,46 @@ clear_list_messages (struct GNUNET_MESSENGER_ListMessages 
*messages);
 /**
  * Adds a specific <i>hash</i> from a message to the end of the list.
  *
- * @param messages List of hashes
- * @param hash Hash of message
+ * @param[in/out] messages List of hashes
+ * @param[in] hash Hash of message
  */
 void
 add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const 
struct GNUNET_HashCode *hash);
 
+/**
+ * Copies all message hashes from an <i>origin</i> to another list.
+ *
+ * @param[in/out] messages Destination list of hashes
+ * @param[in] origin Source list of hashes
+ */
+void
+copy_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const 
struct GNUNET_MESSENGER_ListMessages *origin);
+
 /**
  * Removes the first entry with a matching <i>hash</i> from the list.
  *
- * @param messages List of hashes
- * @param hash Hash of message
+ * @param[in/out] messages List of hashes
+ * @param[in] hash Hash of message
  */
 void
 remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, 
const struct GNUNET_HashCode *hash);
 
+/**
+ * Loads the list of message hashes from a file under a given <i>path</i>.
+ *
+ * @param[out] messages List of hashes
+ * @param[in] path Path of file
+ */
+void
+load_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char 
*path);
+
+/**
+ * Saves the list of message hashes to a file under a given <i>path</i>.
+ *
+ * @param[in] messages List of hashes
+ * @param[in] path Path of file
+ */
+void
+save_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char 
*path);
+
 #endif //GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H
diff --git a/src/messenger/gnunet-service-messenger_member.c 
b/src/messenger/gnunet-service-messenger_member.c
new file mode 100644
index 000000000..6e39cec13
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_member.c
@@ -0,0 +1,379 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_member.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_member.h"
+
+#include "gnunet-service-messenger_member_session.h"
+
+struct GNUNET_MESSENGER_Member*
+create_member (struct GNUNET_MESSENGER_MemberStore *store, const struct 
GNUNET_ShortHashCode *id)
+{
+  GNUNET_assert (store);
+
+  struct GNUNET_MESSENGER_Member *member = GNUNET_new(struct 
GNUNET_MESSENGER_Member);
+
+  member->store = store;
+
+  if (id)
+    GNUNET_memcpy(&(member->id), id, sizeof(member->id));
+  else if (GNUNET_YES != generate_free_member_id(&(member->id), 
store->members))
+  {
+    GNUNET_free (member);
+    return NULL;
+  }
+
+  member->sessions = GNUNET_CONTAINER_multihashmap_create(2, GNUNET_NO);
+
+  return member;
+}
+
+static int
+iterate_destroy_session (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_MemberSession *session = value;
+  destroy_member_session(session);
+  return GNUNET_YES;
+}
+
+void
+destroy_member (struct GNUNET_MESSENGER_Member *member)
+{
+  GNUNET_assert((member) && (member->sessions));
+
+  GNUNET_CONTAINER_multihashmap_iterate (member->sessions, 
iterate_destroy_session, NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (member->sessions);
+
+  GNUNET_free (member);
+}
+
+const struct GNUNET_ShortHashCode*
+get_member_id (const struct GNUNET_MESSENGER_Member *member)
+{
+  GNUNET_assert (member);
+
+  return &(member->id);
+}
+
+static int
+callback_scan_for_sessions (void *cls, const char *filename)
+{
+  struct GNUNET_MESSENGER_Member *member = cls;
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_YES))
+  {
+    char *directory;
+
+    GNUNET_asprintf (&directory, "%s%c", filename, DIR_SEPARATOR);
+
+    load_member_session(member, directory);
+  }
+
+  return GNUNET_OK;
+}
+
+void
+load_member (struct GNUNET_MESSENGER_MemberStore *store, const char *directory)
+{
+  GNUNET_assert ((store) && (directory));
+
+  char *config_file;
+  GNUNET_asprintf (&config_file, "%s%s", directory, "member.cfg");
+
+  struct GNUNET_MESSENGER_Member *member = NULL;
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (config_file))
+    goto free_config;
+
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+  if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file))
+  {
+    struct GNUNET_ShortHashCode id;
+
+    if (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "member", "id", &id, 
sizeof(id)))
+      goto destroy_config;
+
+    member = add_store_member(store, &id);
+  }
+
+destroy_config:
+
+  GNUNET_CONFIGURATION_destroy (cfg);
+
+free_config:
+  GNUNET_free(config_file);
+
+  if (!member)
+    return;
+
+  char *scan_dir;
+  GNUNET_asprintf (&scan_dir, "%s%s%c", directory, "sessions", DIR_SEPARATOR);
+
+  if (GNUNET_OK == GNUNET_DISK_directory_test (scan_dir, GNUNET_YES))
+    GNUNET_DISK_directory_scan (scan_dir, callback_scan_for_sessions, member);
+
+  GNUNET_free(scan_dir);
+}
+
+static int
+iterate_load_next_session (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  const char* sessions_directory = cls;
+
+  char* load_dir;
+  GNUNET_asprintf (&load_dir, "%s%s%c", sessions_directory, GNUNET_h2s(key), 
DIR_SEPARATOR);
+
+  struct GNUNET_MESSENGER_MemberSession *session = value;
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (load_dir, GNUNET_YES))
+    load_member_session_next (session, load_dir);
+
+  GNUNET_free (load_dir);
+  return GNUNET_YES;
+}
+
+void
+load_member_next_sessions (const struct GNUNET_MESSENGER_Member *member, const 
char *directory)
+{
+  GNUNET_assert ((member) && (directory));
+
+  char* load_dir;
+  GNUNET_asprintf (&load_dir, "%s%s%c", directory, "sessions", DIR_SEPARATOR);
+
+  GNUNET_CONTAINER_multihashmap_iterate (member->sessions, 
iterate_load_next_session, load_dir);
+
+  GNUNET_free(load_dir);
+}
+
+static int
+iterate_save_session (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  const char* sessions_directory = cls;
+
+  char* save_dir;
+  GNUNET_asprintf (&save_dir, "%s%s%c", sessions_directory, GNUNET_h2s(key), 
DIR_SEPARATOR);
+
+  struct GNUNET_MESSENGER_MemberSession *session = value;
+
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (save_dir, GNUNET_NO)) ||
+      (GNUNET_OK == GNUNET_DISK_directory_create (save_dir)))
+    save_member_session (session, save_dir);
+
+  GNUNET_free (save_dir);
+  return GNUNET_YES;
+}
+
+void
+save_member (struct GNUNET_MESSENGER_Member *member, const char *directory)
+{
+  GNUNET_assert ((member) && (directory));
+
+  char *config_file;
+  GNUNET_asprintf (&config_file, "%s%s", directory, "member.cfg");
+
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+  char *id_data = GNUNET_STRINGS_data_to_string_alloc (&(member->id), 
sizeof(member->id));
+
+  if (id_data)
+  {
+    GNUNET_CONFIGURATION_set_value_string (cfg, "member", "id", id_data);
+
+    GNUNET_free(id_data);
+  }
+
+  GNUNET_CONFIGURATION_write (cfg, config_file);
+  GNUNET_CONFIGURATION_destroy (cfg);
+
+  GNUNET_free(config_file);
+
+  char* save_dir;
+  GNUNET_asprintf (&save_dir, "%s%s%c", directory, "sessions", DIR_SEPARATOR);
+
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (save_dir, GNUNET_NO)) ||
+      (GNUNET_OK == GNUNET_DISK_directory_create (save_dir)))
+    GNUNET_CONTAINER_multihashmap_iterate (member->sessions, 
iterate_save_session, save_dir);
+
+  GNUNET_free(save_dir);
+}
+
+static void
+sync_session_contact_from_next (struct GNUNET_MESSENGER_MemberSession 
*session, struct GNUNET_MESSENGER_MemberSession *next)
+{
+  GNUNET_assert((session) && (next));
+
+  if (session == next)
+    return;
+
+  if (next->next)
+    sync_session_contact_from_next (session, next->next);
+  else
+    session->contact = next->contact;
+}
+
+static int
+iterate_sync_session_contact (void *cls, const struct GNUNET_HashCode *key, 
void *value)
+{
+  struct GNUNET_MESSENGER_MemberSession *session = value;
+
+  if (session->next)
+    sync_session_contact_from_next (session, session->next);
+
+  return GNUNET_YES;
+}
+
+void
+sync_member_contacts (struct GNUNET_MESSENGER_Member *member)
+{
+  GNUNET_assert ((member) && (member->sessions));
+
+  GNUNET_CONTAINER_multihashmap_iterate (member->sessions, 
iterate_sync_session_contact, NULL);
+}
+
+struct GNUNET_MESSENGER_MemberSession*
+get_member_session (const struct GNUNET_MESSENGER_Member *member, const struct 
GNUNET_IDENTITY_PublicKey *public_key)
+{
+  GNUNET_assert ((member) && (public_key));
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash(public_key, sizeof(*public_key), &hash);
+
+  return GNUNET_CONTAINER_multihashmap_get(member->sessions, &hash);
+}
+
+struct GNUNET_MESSENGER_ClosureSearchSession {
+  const struct GNUNET_MESSENGER_Message *message;
+  const struct GNUNET_HashCode *hash;
+
+  struct GNUNET_MESSENGER_MemberSession *match;
+};
+
+static int
+iterate_search_session (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_ClosureSearchSession* search = cls;
+  struct GNUNET_MESSENGER_MemberSession *session = value;
+
+  if (GNUNET_OK != verify_member_session_as_sender(session, search->message, 
search->hash))
+    return GNUNET_YES;
+
+  search->match = session;
+  return GNUNET_NO;
+}
+
+static struct GNUNET_MESSENGER_MemberSession*
+try_member_session (struct GNUNET_MESSENGER_Member *member, const struct 
GNUNET_IDENTITY_PublicKey *public_key)
+{
+  struct GNUNET_MESSENGER_MemberSession* session = get_member_session(member, 
public_key);
+
+  if (session)
+    return session;
+
+  session = create_member_session(member, public_key);
+
+  if (session)
+    add_member_session(member, session);
+
+  return session;
+}
+
+struct GNUNET_MESSENGER_MemberSession*
+get_member_session_of (struct GNUNET_MESSENGER_Member *member, const struct 
GNUNET_MESSENGER_Message *message,
+                       const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert ((member) && (message) && (hash) &&
+                 (0 == GNUNET_memcmp(&(member->id), 
&(message->header.sender_id))));
+
+  if (GNUNET_MESSENGER_KIND_INFO == message->header.kind)
+    return try_member_session(member, &(message->body.info.host_key));
+  else if (GNUNET_MESSENGER_KIND_JOIN == message->header.kind)
+    return try_member_session(member, &(message->body.join.key));
+
+  struct GNUNET_MESSENGER_ClosureSearchSession search;
+
+  search.message = message;
+  search.hash = hash;
+
+  search.match = NULL;
+  GNUNET_CONTAINER_multihashmap_iterate(member->sessions, 
iterate_search_session, &search);
+
+  return search.match;
+}
+
+void
+add_member_session (struct GNUNET_MESSENGER_Member *member, struct 
GNUNET_MESSENGER_MemberSession *session)
+{
+  if (!session)
+    return;
+
+  GNUNET_assert((member) && (session->member == member));
+
+  const struct GNUNET_IDENTITY_PublicKey *public_key = 
get_member_session_public_key(session);
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash(public_key, sizeof(*public_key), &hash);
+
+  GNUNET_CONTAINER_multihashmap_put(member->sessions, &hash, session, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+}
+
+void
+remove_member_session (struct GNUNET_MESSENGER_Member *member, struct 
GNUNET_MESSENGER_MemberSession *session)
+{
+  GNUNET_assert ((member) && (session) && (session->member == member));
+
+  const struct GNUNET_IDENTITY_PublicKey *public_key = 
get_member_session_public_key(session);
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash(public_key, sizeof(*public_key), &hash);
+
+  GNUNET_CONTAINER_multihashmap_remove(member->sessions, &hash, session);
+}
+
+struct GNUNET_MESSENGER_ClosureIterateSessions {
+  GNUNET_MESSENGER_MemberIteratorCallback it;
+  void *cls;
+};
+
+static int
+iterate_member_sessions_it (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_ClosureIterateSessions *iterate = cls;
+  struct GNUNET_MESSENGER_MemberSession *session = value;
+
+  return iterate->it (iterate->cls, get_member_session_public_key(session), 
session);
+}
+
+int
+iterate_member_sessions (struct GNUNET_MESSENGER_Member *member, 
GNUNET_MESSENGER_MemberIteratorCallback it, void *cls)
+{
+  GNUNET_assert ((member) && (member->sessions) && (it));
+
+  struct GNUNET_MESSENGER_ClosureIterateSessions iterate;
+
+  iterate.it = it;
+  iterate.cls = cls;
+
+  return GNUNET_CONTAINER_multihashmap_iterate(member->sessions, 
iterate_member_sessions_it, &iterate);
+}
diff --git a/src/messenger/gnunet-service-messenger_member.h 
b/src/messenger/gnunet-service-messenger_member.h
new file mode 100644
index 000000000..fb2e57cfb
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_member.h
@@ -0,0 +1,170 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_member.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MEMBER_H
+#define GNUNET_SERVICE_MESSENGER_MEMBER_H
+
+#include "messenger_api_contact.h"
+
+#include "gnunet-service-messenger_list_messages.h"
+#include "gnunet-service-messenger_member_store.h"
+#include "messenger_api_message.h"
+#include "messenger_api_util.h"
+
+struct GNUNET_MESSENGER_Member
+{
+  struct GNUNET_MESSENGER_MemberStore *store;
+  struct GNUNET_ShortHashCode id;
+
+  struct GNUNET_CONTAINER_MultiHashMap *sessions;
+};
+
+/**
+ * Creates and allocates a new member of a <i>room</i> with an optionally 
defined or
+ * random <i>id</i>.
+ *
+ * If the creation fails, NULL gets returned.
+ *
+ * @param[in/out] store Member store
+ * @param[in] id Member id or NULL
+ * @return New member or NULL
+ */
+struct GNUNET_MESSENGER_Member*
+create_member (struct GNUNET_MESSENGER_MemberStore *store, const struct 
GNUNET_ShortHashCode *id);
+
+/**
+ * Destroys a member and frees its memory fully.
+ *
+ * @param[in/out] member Member
+ */
+void
+destroy_member (struct GNUNET_MESSENGER_Member *member);
+
+/**
+ * Returns the current id of a given <i>member</i>.
+ *
+ * @param[in] member Member
+ * @return Member id
+ */
+const struct GNUNET_ShortHashCode*
+get_member_id (const struct GNUNET_MESSENGER_Member *member);
+
+/**
+ * Loads data from a <i>directory</i> into a new allocated and created member
+ * of a <i>store</i> if the required information can be read from the content
+ * of the given directory.
+ *
+ * @param[out] store Member store
+ * @param[in] directory Path to a directory
+ */
+void
+load_member (struct GNUNET_MESSENGER_MemberStore *store, const char 
*directory);
+
+/**
+ * Loads data about next sessions from a <i>directory</i> into an empty loaded
+ * <i>member</i> which does not contain a fully built session graph yet.
+ *
+ * @param[in/out] member Member
+ * @param[in] directory Path to a directory
+ */
+void
+load_member_next_sessions (const struct GNUNET_MESSENGER_Member *member, const 
char *directory);
+
+/**
+ * Saves data from a <i>member</i> into a directory which
+ * can be load to restore the member completely.
+ *
+ * @param[in] member Member
+ * @param[in] directory Path to a directory
+ */
+void
+save_member (struct GNUNET_MESSENGER_Member *member, const char *directory);
+
+/**
+ * Synchronizes contacts between all sessions from a given <i>member</i>
+ * and other sessions which are linked to them.
+ *
+ * @param[in/out] member Member
+ */
+void
+sync_member_contacts (struct GNUNET_MESSENGER_Member *member);
+
+/**
+ * Returns the member session of a <i>member</i> identified by a given public 
key.
+ * If the member does not provide a session with the given key, NULL gets 
returned.
+ *
+ * @param[in] member Member
+ * @param[in] public_key Public key of EGO
+ * @return Member session
+ */
+struct GNUNET_MESSENGER_MemberSession*
+get_member_session (const struct GNUNET_MESSENGER_Member *member, const struct 
GNUNET_IDENTITY_PublicKey *public_key);
+
+/**
+ * Returns the member session of a <i>member</i> using a public key which can 
verify
+ * the signature of a given <i>message</i> and its <i>hash</i>. If the member 
does
+ * not provide a matching session, NULL gets returned.
+ *
+ * @param[in] member Member
+ * @param[in] message Message
+ * @param[in] hash Hash of message
+ * @return Member session
+ */
+struct GNUNET_MESSENGER_MemberSession*
+get_member_session_of (struct GNUNET_MESSENGER_Member *member, const struct 
GNUNET_MESSENGER_Message *message,
+                       const struct GNUNET_HashCode *hash);
+
+/**
+ * Adds a given member <i>session</i> to its <i>member</i>.
+ *
+ * @param[in/out] member Member
+ * @param[in/out] session Member session
+ */
+void
+add_member_session (struct GNUNET_MESSENGER_Member *member, struct 
GNUNET_MESSENGER_MemberSession *session);
+
+/**
+ * Removes a given member <i>session</i> from its <i>member</i>.
+ *
+ * @param[in/out] member Member
+ * @param[in/out] session Member session
+ */
+void
+remove_member_session (struct GNUNET_MESSENGER_Member *member, struct 
GNUNET_MESSENGER_MemberSession *session);
+
+/**
+ * Iterate through all member sessions currently connected to a given 
<i>member</i>
+ * and call the provided iterator callback with a selected closure. The 
function
+ * will return the amount of member sessions it iterated through.
+ *
+ * @param[in/out] member Member
+ * @param[in] it Iterator callback
+ * @param[in/out] cls Closure
+ * @return Amount of sessions iterated through
+ */
+int
+iterate_member_sessions (struct GNUNET_MESSENGER_Member *member, 
GNUNET_MESSENGER_MemberIteratorCallback it, void* cls);
+
+#endif //GNUNET_SERVICE_MESSENGER_MEMBER_H
diff --git a/src/messenger/gnunet-service-messenger_member_session.c 
b/src/messenger/gnunet-service-messenger_member_session.c
new file mode 100644
index 000000000..636285d93
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_member_session.c
@@ -0,0 +1,707 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_member_session.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_member_session.h"
+
+#include "gnunet-service-messenger_room.h"
+#include "gnunet-service-messenger_message_store.h"
+
+#include "messenger_api_contact_store.h"
+
+struct GNUNET_MESSENGER_MemberSession*
+create_member_session (struct GNUNET_MESSENGER_Member *member,
+                       const struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  if ((!member) || (!pubkey) || (!(member->store)))
+    return NULL;
+
+  struct GNUNET_MESSENGER_MemberSession *session = GNUNET_new(struct 
GNUNET_MESSENGER_MemberSession);
+  session->member = member;
+
+  GNUNET_memcpy(&(session->public_key), pubkey, sizeof(session->public_key));
+
+  get_context_from_member (
+      get_member_session_key (session),
+      get_member_session_id (session),
+      &(session->context)
+  );
+
+  struct GNUNET_MESSENGER_ContactStore *store = 
get_member_contact_store(session->member->store);
+
+  session->contact = get_store_contact(
+      store,
+      get_member_session_context (session),
+      get_member_session_public_key (session)
+  );
+
+  if (!(session->contact))
+  {
+    GNUNET_free(session);
+    return NULL;
+  }
+
+  increase_contact_rc (session->contact);
+
+  session->history = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO);
+
+  init_list_messages(&(session->messages));
+
+  session->next = NULL;
+
+  session->closed = GNUNET_NO;
+  session->completed = GNUNET_NO;
+
+  return session;
+}
+
+static void
+check_member_session_completion (struct GNUNET_MESSENGER_MemberSession 
*session)
+{
+  GNUNET_assert (session);
+
+  if (!session->messages.tail)
+  {
+    session->completed = GNUNET_YES;
+    goto completion;
+  }
+
+  const struct GNUNET_HashCode* start = &(session->messages.head->hash);
+  const struct GNUNET_HashCode* end = &(session->messages.tail->hash);
+
+  struct GNUNET_MESSENGER_ListMessages level;
+  init_list_messages(&level);
+
+  add_to_list_messages(&level, end);
+
+  struct GNUNET_MESSENGER_MessageStore *store = 
get_room_message_store(session->member->store->room);
+
+  struct GNUNET_MESSENGER_ListMessages list;
+  init_list_messages(&list);
+
+  while (level.head)
+  {
+    struct GNUNET_MESSENGER_ListMessage *element;
+
+    for (element = level.head; element; element = element->next)
+    {
+      const struct GNUNET_MESSENGER_MessageLink *link = get_store_message_link(
+          store, &(element->hash), GNUNET_NO
+      );
+
+      if (!link)
+        continue;
+
+      add_to_list_messages(&list, &(link->first));
+
+      if (GNUNET_YES == link->multiple)
+        add_to_list_messages(&list, &(link->second));
+    }
+
+    clear_list_messages(&level);
+
+    for (element = list.head; element; element = element->next)
+      if (GNUNET_YES == check_member_session_history(session, 
&(element->hash), GNUNET_YES))
+        break;
+
+    if (element)
+      if (0 != GNUNET_CRYPTO_hash_cmp(&(element->hash), start))
+        add_to_list_messages(&level, &(element->hash));
+      else
+        session->completed = GNUNET_YES;
+    else
+      copy_list_messages(&level, &list);
+
+    clear_list_messages(&list);
+  }
+
+completion:
+  if (GNUNET_YES == is_member_session_completed(session))
+  {
+    GNUNET_CONTAINER_multihashmap_destroy (session->history);
+
+    struct GNUNET_MESSENGER_ContactStore *store = 
get_member_contact_store(session->member->store);
+
+    if ((session->contact) && (GNUNET_YES == decrease_contact_rc 
(session->contact)))
+      remove_store_contact (
+          store,
+          session->contact,
+          get_member_session_context(session)
+      );
+
+    session->contact = NULL;
+  }
+}
+
+static int
+iterate_copy_history (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_MemberSession *next = cls;
+
+  GNUNET_CONTAINER_multihashmap_put(next->history, key, (value? next : NULL),
+                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+
+  return GNUNET_YES;
+}
+
+struct GNUNET_MESSENGER_MemberSession*
+switch_member_session (struct GNUNET_MESSENGER_MemberSession *session,
+                       const struct GNUNET_MESSENGER_Message *message,
+                       const struct GNUNET_HashCode *hash)
+{
+  if ((!session) || (!message) || (!hash))
+    return NULL;
+
+  GNUNET_assert((GNUNET_MESSENGER_KIND_ID == message->header.kind) ||
+                (GNUNET_MESSENGER_KIND_KEY == message->header.kind));
+
+  struct GNUNET_MESSENGER_MemberSession *next = GNUNET_new(struct 
GNUNET_MESSENGER_MemberSession);
+
+  if (GNUNET_MESSENGER_KIND_ID == message->header.kind)
+    next->member = add_store_member(session->member->store, 
&(message->body.id.id));
+  else
+    next->member = session->member;
+
+  if (GNUNET_MESSENGER_KIND_KEY == message->header.kind)
+    GNUNET_memcpy(&(next->public_key), &(message->body.key.key), 
sizeof(next->public_key));
+  else
+    GNUNET_memcpy(&(next->public_key), get_member_session_public_key(session), 
sizeof(next->public_key));
+
+  get_context_from_member (
+      get_member_session_key (next),
+      get_member_session_id (next),
+      &(next->context)
+  );
+
+  update_store_contact(
+      get_member_contact_store(next->member->store),
+      get_member_session_contact(session),
+      get_member_session_context(session),
+      get_member_session_context(next),
+      get_member_session_public_key(next)
+  );
+
+  next->contact = get_member_session_contact(session);
+
+  if (!(next->contact))
+  {
+    GNUNET_free(next);
+    return NULL;
+  }
+
+  increase_contact_rc (next->contact);
+
+  next->history = GNUNET_CONTAINER_multihashmap_create(
+      GNUNET_CONTAINER_multihashmap_size(session->history), GNUNET_NO
+  );
+
+  GNUNET_CONTAINER_multihashmap_iterate(session->history, 
iterate_copy_history, next);
+
+  init_list_messages(&(next->messages));
+  copy_list_messages(&(next->messages), &(session->messages));
+
+  session->next = next;
+  next->next = NULL;
+
+  session->closed = GNUNET_YES;
+  next->closed = GNUNET_NO;
+  next->completed = GNUNET_NO;
+
+  check_member_session_completion (session);
+
+  return next;
+}
+
+void
+destroy_member_session(struct GNUNET_MESSENGER_MemberSession* session)
+{
+  GNUNET_assert (session);
+
+  GNUNET_CONTAINER_multihashmap_destroy (session->history);
+
+  clear_list_messages (&(session->messages));
+
+  struct GNUNET_MESSENGER_Contact *contact = get_member_session_contact 
(session);
+
+  if ((contact) && (GNUNET_YES == decrease_contact_rc (contact)))
+    remove_store_contact (
+        get_member_contact_store(session->member->store),
+        contact,
+        get_member_session_context(session)
+    );
+
+  GNUNET_free(session);
+}
+
+int
+reset_member_session (struct GNUNET_MESSENGER_MemberSession* session,
+                      const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert ((session) && (hash));
+
+  struct GNUNET_MESSENGER_ContactStore *store = 
get_member_contact_store(session->member->store);
+  struct GNUNET_MESSENGER_Contact *contact = get_store_contact(
+      store,
+      get_member_session_context (session),
+      get_member_session_public_key (session)
+  );
+
+  if (!contact)
+    return GNUNET_SYSERR;
+
+  if (contact == session->contact)
+    goto clear_messages;
+
+  session->contact = contact;
+  increase_contact_rc (session->contact);
+
+clear_messages:
+  clear_list_messages(&(session->messages));
+  add_to_list_messages(&(session->messages), hash);
+
+  session->next = NULL;
+  session->closed = GNUNET_NO;
+  session->completed = GNUNET_NO;
+
+  return GNUNET_OK;
+}
+
+void
+close_member_session (struct GNUNET_MESSENGER_MemberSession* session)
+{
+  GNUNET_assert (session);
+
+  session->closed = GNUNET_YES;
+  check_member_session_completion (session);
+}
+
+int
+is_member_session_closed (const struct GNUNET_MESSENGER_MemberSession* session)
+{
+  GNUNET_assert(session);
+
+  return session->closed;
+}
+
+int
+is_member_session_completed (const struct GNUNET_MESSENGER_MemberSession* 
session)
+{
+  GNUNET_assert(session);
+
+  return session->completed;
+}
+
+const struct GNUNET_HashCode*
+get_member_session_key (const struct GNUNET_MESSENGER_MemberSession* session)
+{
+  GNUNET_assert((session) && (session->member));
+
+  return get_member_store_key(session->member->store);
+}
+
+const struct GNUNET_ShortHashCode*
+get_member_session_id (const struct GNUNET_MESSENGER_MemberSession* session)
+{
+  GNUNET_assert(session);
+
+  return get_member_id(session->member);
+}
+
+const struct GNUNET_IDENTITY_PublicKey*
+get_member_session_public_key (const struct GNUNET_MESSENGER_MemberSession* 
session)
+{
+  GNUNET_assert(session);
+
+  return &(session->public_key);
+}
+
+const struct GNUNET_HashCode*
+get_member_session_context (const struct GNUNET_MESSENGER_MemberSession* 
session)
+{
+  GNUNET_assert(session);
+
+  return &(session->context);
+}
+
+struct GNUNET_MESSENGER_Contact*
+get_member_session_contact (struct GNUNET_MESSENGER_MemberSession* session)
+{
+  GNUNET_assert (session);
+
+  return session->contact;
+}
+
+int verify_member_session_as_sender (const struct 
GNUNET_MESSENGER_MemberSession *session,
+                                     const struct GNUNET_MESSENGER_Message 
*message,
+                                     const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((session) && (message) && (hash));
+
+  if (GNUNET_YES == is_member_session_completed(session))
+    return GNUNET_SYSERR;
+
+  if (0 != GNUNET_memcmp(get_member_session_id(session), 
&(message->header.sender_id)))
+    return GNUNET_SYSERR;
+
+  return verify_message(message, hash, get_member_session_public_key(session));
+}
+
+int
+check_member_session_history (const struct GNUNET_MESSENGER_MemberSession 
*session,
+                              const struct GNUNET_HashCode *hash, int 
ownership)
+{
+  GNUNET_assert((session) && (hash));
+
+  if (GNUNET_YES == ownership)
+    return (NULL != GNUNET_CONTAINER_multihashmap_get(session->history, hash)? 
GNUNET_YES : GNUNET_NO);
+  else
+    return GNUNET_CONTAINER_multihashmap_contains(session->history, hash);
+}
+
+static void
+update_member_chain_history (struct GNUNET_MESSENGER_MemberSession *session,
+                             const struct GNUNET_HashCode *hash, int ownership)
+{
+  GNUNET_assert ((session) && (hash));
+
+  if ((GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(session->history, hash, 
(GNUNET_YES == ownership? session : NULL),
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) && (session->next))
+    update_member_chain_history (session->next, hash, ownership);
+}
+
+void
+update_member_session_history (struct GNUNET_MESSENGER_MemberSession *session,
+                               const struct GNUNET_MESSENGER_Message *message,
+                               const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((session) && (message) && (hash));
+
+  if (GNUNET_YES == is_member_session_completed(session))
+    return;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Updating sessions history (%s) += 
(%s)\n",
+             GNUNET_sh2s(get_member_session_id(session)), GNUNET_h2s(hash));
+
+  if (GNUNET_OK == verify_member_session_as_sender (session, message, hash))
+  {
+    if (GNUNET_YES == is_message_session_bound (message))
+      add_to_list_messages(&(session->messages), hash);
+
+    update_member_chain_history (session, hash, GNUNET_YES);
+  }
+  else
+    update_member_chain_history (session, hash, GNUNET_NO);
+
+  if (GNUNET_YES == session->closed)
+    check_member_session_completion(session);
+}
+
+static void
+clear_member_chain_history (struct GNUNET_MESSENGER_MemberSession *session,
+                            const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert ((session) && (hash));
+
+  if ((0 < GNUNET_CONTAINER_multihashmap_remove_all(session->history, hash)) 
&& (session->next))
+    clear_member_session_history(session->next, hash);
+}
+
+void
+clear_member_session_history (struct GNUNET_MESSENGER_MemberSession *session,
+                              const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((session) && (hash));
+
+  clear_member_chain_history (session, hash);
+}
+
+struct GNUNET_MESSENGER_MemberSessionHistoryEntry
+{
+  struct GNUNET_HashCode hash;
+  unsigned char ownership;
+};
+
+static void
+load_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, 
const char *path)
+{
+  GNUNET_assert((session) && (path));
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (path))
+    return;
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
+      path, GNUNET_DISK_OPEN_READ, permission
+  );
+
+  if (!handle)
+    return;
+
+  GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
+
+  struct GNUNET_MESSENGER_MemberSessionHistoryEntry entry;
+  ssize_t len;
+
+  int status;
+
+  do {
+    len = GNUNET_DISK_file_read(handle, &(entry.hash), sizeof(entry.hash));
+
+    if (len != sizeof(entry.hash))
+      break;
+
+    len = GNUNET_DISK_file_read(handle, &(entry.ownership), 
sizeof(entry.ownership));
+
+    if (len != sizeof(entry.ownership))
+      break;
+
+    status = GNUNET_CONTAINER_multihashmap_put(session->history, 
&(entry.hash), (entry.ownership? session : NULL),
+                                               
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  } while (status == GNUNET_OK);
+
+  GNUNET_DISK_file_close(handle);
+}
+
+void
+load_member_session (struct GNUNET_MESSENGER_Member *member, const char 
*directory)
+{
+  GNUNET_assert ((member) && (directory));
+
+  char *config_file;
+  GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg");
+
+  struct GNUNET_MESSENGER_MemberSession *session = NULL;
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (config_file))
+    goto free_config;
+
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+  if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file))
+  {
+    char *key_data;
+
+    if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "session", 
"key", &key_data))
+      goto destroy_config;
+
+    struct GNUNET_IDENTITY_PublicKey key;
+
+    enum GNUNET_GenericReturnValue key_return = 
GNUNET_IDENTITY_public_key_from_string(key_data, &key);
+
+    GNUNET_free(key_data);
+
+    if (GNUNET_OK != key_return)
+      goto destroy_config;
+
+    session = create_member_session(member, &key);
+  }
+
+destroy_config:
+  GNUNET_CONFIGURATION_destroy (cfg);
+
+free_config:
+  GNUNET_free(config_file);
+
+  if (!session)
+    return;
+
+  char *history_file;
+  GNUNET_asprintf (&history_file, "%s%s", directory, "history.map");
+
+  load_member_session_history (session, history_file);
+  GNUNET_free(history_file);
+
+  char *messages_file;
+  GNUNET_asprintf (&messages_file, "%s%s", directory, "messages.list");
+
+  load_list_messages(&(session->messages), messages_file);
+  GNUNET_free(messages_file);
+
+  add_member_session(member, session);
+}
+
+static struct GNUNET_MESSENGER_MemberSession*
+get_cycle_safe_next_session (struct GNUNET_MESSENGER_MemberSession *session, 
struct GNUNET_MESSENGER_MemberSession *next)
+{
+  if (!next)
+    return NULL;
+
+  struct GNUNET_MESSENGER_MemberSession *check = next;
+
+  do {
+    if (check == session)
+      return NULL;
+
+    check = check->next;
+  } while (check);
+
+  return next;
+}
+
+void
+load_member_session_next (struct GNUNET_MESSENGER_MemberSession *session, 
const char *directory)
+{
+  GNUNET_assert ((session) && (directory));
+
+  char *config_file;
+  GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg");
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (config_file))
+    goto free_config;
+
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+  if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file))
+  {
+    char *key_data;
+
+    if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "session", 
"next_key", &key_data))
+      goto destroy_config;
+
+    struct GNUNET_IDENTITY_PublicKey next_key;
+
+    enum GNUNET_GenericReturnValue key_return = 
GNUNET_IDENTITY_public_key_from_string(key_data, &next_key);
+
+    GNUNET_free(key_data);
+
+    if (GNUNET_OK != key_return)
+      goto destroy_config;
+
+    struct GNUNET_ShortHashCode next_id;
+
+    if (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "session", "next_id", 
&next_id, sizeof(next_id)))
+      goto destroy_config;
+
+    struct GNUNET_MESSENGER_Member *member = 
get_store_member(session->member->store, &next_id);
+
+    session->next = get_cycle_safe_next_session(
+        session, member? get_member_session (member, &next_key) : NULL
+    );
+  }
+
+destroy_config:
+  GNUNET_CONFIGURATION_destroy (cfg);
+
+free_config:
+  GNUNET_free(config_file);
+}
+
+static int
+iterate_save_member_session_history_hentries (void *cls, const struct 
GNUNET_HashCode *key, void *value)
+{
+  struct GNUNET_DISK_FileHandle *handle = cls;
+  unsigned char ownership = value? GNUNET_YES : GNUNET_NO;
+
+  GNUNET_DISK_file_write(handle, key, sizeof(*key));
+  GNUNET_DISK_file_write(handle, &ownership, sizeof(ownership));
+
+  return GNUNET_YES;
+}
+
+static void
+save_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, 
const char *path)
+{
+  GNUNET_assert((session) && (path));
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
+      path, GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE, permission
+  );
+
+  if (!handle)
+    return;
+
+  GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
+
+  GNUNET_CONTAINER_multihashmap_iterate(
+      session->history,
+      iterate_save_member_session_history_hentries,
+      handle
+  );
+
+  GNUNET_DISK_file_sync(handle);
+  GNUNET_DISK_file_close(handle);
+}
+
+void
+save_member_session (struct GNUNET_MESSENGER_MemberSession *session, const 
char *directory)
+{
+  GNUNET_assert ((session) && (directory));
+
+  char *config_file;
+  GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg");
+
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+  char *key_data = 
GNUNET_IDENTITY_public_key_to_string(get_member_session_public_key(session));
+
+  if (key_data)
+  {
+    GNUNET_CONFIGURATION_set_value_string (cfg, "session", "key", key_data);
+
+    GNUNET_free(key_data);
+  }
+
+  if (session->next)
+  {
+    const struct GNUNET_ShortHashCode *next_id = 
get_member_session_id(session->next);
+
+    char *next_id_data = GNUNET_STRINGS_data_to_string_alloc (next_id, 
sizeof(*next_id));
+
+    if (next_id_data)
+    {
+      GNUNET_CONFIGURATION_set_value_string (cfg, "session", "next_id", 
next_id_data);
+
+      GNUNET_free(next_id_data);
+    }
+
+    key_data = 
GNUNET_IDENTITY_public_key_to_string(get_member_session_public_key(session->next));
+
+    if (key_data)
+    {
+      GNUNET_CONFIGURATION_set_value_string (cfg, "session", "next_key", 
key_data);
+
+      GNUNET_free(key_data);
+    }
+  }
+
+  GNUNET_CONFIGURATION_write (cfg, config_file);
+  GNUNET_CONFIGURATION_destroy (cfg);
+
+  GNUNET_free(config_file);
+
+  char *history_file;
+  GNUNET_asprintf (&history_file, "%s%s", directory, "history.map");
+
+  save_member_session_history (session, history_file);
+  GNUNET_free(history_file);
+
+  char *messages_file;
+  GNUNET_asprintf (&messages_file, "%s%s", directory, "messages.list");
+
+  save_list_messages(&(session->messages), messages_file);
+  GNUNET_free(messages_file);
+}
diff --git a/src/messenger/gnunet-service-messenger_member_session.h 
b/src/messenger/gnunet-service-messenger_member_session.h
new file mode 100644
index 000000000..dd753be2a
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_member_session.h
@@ -0,0 +1,275 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_member_session.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MEMBER_SESSION_H
+#define GNUNET_SERVICE_MESSENGER_MEMBER_SESSION_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_identity_service.h"
+
+#include "gnunet-service-messenger_member.h"
+
+#include "messenger_api_contact.h"
+
+struct GNUNET_MESSENGER_MemberSession {
+  struct GNUNET_MESSENGER_Member *member;
+
+  struct GNUNET_IDENTITY_PublicKey public_key;
+  struct GNUNET_HashCode context;
+
+  struct GNUNET_MESSENGER_Contact *contact;
+
+  struct GNUNET_CONTAINER_MultiHashMap *history;
+  struct GNUNET_MESSENGER_ListMessages messages;
+
+  struct GNUNET_MESSENGER_MemberSession* next;
+
+  int closed;
+  int completed;
+};
+
+/**
+ * Creates and allocates a new member session of a <i>member</i> with a given
+ * public key.
+ *
+ * If the creation fails, NULL gets returned.
+ *
+ * @param[in/out] member Member
+ * @param[in] pubkey Public key of EGO
+ * @return New member session
+ */
+struct GNUNET_MESSENGER_MemberSession*
+create_member_session (struct GNUNET_MESSENGER_Member *member,
+                       const struct GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Creates and allocates a new member session closing and replacing a given
+ * other <i>session</i> of the same member. The new session could have 
significant
+ * changes to the members public key or its member id depending on the used
+ * <i>message</i> to switch session. The new session will be linked to the old
+ * one.
+ *
+ * @param[in/out] session Old member session
+ * @param[in] message Message
+ * @param[in] hash Hash of message
+ * @return New member session
+ */
+struct GNUNET_MESSENGER_MemberSession*
+switch_member_session (struct GNUNET_MESSENGER_MemberSession *session,
+                       const struct GNUNET_MESSENGER_Message *message,
+                       const struct GNUNET_HashCode *hash);
+
+/**
+ * Destroys a member session and frees its memory fully.
+ *
+ * @param[in/out] session Member session
+ */
+void
+destroy_member_session(struct GNUNET_MESSENGER_MemberSession* session);
+
+/**
+ * Resets a given member <i>session</i> which re-opens a member
+ * session for new usage. Every connection to other sessions will be
+ * be dropped. The member sessions messages will be cleared but old
+ * history from uncompleted sessions however can be reused!
+ *
+ * @param[in/out] session Member session
+ * @param[in] hash Hash of initial message (JOIN message!)
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
+ */
+int
+reset_member_session (struct GNUNET_MESSENGER_MemberSession* session,
+                      const struct GNUNET_HashCode *hash);
+
+/**
+ * Closes a given member <i>session</i> which opens the request
+ * for completion of the given member session.
+ *
+ * Closing a session may complete a session and can't be used without
+ * a reset! ( @see #reset_member_session() )
+ *
+ * @param[in/out] session Member session
+ */
+void
+close_member_session (struct GNUNET_MESSENGER_MemberSession* session);
+
+/**
+ * Returns if the given member <i>session</i> has been closed.
+ *
+ * @param[in] session Member session
+ * @return #GNUNET_YES or #GNUNET_NO
+ */
+int
+is_member_session_closed (const struct GNUNET_MESSENGER_MemberSession* 
session);
+
+/**
+ * Returns if the given member <i>session</i> has been completed.
+ *
+ * A completed member session can't verify any message as its own and
+ * it won't add any message to its history.
+ *
+ * @param[in] session Member session
+ * @return #GNUNET_YES or #GNUNET_NO
+ */
+int
+is_member_session_completed (const struct GNUNET_MESSENGER_MemberSession* 
session);
+
+/**
+ * Returns the key of the room a given member <i>session</i> belongs to.
+ *
+ * @param[in] session Member session
+ * @return Key of room
+ */
+const struct GNUNET_HashCode*
+get_member_session_key (const struct GNUNET_MESSENGER_MemberSession* session);
+
+/**
+ * Returns the member id of a given member <i>session</i>.
+ *
+ * @param[in] session Member session
+ * @return Member id
+ */
+const struct GNUNET_ShortHashCode*
+get_member_session_id (const struct GNUNET_MESSENGER_MemberSession* session);
+
+/**
+ * Returns the public key from an EGO of a given member <i>session</i>.
+ *
+ * @param[in] session Member session
+ * @return Public key of EGO
+ */
+const struct GNUNET_IDENTITY_PublicKey*
+get_member_session_public_key (const struct GNUNET_MESSENGER_MemberSession* 
session);
+
+/**
+ * Returns the member context of a given member <i>session</i>.
+ *
+ * @param[in] session Member session
+ * @return Member context as hash
+ */
+const struct GNUNET_HashCode*
+get_member_session_context (const struct GNUNET_MESSENGER_MemberSession* 
session);
+
+/**
+ * Retruns the contact which is connected to a given member <i>session</i>.
+ *
+ * @param[in] session Member session
+ * @return Contact
+ */
+struct GNUNET_MESSENGER_Contact*
+get_member_session_contact (struct GNUNET_MESSENGER_MemberSession* session);
+
+/**
+ * Verifies a given member <i>session</i> as sender of a selected 
<i>message</i> and
+ * its <i>hash</i>. The function returns #GNUNET_OK if the message session is 
verified
+ * as sender, otherwise #GNUNET_SYSERR.
+ *
+ * @see #is_member_session_completed() for verification.
+ *
+ * @param[in] session Member session
+ * @param[in] message Message
+ * @param[in] hash Hash of message
+ * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR
+ */
+int
+verify_member_session_as_sender (const struct GNUNET_MESSENGER_MemberSession 
*session,
+                                 const struct GNUNET_MESSENGER_Message 
*message,
+                                 const struct GNUNET_HashCode *hash);
+
+/**
+ * Checks the history of a <i>session</i> for a specific message which is 
identified
+ * by its <i>hash</i> and if the <i>ownership</i> flag is set, if the message 
is
+ * owned by the sessions contact.
+ *
+ * @param[in] session Member session
+ * @param[in] hash Hash of message
+ * @param[in] ownership Ownership flag
+ * @return #GNUNET_YES if found, otherwise #GNUNET_NO
+ */
+int
+check_member_session_history (const struct GNUNET_MESSENGER_MemberSession 
*session,
+                              const struct GNUNET_HashCode *hash, int 
ownership);
+
+/**
+ * Adds a given <i>message</i> to the history of a <i>session</i> using the 
messages
+ * <i>hash</i>. The ownership will be set automatically.
+ *
+ * @see #is_member_session_completed() for updating a history.
+ *
+ * @param[in/out] session Member session
+ * @param[in] message Message
+ * @param[in] hash Hash of message
+ */
+void
+update_member_session_history (struct GNUNET_MESSENGER_MemberSession *session,
+                               const struct GNUNET_MESSENGER_Message *message,
+                               const struct GNUNET_HashCode *hash);
+
+/**
+ * Removes a message from the history of a <i>session</i> using the messages
+ * <i>hash</i>.
+ *
+ * @param[in/out] session Member session
+ * @param[in] hash Hash of message
+ */
+void
+clear_member_session_history (struct GNUNET_MESSENGER_MemberSession *session,
+                              const struct GNUNET_HashCode *hash);
+
+/**
+ * Loads data from a <i>directory</i> into a new allocated and created member
+ * session of a <i>member</i> if the required information can be read from the
+ * content of the given directory.
+ *
+ * @param[out] member Member
+ * @param[in] directory Path to a directory
+ */
+void
+load_member_session (struct GNUNET_MESSENGER_Member *member, const char 
*directory);
+
+/**
+ * Loads the connection from one <i>session</i> to another through the
+ * next attribute. Necessary information will be loaded from a configuration
+ * file inside of a given <i>directory</i>.
+ *
+ * @param[in/out] session Member session
+ * @param[in] directory Path to a directory
+ */
+void
+load_member_session_next (struct GNUNET_MESSENGER_MemberSession *session, 
const char *directory);
+
+/**
+ * Saves data from a member <i>session</i> into a <i>directory</i> which can be
+ * load to restore the member session completely.
+ *
+ * @param[in] session Member session
+ * @param[in] directory Path to a directory
+ */
+void
+save_member_session (struct GNUNET_MESSENGER_MemberSession *session, const 
char *directory);
+
+#endif //GNUNET_SERVICE_MESSENGER_MEMBER_SESSION_H
diff --git a/src/messenger/gnunet-service-messenger_member_store.c 
b/src/messenger/gnunet-service-messenger_member_store.c
new file mode 100644
index 000000000..2925965d4
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_member_store.c
@@ -0,0 +1,250 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_member_store.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_member_store.h"
+
+#include "gnunet-service-messenger_member.h"
+#include "gnunet-service-messenger_service.h"
+#include "gnunet-service-messenger_room.h"
+
+void
+init_member_store (struct GNUNET_MESSENGER_MemberStore *store, struct 
GNUNET_MESSENGER_SrvRoom *room)
+{
+  GNUNET_assert ((store) && (room));
+
+  store->room = room;
+  store->members = GNUNET_CONTAINER_multishortmap_create(8, GNUNET_NO);
+}
+
+static int
+iterate_destroy_members (void *cls, const struct GNUNET_ShortHashCode *key, 
void *value)
+{
+  struct GNUNET_MESSENGER_Member *member = value;
+  destroy_member(member);
+  return GNUNET_YES;
+}
+
+void
+clear_member_store (struct GNUNET_MESSENGER_MemberStore *store)
+{
+  GNUNET_assert ((store) && (store->members));
+
+  GNUNET_CONTAINER_multishortmap_iterate (store->members, 
iterate_destroy_members, NULL);
+  GNUNET_CONTAINER_multishortmap_destroy (store->members);
+}
+
+
+struct GNUNET_MESSENGER_ContactStore*
+get_member_contact_store (struct GNUNET_MESSENGER_MemberStore *store)
+{
+  GNUNET_assert ((store) && (store->room));
+
+  struct GNUNET_MESSENGER_SrvRoom *room = store->room;
+
+  return get_service_contact_store(room->service);
+}
+
+const struct GNUNET_HashCode*
+get_member_store_key (const struct GNUNET_MESSENGER_MemberStore *store)
+{
+  GNUNET_assert (store);
+
+  return get_room_key((const struct GNUNET_MESSENGER_SrvRoom*) store->room);
+}
+
+static int
+callback_scan_for_members (void *cls, const char *filename)
+{
+  struct GNUNET_MESSENGER_MemberStore *store = cls;
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_YES))
+  {
+    char *directory;
+
+    GNUNET_asprintf (&directory, "%s%c", filename, DIR_SEPARATOR);
+
+    load_member(store, directory);
+
+    GNUNET_free(directory);
+  }
+
+  return GNUNET_OK;
+}
+
+static int
+iterate_load_next_member_sessions (void *cls, const struct 
GNUNET_ShortHashCode *id, void *value)
+{
+  const char *sync_dir = cls;
+
+  struct GNUNET_MESSENGER_Member *member = value;
+
+  if (!member)
+    return GNUNET_YES;
+
+  char *member_dir;
+  GNUNET_asprintf (&member_dir, "%s%s%c", sync_dir, GNUNET_sh2s(id), 
DIR_SEPARATOR);
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (member_dir, GNUNET_YES))
+    load_member_next_sessions (member, member_dir);
+
+  GNUNET_free(member_dir);
+  return GNUNET_YES;
+}
+
+static int
+iterate_sync_member_contacts (void *cls, const struct GNUNET_ShortHashCode 
*id, void *value)
+{
+  struct GNUNET_MESSENGER_Member *member = value;
+
+  if (!member)
+    return GNUNET_YES;
+
+  sync_member_contacts (member);
+  return GNUNET_YES;
+}
+
+void
+load_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char 
*directory)
+{
+  GNUNET_assert ((store) && (directory));
+
+  char *scan_dir;
+  GNUNET_asprintf (&scan_dir, "%s%s%c", directory, "members", DIR_SEPARATOR);
+
+  if (GNUNET_OK == GNUNET_DISK_directory_test (scan_dir, GNUNET_YES))
+    GNUNET_DISK_directory_scan (scan_dir, callback_scan_for_members, store);
+
+  GNUNET_CONTAINER_multishortmap_iterate(store->members, 
iterate_load_next_member_sessions, scan_dir);
+  GNUNET_CONTAINER_multishortmap_iterate(store->members, 
iterate_sync_member_contacts, NULL);
+
+  GNUNET_free(scan_dir);
+}
+
+static int
+iterate_save_members (void *cls, const struct GNUNET_ShortHashCode *id, void 
*value)
+{
+  const char *save_dir = cls;
+
+  struct GNUNET_MESSENGER_Member *member = value;
+
+  if (!member)
+    return GNUNET_YES;
+
+  char *member_dir;
+  GNUNET_asprintf (&member_dir, "%s%s%c", save_dir, GNUNET_sh2s(id), 
DIR_SEPARATOR);
+
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (member_dir, GNUNET_NO)) ||
+      (GNUNET_OK == GNUNET_DISK_directory_create (member_dir)))
+    save_member(member, member_dir);
+
+  GNUNET_free(member_dir);
+  return GNUNET_YES;
+}
+
+void
+save_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char 
*directory)
+{
+  GNUNET_assert ((store) && (directory));
+
+  char* save_dir;
+  GNUNET_asprintf (&save_dir, "%s%s%c", directory, "members", DIR_SEPARATOR);
+
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (save_dir, GNUNET_NO)) ||
+      (GNUNET_OK == GNUNET_DISK_directory_create (save_dir)))
+    GNUNET_CONTAINER_multishortmap_iterate(store->members, 
iterate_save_members, save_dir);
+
+  GNUNET_free(save_dir);
+}
+
+struct GNUNET_MESSENGER_Member*
+get_store_member (const struct GNUNET_MESSENGER_MemberStore *store, const 
struct GNUNET_ShortHashCode *id)
+{
+  GNUNET_assert ((store) && (store->members) && (id));
+
+  return GNUNET_CONTAINER_multishortmap_get (store->members, id);
+}
+
+struct GNUNET_MESSENGER_Member*
+get_store_member_of (struct GNUNET_MESSENGER_MemberStore *store, const struct 
GNUNET_MESSENGER_Message *message)
+{
+  if ((GNUNET_MESSENGER_KIND_INFO == message->header.kind) ||
+      (GNUNET_MESSENGER_KIND_JOIN == message->header.kind))
+    return add_store_member(store, &(message->header.sender_id));
+  else
+    return get_store_member(store, &(message->header.sender_id));
+}
+
+struct GNUNET_MESSENGER_Member*
+add_store_member (struct GNUNET_MESSENGER_MemberStore *store, const struct 
GNUNET_ShortHashCode *id)
+{
+  GNUNET_assert ((store) && (store->members));
+
+  struct GNUNET_MESSENGER_Member *member = id? get_store_member(store, id) : 
NULL;
+
+  if (member)
+    return member;
+
+  member = create_member(store, id);
+
+  if (!member)
+    return NULL;
+
+  if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put (store->members, 
get_member_id(member), member,
+                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+  {
+    destroy_member(member);
+    return NULL;
+  }
+
+  return member;
+}
+
+struct GNUNET_MESSENGER_ClosureIterateMembers {
+  GNUNET_MESSENGER_MemberIteratorCallback it;
+  void *cls;
+};
+
+static int
+iterate_store_members_it (void *cls, const struct GNUNET_ShortHashCode *key, 
void *value)
+{
+  struct GNUNET_MESSENGER_ClosureIterateMembers *iterate = cls;
+  struct GNUNET_MESSENGER_Member *member = value;
+
+  return iterate_member_sessions(member, iterate->it, iterate->cls);
+}
+
+int
+iterate_store_members (struct GNUNET_MESSENGER_MemberStore *store, 
GNUNET_MESSENGER_MemberIteratorCallback it,
+                       void* cls)
+{
+  GNUNET_assert ((store) && (store->members) && (it));
+
+  struct GNUNET_MESSENGER_ClosureIterateMembers iterate;
+
+  iterate.it = it;
+  iterate.cls = cls;
+
+  return GNUNET_CONTAINER_multishortmap_iterate(store->members, 
iterate_store_members_it, &iterate);
+}
diff --git a/src/messenger/gnunet-service-messenger_member_store.h 
b/src/messenger/gnunet-service-messenger_member_store.h
new file mode 100644
index 000000000..859e4683d
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_member_store.h
@@ -0,0 +1,151 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_member_store.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MEMBER_STORE_H
+#define GNUNET_SERVICE_MESSENGER_MEMBER_STORE_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_identity_service.h"
+#include "messenger_api_message.h"
+
+struct GNUNET_MESSENGER_SrvRoom;
+
+struct GNUNET_MESSENGER_Member;
+struct GNUNET_MESSENGER_MemberSession;
+
+struct GNUNET_MESSENGER_MemberStore
+{
+  struct GNUNET_MESSENGER_SrvRoom *room;
+
+  struct GNUNET_CONTAINER_MultiShortmap *members;
+};
+
+typedef int (*GNUNET_MESSENGER_MemberIteratorCallback) (
+    void *cls,
+    const struct GNUNET_IDENTITY_PublicKey *public_key,
+    struct GNUNET_MESSENGER_MemberSession *session);
+
+/**
+ * Initializes a member <i>store</i> as fully empty connected to a <i>room</i>.
+ *
+ * @param[out] store Member store
+ * @param room Room
+ */
+void
+init_member_store (struct GNUNET_MESSENGER_MemberStore *store, struct 
GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Clears a member <i>store</i>, wipes its content and deallocates its memory.
+ *
+ * @param[in/out] store Member store
+ */
+void
+clear_member_store (struct GNUNET_MESSENGER_MemberStore *store);
+
+/**
+ * Returns the used contact store of a given member <i>store</i>.
+ *
+ * @param[in/out] store Member store
+ * @return Contact store
+ */
+struct GNUNET_MESSENGER_ContactStore*
+get_member_contact_store (struct GNUNET_MESSENGER_MemberStore *store);
+
+/**
+ * Returns the shared secret you need to access a room of the <i>store</i>.
+ *
+ * @param[in] store Member store
+ * @return Shared secret
+ */
+const struct GNUNET_HashCode*
+get_member_store_key (const struct GNUNET_MESSENGER_MemberStore *store);
+
+/**
+ * Loads members from a directory into a member <i>store</i>.
+ *
+ * @param[out] store Member store
+ * @param[in] directory Path to a directory
+ */
+void
+load_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char 
*directory);
+
+/**
+ * Saves members from a member <i>store</i> into a directory.
+ *
+ * @param[in] store Member store
+ * @param[in] directory Path to a directory
+ */
+void
+save_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char 
*directory);
+
+/**
+ * Returns the member in a <i>store</i> identified by a given <i>id</i>. If 
the <i>store</i>
+ * does not contain a member with the given <i>id</i>, NULL gets returned.
+ *
+ * @param[in] store Member store
+ * @param[in] id Member id
+ * @return Member or NULL
+ */
+struct GNUNET_MESSENGER_Member*
+get_store_member (const struct GNUNET_MESSENGER_MemberStore *store, const 
struct GNUNET_ShortHashCode *id);
+
+/**
+ * Returns the member of a <i>store</i> using a sender id of a given 
<i>message</i>.
+ * If the member does not provide a matching session, NULL gets returned.
+ *
+ * @param[in/out] store Member store
+ * @param[in] message Message
+ * @return Member or NULL
+ */
+struct GNUNET_MESSENGER_Member*
+get_store_member_of (struct GNUNET_MESSENGER_MemberStore *store, const struct 
GNUNET_MESSENGER_Message *message);
+
+/**
+ * Adds a member to a <i>store</i> under a specific <i>id</i> and returns it 
on success.
+ *
+ * @param[in/out] store Member store
+ * @param[in] id Member id
+ * @return Member or NULL
+ */
+struct GNUNET_MESSENGER_Member*
+add_store_member (struct GNUNET_MESSENGER_MemberStore *store, const struct 
GNUNET_ShortHashCode *id);
+
+/**
+ * Iterate through all member sessions currently connected to the members of 
the given
+ * member <i>store</i> and call the provided iterator callback with a selected 
closure.
+ * The function will return the amount of members it iterated through.
+ *
+ * @param[in/out] store Member store
+ * @param[in] it Iterator callback
+ * @param[in/out] cls Closure
+ * @return Amount of members iterated through
+ */
+int
+iterate_store_members (struct GNUNET_MESSENGER_MemberStore *store, 
GNUNET_MESSENGER_MemberIteratorCallback it,
+                       void* cls);
+
+#endif //GNUNET_SERVICE_MESSENGER_MEMBER_STORE_H
diff --git a/src/messenger/gnunet-service-messenger_message_handle.c 
b/src/messenger/gnunet-service-messenger_message_handle.c
index 1652435c8..c22e51fbf 100644
--- a/src/messenger/gnunet-service-messenger_message_handle.c
+++ b/src/messenger/gnunet-service-messenger_message_handle.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -25,75 +25,59 @@
 
 #include "gnunet-service-messenger_message_handle.h"
 
-void
-handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+static void
+handle_session_switch (struct GNUNET_MESSENGER_MemberSession *session,
+                       const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, 
&(message->header.sender_id));
-
-  if (!contact)
-    add_room_contact (room, &(message->header.sender_id), 
&(message->body.join.key));
-
-  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
-
-  if (!info)
-  {
-    info = GNUNET_new(struct GNUNET_MESSENGER_MemberInfo);
-
-    info->access = GNUNET_MESSENGER_MEMBER_UNKNOWN;
-    init_list_messages (&(info->session_messages));
-  }
-  else
-    clear_list_messages (&(info->session_messages));
+  struct GNUNET_MESSENGER_MemberSession *next = switch_member_session(session, 
message, hash);
 
-  if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_put (room->member_infos, 
&(message->header.sender_id), info,
-                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
-    add_to_list_messages (&(info->session_messages), hash);
+  if (next != session)
+    add_member_session(next->member, next);
 }
 
 void
-handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Member (%s) joins room (%s).\n",
+             GNUNET_sh2s (&(message->header.sender_id)), 
GNUNET_h2s(get_room_key(room)));
 
-  if (info)
-    clear_list_messages (&(info->session_messages));
+  if (GNUNET_OK != reset_member_session(session, hash))
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Resetting member session failed!\n");
 }
 
 void
-handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, 
&(message->header.sender_id));
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Member (%s) leaves room (%s).\n",
+             GNUNET_sh2s (&(message->header.sender_id)), 
GNUNET_h2s(get_room_key(room)));
 
-  if (contact)
-    set_contact_name (contact, message->body.name.name);
-
-  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
-
-  if (info)
-    add_to_list_messages (&(info->session_messages), hash);
+  close_member_session(session);
 }
 
 void
-handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, 
&(message->header.sender_id));
+  struct GNUNET_MESSENGER_Contact *contact = 
get_member_session_contact(session);
 
-  if (contact)
-    swap_service_contact_by_pubkey (room->service, contact, 
&(message->body.key.key));
+  if (!contact)
+    return;
 
-  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
+  set_contact_name (contact, message->body.name.name);
+}
 
-  if (info)
-    add_to_list_messages (&(info->session_messages), hash);
+void
+handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                    const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
+{
+  handle_session_switch (session, message, hash);
 }
 
 void
-handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
   if (GNUNET_NO == contains_list_tunnels (&(room->basement), 
&(message->body.peer.peer)))
     add_to_list_tunnels (&(room->basement), &(message->body.peer.peer));
@@ -103,20 +87,15 @@ handle_message_peer (struct GNUNET_MESSENGER_SrvRoom 
*room, struct GNUNET_MESSEN
 }
 
 void
-handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
-
-  if (info)
-    add_to_list_messages (&(info->session_messages), hash);
-
-  switch_room_member_id (room, &(message->header.sender_id), 
&(message->body.id.id), hash);
+  handle_session_switch (session, message, hash);
 }
 
 void
-handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
   struct GNUNET_MESSENGER_ListTunnel *element = find_list_tunnels 
(&(room->basement), &(message->body.peer.peer), NULL);
 
@@ -128,3 +107,16 @@ handle_message_miss (struct GNUNET_MESSENGER_SrvRoom 
*room, struct GNUNET_MESSEN
   if (room->peer_message)
     rebuild_room_basement_structure (room);
 }
+
+void
+handle_message_delete (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                       const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_TIME_Relative delay = GNUNET_TIME_relative_ntoh 
(message->body.delete.delay);
+  struct GNUNET_TIME_Absolute action = GNUNET_TIME_absolute_ntoh 
(message->header.timestamp);
+
+  action = GNUNET_TIME_absolute_add (action, delay);
+  delay = GNUNET_TIME_absolute_get_difference (GNUNET_TIME_absolute_get (), 
action);
+
+  delete_room_message (room, session, &(message->body.delete.hash), delay);
+}
diff --git a/src/messenger/gnunet-service-messenger_message_handle.h 
b/src/messenger/gnunet-service-messenger_message_handle.h
index d091e1d11..844142b77 100644
--- a/src/messenger/gnunet-service-messenger_message_handle.h
+++ b/src/messenger/gnunet-service-messenger_message_handle.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -31,6 +31,7 @@
 
 #include "gnunet-service-messenger_message_kind.h"
 
+#include "gnunet-service-messenger_member_session.h"
 #include "gnunet-service-messenger_tunnel.h"
 #include "messenger_api_message.h"
 
@@ -38,91 +39,104 @@
  * Handles a received or sent join message to make changes of current member 
information.
  * (add matching member and clear member info)
  *
- * @param room Room of the message
- * @param tunnel Receiving/sending connection (may be NULL)
- * @param message JOIN-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message JOIN-Message
+ * @param[in] hash Hash of the message
  */
 void
-handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received or sent leave message to make changes of current member 
information.
  * (remove matching member and clear member info)
  *
- * @param room Room of the message
- * @param tunnel Receiving/sending connection (may be NULL)
- * @param message LEAVE-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message LEAVE-Message
+ * @param[in] hash Hash of the message
  */
 void
-handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received or sent name message to rename a current member.
  * (change name of matching member)
  *
- * @param room Room of the message
- * @param tunnel Receiving/sending connection (may be NULL)
- * @param message NAME-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message NAME-Message
+ * @param[in] hash Hash of the message
  */
 void
-handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received or sent key message to change the key of a member and 
rearrange the contacts accordingly.
  * (move the member in the contacts and change its key)
  *
- * @param room Room of the message
- * @param tunnel Receiving/sending connection (may be NULL)
- * @param message KEY-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message KEY-Message
+ * @param[in] hash Hash of the message
  */
 void
-handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                    const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received or sent peer message to make changes of the basement in 
the room.
  * (add a new peer to the basement and restructure connections based on 
updated list of peers)
  *
- * @param room Room of the message
- * @param tunnel Receiving/sending connection (may be NULL)
- * @param message PEER-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message PEER-Message
+ * @param[in] hash Hash of the message
  */
 void
-handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received or sent id message to change a members id.
  * (change id of matching member)
  *
- * @param room Room of the message
- * @param tunnel Receiving/sending connection (may be NULL)
- * @param message ID-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message ID-Message
+ * @param[in] hash Hash of the message
  */
 void
-handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received or sent miss message to drop a peer from the basement in 
the room.
  * (remove a peer from the basement and restructure connections based on 
updated list of peers)
  *
- * @param room Room of the message
- * @param tunnel Receiving/sending connection (may be NULL)
- * @param message MISS-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message MISS-Message
+ * @param[in] hash Hash of the message
  */
 void
-handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a received or sent delete message to delete a specific message from 
the store.
+ * (remove a message from the store of a room under a given delay)
+ *
+ * @param[in/out] room Room of the message
+ * @param[in/out] session Member session
+ * @param[in] message DELETE-Message
+ * @param[in] hash Hash of the message
+ */
+void
+handle_message_delete (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                       const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 #endif //GNUNET_SERVICE_MESSENGER_MESSAGE_HANDLE_H
diff --git a/src/messenger/gnunet-service-messenger_message_kind.c 
b/src/messenger/gnunet-service-messenger_message_kind.c
index 9c829fe09..ef9bbfd2e 100644
--- a/src/messenger/gnunet-service-messenger_message_kind.c
+++ b/src/messenger/gnunet-service-messenger_message_kind.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -24,11 +24,15 @@
  */
 
 #include "gnunet-service-messenger_message_kind.h"
-#include "gnunet-service-messenger_util.h"
+
+#include "messenger_api_util.h"
 
 struct GNUNET_MESSENGER_Message*
-create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct 
GNUNET_CONTAINER_MultiShortmap *members)
+create_message_info (const struct GNUNET_MESSENGER_Ego *ego)
 {
+  if (!ego)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_INFO);
 
   if (!message)
@@ -36,18 +40,17 @@ create_message_info (struct GNUNET_MESSENGER_Ego *ego, 
struct GNUNET_CONTAINER_M
 
   GNUNET_memcpy(&(message->body.info.host_key), &(ego->pub), sizeof(ego->pub));
 
-  if (GNUNET_YES == generate_free_member_id (&(message->body.info.unique_id), 
members))
-    return message;
-  else
-  {
-    destroy_message (message);
-    return NULL;
-  }
+  message->body.info.messenger_version = GNUNET_MESSENGER_VERSION;
+
+  return message;
 }
 
 struct GNUNET_MESSENGER_Message*
-create_message_join (struct GNUNET_MESSENGER_Ego *ego)
+create_message_join (const struct GNUNET_MESSENGER_Ego *ego)
 {
+  if (!ego)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_JOIN);
 
   if (!message)
@@ -67,6 +70,9 @@ create_message_leave ()
 struct GNUNET_MESSENGER_Message*
 create_message_name (const char *name)
 {
+  if (!name)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_NAME);
 
   if (!message)
@@ -79,6 +85,9 @@ create_message_name (const char *name)
 struct GNUNET_MESSENGER_Message*
 create_message_key (const struct GNUNET_IDENTITY_PrivateKey *key)
 {
+  if (!key)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_KEY);
 
   if (!message)
@@ -91,6 +100,9 @@ create_message_key (const struct GNUNET_IDENTITY_PrivateKey 
*key)
 struct GNUNET_MESSENGER_Message*
 create_message_peer (const struct GNUNET_MESSENGER_Service *service)
 {
+  if (!service)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_PEER);
 
   if (!message)
@@ -108,6 +120,9 @@ create_message_peer (const struct GNUNET_MESSENGER_Service 
*service)
 struct GNUNET_MESSENGER_Message*
 create_message_id (const struct GNUNET_ShortHashCode *unique_id)
 {
+  if (!unique_id)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_ID);
 
   if (!message)
@@ -121,6 +136,9 @@ create_message_id (const struct GNUNET_ShortHashCode 
*unique_id)
 struct GNUNET_MESSENGER_Message*
 create_message_miss (const struct GNUNET_PeerIdentity *peer)
 {
+  if (!peer)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_MISS);
 
   if (!message)
@@ -136,6 +154,9 @@ create_message_miss (const struct GNUNET_PeerIdentity *peer)
 struct GNUNET_MESSENGER_Message*
 create_message_merge (const struct GNUNET_HashCode *previous)
 {
+  if (!previous)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_MERGE);
 
   if (!message)
@@ -149,6 +170,9 @@ create_message_merge (const struct GNUNET_HashCode 
*previous)
 struct GNUNET_MESSENGER_Message*
 create_message_request (const struct GNUNET_HashCode *hash)
 {
+  if (!hash)
+    return NULL;
+
   struct GNUNET_HashCode zero;
   memset (&zero, 0, sizeof(zero));
 
@@ -168,6 +192,9 @@ create_message_request (const struct GNUNET_HashCode *hash)
 struct GNUNET_MESSENGER_Message*
 create_message_invite (const struct GNUNET_PeerIdentity *door, const struct 
GNUNET_HashCode *key)
 {
+  if ((!door) || (!key))
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_INVITE);
 
   if (!message)
@@ -182,6 +209,9 @@ create_message_invite (const struct GNUNET_PeerIdentity 
*door, const struct GNUN
 struct GNUNET_MESSENGER_Message*
 create_message_text (const char *text)
 {
+  if (!text)
+    return NULL;
+
   struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_TEXT);
 
   if (!message)
@@ -190,3 +220,20 @@ create_message_text (const char *text)
   message->body.text.text = GNUNET_strdup(text);
   return message;
 }
+
+struct GNUNET_MESSENGER_Message*
+create_message_delete (const struct GNUNET_HashCode *hash, const struct 
GNUNET_TIME_Relative delay)
+{
+  if (!hash)
+    return NULL;
+
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_DELETE);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_memcpy(&(message->body.delete.hash), hash, sizeof(struct 
GNUNET_HashCode));
+  message->body.delete.delay = GNUNET_TIME_relative_hton (delay);
+
+  return message;
+}
diff --git a/src/messenger/gnunet-service-messenger_message_kind.h 
b/src/messenger/gnunet-service-messenger_message_kind.h
index dd89d0b2f..c098868c0 100644
--- a/src/messenger/gnunet-service-messenger_message_kind.h
+++ b/src/messenger/gnunet-service-messenger_message_kind.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -30,31 +30,32 @@
 #include "gnunet_container_lib.h"
 #include "gnunet_crypto_lib.h"
 #include "gnunet_identity_service.h"
+#include "gnunet_time_lib.h"
 
 #include "messenger_api_message.h"
 #include "gnunet-service-messenger_service.h"
 #include "messenger_api_ego.h"
 
 /**
- * Creates and allocates a new info message containing the hosts public key 
and a newly generated unique member id.
+ * Creates and allocates a new info message containing the hosts EGO public 
key and a newly generated unique member id.
  * (all values are stored as copy)
  *
- * @param ego EGO of the host
- * @param members Map of all assigned member ids
+ * @param[in] ego EGO of the host
+ * @param[in] members Map of all assigned member ids
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
-create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct 
GNUNET_CONTAINER_MultiShortmap *members);
+create_message_info (const struct GNUNET_MESSENGER_Ego *ego);
 
 /**
- * Creates and allocates a new join message containing the clients public key.
+ * Creates and allocates a new join message containing the clients EGO public 
key.
  * (all values are stored as copy)
  *
- * @param ego EGO of the client
+ * @param[in] ego EGO of the client
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
-create_message_join (struct GNUNET_MESSENGER_Ego *ego);
+create_message_join (const struct GNUNET_MESSENGER_Ego *ego);
 
 /**
  * Creates and allocates a new leave message.
@@ -68,17 +69,17 @@ create_message_leave ();
  * Creates and allocates a new name message containing the <i>name</i> to 
change to.
  * (all values are stored as copy)
  *
- * @param name New name
+ * @param[in] name New name
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
 create_message_name (const char *name);
 
 /**
- * Creates and allocates a new key message containing the public key to change 
to derived
+ * Creates and allocates a new key message containing the public <i>key</i> to 
change to derived
  * from its private counterpart. (all values are stored as copy)
  *
- * @param key Private key of EGO
+ * @param[in] key Private key of EGO
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
@@ -88,7 +89,7 @@ create_message_key (const struct GNUNET_IDENTITY_PrivateKey 
*key);
  * Creates and allocates a new peer message containing a services peer 
identity.
  * (all values are stored as copy)
  *
- * @param service Service
+ * @param[in] service Service
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
@@ -98,38 +99,38 @@ create_message_peer (const struct GNUNET_MESSENGER_Service 
*service);
  * Creates and allocates a new id message containing the unique member id to 
change to.
  * (all values are stored as copy)
  *
- * @param unique_id Unique member id
+ * @param[in] unique_id Unique member id
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
 create_message_id (const struct GNUNET_ShortHashCode *unique_id);
 
 /**
- * Creates and allocates a new miss message containing the missing peer 
identity.
+ * Creates and allocates a new miss message containing the missing <i>peer</i> 
identity.
  * (all values are stored as copy)
  *
- * @param peer Missing peer identity
+ * @param[in] peer Missing peer identity
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
 create_message_miss (const struct GNUNET_PeerIdentity *peer);
 
 /**
- * Creates and allocates a new merge message containing the hash of a second 
previous message
+ * Creates and allocates a new merge message containing the hash of a second 
<i>previous</i> message
  * besides the regular previous message mentioned in a messages header.
  * (all values are stored as copy)
  *
- * @param previous Hash of message
+ * @param[in] previous Hash of message
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
 create_message_merge (const struct GNUNET_HashCode *previous);
 
 /**
- * Creates and allocates a new request message containing the hash of a 
missing message.
+ * Creates and allocates a new request message containing the <i>hash</i> of a 
missing message.
  * (all values are stored as copy)
  *
- * @param hash Hash of message
+ * @param[in] hash Hash of message
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
@@ -140,21 +141,32 @@ create_message_request (const struct GNUNET_HashCode 
*hash);
  * to a room using a given <i>key</i> as shared secret for communication.
  * (all values are stored as copy)
  *
- * @param door Peer identity
- * @param key Shared secret of a room
+ * @param[in] door Peer identity
+ * @param[in] key Shared secret of a room
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
 create_message_invite (const struct GNUNET_PeerIdentity *door, const struct 
GNUNET_HashCode *key);
 
 /**
- * Creates and allocates a new text message containing a string representing 
text.
+ * Creates and allocates a new <i>text message containing a string 
representing text.
  * (all values are stored as copy)
  *
- * @param text Text
+ * @param[in] text Text
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
 create_message_text (const char *text);
 
+/**
+ * Creates and allocates a new delete message containing the <i>hash</i> of a 
message to delete after a specific <i>delay</i>.
+ * (all values are stored as copy)
+ *
+ * @param[in] hash Hash of message
+ * @param[in] delay Delay of deletion
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_delete (const struct GNUNET_HashCode *hash, const struct 
GNUNET_TIME_Relative delay);
+
 #endif //GNUNET_SERVICE_MESSENGER_MESSAGE_KIND_H
diff --git a/src/messenger/gnunet-service-messenger_message_recv.c 
b/src/messenger/gnunet-service-messenger_message_recv.c
index aa28a36ea..8aab805d2 100644
--- a/src/messenger/gnunet-service-messenger_message_recv.c
+++ b/src/messenger/gnunet-service-messenger_message_recv.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -24,137 +24,64 @@
  */
 
 #include "gnunet-service-messenger_message_recv.h"
-#include "gnunet-service-messenger_message_handle.h"
 
-void
-recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
-{
-  int conflict = GNUNET_CONTAINER_multishortmap_contains (room->members, 
&(message->body.info.unique_id));
-
-  if (GNUNET_NO == conflict)
-  {
-    struct GNUNET_MESSENGER_Message *sync_message = create_message_id 
(&(message->body.info.unique_id));
-    struct GNUNET_HashCode sync_hash;
-
-    send_room_message_ext (room, room->host, sync_message, &sync_hash, tunnel);
-    destroy_message (sync_message);
-
-    switch_room_member_id (room, get_room_host_id (room), 
&(message->body.info.unique_id), NULL);
-
-    change_room_host_id (room, &(message->body.info.unique_id));
-  }
-
-  if (!tunnel->contact_id)
-    tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
-
-  GNUNET_memcpy(tunnel->contact_id, &(message->header.sender_id), 
sizeof(struct GNUNET_ShortHashCode));
-
-  struct GNUNET_ShortHashCode original_id;
-
-  if (GNUNET_YES == conflict)
-  {
-    GNUNET_memcpy(&original_id, get_room_host_id (room), sizeof(struct 
GNUNET_ShortHashCode));
-
-    change_room_host_id (room, &(message->body.info.unique_id));
-  }
-
-  {
-    struct GNUNET_MESSENGER_Message *join_message = create_message_join 
(room->host->ego);
-    struct GNUNET_HashCode join_hash;
-
-    send_tunnel_message (tunnel, room->host, join_message, &join_hash);
-    destroy_message (join_message);
-  }
-
-  if ((GNUNET_YES == conflict) && (0 != GNUNET_memcmp(&original_id, 
get_room_host_id (room))))
-  {
-    struct GNUNET_MESSENGER_Message *sync_message = create_message_id 
(&original_id);
-    struct GNUNET_HashCode sync_hash;
-
-    send_tunnel_message (tunnel, room->host, sync_message, &sync_hash);
-    destroy_message (sync_message);
-  }
-}
-
-struct GNUNET_MESSENGER_MemberInfoSpread
-{
-  struct GNUNET_MESSENGER_SrvRoom *room;
-  struct GNUNET_MESSENGER_SrvTunnel *tunnel;
-};
+#include "gnunet-service-messenger_operation.h"
 
 static int
-iterate_send_member_infos (void *cls, const struct GNUNET_ShortHashCode *key, 
void *value)
+iterate_forward_members (void *cls, const struct GNUNET_IDENTITY_PublicKey 
*public_key,
+                         struct GNUNET_MESSENGER_MemberSession *session)
 {
-  struct GNUNET_MESSENGER_MemberInfo *info = value;
-  struct GNUNET_MESSENGER_MemberInfoSpread *spread = cls;
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
+  struct GNUNET_MESSENGER_SrvRoom *room = tunnel->room;
 
-  struct GNUNET_MESSENGER_ListMessage *element = info->session_messages.head;
+  struct GNUNET_MESSENGER_ListMessage *element;
 
-  while (element)
-  {
-    const struct GNUNET_MESSENGER_Message *message = get_room_message 
(spread->room, spread->room->host,
-                                                                       
&(element->hash), GNUNET_NO);
-
-    if (message)
-      forward_tunnel_message (spread->tunnel, message, &(element->hash));
-
-    element = element->next;
-  }
+  for (element = session->messages.head; element; element = element->next)
+    forward_tunnel_message(tunnel, get_room_message(room, NULL, 
&(element->hash), GNUNET_NO), &(element->hash));
 
   return GNUNET_YES;
 }
 
-void
-recv_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+int
+recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  const struct GNUNET_MESSENGER_Message *info_msg = get_room_message (room, 
room->host, &(message->header.previous),
-                                                                      
GNUNET_NO);
+  const uint32_t version = get_tunnel_messenger_version(tunnel);
 
-  if ((info_msg) && (0 == GNUNET_memcmp(&(info_msg->header.sender_id), 
get_room_host_id (room)))
-      && (GNUNET_MESSENGER_KIND_INFO == info_msg->header.kind))
+  if (GNUNET_OK != update_tunnel_messenger_version(tunnel, 
message->body.info.messenger_version))
   {
-    struct GNUNET_MESSENGER_MemberInfoSpread spread;
+    disconnect_tunnel(tunnel);
+    return GNUNET_NO;
+  }
 
-    spread.room = room;
+  if (version == get_tunnel_messenger_version(tunnel))
+    return GNUNET_NO;
 
-    if ((tunnel) && (tunnel->contact_id) && (0 == 
GNUNET_memcmp(tunnel->contact_id, &(message->header.sender_id))))
-      spread.tunnel = tunnel;
-    else
-      spread.tunnel = find_room_tunnel_to (room, &(message->header.sender_id));
+  if (room->host)
+  {
+    const struct GNUNET_MESSENGER_Ego *ego = get_handle_ego(room->host);
 
-    if (spread.tunnel)
-      GNUNET_CONTAINER_multishortmap_iterate (room->member_infos, 
iterate_send_member_infos, &spread);
+    send_tunnel_message (tunnel, room->host, create_message_info(ego));
   }
 
-  handle_message_join (room, tunnel, message, hash);
-}
+  struct GNUNET_PeerIdentity peer;
+  get_tunnel_peer_identity(tunnel, &peer);
 
-void
-recv_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
-{
-  handle_message_leave (room, tunnel, message, hash);
-}
+  if (GNUNET_YES != contains_list_tunnels(&(room->basement), &peer))
+  {
+    struct GNUNET_MESSENGER_MemberStore *member_store = 
get_room_member_store(room);
 
-void
-recv_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
-{
-  handle_message_name (room, tunnel, message, hash);
-}
+    iterate_store_members(member_store, iterate_forward_members, tunnel);
+  }
 
-void
-recv_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                  struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
-{
-  handle_message_key (room, tunnel, message, hash);
+  check_room_peer_status(room, tunnel);
+
+  return GNUNET_NO;
 }
 
-void
+int
 recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
   struct GNUNET_PeerIdentity peer;
   GNUNET_PEER_resolve (tunnel->peer, &peer);
@@ -164,41 +91,48 @@ recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, 
struct GNUNET_MESSENGE
     if (!tunnel->peer_message)
       tunnel->peer_message = GNUNET_new(struct GNUNET_HashCode);
 
-    GNUNET_memcpy(tunnel->peer_message, hash, sizeof(struct GNUNET_HashCode));
-
-    if (!tunnel->contact_id)
-      tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
-
-    GNUNET_memcpy(tunnel->contact_id, &(message->header.sender_id), 
sizeof(struct GNUNET_ShortHashCode));
+    GNUNET_memcpy(tunnel->peer_message, &hash, sizeof(hash));
   }
 
-  handle_message_peer (room, tunnel, message, hash);
+  return GNUNET_YES;
 }
 
-void
-recv_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                 struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+int
+recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  if ((tunnel->contact_id) && (0 == GNUNET_memcmp(tunnel->contact_id, 
&(message->header.sender_id))))
-    GNUNET_memcpy(tunnel->contact_id, &(message->body.id.id), sizeof(struct 
GNUNET_ShortHashCode));
+  const struct GNUNET_MESSENGER_Message *msg = get_room_message (
+      room, NULL, &(message->body.request.hash), GNUNET_NO
+  );
 
-  handle_message_id (room, tunnel, message, hash);
-}
+  if (!msg)
+  {
+    struct GNUNET_MESSENGER_OperationStore *operation_store = 
get_room_operation_store(room);
 
-void
-recv_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
-{
-  handle_message_miss (room, tunnel, message, hash);
-}
+    use_store_operation(
+        operation_store,
+        &(message->body.request.hash),
+        GNUNET_MESSENGER_OP_REQUEST,
+        GNUNET_MESSENGER_REQUEST_DELAY
+    );
 
-void
-recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
-{
-  const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, 
room->host, &(message->body.request.hash),
-                                                                 GNUNET_NO);
+    return GNUNET_YES;
+  }
+
+  struct GNUNET_MESSENGER_MemberStore *member_store = 
get_room_member_store(room);
+  struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, 
message);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Callback for message (%s)\n", 
GNUNET_h2s (hash));
+
+  if (!member)
+    return GNUNET_NO;
+
+  struct GNUNET_MESSENGER_MemberSession *session = 
get_member_session_of(member, message, hash);
+
+  if ((!session) || (GNUNET_YES != check_member_session_history(session, hash, 
GNUNET_NO)))
+    return GNUNET_NO;
+
+  forward_tunnel_message (tunnel, msg, &(message->body.request.hash));
 
-  if (msg)
-    forward_tunnel_message (tunnel, msg, &(message->body.request.hash));
+  return GNUNET_NO;
 }
diff --git a/src/messenger/gnunet-service-messenger_message_recv.h 
b/src/messenger/gnunet-service-messenger_message_recv.h
index 245612cb0..9cb36c466 100644
--- a/src/messenger/gnunet-service-messenger_message_recv.h
+++ b/src/messenger/gnunet-service-messenger_message_recv.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -29,6 +29,9 @@
 #include "platform.h"
 #include "gnunet_crypto_lib.h"
 
+#include "gnunet-service-messenger_message_kind.h"
+
+#include "gnunet-service-messenger_member_session.h"
 #include "gnunet-service-messenger_tunnel.h"
 #include "messenger_api_message.h"
 
@@ -36,124 +39,43 @@
  * Handles a received info message to change the current member id to the one 
generated by
  * the host connected to. (all current tunnels will be informed about the id 
change)
  *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message INFO-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] tunnel Receiving connection
+ * @param[in] message INFO-Message
+ * @param[in] hash Hash of the message
+ * @return #GNUNET_NO to not forward the message
  */
-void
+int
 recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
-
-/**
- * Handles a received join message to forward all member information to the 
new member if the message was
- * the direct reaction to a previous info message from this peer.
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message JOIN-Message
- * @param hash Hash of the message
- */
-void
-recv_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
-
-/**
- * Handles a received leave message.
- * @see handle_message_leave()
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message LEAVE-Message
- * @param hash Hash of the message
- */
-void
-recv_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
-
-/**
- * Handles a received name message.
- * @see handle_message_name()
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message NAME-Message
- * @param hash Hash of the message
- */
-void
-recv_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
-
-/**
- * Handles a received key message.
- * @see handle_message_key()
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message KEY-Message
- * @param hash Hash of the message
- */
-void
-recv_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                  struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received peer message to link it to its origin tunnel if the peer 
identity matches.
  * (the peer message and the member id can potentially be linked to the tunnel)
  *
- * TODO: This handling will only check the one given tunnel!
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message PEER-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] tunnel Receiving connection
+ * @param[in] message PEER-Message
+ * @param[in] hash Hash of the message
+ * @return #GNUNET_YES to forward the message
  */
-void
+int
 recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
-
-/**
- * Handles a received id message to change the tunnels linked member id if 
necessary.
- * (the tunnels linked member id will be changed if the sender id is matching)
- *
- * TODO: This handling will only check the one given tunnel!
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message ID-Message
- * @param hash Hash of the message
- */
-void
-recv_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                 struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
-
-/**
- * Handles a received miss message.
- * @see handle_message_miss()
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message MISS-Message
- * @param hash Hash of the message
- */
-void
-recv_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a received request message by checking for the requested message 
and forwarding it back
  * if the message was found.
  * (this can also cause this peer to send a new request instead of only 
forwarding the received one)
  *
- * TODO: Requests can cause exponentially more requests!
- *
- * @param room Room of the message
- * @param tunnel Receiving connection
- * @param message REQUEST-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] tunnel Receiving connection
+ * @param[in] message REQUEST-Message
+ * @param[in] hash Hash of the message
+ * @return #GNUNET_YES or #GNUNET_NO depending on required forwarding
  */
-void
+int
 recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 #endif //GNUNET_SERVICE_MESSENGER_MESSAGE_RECV_H
diff --git a/src/messenger/gnunet-service-messenger_message_send.c 
b/src/messenger/gnunet-service-messenger_message_send.c
index 86cf9b888..59bbaea8d 100644
--- a/src/messenger/gnunet-service-messenger_message_send.c
+++ b/src/messenger/gnunet-service-messenger_message_send.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -24,95 +24,38 @@
  */
 
 #include "gnunet-service-messenger_message_send.h"
-#include "gnunet-service-messenger_message_handle.h"
 
-void
-send_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash)
-{
-  if (!tunnel->contact_id)
-  {
-    tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
-
-    GNUNET_memcpy(tunnel->contact_id, &(message->body.info.unique_id), 
sizeof(struct GNUNET_ShortHashCode));
-  }
-  else
-  {
-    disconnect_tunnel (tunnel);
-  }
-}
+#include "gnunet-service-messenger_member.h"
+#include "gnunet-service-messenger_member_session.h"
+#include "gnunet-service-messenger_operation.h"
 
 void
 send_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash)
-{
-  handle_message_join (room, tunnel, message, hash);
-
-  if (room->peer_message)
-  {
-    const struct GNUNET_MESSENGER_Message *peer_message = get_room_message 
(room, handle, room->peer_message,
-                                                                            
GNUNET_NO);
-
-    if ((peer_message) && (tunnel))
-    {
-      forward_tunnel_message (tunnel, peer_message, room->peer_message);
-    }
-  }
-}
-
-void
-send_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                    struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                    const struct GNUNET_HashCode *hash)
-{
-  handle_message_leave (room, tunnel, message, hash);
-}
-
-void
-send_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash)
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  handle_message_name (room, tunnel, message, hash);
-}
-
-void
-send_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                  struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                  const struct GNUNET_HashCode *hash)
-{
-  handle_message_key (room, tunnel, message, hash);
+  check_room_peer_status(room, NULL);
 }
 
 void
 send_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash)
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
   if (!room->peer_message)
-  {
     room->peer_message = GNUNET_new(struct GNUNET_HashCode);
-  }
 
   GNUNET_memcpy(room->peer_message, hash, sizeof(struct GNUNET_HashCode));
-
-  handle_message_peer (room, tunnel, message, hash);
 }
 
 void
-send_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                 struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                 const struct GNUNET_HashCode *hash)
+send_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  handle_message_id (room, tunnel, message, hash);
-}
+  struct GNUNET_MESSENGER_OperationStore *operation_store = 
get_room_operation_store(room);
 
-void
-send_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash)
-{
-  handle_message_miss (room, tunnel, message, hash);
+  use_store_operation(
+      operation_store,
+      &(message->body.request.hash),
+      GNUNET_MESSENGER_OP_REQUEST,
+      GNUNET_MESSENGER_REQUEST_DELAY
+  );
 }
diff --git a/src/messenger/gnunet-service-messenger_message_send.h 
b/src/messenger/gnunet-service-messenger_message_send.h
index c1096205a..8e3ff4495 100644
--- a/src/messenger/gnunet-service-messenger_message_send.h
+++ b/src/messenger/gnunet-service-messenger_message_send.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -29,127 +29,48 @@
 #include "platform.h"
 #include "gnunet_crypto_lib.h"
 
+#include "gnunet-service-messenger_message_kind.h"
+
 #include "gnunet-service-messenger_tunnel.h"
 #include "messenger_api_message.h"
 
-/**
- * Handles a sent info message to setup a tunnels linked member id.
- * (if a tunnel has already got a member id linked to it, the connection will 
be closed)
- *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message INFO-Message
- * @param hash Hash of the message
- */
-void
-send_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash);
-
 /**
  * Handles a sent join message to ensure growth of the decentralized room 
structure.
  * (if the service provides a peer message for this room currently, it will be 
forwarded)
  *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message JOIN-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] handle Sending handle
+ * @param[in] message JOIN-Message
+ * @param[in] hash Hash of the message
  */
 void
 send_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash);
-
-/**
- * Handles a sent leave message.
- * @see handle_message_leave()
- *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message LEAVE-Message
- * @param hash Hash of the message
- */
-void
-send_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                    struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                    const struct GNUNET_HashCode *hash);
-
-/**
- * Handles a sent name message.
- * @see handle_message_name()
- *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message NAME-Message
- * @param hash Hash of the message
- */
-void
-send_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash);
-
-/**
- * Handles a sent key message.
- * @see handle_message_key()
- *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message KEY-Message
- * @param hash Hash of the message
- */
-void
-send_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                  struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                  const struct GNUNET_HashCode *hash);
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
  * Handles a sent peer message to update the rooms peer message of this 
service.
  * (a set peer message indicates this service being a part of the 
decentralized room structure)
  *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message PEER-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] handle Sending handle
+ * @param[in] message PEER-Message
+ * @param[in] hash Hash of the message
  */
 void
 send_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash);
-
-/**
- * Handles a sent id message.
- * @see handle_message_id()
- *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message ID-Message
- * @param hash Hash of the message
- */
-void
-send_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                 struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                 const struct GNUNET_HashCode *hash);
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 /**
- * Handles a sent miss message.
- * @see handle_message_miss()
+ * Handles a sent request message to trigger the request operation for this 
service.
+ * (the request operation will deactivate the possibility of spamming requests)
  *
- * @param room Room of the message
- * @param handle Sending handle
- * @param tunnel Sending connection (may be NULL)
- * @param message MISS-Message
- * @param hash Hash of the message
+ * @param[in/out] room Room of the message
+ * @param[in/out] handle Sending handle
+ * @param[in] message PEER-Message
+ * @param[in] hash Hash of the message
  */
 void
-send_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash);
+send_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 #endif //GNUNET_SERVICE_MESSENGER_MESSAGE_SEND_H
diff --git a/src/messenger/gnunet-service-messenger_message_store.c 
b/src/messenger/gnunet-service-messenger_message_store.c
index 5933d6390..1f3d262ac 100644
--- a/src/messenger/gnunet-service-messenger_message_store.c
+++ b/src/messenger/gnunet-service-messenger_message_store.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -29,10 +29,16 @@
 void
 init_message_store (struct GNUNET_MESSENGER_MessageStore *store)
 {
+  GNUNET_assert(store);
+
   store->storage_messages = NULL;
 
   store->entries = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
   store->messages = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  store->links = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  store->rewrite_entries = GNUNET_NO;
+  store->write_links = GNUNET_NO;
 }
 
 static int
@@ -55,9 +61,21 @@ iterate_destroy_messages (void *cls, const struct 
GNUNET_HashCode *key, void *va
   return GNUNET_YES;
 }
 
+static int
+iterate_destroy_links (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_HashCode *previous = value;
+
+  GNUNET_free(previous);
+
+  return GNUNET_YES;
+}
+
 void
 clear_message_store (struct GNUNET_MESSENGER_MessageStore *store)
 {
+  GNUNET_assert(store);
+
   if (store->storage_messages)
   {
     GNUNET_DISK_file_close (store->storage_messages);
@@ -67,9 +85,11 @@ clear_message_store (struct GNUNET_MESSENGER_MessageStore 
*store)
 
   GNUNET_CONTAINER_multihashmap_iterate (store->entries, 
iterate_destroy_entries, NULL);
   GNUNET_CONTAINER_multihashmap_iterate (store->messages, 
iterate_destroy_messages, NULL);
+  GNUNET_CONTAINER_multihashmap_iterate (store->links, iterate_destroy_links, 
NULL);
 
   GNUNET_CONTAINER_multihashmap_destroy (store->entries);
   GNUNET_CONTAINER_multihashmap_destroy (store->messages);
+  GNUNET_CONTAINER_multihashmap_destroy (store->links);
 }
 
 struct GNUNET_MESSENGER_MessageEntryStorage
@@ -78,36 +98,15 @@ struct GNUNET_MESSENGER_MessageEntryStorage
   struct GNUNET_MESSENGER_MessageEntry entry;
 };
 
-void
-load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory)
+static void
+load_message_store_entries (struct GNUNET_MESSENGER_MessageStore *store, const 
char *filename)
 {
-  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
-
-  if (store->storage_messages)
-    GNUNET_DISK_file_close (store->storage_messages);
-
-  char *filename;
-  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
-
-  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
-    store->storage_messages = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_READ, permission);
-  else
-    store->storage_messages = NULL;
-
-  GNUNET_free(filename);
-
-  if (!store->storage_messages)
-    return;
-
-  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
-
-  if (GNUNET_YES != GNUNET_DISK_file_test (filename))
-    goto free_filename;
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ);
 
   struct GNUNET_DISK_FileHandle *entries = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_READ, permission);
 
   if (!entries)
-    goto free_filename;
+    return;
 
   struct GNUNET_MESSENGER_MessageEntryStorage storage;
   struct GNUNET_MESSENGER_MessageEntry *entry;
@@ -120,9 +119,13 @@ load_message_store (struct GNUNET_MESSENGER_MessageStore 
*store, const char *dir
     {
       GNUNET_memcpy(entry, &(storage.entry), sizeof(*entry));
 
-      if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, 
&(storage.hash), entry,
-                                                          
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+      if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains 
(store->entries, &(storage.hash))) ||
+          (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, 
&(storage.hash), entry,
+                                                           
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+      {
+        store->rewrite_entries = GNUNET_YES;
         GNUNET_free(entry);
+      }
     }
     else
     {
@@ -134,22 +137,118 @@ load_message_store (struct GNUNET_MESSENGER_MessageStore 
*store, const char *dir
   while (entry);
 
   GNUNET_DISK_file_close (entries);
+}
+
+struct GNUNET_MESSENGER_MessageLinkStorage
+{
+  struct GNUNET_HashCode hash;
+  struct GNUNET_MESSENGER_MessageLink link;
+};
+
+static void
+load_message_store_links (struct GNUNET_MESSENGER_MessageStore *store, const 
char *filename)
+{
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ);
+
+  struct GNUNET_DISK_FileHandle *entries = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_READ, permission);
+
+  if (!entries)
+    return;
+
+  struct GNUNET_MESSENGER_MessageLinkStorage storage;
+  struct GNUNET_MESSENGER_MessageLink *link = NULL;
+
+  do
+  {
+    if ((sizeof(storage.hash) != GNUNET_DISK_file_read (entries, 
&(storage.hash), sizeof(storage.hash))) ||
+        (sizeof(storage.link.multiple) != GNUNET_DISK_file_read (entries, 
&(storage.link.multiple), sizeof(storage.link.multiple))) ||
+        (sizeof(storage.link.first) != GNUNET_DISK_file_read (entries, 
&(storage.link.first), sizeof(storage.link.first))) ||
+        ((GNUNET_YES == storage.link.multiple) &&
+         (sizeof(storage.link.second) != GNUNET_DISK_file_read (entries, 
&(storage.link.second), sizeof(storage.link.second)))))
+      break;
+
+    link = GNUNET_new(struct GNUNET_MESSENGER_MessageLink);
+
+    GNUNET_memcpy(link, &(storage.link), sizeof(*link));
+
+    if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->links, 
&(storage.hash))) ||
+        (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->links, 
&(storage.hash), link,
+                                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+      break;
+  }
+  while (link);
+
+  if (link)
+    GNUNET_free(link);
+
+  GNUNET_DISK_file_close (entries);
+}
+
+void
+load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory)
+{
+  GNUNET_assert((store) && (directory));
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  if (store->storage_messages)
+    GNUNET_DISK_file_close (store->storage_messages);
+
+  char *filename;
+  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
+
+  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
+    store->storage_messages = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_READWRITE, permission);
+  else
+    store->storage_messages = NULL;
+
+  GNUNET_free(filename);
+
+  if (!store->storage_messages)
+    return;
+
+  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
+
+  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
+    load_message_store_entries(store, filename);
+
+  GNUNET_free(filename);
+
+  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
+
+  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
+    load_message_store_links(store, filename);
 
-free_filename:
   GNUNET_free(filename);
 }
 
-struct GNUNET_MESSENGER_MessageSave
+struct GNUNET_MESSENGER_ClosureMessageSave
 {
   struct GNUNET_MESSENGER_MessageStore *store;
 
-  struct GNUNET_DISK_FileHandle *storage_entries;
+  struct GNUNET_DISK_FileHandle *storage;
 };
 
+static int
+iterate_save_entries (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_ClosureMessageSave *save = cls;
+  struct GNUNET_MESSENGER_MessageEntry *entry = value;
+
+  struct GNUNET_MESSENGER_MessageEntryStorage storage;
+
+  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
+  GNUNET_memcpy(&(storage.entry), entry, sizeof(*entry));
+
+  GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage));
+
+  return GNUNET_YES;
+}
+
 static int
 iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void 
*value)
 {
-  struct GNUNET_MESSENGER_MessageSave *save = cls;
+  struct GNUNET_MESSENGER_ClosureMessageSave *save = cls;
 
   if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains 
(save->store->entries, key))
     return GNUNET_YES;
@@ -159,16 +258,16 @@ iterate_save_messages (void *cls, const struct 
GNUNET_HashCode *key, void *value
 
   GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
 
-  storage.entry.length = get_message_size (message);
+  storage.entry.length = get_message_size (message, GNUNET_YES);
   storage.entry.offset = GNUNET_DISK_file_seek (save->store->storage_messages, 
0, GNUNET_DISK_SEEK_END);
 
-  if ((GNUNET_SYSERR == storage.entry.offset) ||
-      (sizeof(storage) != GNUNET_DISK_file_write (save->storage_entries, 
&storage, sizeof(storage))))
+  if ((GNUNET_SYSERR == storage.entry.offset) || (sizeof(storage)
+      != GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage))))
     return GNUNET_YES;
 
   char *buffer = GNUNET_malloc(storage.entry.length);
 
-  encode_message (message, storage.entry.length, buffer);
+  encode_message (message, storage.entry.length, buffer, GNUNET_YES);
 
   GNUNET_DISK_file_write (save->store->storage_messages, buffer, 
storage.entry.length);
 
@@ -177,25 +276,74 @@ iterate_save_messages (void *cls, const struct 
GNUNET_HashCode *key, void *value
   return GNUNET_YES;
 }
 
+static int
+iterate_save_links (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+  struct GNUNET_MESSENGER_ClosureMessageSave *save = cls;
+  struct GNUNET_MESSENGER_MessageLink *link = value;
+
+  GNUNET_DISK_file_write (save->storage, key, sizeof(*key));
+  GNUNET_DISK_file_write (save->storage, &(link->multiple), 
sizeof(link->multiple));
+  GNUNET_DISK_file_write (save->storage, &(link->first), sizeof(link->first));
+
+  if (GNUNET_YES == link->multiple)
+    GNUNET_DISK_file_write (save->storage, &(link->second), 
sizeof(link->second));
+
+  return GNUNET_YES;
+}
+
 void
 save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory)
 {
-  struct GNUNET_MESSENGER_MessageSave save;
+  GNUNET_assert((store) && (directory));
+
+  struct GNUNET_MESSENGER_ClosureMessageSave save;
 
   enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
 
   char *filename;
+
+  if (GNUNET_YES != store->write_links)
+    goto save_entries;
+
+  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
+
+  save.store = store;
+  save.storage = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_WRITE | 
GNUNET_DISK_OPEN_CREATE, permission);
+
+  if (!save.storage)
+    goto save_entries;
+
+  if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage, 0, 
GNUNET_DISK_SEEK_SET))
+    goto close_links;
+
+  GNUNET_CONTAINER_multihashmap_iterate (store->links, iterate_save_links, 
&save);
+  store->write_links = GNUNET_NO;
+
+close_links:
+  GNUNET_DISK_file_close (save.storage);
+
+save_entries:
+  GNUNET_free(filename);
   GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
 
   save.store = store;
-  save.storage_entries = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE, permission);
+  save.storage = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_WRITE | 
GNUNET_DISK_OPEN_CREATE, permission);
 
   GNUNET_free(filename);
 
-  if (!save.storage_entries)
+  if (!save.storage)
     return;
 
-  if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage_entries, 0, 
GNUNET_DISK_SEEK_END))
+  if (GNUNET_YES == store->rewrite_entries)
+  {
+    if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage, 0, 
GNUNET_DISK_SEEK_SET))
+      goto close_entries;
+
+    GNUNET_CONTAINER_multihashmap_iterate (store->entries, 
iterate_save_entries, &save);
+    store->rewrite_entries = GNUNET_NO;
+  }
+  else if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage, 0, 
GNUNET_DISK_SEEK_END))
     goto close_entries;
 
   if (store->storage_messages)
@@ -213,16 +361,18 @@ save_message_store (struct GNUNET_MESSENGER_MessageStore 
*store, const char *dir
     GNUNET_CONTAINER_multihashmap_iterate (store->messages, 
iterate_save_messages, &save);
 
     GNUNET_DISK_file_sync (store->storage_messages);
-    GNUNET_DISK_file_sync (save.storage_entries);
+    GNUNET_DISK_file_sync (save.storage);
   }
 
 close_entries:
-  GNUNET_DISK_file_close (save.storage_entries);
+  GNUNET_DISK_file_close (save.storage);
 }
 
 int
-contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash)
+contains_store_message (const struct GNUNET_MESSENGER_MessageStore *store, 
const struct GNUNET_HashCode *hash)
 {
+  GNUNET_assert((store) && (hash));
+
   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->messages, 
hash))
     return GNUNET_YES;
 
@@ -232,6 +382,8 @@ contains_store_message (struct 
GNUNET_MESSENGER_MessageStore *store, const struc
 const struct GNUNET_MESSENGER_Message*
 get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash)
 {
+  GNUNET_assert((store) && (hash));
+
   struct GNUNET_MESSENGER_Message *message = GNUNET_CONTAINER_multihashmap_get 
(store->messages, hash);
 
   if (message)
@@ -250,33 +402,141 @@ get_store_message (struct GNUNET_MESSENGER_MessageStore 
*store, const struct GNU
 
   char *buffer = GNUNET_malloc(entry->length);
 
+  if (!buffer)
+    return NULL;
+
   if (GNUNET_DISK_file_read (store->storage_messages, buffer, entry->length) 
!= entry->length)
     goto free_buffer;
 
-
   message = create_message (GNUNET_MESSENGER_KIND_UNKNOWN);
 
-  if ((GNUNET_YES != decode_message (message, entry->length, buffer)) || 
(GNUNET_OK
-      != GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
-                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
-  {
-    destroy_message (message);
+  const int decoding = decode_message (message, entry->length, buffer, 
GNUNET_YES, NULL);
 
-    message = NULL;
+  struct GNUNET_HashCode check;
+  hash_message (message, entry->length, buffer, &check);
 
+  if ((GNUNET_YES != decoding) || (GNUNET_CRYPTO_hash_cmp (hash, &check) != 0))
+  {
     GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry);
+    store->rewrite_entries = GNUNET_YES;
+
+    goto free_message;
   }
 
+  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (store->messages, hash, 
message,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    goto free_buffer;
+
+free_message: destroy_message (message);
+  message = NULL;
+
 free_buffer:
   GNUNET_free(buffer);
 
   return message;
 }
 
+const struct GNUNET_MESSENGER_MessageLink*
+get_store_message_link (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash,
+                        int deleted_only)
+{
+  if (deleted_only)
+    goto get_link;
+
+  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, 
hash);
+
+  if (!message)
+    goto get_link;
+
+  static struct GNUNET_MESSENGER_MessageLink link;
+
+  GNUNET_memcpy(&(link.first), &(message->header.previous), 
sizeof(link.first));
+
+  link.multiple = GNUNET_MESSENGER_KIND_MERGE == message->header.kind? 
GNUNET_YES : GNUNET_NO;
+
+  if (GNUNET_YES == link.multiple)
+    GNUNET_memcpy(&(link.second), &(message->body.merge.previous), 
sizeof(link.second));
+  else
+    GNUNET_memcpy(&(link.second), &(message->header.previous), 
sizeof(link.second));
+
+  return &link;
+
+get_link:
+  return GNUNET_CONTAINER_multihashmap_get (store->links, hash);
+}
+
 int
 put_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash,
                    struct GNUNET_MESSENGER_Message *message)
 {
+  GNUNET_assert((store) && (hash) && (message));
+
   return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
                                             
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
 }
+
+static void
+add_link (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash,
+          const struct GNUNET_MESSENGER_Message *message)
+{
+  struct GNUNET_MESSENGER_MessageLink *link = GNUNET_new(struct 
GNUNET_MESSENGER_MessageLink);
+
+  GNUNET_memcpy(&(link->first), &(message->header.previous), 
sizeof(link->first));
+
+  link->multiple = GNUNET_MESSENGER_KIND_MERGE == message->header.kind? 
GNUNET_YES : GNUNET_NO;
+
+  if (GNUNET_YES == link->multiple)
+    GNUNET_memcpy(&(link->second), &(message->body.merge.previous), 
sizeof(link->second));
+  else
+    GNUNET_memcpy(&(link->second), &(message->header.previous), 
sizeof(link->second));
+
+  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->links, hash, link,
+                                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    GNUNET_free(link);
+}
+
+int
+delete_store_message (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((store) && (hash));
+
+  const struct GNUNET_MESSENGER_MessageEntry *entry = 
GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
+
+  if (!entry)
+    goto clear_memory;
+
+  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, 
hash);
+
+  if (message)
+    add_link (store, hash, message);
+
+  if (!store->storage_messages)
+    goto clear_entry;
+
+  if (entry->offset != GNUNET_DISK_file_seek (store->storage_messages, 
entry->offset, GNUNET_DISK_SEEK_SET))
+    return GNUNET_SYSERR;
+
+  char *clear_buffer = GNUNET_malloc(entry->length);
+
+  if (!clear_buffer)
+    return GNUNET_SYSERR;
+
+  GNUNET_CRYPTO_zero_keys (clear_buffer, entry->length);
+
+  if ((entry->length != GNUNET_DISK_file_write (store->storage_messages, 
clear_buffer, entry->length)) || (GNUNET_OK
+      != GNUNET_DISK_file_sync (store->storage_messages)))
+  {
+    GNUNET_free(clear_buffer);
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_free(clear_buffer);
+
+clear_entry:
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->entries, 
hash, entry))
+    store->rewrite_entries = GNUNET_YES;
+
+clear_memory:
+  GNUNET_CONTAINER_multihashmap_remove_all (store->messages, hash);
+  return GNUNET_OK;
+}
diff --git a/src/messenger/gnunet-service-messenger_message_store.h 
b/src/messenger/gnunet-service-messenger_message_store.h
index e58459b21..87305826a 100644
--- a/src/messenger/gnunet-service-messenger_message_store.h
+++ b/src/messenger/gnunet-service-messenger_message_store.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -36,85 +36,127 @@ struct GNUNET_MESSENGER_MessageEntry
   uint16_t length;
 };
 
+struct GNUNET_MESSENGER_Message;
+
+struct GNUNET_MESSENGER_MessageLink
+{
+  uint8_t multiple;
+
+  struct GNUNET_HashCode first;
+  struct GNUNET_HashCode second;
+};
+
 struct GNUNET_MESSENGER_MessageStore
 {
   struct GNUNET_DISK_FileHandle *storage_messages;
 
   struct GNUNET_CONTAINER_MultiHashMap *entries;
   struct GNUNET_CONTAINER_MultiHashMap *messages;
+  struct GNUNET_CONTAINER_MultiHashMap *links;
+
+  int rewrite_entries;
+  int write_links;
 };
 
 /**
- * Initializes a message store as fully empty.
+ * Initializes a message <i>store</i> as fully empty.
  *
- * @param store Message store
+ * @param[out] store Message store
  */
 void
 init_message_store (struct GNUNET_MESSENGER_MessageStore *store);
 
 /**
- * Clears a message store, wipes its content and deallocates its memory.
+ * Clears a message <i>store</i>, wipes its content and deallocates its memory.
  *
- * @param store Message store
+ * @param[in/out] store Message store
  */
 void
 clear_message_store (struct GNUNET_MESSENGER_MessageStore *store);
 
 /**
- * Loads messages from a directory into a message store.
+ * Loads messages from a <i>directory</i> into a message <i>store</i>.
  *
- * @param store Message store
- * @param directory Path to a directory
+ * @param[out] store Message store
+ * @param[in] directory Path to a directory
  */
 void
 load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory);
 
 /**
- * Saves messages from a message store into a directory.
+ * Saves messages from a message <i>store</i> into a <i>directory</i>.
  *
- * @param store Message store
- * @param directory Path to a directory
+ * @param[in] store Message store
+ * @param[in] directory Path to a directory
  */
 void
 save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory);
 
 /**
- * Checks if a message matching a given <i>hash</i> is stored in a message 
store. The function returns
- * GNUNET_YES if a match is found, GNUNET_NO otherwise.
+ * Checks if a message matching a given <i>hash</i> is stored in a message 
<i>store</i>.
+ * The function returns #GNUNET_YES if a match is found, #GNUNET_NO otherwise.
  *
  * The message has not to be loaded from disk into memory for this check!
  *
- * @param store Message store
- * @param hash Hash of message
- * @return GNUNET_YES on match, otherwise GNUNET_NO
+ * @param[in] store Message store
+ * @param[in] hash Hash of message
+ * @return #GNUNET_YES on match, otherwise #GNUNET_NO
  */
 int
-contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash);
+contains_store_message (const struct GNUNET_MESSENGER_MessageStore *store, 
const struct GNUNET_HashCode *hash);
 
 /**
- * Returns the message from a message store matching a given <i>hash</i>. If 
no matching message is found,
- * NULL gets returned.
+ * Returns the message from a message <i>store</i> matching a given 
<i>hash</i>. If no matching
+ * message is found, NULL gets returned.
  *
  * This function requires the message to be loaded into memory!
  * @see contains_store_message()
  *
- * @param store Message store
- * @param hash Hash of message
+ * @param[in/out] store Message store
+ * @param[in] hash Hash of message
  * @return Message or NULL
  */
 const struct GNUNET_MESSENGER_Message*
 get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash);
 
+/**
+ * Returns the message link from a message <i>store</i> matching a given 
<i>hash</i>. If the
+ * flag is set to #GNUNET_YES, only links from deleted messages will be 
returned or NULL.
+ *
+ * Otherwise message links will also returned for messages found in the store 
under the given
+ * hash. The link which will be returned copies link information from the 
message for
+ * temporary usage.
+ *
+ * @param[in/out] store Message store
+ * @param[in] hash Hash of message
+ * @param[in] deleted_only Flag
+ * @return Message link or NULL
+ */
+const struct GNUNET_MESSENGER_MessageLink*
+get_store_message_link (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash,
+                        int deleted_only);
+
 /**
  * Stores a message into the message store. The result indicates if the 
operation was successful.
  *
- * @param store Message store
- * @param hash Hash of message
- * @param message Message
- * @return GNUNET_OK on success, otherwise GNUNET_NO
+ * @param[in/out] store Message store
+ * @param[in] hash Hash of message
+ * @param[in/out] message Message
+ * @return #GNUNET_OK on success, otherwise #GNUNET_NO
  */
 int
 put_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash,
                    struct GNUNET_MESSENGER_Message *message);
 
+/**
+ * Deletes a message in the message store. It will be removed from disk space 
and memory. The result
+ * indicates if the operation was successful.
+ *
+ * @param[in/out] store Message store
+ * @param[in] hash Hash of message
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+int
+delete_store_message (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash);
+
 #endif //GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H
diff --git a/src/messenger/gnunet-service-messenger_operation.c 
b/src/messenger/gnunet-service-messenger_operation.c
new file mode 100644
index 000000000..d0c378699
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_operation.c
@@ -0,0 +1,214 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_operation.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_operation.h"
+
+#include "gnunet-service-messenger_operation_store.h"
+
+struct GNUNET_MESSENGER_Operation*
+create_operation (const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert(hash);
+
+  struct GNUNET_MESSENGER_Operation *op = GNUNET_new(struct 
GNUNET_MESSENGER_Operation);
+
+  op->type = GNUNET_MESSENGER_OP_UNKNOWN;
+  GNUNET_memcpy(&(op->hash), hash, sizeof(*hash));
+  op->timestamp = GNUNET_TIME_absolute_get_zero_();
+  op->store = NULL;
+  op->task = NULL;
+
+  return op;
+}
+
+void
+destroy_operation (struct GNUNET_MESSENGER_Operation *op)
+{
+  GNUNET_assert(op);
+
+  if (op->task)
+    GNUNET_SCHEDULER_cancel(op->task);
+
+  GNUNET_free(op);
+}
+
+static void
+callback_operation (void *cls);
+
+struct GNUNET_MESSENGER_Operation*
+load_operation (struct GNUNET_MESSENGER_OperationStore *store, const char 
*path)
+{
+  GNUNET_assert((store) && (path));
+
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+  struct GNUNET_MESSENGER_Operation* op = NULL;
+
+  if (GNUNET_OK != GNUNET_CONFIGURATION_parse(cfg, path))
+    goto destroy_config;
+
+  struct GNUNET_HashCode hash;
+
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "operation", "hash", 
&hash, sizeof(hash)))
+    goto destroy_config;
+
+  op = create_operation(&hash);
+
+  unsigned long long type_number;
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg, "operation", 
"type", &type_number))
+    switch (type_number)
+    {
+    case GNUNET_MESSENGER_OP_REQUEST:
+      op->type = GNUNET_MESSENGER_OP_REQUEST;
+      break;
+    case GNUNET_MESSENGER_OP_DELETE:
+      op->type = GNUNET_MESSENGER_OP_DELETE;
+      break;
+    case GNUNET_MESSENGER_OP_MERGE:
+      op->type = GNUNET_MESSENGER_OP_MERGE;
+      break;
+    default:
+      break;
+    }
+
+  if ((GNUNET_MESSENGER_OP_UNKNOWN == op->type) ||
+      (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "operation", 
"timestamp", &(op->timestamp), sizeof(op->timestamp))))
+  {
+    destroy_operation(op);
+    op = NULL;
+    goto destroy_config;
+  }
+
+  const struct GNUNET_TIME_Relative delay = 
GNUNET_TIME_absolute_get_remaining(op->timestamp);
+
+  op->task = GNUNET_SCHEDULER_add_delayed_with_priority(
+      delay,
+      GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
+      callback_operation,
+      op
+  );
+
+  op->store = store;
+
+destroy_config:
+  GNUNET_CONFIGURATION_destroy (cfg);
+
+  return op;
+}
+
+void
+save_operation (const struct GNUNET_MESSENGER_Operation *op, const char *path)
+{
+  GNUNET_assert((path) && (op));
+
+  struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+  char *hash_data;
+  hash_data = GNUNET_STRINGS_data_to_string_alloc (&(op->hash), 
sizeof(op->hash));
+
+  if (hash_data)
+  {
+    GNUNET_CONFIGURATION_set_value_string (cfg, "operation", "hash", 
hash_data);
+
+    GNUNET_free(hash_data);
+  }
+
+  GNUNET_CONFIGURATION_set_value_number(cfg, "operation", "type", op->type);
+
+  char *timestamp_data;
+  timestamp_data = GNUNET_STRINGS_data_to_string_alloc (&(op->timestamp), 
sizeof(op->timestamp));
+
+  if (timestamp_data)
+  {
+    GNUNET_CONFIGURATION_set_value_string (cfg, "operation", "timestamp", 
timestamp_data);
+
+    GNUNET_free(timestamp_data);
+  }
+
+  GNUNET_CONFIGURATION_write (cfg, path);
+  GNUNET_CONFIGURATION_destroy (cfg);
+}
+
+extern void
+callback_store_operation (struct GNUNET_MESSENGER_OperationStore *store,
+                          enum GNUNET_MESSENGER_OperationType type,
+                          const struct GNUNET_HashCode *hash);
+
+static void
+callback_operation (void *cls)
+{
+  struct GNUNET_MESSENGER_Operation *op = cls;
+
+  op->task = NULL;
+
+  callback_store_operation (op->store, op->type, &(op->hash));
+}
+
+int
+start_operation (struct GNUNET_MESSENGER_Operation *op,
+                 enum GNUNET_MESSENGER_OperationType type,
+                 struct GNUNET_MESSENGER_OperationStore *store,
+                 struct GNUNET_TIME_Relative delay)
+{
+  GNUNET_assert((op) && (store));
+
+  if (op->task)
+    return GNUNET_SYSERR;
+
+  const struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_add(
+      GNUNET_TIME_absolute_get(),
+      delay
+  );
+
+  op->task = GNUNET_SCHEDULER_add_delayed_with_priority(
+      delay,
+      GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
+      callback_operation,
+      op
+  );
+
+  op->type = type;
+  op->timestamp = timestamp;
+  op->store = store;
+
+  return GNUNET_OK;
+}
+
+int
+stop_operation (struct GNUNET_MESSENGER_Operation *op)
+{
+  GNUNET_assert(op);
+
+  if (!op->task)
+    return GNUNET_SYSERR;
+
+  GNUNET_SCHEDULER_cancel(op->task);
+  op->task = NULL;
+
+  op->type = GNUNET_MESSENGER_OP_UNKNOWN;
+  op->timestamp = GNUNET_TIME_absolute_get_zero_();
+  op->store = NULL;
+
+  return GNUNET_OK;
+}
diff --git a/src/messenger/gnunet-service-messenger_operation.h 
b/src/messenger/gnunet-service-messenger_operation.h
new file mode 100644
index 000000000..7757b8e88
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_operation.h
@@ -0,0 +1,129 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_operation.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_OPERATION_H
+#define GNUNET_SERVICE_MESSENGER_OPERATION_H
+
+#include "platform.h"
+#include "gnunet_configuration_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_strings_lib.h"
+#include "gnunet_time_lib.h"
+
+enum GNUNET_MESSENGER_OperationType
+{
+  GNUNET_MESSENGER_OP_REQUEST = 1,
+  GNUNET_MESSENGER_OP_DELETE = 2,
+  GNUNET_MESSENGER_OP_MERGE = 3,
+
+  GNUNET_MESSENGER_OP_UNKNOWN = 0
+};
+
+struct GNUNET_MESSENGER_OperationStore;
+
+struct GNUNET_MESSENGER_Operation
+{
+  enum GNUNET_MESSENGER_OperationType type;
+
+  struct GNUNET_HashCode hash;
+  struct GNUNET_TIME_Absolute timestamp;
+
+  struct GNUNET_MESSENGER_OperationStore *store;
+  struct GNUNET_SCHEDULER_Task* task;
+};
+
+/**
+ * Creates and allocates a new operation under a given <i>hash</i>.
+ *
+ * @param[in] hash Hash of message
+ */
+struct GNUNET_MESSENGER_Operation*
+create_operation (const struct GNUNET_HashCode *hash);
+
+/**
+ * Destroys an operation and frees its memory fully.
+ *
+ * @param[in/out] op Operation
+ */
+void
+destroy_operation (struct GNUNET_MESSENGER_Operation *op);
+
+/**
+ * Loads data from a configuration file at a selected <i>path</i> into
+ * a new allocated and created operation for a specific operation
+ * <i>store</i> if the required information could be read successfully.
+ *
+ * The method will return the new operation and it will be started
+ * automatically to match its timestamp of execution.
+ *
+ * If the method fails to restore any valid operation from the file,
+ * NULL gets returned instead.
+ *
+ * @param[in/out] store Operation store
+ * @param[in] path Path of a configuration file
+ */
+struct GNUNET_MESSENGER_Operation*
+load_operation (struct GNUNET_MESSENGER_OperationStore *store, const char 
*path);
+
+/**
+ * Saves data from an <i>operation</i> into a configuration file at a
+ * selected <i>path</i> which can be load to restore the operation
+ * completely and continue its process.
+ *
+ * @param[in] op Operation
+ * @param[in] path Path of a configuration file
+ */
+void
+save_operation (const struct GNUNET_MESSENGER_Operation *op, const char *path);
+
+/**
+ * Starts an inactive operation with a given <i>delay</i> in a
+ * specific operation <i>store</i>. The method will replace the
+ * operations type to process it correctly. An opeation can't be
+ * started twice, it has to be stopped or fully processed first.
+ *
+ * @param[in/out] op Operation
+ * @param[in] type Type of operation
+ * @param[in/out] store Operation store
+ * @param[in] delay Delay
+ * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR
+ */
+int
+start_operation (struct GNUNET_MESSENGER_Operation *op,
+                 enum GNUNET_MESSENGER_OperationType type,
+                 struct GNUNET_MESSENGER_OperationStore *store,
+                 struct GNUNET_TIME_Relative delay);
+
+/**
+ * Stops an active operation and resets its type to be
+ * #GNUNET_MESSENGER_OP_UNKNOWN.
+ *
+ * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR
+ */
+int
+stop_operation (struct GNUNET_MESSENGER_Operation *op);
+
+#endif //GNUNET_SERVICE_MESSENGER_OPERATION_H
diff --git a/src/messenger/gnunet-service-messenger_operation_store.c 
b/src/messenger/gnunet-service-messenger_operation_store.c
new file mode 100644
index 000000000..05985ef84
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_operation_store.c
@@ -0,0 +1,224 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_operation_store.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_operation_store.h"
+
+#include "gnunet-service-messenger_operation.h"
+#include "gnunet-service-messenger_room.h"
+
+void
+init_operation_store (struct GNUNET_MESSENGER_OperationStore *store, struct 
GNUNET_MESSENGER_SrvRoom *room)
+{
+  GNUNET_assert((store) && (room));
+
+  store->room = room;
+  store->operations = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO);
+}
+
+static int
+iterate_destroy_operations (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Operation *op = value;
+
+  destroy_operation(op);
+
+  return GNUNET_YES;
+}
+
+void
+clear_operation_store (struct GNUNET_MESSENGER_OperationStore *store)
+{
+  GNUNET_assert(store);
+
+  GNUNET_CONTAINER_multihashmap_iterate (store->operations, 
iterate_destroy_operations, NULL);
+  GNUNET_CONTAINER_multihashmap_destroy(store->operations);
+}
+
+static int
+callback_scan_for_operations (void *cls, const char *filename)
+{
+  struct GNUNET_MESSENGER_OperationStore *store = cls;
+
+  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
+  {
+    char *path;
+
+    GNUNET_asprintf (&path, "%s%c", filename, DIR_SEPARATOR);
+
+    struct GNUNET_MESSENGER_Operation *op = load_operation(store, path);
+
+    if ((op) && (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
+        store->operations,
+        &(op->hash), op,
+        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+    {
+      destroy_operation(op);
+    }
+
+    GNUNET_free(path);
+  }
+
+  return GNUNET_OK;
+}
+
+void
+load_operation_store (struct GNUNET_MESSENGER_OperationStore *store,
+                      const char *directory)
+{
+  GNUNET_assert ((store) && (directory));
+
+  if (GNUNET_OK == GNUNET_DISK_directory_test (directory, GNUNET_YES))
+    GNUNET_DISK_directory_scan (directory, callback_scan_for_operations, 
store);
+}
+
+static int
+iterate_save_operations (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  const char *save_dir = cls;
+
+  struct GNUNET_MESSENGER_Operation *op = value;
+
+  if (!op)
+    return GNUNET_YES;
+
+  char *op_dir;
+  GNUNET_asprintf (&op_dir, "%s%s.cfg", save_dir, GNUNET_h2s(key));
+  save_operation(op, op_dir);
+
+  GNUNET_free(op_dir);
+  return GNUNET_YES;
+}
+
+void
+save_operation_store (const struct GNUNET_MESSENGER_OperationStore *store,
+                      const char *directory)
+{
+  GNUNET_assert ((store) && (directory));
+
+  char* save_dir;
+  GNUNET_asprintf (&save_dir, "%s%s%c", directory, "operations", 
DIR_SEPARATOR);
+
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (save_dir, GNUNET_NO)) ||
+      (GNUNET_OK == GNUNET_DISK_directory_create (save_dir)))
+    GNUNET_CONTAINER_multihashmap_iterate (store->operations, 
iterate_save_operations, save_dir);
+
+  GNUNET_free(save_dir);
+}
+
+enum GNUNET_MESSENGER_OperationType
+get_store_operation_type (const struct GNUNET_MESSENGER_OperationStore *store,
+                          const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((store) && (hash));
+
+  struct GNUNET_MESSENGER_Operation *op = 
GNUNET_CONTAINER_multihashmap_get(store->operations, hash);
+
+  if (!op)
+    return GNUNET_MESSENGER_OP_UNKNOWN;
+
+  return op->type;
+}
+
+int
+use_store_operation (struct GNUNET_MESSENGER_OperationStore *store,
+                     const struct GNUNET_HashCode *hash,
+                     enum GNUNET_MESSENGER_OperationType type,
+                     struct GNUNET_TIME_Relative delay)
+{
+  GNUNET_assert((store) && (hash));
+
+  struct GNUNET_MESSENGER_Operation *op = 
GNUNET_CONTAINER_multihashmap_get(store->operations, hash);
+
+  if (op)
+    goto use_op;
+
+  op = create_operation(hash);
+
+  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->operations, hash, 
op, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+  {
+    destroy_operation(op);
+
+    return GNUNET_SYSERR;
+  }
+
+use_op:
+  if ((op->type != GNUNET_MESSENGER_OP_UNKNOWN) &&
+      (type == GNUNET_MESSENGER_OP_DELETE))
+    stop_operation (op);
+
+  return start_operation(op, type, store, delay);
+}
+
+void
+cancel_store_operation (struct GNUNET_MESSENGER_OperationStore *store,
+                        const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((store) && (hash));
+
+  struct GNUNET_MESSENGER_Operation *op = 
GNUNET_CONTAINER_multihashmap_get(store->operations, hash);
+
+  if (!op)
+    return;
+
+  stop_operation(op);
+
+  GNUNET_CONTAINER_multihashmap_remove(store->operations, hash, op);
+
+  destroy_operation(op);
+}
+
+extern void
+callback_room_deletion (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_HashCode *hash);
+
+extern void
+callback_room_merge (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_HashCode *hash);
+
+void
+callback_store_operation (struct GNUNET_MESSENGER_OperationStore *store,
+                          enum GNUNET_MESSENGER_OperationType type,
+                          const struct GNUNET_HashCode *hash)
+{
+  GNUNET_assert((store) && (hash));
+
+  struct GNUNET_HashCode op_hash;
+  GNUNET_memcpy(&op_hash, hash, sizeof(op_hash));
+  cancel_store_operation (store, &op_hash);
+
+  struct GNUNET_MESSENGER_SrvRoom *room = store->room;
+
+  switch (type)
+  {
+  case GNUNET_MESSENGER_OP_REQUEST:
+    break;
+  case GNUNET_MESSENGER_OP_DELETE:
+    callback_room_deletion (room, &op_hash);
+    break;
+  case GNUNET_MESSENGER_OP_MERGE:
+    callback_room_merge (room, &op_hash);
+    break;
+  default:
+    break;
+  }
+}
diff --git a/src/messenger/gnunet-service-messenger_operation_store.h 
b/src/messenger/gnunet-service-messenger_operation_store.h
new file mode 100644
index 000000000..2fd604340
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_operation_store.h
@@ -0,0 +1,131 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_operation_store.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_OPERATION_STORE_H
+#define GNUNET_SERVICE_MESSENGER_OPERATION_STORE_H
+
+#include "platform.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+struct GNUNET_MESSENGER_SrvRoom;
+
+struct GNUNET_MESSENGER_OperationStore
+{
+  struct GNUNET_MESSENGER_SrvRoom *room;
+
+  struct GNUNET_CONTAINER_MultiHashMap *operations;
+};
+
+/**
+ * Initializes an operation <i>store</i> as fully empty with a given 
<i>room</i>.
+ *
+ * @param[out] store Operation store
+ * @param[in/out] room Room
+ */
+void
+init_operation_store (struct GNUNET_MESSENGER_OperationStore *store, struct 
GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Clears an operation <i>store</i>, stops all operations and deallocates its 
memory.
+ *
+ * @param[in/out] store Operation store
+ */
+void
+clear_operation_store (struct GNUNET_MESSENGER_OperationStore *store);
+
+/**
+ * Loads operations from a <i>directory</i> into an operation <i>store</i>.
+ *
+ * @param[out] store Operation store
+ * @param[in] directory Path to a directory
+ */
+void
+load_operation_store (struct GNUNET_MESSENGER_OperationStore *store,
+                      const char *directory);
+
+/**
+ * Saves operations from an operation <i>store</i> into a <i>directory</i>.
+ *
+ * @param[in] store Operation store
+ * @param[in] directory Path to a directory
+ */
+void
+save_operation_store (const struct GNUNET_MESSENGER_OperationStore *store,
+                      const char *directory);
+
+/**
+ * Retruns the type of the active operation under a given <i>hash</i> in
+ * a specific operation <i>store</i>. If there is no active operation under
+ * the given <i>hash</i>, #GNUNET_MESSENGER_OP_UNKNOWN gets returned instead.
+ *
+ * @param[in] store Operation store
+ * @param[in] hash Hash of message
+ * @return Type of operation or #GNUNET_MESSENGER_OP_UNKNOWN
+ */
+enum GNUNET_MESSENGER_OperationType
+get_store_operation_type (const struct GNUNET_MESSENGER_OperationStore *store,
+                          const struct GNUNET_HashCode *hash);
+
+/**
+ * Tries to use an operation under a given <i>hash</i> in a specific
+ * operation <i>store</i>. The operation will use the selected <i>type</i>
+ * if successful. The operation will be delayed by a given <i>delay</i>.
+ *
+ * If the selected type is #GNUNET_MESSENGER_OP_DELETE any active operation
+ * under the given hash will be stopped and replaced.
+ *
+ * If the new operation could be started successfully the method returns
+ * #GNUNET_OK, otherwise #GNUNET_SYSERR.
+ *
+ * @param[in/out] store Operation store
+ * @param[in] hash Hash of message
+ * @param[in] type Operation type
+ * @param[in] delay Delay
+ * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR
+ */
+int
+use_store_operation (struct GNUNET_MESSENGER_OperationStore *store,
+                     const struct GNUNET_HashCode *hash,
+                     enum GNUNET_MESSENGER_OperationType type,
+                     struct GNUNET_TIME_Relative delay);
+
+/**
+ * Stops any active operation under a given <i>hash</i> in a specific
+ * operation <i>store</i>.
+ *
+ * Beware that calling this method will also implicitly free the memory
+ * of any active operation under the given hash!
+ *
+ * @param[in/out] store Operation store
+ * @param[in] hash Hash of message
+ */
+void
+cancel_store_operation (struct GNUNET_MESSENGER_OperationStore *store,
+                        const struct GNUNET_HashCode *hash);
+
+#endif //GNUNET_SERVICE_MESSENGER_OPERATION_STORE_H
diff --git a/src/messenger/gnunet-service-messenger_room.c 
b/src/messenger/gnunet-service-messenger_room.c
index 7383e1d20..e0bec7991 100644
--- a/src/messenger/gnunet-service-messenger_room.c
+++ b/src/messenger/gnunet-service-messenger_room.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -25,10 +25,19 @@
 
 #include "gnunet-service-messenger_room.h"
 
+#include "gnunet-service-messenger_member.h"
+#include "gnunet-service-messenger_member_session.h"
+
 #include "gnunet-service-messenger_message_kind.h"
+#include "gnunet-service-messenger_message_handle.h"
+#include "gnunet-service-messenger_message_send.h"
 
+#include "gnunet-service-messenger_operation.h"
+
+#include "gnunet-service-messenger.h"
 #include "gnunet-service-messenger_service.h"
-#include "gnunet-service-messenger_util.h"
+#include "gnunet-service-messenger_tunnel.h"
+#include "messenger_api_util.h"
 
 static void
 idle_request_room_messages (void *cls);
@@ -47,11 +56,10 @@ create_room (struct GNUNET_MESSENGER_SrvHandle *handle, 
const struct GNUNET_Hash
   GNUNET_memcpy(&(room->key), key, sizeof(struct GNUNET_HashCode));
 
   room->tunnels = GNUNET_CONTAINER_multipeermap_create (8, GNUNET_NO);
-  room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO);
-  room->member_infos = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO);
 
-  init_message_store (&(room->store));
-  room->requested = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  init_member_store(get_room_member_store(room), room);
+  init_message_store (get_room_message_store(room));
+  init_operation_store(get_room_operation_store(room), room);
 
   init_list_tunnels (&(room->basement));
   init_list_messages (&(room->last_messages));
@@ -61,10 +69,8 @@ create_room (struct GNUNET_MESSENGER_SrvHandle *handle, 
const struct GNUNET_Hash
   init_list_messages (&(room->handling));
   room->idle = NULL;
 
-  room->strict_access = GNUNET_NO;
-
   if (room->service->dir)
-    load_service_room_and_messages (room->service, room);
+    load_room (room);
 
   room->idle = GNUNET_SCHEDULER_add_with_priority 
(GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages, room);
 
@@ -79,34 +85,8 @@ iterate_destroy_tunnels (void *cls, const struct 
GNUNET_PeerIdentity *key, void
   return GNUNET_YES;
 }
 
-static int
-iterate_clear_members (void *cls, const struct GNUNET_ShortHashCode *key, void 
*value)
-{
-  struct GNUNET_MESSENGER_SrvContact *contact = value;
-
-  if (GNUNET_YES == decrease_contact_rc (contact))
-  {
-    struct GNUNET_MESSENGER_SrvRoom *room = cls;
-
-    const struct GNUNET_HashCode *id = get_contact_id_from_key (contact);
-
-    if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove 
(room->service->contacts, id, contact))
-      destroy_contact (contact);
-  }
-
-  return GNUNET_YES;
-}
-
-static int
-iterate_destroy_member_infos (void *cls, const struct GNUNET_ShortHashCode 
*key, void *value)
-{
-  struct GNUNET_MESSENGER_MemberInfo *info = value;
-
-  clear_list_messages (&(info->session_messages));
-
-  GNUNET_free(info);
-  return GNUNET_YES;
-}
+static void
+handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room);
 
 void
 destroy_room (struct GNUNET_MESSENGER_SrvRoom *room)
@@ -123,26 +103,18 @@ destroy_room (struct GNUNET_MESSENGER_SrvRoom *room)
   if (room->port)
     GNUNET_CADET_close_port (room->port);
 
-  merge_room_last_messages (room, room->host);
-
-  GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, 
iterate_destroy_tunnels,
-  NULL);
+  GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, 
iterate_destroy_tunnels, NULL);
 
   handle_room_messages (room);
 
   if (room->service->dir)
-    save_service_room_and_messages (room->service, room);
-
-  GNUNET_CONTAINER_multishortmap_iterate (room->members, 
iterate_clear_members, room);
-  GNUNET_CONTAINER_multishortmap_iterate (room->member_infos, 
iterate_destroy_member_infos, NULL);
+    save_room (room);
 
-  clear_message_store (&(room->store));
-
-  GNUNET_CONTAINER_multihashmap_destroy (room->requested);
+  clear_member_store (get_room_member_store(room));
+  clear_message_store (get_room_message_store(room));
+  clear_operation_store(get_room_operation_store(room));
 
   GNUNET_CONTAINER_multipeermap_destroy (room->tunnels);
-  GNUNET_CONTAINER_multishortmap_destroy (room->members);
-  GNUNET_CONTAINER_multishortmap_destroy (room->member_infos);
 
   clear_list_tunnels (&(room->basement));
   clear_list_messages (&(room->last_messages));
@@ -153,47 +125,28 @@ destroy_room (struct GNUNET_MESSENGER_SrvRoom *room)
   GNUNET_free(room);
 }
 
-struct GNUNET_MESSENGER_SrvContact*
-get_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id)
+struct GNUNET_MESSENGER_MemberStore*
+get_room_member_store (struct GNUNET_MESSENGER_SrvRoom *room)
 {
-  GNUNET_assert((room) && (room->members));
-
-  return GNUNET_CONTAINER_multishortmap_get (room->members, id);
-}
-
-void
-add_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id,
-                  const struct GNUNET_IDENTITY_PublicKey *pubkey)
-{
-  struct GNUNET_MESSENGER_SrvContact *contact = get_service_contact_by_pubkey 
(room->service, pubkey);
+  GNUNET_assert(room);
 
-  if (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put (room->members, id, 
contact,
-                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
-    increase_contact_rc (contact);
+  return &(room->member_store);
 }
 
-struct GNUNET_MESSENGER_MemberInfo*
-get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id)
+struct GNUNET_MESSENGER_MessageStore*
+get_room_message_store (struct GNUNET_MESSENGER_SrvRoom *room)
 {
-  GNUNET_assert((room) && (room->member_infos));
+  GNUNET_assert(room);
 
-  return GNUNET_CONTAINER_multishortmap_get (room->member_infos, id);
+  return &(room->message_store);
 }
 
-struct GNUNET_ShortHashCode*
-generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room)
+struct GNUNET_MESSENGER_OperationStore*
+get_room_operation_store (struct GNUNET_MESSENGER_SrvRoom *room)
 {
-  struct GNUNET_ShortHashCode *unique_id = GNUNET_new(struct 
GNUNET_ShortHashCode);
-
   GNUNET_assert(room);
 
-  if (GNUNET_YES == generate_free_member_id (unique_id, room->members))
-    return unique_id;
-  else
-  {
-    GNUNET_free(unique_id);
-    return NULL;
-  }
+  return &(room->operation_store);
 }
 
 const struct GNUNET_ShortHashCode*
@@ -201,7 +154,7 @@ get_room_host_id (const struct GNUNET_MESSENGER_SrvRoom 
*room)
 {
   GNUNET_assert(room);
 
-  return get_handle_member_id (room->host, &(room->key));
+  return get_handle_member_id (room->host, get_room_key(room));
 }
 
 void
@@ -209,47 +162,17 @@ change_room_host_id (struct GNUNET_MESSENGER_SrvRoom 
*room, const struct GNUNET_
 {
   GNUNET_assert(room);
 
-  change_handle_member_id (room->host, &(room->key), unique_id);
+  change_handle_member_id (room->host, get_room_key(room), unique_id);
 }
 
 static int
 send_room_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
                 struct GNUNET_MESSENGER_SrvTunnel *tunnel)
 {
-  if (!handle)
-    return GNUNET_NO;
-
-  merge_room_last_messages (room, handle);
-
-  if (!is_tunnel_connected (tunnel))
-    return GNUNET_NO;
-
-  struct GNUNET_MESSENGER_Message *message = create_message_info 
(get_handle_ego(handle), room->members);
-
-  if (!message)
+  if ((!handle) || (!is_tunnel_connected (tunnel)))
     return GNUNET_NO;
 
-  if ((tunnel->peer_message) && (tunnel->contact_id))
-  {
-    GNUNET_memcpy(&(message->body.info.unique_id), &(tunnel->contact_id), 
sizeof(struct GNUNET_ShortHashCode));
-    GNUNET_free(tunnel->contact_id);
-
-    tunnel->contact_id = NULL;
-  }
-
-  struct GNUNET_HashCode hash;
-
-  send_tunnel_message (tunnel, handle, message, &hash);
-  destroy_message (message);
-
-  if (tunnel->contact_id)
-  {
-    GNUNET_free(tunnel->contact_id);
-
-    tunnel->contact_id = NULL;
-  }
-
-  return GNUNET_YES;
+  return send_tunnel_message (tunnel, handle, create_message_info 
(get_handle_ego (handle)));
 }
 
 static void*
@@ -257,63 +180,51 @@ callback_room_connect (void *cls, struct 
GNUNET_CADET_Channel *channel, const st
 {
   struct GNUNET_MESSENGER_SrvRoom *room = cls;
 
-  struct GNUNET_MESSENGER_SrvTunnel *tunnel = 
GNUNET_CONTAINER_multipeermap_get (room->tunnels, source);
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel = create_tunnel (room, source);
 
-  if (tunnel)
+  if ((tunnel) &&
+      (GNUNET_OK != GNUNET_CONTAINER_multipeermap_put (room->tunnels, source, 
tunnel,
+                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)))
   {
-    if (GNUNET_YES == bind_tunnel (tunnel, channel))
-    {
-      if (GNUNET_YES == send_room_info (room, room->host, tunnel))
-        return tunnel;
-      else
-      {
-        disconnect_tunnel (tunnel);
-        return NULL;
-      }
-    }
-    else
-    {
-      delayed_disconnect_channel (channel);
-      return NULL;
-    }
+    destroy_tunnel (tunnel);
+    tunnel = NULL;
   }
-  else
+
+  if (!tunnel)
   {
-    tunnel = create_tunnel (room, source);
+    delayed_disconnect_channel (channel);
+    return NULL;
+  }
 
-    if ((GNUNET_YES == bind_tunnel (tunnel, channel)) && (GNUNET_OK
-        == GNUNET_CONTAINER_multipeermap_put (room->tunnels, source, tunnel,
-                                              
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
-    {
-      if (GNUNET_YES == send_room_info (room, room->host, tunnel))
-        return tunnel;
-      else
-      {
-        GNUNET_CONTAINER_multipeermap_remove (room->tunnels, source, tunnel);
+  bind_tunnel(tunnel, channel);
 
-        disconnect_tunnel (tunnel);
-        destroy_tunnel (tunnel);
-        return NULL;
-      }
-    }
-    else
-    {
-      tunnel->channel = NULL;
-      destroy_tunnel (tunnel);
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "New tunnel in room (%s) established to 
peer: %s\n",
+             GNUNET_h2s(get_room_key(room)), GNUNET_i2s (source));
 
-      delayed_disconnect_channel (channel);
-      return NULL;
-    }
-  }
+  if (GNUNET_YES == send_room_info (room, room->host, tunnel))
+    return tunnel;
+
+  disconnect_tunnel (tunnel);
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (room->tunnels, 
source, tunnel))
+    destroy_tunnel (tunnel);
+
+  return NULL;
 }
 
 static int
 join_room (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-           const struct GNUNET_ShortHashCode *member_id)
+           struct GNUNET_MESSENGER_Member *member)
 {
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Joining room: %s (%s)\n", 
GNUNET_h2s(get_room_key(room)), GNUNET_sh2s(member_id));
+  GNUNET_assert((room) && (handle) && (member));
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Joining room: %s (%s)\n", GNUNET_h2s 
(get_room_key (room)),
+             GNUNET_sh2s (get_member_id(member)));
+
+  if (GNUNET_OK != change_handle_member_id (handle, get_room_key(room), 
get_member_id(member)))
+    return GNUNET_NO;
 
-  struct GNUNET_MESSENGER_Message *message = create_message_join 
(get_handle_ego(handle));
+  struct GNUNET_MESSENGER_Message *message = create_message_join 
(get_handle_ego (handle));
 
   if (!message)
   {
@@ -322,48 +233,74 @@ join_room (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHan
     return GNUNET_NO;
   }
 
-  struct GNUNET_HashCode hash;
+  return send_room_message (room, handle, message);
+}
 
-  send_room_message (room, handle, message, &hash);
-  destroy_message (message);
+struct GNUNET_MESSENGER_MemberNotify
+{
+  struct GNUNET_MESSENGER_SrvRoom *room;
+  struct GNUNET_MESSENGER_SrvHandle *handle;
+  struct GNUNET_MESSENGER_MemberSession *session;
+};
 
-  struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_new(struct 
GNUNET_MESSENGER_MemberInfo);
+static int
+iterate_notify_about_members (void *cls, const struct 
GNUNET_IDENTITY_PublicKey *public_key,
+                              struct GNUNET_MESSENGER_MemberSession *session)
+{
+  struct GNUNET_MESSENGER_MemberNotify *notify = cls;
 
-  info->access = GNUNET_MESSENGER_MEMBER_ALLOWED;
-  init_list_messages (&(info->session_messages));
+  if (notify->session == session)
+    return GNUNET_YES;
 
-  if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_put (room->member_infos, 
member_id, info,
-                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
-  {
-    change_handle_member_id (handle, &(room->key), member_id);
+  struct GNUNET_MESSENGER_ListMessage *element;
 
-    add_to_list_messages (&(info->session_messages), &hash);
-    return GNUNET_YES;
-  }
-  else
+  for (element = session->messages.head; element; element = element->next)
   {
-    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Your member information could not 
be registered!\n");
+    const struct GNUNET_MESSENGER_Message *message = get_room_message (
+        notify->room, notify->handle, &(element->hash), GNUNET_NO
+    );
 
-    GNUNET_free(info);
-    return GNUNET_NO;
+    if (message)
+      notify_handle_message (notify->handle, get_room_key(notify->room), 
session, message, &(element->hash));
   }
+
+  return GNUNET_YES;
 }
 
 static int
 join_room_locally (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle)
 {
-  const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, 
&(room->key));
+  const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, 
get_room_key(room));
+
+  struct GNUNET_MESSENGER_MemberStore *member_store = 
get_room_member_store(room);
+  struct GNUNET_MESSENGER_Member *member = add_store_member(member_store, 
member_id);
+
+  if (GNUNET_NO == join_room (room, handle, member))
+    return GNUNET_NO;
 
-  struct GNUNET_MESSENGER_MemberInfo *info = 
GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id);
+  const struct GNUNET_MESSENGER_Ego *ego = get_handle_ego(handle);
+  struct GNUNET_MESSENGER_MemberSession *session = get_member_session (member, 
&(ego->pub));
 
-  if ((!info) && (GNUNET_NO == join_room (room, handle, member_id)))
+  if (!session)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "A valid session is required to join 
a room!\n");
     return GNUNET_NO;
+  }
+
+  struct GNUNET_MESSENGER_MemberNotify notify;
+
+  notify.room = room;
+  notify.handle = handle;
+  notify.session = session;
+
+  iterate_store_members(get_room_member_store(room), 
iterate_notify_about_members, &notify);
 
   return GNUNET_YES;
 }
 
 extern int
 check_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header);
+
 extern void
 handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header);
 
@@ -373,24 +310,39 @@ callback_tunnel_disconnect (void *cls, const struct 
GNUNET_CADET_Channel *channe
 int
 open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle)
 {
+  GNUNET_assert((room) && (handle));
+
   if (room->port)
     return join_room_locally (room, handle);
 
   struct GNUNET_CADET_Handle *cadet = get_room_cadet (room);
-  struct GNUNET_HashCode *key = get_room_key (room);
+  const struct GNUNET_HashCode *key = get_room_key (room);
 
   struct GNUNET_MQ_MessageHandler handlers[] = { 
GNUNET_MQ_hd_var_size(tunnel_message, GNUNET_MESSAGE_TYPE_CADET_CLI,
                                                                        struct 
GNUNET_MessageHeader, NULL),
                                                  GNUNET_MQ_handler_end() };
 
-  room->port = GNUNET_CADET_open_port (cadet, key, callback_room_connect, 
room, NULL,
-                                       callback_tunnel_disconnect, handlers);
+  room->port = GNUNET_CADET_open_port (cadet, key, callback_room_connect, 
room, NULL, callback_tunnel_disconnect,
+                                       handlers);
 
-  const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, 
&(room->key));
+  if (room->port)
+    GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Port of room (%s) was opened!\n",
+               GNUNET_h2s(get_room_key(room)));
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Port of room (%s) could not be 
opened!\n",
+               GNUNET_h2s(get_room_key(room)));
+
+  const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, 
get_room_key(room));
+
+  struct GNUNET_MESSENGER_MemberStore *member_store = 
get_room_member_store(room);
+  struct GNUNET_MESSENGER_Member *member = get_store_member(member_store, 
member_id);
 
-  struct GNUNET_MESSENGER_MemberInfo *info = 
GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id);
+  if (member)
+    goto exit_open_room;
 
-  if ((!info) && (GNUNET_NO == join_room (room, handle, member_id)) && 
(room->port))
+  member = add_store_member(member_store, member_id);
+
+  if ((GNUNET_NO == join_room (room, handle, member)) && (room->port))
   {
     GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not join the room, 
therefore it keeps closed!\n");
 
@@ -400,102 +352,57 @@ open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHan
     return GNUNET_NO;
   }
 
-  struct GNUNET_MESSENGER_Message *message = create_message_peer 
(room->service);
-
-  if (message)
-  {
-    struct GNUNET_HashCode hash;
-
-    send_room_message (room, handle, message, &hash);
-    destroy_message (message);
-  }
-
-  return (room->port ? GNUNET_YES : GNUNET_NO);
+exit_open_room:
+  return (room->port ? send_room_message (room, handle, create_message_peer 
(room->service)) : GNUNET_NO);
 }
 
 int
-entry_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+enter_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
                const struct GNUNET_PeerIdentity *door)
 {
-  if (room->peer_message)
-  {
-    const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, 
handle, room->peer_message, GNUNET_NO);
+  GNUNET_assert((room) && (handle) && (door));
 
-    if (0 == GNUNET_memcmp(&(msg->body.peer.peer), door))
-      return join_room_locally (room, handle);
-  }
+  struct GNUNET_PeerIdentity peer;
+
+  if ((GNUNET_OK == get_service_peer_identity (room->service, &peer)) &&
+      (0 == GNUNET_memcmp(&peer, door)))
+    return join_room_locally (room, handle);
 
   struct GNUNET_MESSENGER_SrvTunnel *tunnel = 
GNUNET_CONTAINER_multipeermap_get (room->tunnels, door);
 
-  if (tunnel)
+  if (!tunnel)
   {
-    switch (connect_tunnel (tunnel))
+    tunnel = create_tunnel (room, door);
+
+    if (GNUNET_OK != GNUNET_CONTAINER_multipeermap_put (room->tunnels, door, 
tunnel,
+                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE))
     {
-    case GNUNET_YES:
-      return GNUNET_YES;
-    case GNUNET_NO:
-      return join_room_locally (room, handle);
-    default:
+      GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not connect to that 
door!\n");
+      destroy_tunnel (tunnel);
       return GNUNET_NO;
     }
   }
 
-  tunnel = create_tunnel (room, door);
-
-  if ((GNUNET_YES == connect_tunnel (tunnel)) &&
-      (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (room->tunnels, door, 
tunnel,
-                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
-    return GNUNET_YES;
-  else
+  if (GNUNET_SYSERR == connect_tunnel (tunnel))
   {
-    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not connect to that 
door!\n");
-
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Connection failure during 
entrance!\n");
+    GNUNET_CONTAINER_multipeermap_remove (room->tunnels, door, tunnel);
     destroy_tunnel (tunnel);
     return GNUNET_NO;
   }
-}
-
-struct GNUNET_MESSENGER_SrvTunnelFinder
-{
-  const struct GNUNET_ShortHashCode *needle;
-  struct GNUNET_MESSENGER_SrvTunnel *tunnel;
-};
-
-static int
-iterate_find_tunnel (void *cls, const struct GNUNET_PeerIdentity *peer, void 
*value)
-{
-  struct GNUNET_MESSENGER_SrvTunnel *tunnel = value;
-  struct GNUNET_MESSENGER_SrvTunnelFinder *finder = cls;
-
-  if ((tunnel->contact_id) && (0 == GNUNET_memcmp(tunnel->contact_id, 
finder->needle)))
-  {
-    finder->tunnel = tunnel;
-    return GNUNET_NO;
-  }
-
-  return GNUNET_YES;
-}
-
-struct GNUNET_MESSENGER_SrvTunnel*
-find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *contact_id)
-{
-  struct GNUNET_MESSENGER_SrvTunnelFinder finder;
 
-  finder.needle = contact_id;
-  finder.tunnel = NULL;
-
-  GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_find_tunnel, 
&finder);
-
-  return finder.tunnel;
+  return join_room_locally (room, handle);
 }
 
 struct GNUNET_MQ_Envelope*
-pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+pack_room_message (const struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_MESSENGER_SrvHandle *handle,
                    struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash, int mode)
 {
+  GNUNET_assert((room) && (handle) && (message) && (hash));
+
   message->header.timestamp = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_absolute_get ());
 
-  const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, 
&(room->key));
+  const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, 
get_room_key(room));
 
   GNUNET_assert(id);
 
@@ -524,7 +431,8 @@ iterate_send_room_message (void *cls, const struct 
GNUNET_PeerIdentity *key, voi
 {
   struct GNUNET_MESSENGER_SrvTunnel *tunnel = value;
 
-  if ((!is_tunnel_connected (tunnel)) || (!tunnel->contact_id))
+  if ((!is_tunnel_connected (tunnel)) ||
+      (get_tunnel_messenger_version(tunnel) < GNUNET_MESSENGER_VERSION))
     return GNUNET_YES;
 
   struct GNUNET_MESSENGER_ClosureSendRoom *closure = cls;
@@ -540,80 +448,88 @@ iterate_send_room_message (void *cls, const struct 
GNUNET_PeerIdentity *key, voi
     GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
 
     if (env)
-    {
-      closure->message = copy_message (closure->message);
       closure->packed = GNUNET_YES;
-    }
   }
   else
-  {
-    env = pack_message (closure->message, NULL, NULL,
-    GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
-  }
+    env = pack_message (closure->message, NULL, NULL, 
GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
 
   if (env)
-    send_tunnel_envelope (tunnel, closure->handle, env, closure->message, 
closure->hash);
+    send_tunnel_envelope (tunnel, env, closure->hash);
 
   return GNUNET_YES;
 }
 
-void
-callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle, void *cls,
-                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+int
+update_room_message (struct GNUNET_MESSENGER_SrvRoom *room,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
 
 void
+callback_room_handle_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                              const struct GNUNET_MESSENGER_Message *message, 
const struct GNUNET_HashCode *hash);
+
+int
 send_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash)
+                   struct GNUNET_MESSENGER_Message *message)
 {
-  struct GNUNET_MESSENGER_ClosureSendRoom closure;
-
-  closure.room = room;
-  closure.handle = handle;
-  closure.exclude = NULL;
-  closure.message = message;
-  closure.hash = hash;
-  closure.packed = GNUNET_NO;
+  GNUNET_assert((room) && (handle));
 
-  GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, 
iterate_send_room_message, &closure);
+  if (!message)
+    return GNUNET_NO;
 
-  if ((GNUNET_NO == closure.packed) && (closure.message == message))
-  {
-    pack_room_message (room, handle, message, hash,
-    GNUNET_MESSENGER_PACK_MODE_UNKNOWN);
+  if (GNUNET_YES == is_message_session_bound(message))
+    merge_room_last_messages(room, handle);
 
-    callback_room_sent (room, handle, NULL, copy_message (message), hash);
-  }
-}
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sending message from handle with member 
id: %s\n",
+             GNUNET_sh2s(get_handle_member_id(handle, get_room_key(room))));
 
-void
-send_room_message_ext (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                       struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash,
-                       struct GNUNET_MESSENGER_SrvTunnel *tunnel)
-{
+  struct GNUNET_HashCode hash;
   struct GNUNET_MESSENGER_ClosureSendRoom closure;
 
   closure.room = room;
   closure.handle = handle;
-  closure.exclude = tunnel;
+  closure.exclude = NULL;
   closure.message = message;
-  closure.hash = hash;
+  closure.hash = &hash;
   closure.packed = GNUNET_NO;
 
   GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, 
iterate_send_room_message, &closure);
 
-  if ((GNUNET_NO == closure.packed) && (closure.message == message))
-  {
-    pack_room_message (room, handle, message, hash,
-    GNUNET_MESSENGER_PACK_MODE_UNKNOWN);
+  if (GNUNET_NO == closure.packed)
+    pack_room_message (room, handle, message, &hash, 
GNUNET_MESSENGER_PACK_MODE_UNKNOWN);
+
+  const int new_message = update_room_message (room, message, &hash);
 
-    callback_room_sent (room, handle, NULL, copy_message (message), hash);
+  if (GNUNET_YES != new_message)
+    return GNUNET_SYSERR;
+
+  switch (message->header.kind)
+  {
+  case GNUNET_MESSENGER_KIND_JOIN:
+    send_message_join (room, handle, message, &hash);
+    break;
+  case GNUNET_MESSENGER_KIND_PEER:
+    send_message_peer (room, handle, message, &hash);
+    break;
+  case GNUNET_MESSENGER_KIND_REQUEST:
+    send_message_request (room, handle, message, &hash);
+    break;
+  default:
+    break;
   }
+
+  callback_room_handle_message (room, handle, message, &hash);
+  return GNUNET_YES;
 }
 
 void
 forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
 {
+  GNUNET_assert((room) && (tunnel));
+
+  if (!message)
+    return;
+
   struct GNUNET_MESSENGER_ClosureSendRoom closure;
   struct GNUNET_HashCode message_hash;
 
@@ -622,7 +538,7 @@ forward_room_message (struct GNUNET_MESSENGER_SrvRoom 
*room, struct GNUNET_MESSE
   closure.room = room;
   closure.handle = NULL;
   closure.exclude = tunnel;
-  closure.message = copy_message (message);
+  closure.message = message;
   closure.hash = &message_hash;
   closure.packed = GNUNET_YES;
 
@@ -630,257 +546,241 @@ forward_room_message (struct GNUNET_MESSENGER_SrvRoom 
*room, struct GNUNET_MESSE
 }
 
 void
-merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle)
+check_room_peer_status (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel)
 {
-  if (!handle)
+  if (!room->peer_message)
     return;
 
-  if (!room->last_messages.head)
-    return;
+  const struct GNUNET_MESSENGER_Message *message = get_room_message(room, 
NULL, room->peer_message, GNUNET_NO);
 
-  while (room->last_messages.head != room->last_messages.tail)
+  if (!message)
   {
-    struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.tail;
+    GNUNET_free(room->peer_message);
+    room->peer_message = NULL;
+    return;
+  }
 
-    struct GNUNET_MESSENGER_Message *message = create_message_merge 
(&(element->hash));
+  struct GNUNET_MESSENGER_MemberStore *member_store = 
get_room_member_store(room);
+  struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, 
message);
 
-    if (message)
-    {
-      struct GNUNET_HashCode hash;
+  if (!member)
+    goto resend_peer_message;
 
-      send_room_message (room, handle, message, &hash);
-      destroy_message (message);
-    }
+  struct GNUNET_MESSENGER_MemberSession *session = 
get_member_session_of(member, message, room->peer_message);
 
-    if (element->prev)
-      GNUNET_CONTAINER_DLL_remove(room->last_messages.head, 
room->last_messages.tail, element);
-  }
-}
+  if (GNUNET_YES == is_member_session_closed(session))
+    goto resend_peer_message;
 
-struct GNUNET_CADET_Handle*
-get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room)
-{
-  return room->service->cadet;
-}
+  if (tunnel)
+    forward_tunnel_message(tunnel, message, room->peer_message);
 
-struct GNUNET_HashCode*
-get_room_key (struct GNUNET_MESSENGER_SrvRoom *room)
-{
-  return &(room->key);
+  return;
+
+resend_peer_message:
+  if (room->host)
+    send_room_message (room, room->host, create_message_peer (room->service));
 }
 
-const struct GNUNET_MESSENGER_SrvTunnel*
-get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *peer)
+static void
+merge_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                    const struct GNUNET_HashCode *hash)
 {
-  return GNUNET_CONTAINER_multipeermap_get (room->tunnels, peer);
+  GNUNET_assert((room) && (handle) && (hash));
+
+  send_room_message (room, handle, create_message_merge (hash));
 }
 
-const struct GNUNET_MESSENGER_Message*
-get_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                  const struct GNUNET_HashCode *hash, int request)
+void
+merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle)
 {
-  const struct GNUNET_MESSENGER_Message *message = get_store_message 
(&(room->store), hash);
+  GNUNET_assert(room);
 
-  if ((message) || (!handle) || (GNUNET_YES != request)
-      || (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains 
(room->requested, hash)))
-    return message;
+  if (!handle)
+    return;
 
-  struct GNUNET_MESSENGER_Message *request_msg = create_message_request (hash);
+  if (!room->last_messages.head)
+    return;
 
-  if (request_msg)
-  {
-    if (GNUNET_CONTAINER_multihashmap_put (room->requested, hash, NULL, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST) == GNUNET_OK)
-    {
-      struct GNUNET_HashCode request_hash;
+  while (room->last_messages.head != room->last_messages.tail)
+    merge_room_message (room, handle, &(room->last_messages.tail->hash));
+}
 
-      send_room_message (room, handle, request_msg, &request_hash);
-    }
+void
+callback_room_deletion (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_HashCode *hash)
+{
+  const struct GNUNET_MESSENGER_Message *message = get_room_message(room, 
NULL, hash, GNUNET_NO);
 
-    destroy_message (request_msg);
-  }
+  if (message)
+    add_to_list_messages(&(room->last_messages), &(message->header.previous));
 
-  return message;
+  if (GNUNET_OK != delete_store_message (get_room_message_store(room), hash))
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Deletion of message failed! 
(%s)\n", GNUNET_h2s(hash));
+    return;
+  }
 }
 
 void
-callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls)
+callback_room_merge (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
-
   if (!room->host)
     return;
 
-  struct GNUNET_PeerIdentity identity;
-
-  GNUNET_PEER_resolve (tunnel->peer, &identity);
-
-  if (GNUNET_YES == contains_list_tunnels (&(room->basement), &identity))
-  {
-    struct GNUNET_MESSENGER_Message *message = create_message_miss (&identity);
-
-    if (message)
-    {
-      struct GNUNET_HashCode hash;
-
-      send_room_message (room, room->host, message, &hash);
-      destroy_message (message);
-    }
-  }
+  merge_room_message (room, room->host, hash);
 }
 
 int
-callback_verify_room_message (struct GNUNET_MESSENGER_SrvRoom *room, void *cls,
-                              struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash)
+delete_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_HashCode *hash, const struct 
GNUNET_TIME_Relative delay)
 {
-  if (GNUNET_MESSENGER_KIND_UNKNOWN == message->header.kind)
-    return GNUNET_SYSERR;
+  GNUNET_assert((room) && (session) && (hash));
 
-  struct GNUNET_MESSENGER_SrvContact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members,
-                                                                               
     &(message->header.sender_id));
+  const struct GNUNET_TIME_Relative forever = 
GNUNET_TIME_relative_get_forever_ ();
 
-  if (!contact)
+  if (0 == GNUNET_memcmp(&forever, &delay))
   {
-    if (GNUNET_MESSENGER_KIND_INFO == message->header.kind)
-      contact = get_service_contact_by_pubkey (room->service, 
&(message->body.info.host_key));
-    else if (GNUNET_MESSENGER_KIND_JOIN == message->header.kind)
-      contact = get_service_contact_by_pubkey (room->service, 
&(message->body.join.key));
+    GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Deletion is delayed forever: operation 
is impossible!\n");
+    return GNUNET_SYSERR;
   }
 
-  if ((!contact) || (GNUNET_SYSERR == verify_message (message, hash, 
get_contact_key (contact))))
-    return GNUNET_SYSERR;
+  const struct GNUNET_MESSENGER_Message *message = get_room_message(room, 
NULL, hash, GNUNET_NO);
 
-  if (GNUNET_YES == room->strict_access)
+  if (!message)
+    return GNUNET_YES;
+
+  if (GNUNET_YES != check_member_session_history(session, hash, GNUNET_YES))
   {
-    struct GNUNET_MESSENGER_MemberInfo *info = 
GNUNET_CONTAINER_multishortmap_get (room->member_infos,
-                                                                               
    &(message->header.sender_id));
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Unpermitted request for deletion by 
member (%s) of message (%s)!\n",
+               GNUNET_sh2s(get_member_session_id(session)), GNUNET_h2s(hash));
 
-    if ((info) && (GNUNET_MESSENGER_MEMBER_BLOCKED == info->access))
-      return GNUNET_SYSERR;
+    return GNUNET_NO;
   }
 
-  if (GNUNET_YES == contains_store_message (&(room->store), hash))
-    return GNUNET_NO;
+  struct GNUNET_MESSENGER_OperationStore *operation_store = 
get_room_operation_store(room);
+
+  if (GNUNET_OK != use_store_operation(operation_store, hash, 
GNUNET_MESSENGER_OP_DELETE, delay))
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Deletion has failed: operation 
denied!\n");
+    return GNUNET_SYSERR;
+  }
 
   return GNUNET_YES;
 }
 
-static void
-search_room_for_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_HashCode *hash)
+struct GNUNET_CADET_Handle*
+get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room)
 {
-  const struct GNUNET_MESSENGER_Message *message = get_room_message (room, 
room->host, hash, GNUNET_YES);
+  GNUNET_assert(room);
 
-  if (!message)
-    return;
+  return room->service->cadet;
+}
 
-  if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind)
-    search_room_for_message (room, &(message->body.merge.previous));
+const struct GNUNET_HashCode*
+get_room_key (const struct GNUNET_MESSENGER_SrvRoom *room)
+{
+  GNUNET_assert(room);
 
-  search_room_for_message (room, &(message->header.previous));
+  return &(room->key);
 }
 
-static void
-idle_request_room_messages (void *cls)
+const struct GNUNET_MESSENGER_SrvTunnel*
+get_room_tunnel (const struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *peer)
 {
-  struct GNUNET_MESSENGER_SrvRoom *room = cls;
+  GNUNET_assert((room) && (peer));
 
-  room->idle = NULL;
+  return GNUNET_CONTAINER_multipeermap_get (room->tunnels, peer);
+}
 
-  struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.head;
+const struct GNUNET_MESSENGER_Message*
+get_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                  const struct GNUNET_HashCode *hash, int request)
+{
+  GNUNET_assert((room) && (hash));
 
-  while (element)
-  {
-    search_room_for_message (room, &(element->hash));
+  const struct GNUNET_MESSENGER_Message *message = get_store_message 
(get_room_message_store(room), hash);
 
-    element = element->next;
-  }
+  if ((message) || (!handle) || (GNUNET_YES != request))
+    return message;
 
-  merge_room_last_messages (room, room->host);
+  struct GNUNET_MESSENGER_OperationStore* operation_store = 
get_room_operation_store(room);
 
-  room->idle = GNUNET_SCHEDULER_add_delayed_with_priority 
(GNUNET_TIME_relative_get_second_ (),
-                                                           
GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages,
-                                                           cls);
+  if (GNUNET_OK == use_store_operation(operation_store, hash, 
GNUNET_MESSENGER_OP_REQUEST, GNUNET_MESSENGER_REQUEST_DELAY))
+    send_room_message (room, handle, create_message_request (hash));
+
+  return NULL;
 }
 
 void
-update_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_MESSENGER_Message *message,
-                           const struct GNUNET_HashCode *hash)
+callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls)
 {
-  struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.head;
-  struct GNUNET_MESSENGER_ListMessage *merging = NULL;
-
-  if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind)
-  {
-    merging = room->last_messages.head;
-
-    while (merging)
-    {
-      if (0 == GNUNET_CRYPTO_hash_cmp (&(merging->hash), 
&(message->body.merge.previous)))
-        break;
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
 
-      merging = merging->next;
-    }
+  if (!room->host)
+    return;
 
-    if (merging)
-      element = merging->next;
-  }
+  struct GNUNET_PeerIdentity identity;
+  get_tunnel_peer_identity(tunnel, &identity);
 
-  while (element)
-  {
-    if (0 == GNUNET_CRYPTO_hash_cmp (&(element->hash), 
&(message->header.previous)))
-      break;
+  if ((GNUNET_YES != GNUNET_CONTAINER_multipeermap_remove (room->tunnels, 
&identity, tunnel)) ||
+      (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains(room->tunnels, 
&identity)))
+    return;
 
-    element = element->next;
-  }
+  if (GNUNET_YES == contains_list_tunnels (&(room->basement), &identity))
+    send_room_message (room, room->host, create_message_miss (&identity));
+}
 
-  if ((merging) && (!element))
+int
+callback_verify_room_message (struct GNUNET_MESSENGER_SrvRoom *room, void *cls,
+                              struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash)
+{
+  if (GNUNET_MESSENGER_KIND_UNKNOWN == message->header.kind)
   {
-    element = merging;
-    merging = NULL;
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Message error: Kind is unknown! 
(%d)\n", message->header.kind);
+    return GNUNET_SYSERR;
   }
 
-  if (element)
-  {
-    GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode));
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving message of kind: %s!\n",
+             GNUNET_MESSENGER_name_of_kind(message->header.kind));
 
-    if (merging)
-      GNUNET_CONTAINER_DLL_remove(room->last_messages.head, 
room->last_messages.tail, merging);
-  }
-  else
-    add_to_list_messages (&(room->last_messages), hash);
-
-  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (room->requested, 
hash))
-    GNUNET_CONTAINER_multihashmap_remove_all (room->requested, hash);
+  return GNUNET_OK;
 }
 
-void
-switch_room_member_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *old_id,
-                       const struct GNUNET_ShortHashCode *new_id, const struct 
GNUNET_HashCode *hash)
+static void
+idle_request_room_messages (void *cls)
 {
-  struct GNUNET_MESSENGER_SrvContact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members, old_id);
-
-  if ((contact) && (GNUNET_YES == GNUNET_CONTAINER_multishortmap_remove 
(room->members, old_id, contact)))
-    GNUNET_CONTAINER_multishortmap_put (room->members, new_id, contact,
-                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
-
-  struct GNUNET_MESSENGER_MemberInfo *info = 
GNUNET_CONTAINER_multishortmap_get (room->member_infos, old_id);
+  struct GNUNET_MESSENGER_SrvRoom *room = cls;
 
-  if ((!info) || (GNUNET_YES != GNUNET_CONTAINER_multishortmap_remove 
(room->member_infos, old_id, contact))
-      || (GNUNET_YES != GNUNET_CONTAINER_multishortmap_put 
(room->member_infos, new_id, contact,
-                                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
-    return;
+  room->idle = NULL;
 
-  if (hash)
-    add_to_list_messages (&(info->session_messages), hash);
+  struct GNUNET_MESSENGER_OperationStore *operation_store = 
get_room_operation_store(room);
+
+  if ((room->last_messages.head != room->last_messages.tail) &&
+      (GNUNET_MESSENGER_OP_UNKNOWN == 
get_store_operation_type(operation_store, &(room->last_messages.tail->hash))))
+    use_store_operation(
+        operation_store,
+        &(room->last_messages.tail->hash),
+        GNUNET_MESSENGER_OP_MERGE,
+        GNUNET_MESSENGER_MERGE_DELAY
+    );
+
+  room->idle = GNUNET_SCHEDULER_add_delayed_with_priority (
+      GNUNET_MESSENGER_IDLE_DELAY,
+      GNUNET_SCHEDULER_PRIORITY_IDLE,
+      idle_request_room_messages,
+      cls
+  );
 }
 
 void
 rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room)
 {
+  GNUNET_assert(room);
+
   struct GNUNET_PeerIdentity peer;
   size_t src;
 
-  if ((GNUNET_OK != get_service_peer_identity (room->service, &peer)) || 
(!find_list_tunnels (&(room->basement), &peer,
-                                                                               
               &src)))
+  if ((GNUNET_OK != get_service_peer_identity (room->service, &peer)) ||
+      (!find_list_tunnels (&(room->basement), &peer, &src)))
     return;
 
   size_t count = count_of_tunnels (&(room->basement));
@@ -918,7 +818,7 @@ rebuild_room_basement_structure (struct 
GNUNET_MESSENGER_SrvRoom *room)
   }
 }
 
-void
+static void
 handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room)
 {
   while (room->handling.head)
@@ -927,29 +827,159 @@ handle_room_messages (struct GNUNET_MESSENGER_SrvRoom 
*room)
 
     const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, 
room->host, &(element->hash), GNUNET_NO);
 
-    if (msg)
-      handle_service_message (room->service, room, msg, &(element->hash));
+    if (!msg)
+      goto finish_handling;
+
+    struct GNUNET_MESSENGER_MemberStore *member_store = 
get_room_member_store(room);
+    struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, 
msg);
+
+    if (!member)
+      goto finish_handling;
+
+    struct GNUNET_MESSENGER_MemberSession *session = 
get_member_session_of(member, msg, &(element->hash));
 
+    if (session)
+      handle_service_message (room->service, room, session, msg, 
&(element->hash));
+
+finish_handling:
     GNUNET_CONTAINER_DLL_remove(room->handling.head, room->handling.tail, 
element);
     GNUNET_free(element);
   }
 }
 
-#include "gnunet-service-messenger_message_recv.h"
+static void
+remove_room_last_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_HashCode *hash)
+{
+  remove_from_list_messages(&(room->last_messages), hash);
+
+  struct GNUNET_MESSENGER_OperationStore *operation_store = 
get_room_operation_store(room);
+
+  if (GNUNET_MESSENGER_OP_MERGE == get_store_operation_type(operation_store, 
hash))
+    cancel_store_operation(operation_store, hash);
+}
+
+int
+update_room_message (struct GNUNET_MESSENGER_SrvRoom *room,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  GNUNET_assert((room) && (message) && (hash));
+
+  struct GNUNET_MESSENGER_OperationStore *operation_store = 
get_room_operation_store(room);
+
+  const int requested = (GNUNET_MESSENGER_OP_REQUEST == 
get_store_operation_type(operation_store, hash)?
+      GNUNET_YES : GNUNET_NO
+  );
+
+  if (GNUNET_YES == requested)
+    cancel_store_operation(operation_store, hash);
+
+  const struct GNUNET_MESSENGER_Message *old_message = get_room_message (room, 
NULL, hash, GNUNET_NO);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handle a message in room (%s).\n", 
GNUNET_h2s (get_room_key(room)));
+
+  if ((old_message) || (GNUNET_OK != put_store_message 
(get_room_message_store(room), hash, message)))
+  {
+    if (old_message != message)
+      destroy_message(message);
+
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Duplicate message got dropped!\n");
+    return GNUNET_NO;
+  }
+
+  if ((GNUNET_YES == requested) ||
+      (GNUNET_MESSENGER_KIND_INFO == message->header.kind) ||
+      (GNUNET_MESSENGER_KIND_REQUEST == message->header.kind))
+    return GNUNET_YES;
+
+  if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind)
+    remove_room_last_message(room, &(message->body.merge.previous));
+  remove_room_last_message(room, &(message->header.previous));
+
+  add_to_list_messages (&(room->last_messages), hash);
+
+  return GNUNET_YES;
+}
+
+struct GNUNET_MESSENGER_MemberSessionCompletion
+{
+  struct GNUNET_MESSENGER_MemberSessionCompletion *prev;
+  struct GNUNET_MESSENGER_MemberSessionCompletion *next;
+
+  struct GNUNET_MESSENGER_MemberSession *session;
+};
+
+struct GNUNET_MESSENGER_MemberUpdate
+{
+  const struct GNUNET_MESSENGER_Message *message;
+  const struct GNUNET_HashCode *hash;
+
+  struct GNUNET_MESSENGER_MemberSessionCompletion *head;
+  struct GNUNET_MESSENGER_MemberSessionCompletion *tail;
+};
+
+static int
+iterate_update_member_sessions (void *cls, const struct 
GNUNET_IDENTITY_PublicKey *public_key,
+                                struct GNUNET_MESSENGER_MemberSession *session)
+{
+  struct GNUNET_MESSENGER_MemberUpdate *update = cls;
+
+  update_member_session_history(session, update->message, update->hash);
+
+  if (GNUNET_YES == is_member_session_completed(session))
+  {
+    struct GNUNET_MESSENGER_MemberSessionCompletion *element = GNUNET_new(
+        struct GNUNET_MESSENGER_MemberSessionCompletion
+    );
+
+    element->session = session;
+
+    GNUNET_CONTAINER_DLL_insert_tail(update->head, update->tail, element);
+  }
+
+  return GNUNET_YES;
+}
 
 void
-callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct 
GNUNET_MESSENGER_Message *message,
-                    const struct GNUNET_HashCode *hash)
+callback_room_handle_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                              const struct GNUNET_MESSENGER_Message *message, 
const struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
+  struct GNUNET_MESSENGER_MemberStore *member_store = 
get_room_member_store(room);
+  struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, 
message);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Callback for message (%s)\n", 
GNUNET_h2s (hash));
+
+  if (!member)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Message handling dropped: Member is 
missing!\n");
+    return;
+  }
+
+  struct GNUNET_MESSENGER_MemberSession *session = 
get_member_session_of(member, message, hash);
 
-  if (GNUNET_OK != put_store_message (&(room->store), hash, message))
+  if (!session)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Message handling dropped: Session 
is missing!\n");
     return;
+  }
+
+  struct GNUNET_MESSENGER_MemberUpdate update;
+  update.message = message;
+  update.hash = hash;
+
+  update.head = NULL;
+  update.tail = NULL;
+
+  iterate_store_members(member_store, iterate_update_member_sessions, &update);
+
+  while (update.head)
+  {
+    struct GNUNET_MESSENGER_MemberSessionCompletion *element = update.head;
 
-  update_room_last_messages (room, message, hash);
+    remove_member_session (element->session->member, element->session);
 
-  if (GNUNET_MESSENGER_KIND_INFO != message->header.kind)
-    forward_room_message (room, tunnel, message, hash);
+    GNUNET_CONTAINER_DLL_remove(update.head, update.tail, element);
+    GNUNET_free (element);
+  }
 
   const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES;
 
@@ -957,32 +987,29 @@ callback_room_recv (struct GNUNET_MESSENGER_SrvRoom 
*room, void *cls, struct GNU
 
   switch (message->header.kind)
   {
-  case GNUNET_MESSENGER_KIND_INFO:
-    recv_message_info (room, tunnel, message, hash);
-    break;
   case GNUNET_MESSENGER_KIND_JOIN:
-    recv_message_join (room, tunnel, message, hash);
+    handle_message_join (room, session, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_LEAVE:
-    recv_message_leave (room, tunnel, message, hash);
+    handle_message_leave (room, session, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_NAME:
-    recv_message_name (room, tunnel, message, hash);
+    handle_message_name (room, session, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_KEY:
-    recv_message_key (room, tunnel, message, hash);
+    handle_message_key (room, session, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_PEER:
-    recv_message_peer (room, tunnel, message, hash);
+    handle_message_peer (room, session, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_ID:
-    recv_message_id (room, tunnel, message, hash);
+    handle_message_id (room, session, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_MISS:
-    recv_message_miss (room, tunnel, message, hash);
+    handle_message_miss (room, session, message, hash);
     break;
-  case GNUNET_MESSENGER_KIND_REQUEST:
-    recv_message_request (room, tunnel, message, hash);
+  case GNUNET_MESSENGER_KIND_DELETE:
+    handle_message_delete (room, session, message, hash);
     break;
   default:
     break;
@@ -992,60 +1019,71 @@ callback_room_recv (struct GNUNET_MESSENGER_SrvRoom 
*room, void *cls, struct GNU
     handle_room_messages (room);
 }
 
-#include "gnunet-service-messenger_message_send.h"
+static void
+get_room_data_subdir (struct GNUNET_MESSENGER_SrvRoom *room, char **dir)
+{
+  GNUNET_assert((room) && (dir));
+
+  GNUNET_asprintf (dir, "%s%s%c%s%c", room->service->dir, "rooms", 
DIR_SEPARATOR, GNUNET_h2s (get_room_key(room)), DIR_SEPARATOR);
+}
 
 void
-callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle, void *cls,
-                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+load_room (struct GNUNET_MESSENGER_SrvRoom *room)
 {
-  const struct GNUNET_MESSENGER_Message *old_message = get_room_message (room, 
handle, hash, GNUNET_NO);
+  GNUNET_assert(room);
 
-  if ((old_message) || (GNUNET_OK != put_store_message (&(room->store), hash, 
message)))
+  char *room_dir;
+  get_room_data_subdir (room, &room_dir);
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES))
   {
-    if (old_message != message)
-      GNUNET_free(message);
+    load_member_store (get_room_member_store(room), room_dir);
+    load_message_store (get_room_message_store(room), room_dir);
+    load_operation_store(get_room_operation_store(room), room_dir);
+
+    char *basement_file;
+    GNUNET_asprintf (&basement_file, "%s%s", room_dir, "basement.list");
+
+    load_list_tunnels(&(room->basement), basement_file);
+    GNUNET_free(basement_file);
+
+    char *last_messages_file;
+    GNUNET_asprintf (&last_messages_file, "%s%s", room_dir, 
"last_messages.list");
+
+    load_list_messages(&(room->last_messages), last_messages_file);
+    GNUNET_free(last_messages_file);
   }
-  else
-  {
-    struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; // may be NULL
 
-    update_room_last_messages (room, message, hash);
+  GNUNET_free(room_dir);
+}
+
+void
+save_room (struct GNUNET_MESSENGER_SrvRoom *room)
+{
+  GNUNET_assert(room);
+
+  char *room_dir;
+  get_room_data_subdir (room, &room_dir);
 
-    const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES;
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_NO)) ||
+      (GNUNET_OK == GNUNET_DISK_directory_create (room_dir)))
+  {
+    save_member_store(get_room_member_store(room), room_dir);
+    save_message_store (get_room_message_store(room), room_dir);
+    save_operation_store(get_room_operation_store(room), room_dir);
 
-    add_to_list_messages (&(room->handling), hash);
+    char *basement_file;
+    GNUNET_asprintf (&basement_file, "%s%s", room_dir, "basement.list");
 
-    switch (message->header.kind)
-    {
-    case GNUNET_MESSENGER_KIND_INFO:
-      send_message_info (room, handle, tunnel, message, hash);
-      break;
-    case GNUNET_MESSENGER_KIND_JOIN:
-      send_message_join (room, handle, tunnel, message, hash);
-      break;
-    case GNUNET_MESSENGER_KIND_LEAVE:
-      send_message_leave (room, handle, tunnel, message, hash);
-      break;
-    case GNUNET_MESSENGER_KIND_NAME:
-      send_message_name (room, handle, tunnel, message, hash);
-      break;
-    case GNUNET_MESSENGER_KIND_KEY:
-      send_message_key (room, handle, tunnel, message, hash);
-      break;
-    case GNUNET_MESSENGER_KIND_PEER:
-      send_message_peer (room, handle, tunnel, message, hash);
-      break;
-    case GNUNET_MESSENGER_KIND_ID:
-      send_message_id (room, handle, tunnel, message, hash);
-      break;
-    case GNUNET_MESSENGER_KIND_MISS:
-      send_message_miss (room, handle, tunnel, message, hash);
-      break;
-    default:
-      break;
-    }
+    save_list_tunnels(&(room->basement), basement_file);
+    GNUNET_free(basement_file);
+
+    char *last_messages_file;
+    GNUNET_asprintf (&last_messages_file, "%s%s", room_dir, 
"last_messages.list");
 
-    if (GNUNET_YES == start_handle)
-      handle_room_messages (room);
+    save_list_messages(&(room->last_messages), last_messages_file);
+    GNUNET_free(last_messages_file);
   }
+
+  GNUNET_free(room_dir);
 }
diff --git a/src/messenger/gnunet-service-messenger_room.h 
b/src/messenger/gnunet-service-messenger_room.h
index 36c9e8cf5..a40961177 100644
--- a/src/messenger/gnunet-service-messenger_room.h
+++ b/src/messenger/gnunet-service-messenger_room.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -33,33 +33,29 @@
 #include "gnunet_identity_service.h"
 #include "gnunet_mq_lib.h"
 
-#include "gnunet-service-messenger_contact.h"
-
 #include "gnunet_messenger_service.h"
 #include "gnunet-service-messenger_basement.h"
 #include "gnunet-service-messenger_handle.h"
-#include "gnunet-service-messenger_tunnel.h"
-
 #include "gnunet-service-messenger_list_messages.h"
+
 #include "messenger_api_list_tunnels.h"
 
+#include "gnunet-service-messenger_member_store.h"
 #include "gnunet-service-messenger_message_store.h"
+#include "gnunet-service-messenger_operation_store.h"
 #include "messenger_api_ego.h"
 
-enum GNUNET_MESSENGER_MemberAccess
-{
-  GNUNET_MESSENGER_MEMBER_ALLOWED = 1,
-  GNUNET_MESSENGER_MEMBER_BLOCKED = 1,
+#define GNUNET_MESSENGER_IDLE_DELAY GNUNET_TIME_relative_multiply \
+  (GNUNET_TIME_relative_get_second_ (), 5)
 
-  GNUNET_MESSENGER_MEMBER_UNKNOWN = 0
-};
+#define GNUNET_MESSENGER_REQUEST_DELAY GNUNET_TIME_relative_multiply \
+  (GNUNET_TIME_relative_get_minute_ (), 5)
 
-struct GNUNET_MESSENGER_MemberInfo
-{
-  enum GNUNET_MESSENGER_MemberAccess access;
+#define GNUNET_MESSENGER_MERGE_DELAY GNUNET_TIME_relative_multiply \
+  (GNUNET_TIME_relative_get_second_ (), 30)
 
-  struct GNUNET_MESSENGER_ListMessages session_messages;
-};
+struct GNUNET_MESSENGER_SrvTunnel;
+struct GNUNET_MESSENGER_MemberSession;
 
 struct GNUNET_MESSENGER_SrvRoom
 {
@@ -70,11 +66,10 @@ struct GNUNET_MESSENGER_SrvRoom
   struct GNUNET_HashCode key;
 
   struct GNUNET_CONTAINER_MultiPeerMap *tunnels;
-  struct GNUNET_CONTAINER_MultiShortmap *members;
-  struct GNUNET_CONTAINER_MultiShortmap *member_infos;
 
-  struct GNUNET_MESSENGER_MessageStore store;
-  struct GNUNET_CONTAINER_MultiHashMap *requested;
+  struct GNUNET_MESSENGER_MemberStore member_store;
+  struct GNUNET_MESSENGER_MessageStore message_store;
+  struct GNUNET_MESSENGER_OperationStore operation_store;
 
   struct GNUNET_MESSENGER_ListTunnels basement;
   struct GNUNET_MESSENGER_ListMessages last_messages;
@@ -83,15 +78,13 @@ struct GNUNET_MESSENGER_SrvRoom
 
   struct GNUNET_MESSENGER_ListMessages handling;
   struct GNUNET_SCHEDULER_Task *idle;
-
-  int strict_access;
 };
 
 /**
  * Creates and allocates a new room for a <i>handle</i> with a given 
<i>key</i>.
  *
- * @param handle Handle
- * @param key Key of room
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
  * @return New room
  */
 struct GNUNET_MESSENGER_SrvRoom*
@@ -100,58 +93,42 @@ create_room (struct GNUNET_MESSENGER_SrvHandle *handle, 
const struct GNUNET_Hash
 /**
  * Destroys a room and frees its memory fully.
  *
- * @param room Room
+ * @param[in/out] room Room
  */
 void
 destroy_room (struct GNUNET_MESSENGER_SrvRoom *room);
 
 /**
- * Returns the contact of a member in a <i>room</i> identified by a given 
<i>id</i>. If the <i>room</i>
- * does not contain a member with the given <i>id</i>, NULL gets returned.
- *
- * @param room Room
- * @param id Member id
- * @return Contact or NULL
- */
-struct GNUNET_MESSENGER_SrvContact*
-get_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id);
-
-/**
- * Adds a contact from the service to a <i>room</i> under a specific <i>id</i> 
with a given public key.
+ * Returns the used member store of a given <i>room</i>.
  *
- * @param room Room
- * @param id Member id
- * @param pubkey Public key of EGO
+ * @param[in/out] room Room
+ * @return Member store
  */
-void
-add_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id,
-                  const struct GNUNET_IDENTITY_PublicKey *pubkey);
+struct GNUNET_MESSENGER_MemberStore*
+get_room_member_store (struct GNUNET_MESSENGER_SrvRoom *room);
 
 /**
- * Returns the member information of a member in a <i>room</i> identified by a 
given <i>id</i>. If the <i>room</i>
- * does not contain a member with the given <i>id</i>, NULL gets returned.
+ * Returns the used message store of a given <i>room</i>.
  *
- * @param room Room
- * @param id Member id
- * @return Member information or NULL
+ * @param[in/out] room Room
+ * @return Message store
  */
-struct GNUNET_MESSENGER_MemberInfo*
-get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id);
+struct GNUNET_MESSENGER_MessageStore*
+get_room_message_store (struct GNUNET_MESSENGER_SrvRoom *room);
 
 /**
- * Tries to generate and allocate a new unique member id checking all current 
members for possible
- * duplicates. If the function fails, NULL gets returned.
+ * Returns the used operation store of a given <i>room</i>.
  *
- * @param room Room
- * @return New member id or NULL
+ * @param[in/out] room Room
+ * @return Operation store
  */
-struct GNUNET_ShortHashCode*
-generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room);
+struct GNUNET_MESSENGER_OperationStore*
+get_room_operation_store (struct GNUNET_MESSENGER_SrvRoom *room);
 
 /**
  * Returns the member id of the member representing the handle currently 
hosting this <i>room</i>.
  *
- * @param room Room
+ * @param[in] room Room
  * @return Host member id or NULL
  */
 const struct GNUNET_ShortHashCode*
@@ -160,8 +137,8 @@ get_room_host_id (const struct GNUNET_MESSENGER_SrvRoom 
*room);
 /**
  * Changes the member id of the member representing the handle currently 
hosting this <i>room</i>.
  *
- * @param room Room
- * @param unique_id Unique member id
+ * @param[in/out] room Room
+ * @param[in] unique_id Unique member id
  */
 void
 change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *unique_id);
@@ -172,11 +149,11 @@ change_room_host_id (struct GNUNET_MESSENGER_SrvRoom 
*room, const struct GNUNET_
  *
  * Calling this method should result in joining a room and sending a peer 
message as well for this peer.
  *
- * If the function returns GNUNET_YES the port for this room is guranteed to 
be open for incoming connections.
+ * If the function returns #GNUNET_YES the port for this room is guranteed to 
be open for incoming connections.
  *
- * @param room Room
- * @param handle Handle
- * @return GNUNET_YES on success, GNUNET_NO on failure.
+ * @param[in/out] room Room
+ * @param[in/out] handle Handle
+ * @return #GNUNET_YES on success, #GNUNET_NO on failure.
  */
 int
 open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle);
@@ -186,26 +163,15 @@ open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHan
  * a peer identity of a hosting peer. During the connection the handle will 
join the room as a member, waiting for
  * an info message from the selected host.
  *
- * @param room Room
- * @param handle Handle
- * @param door Peer identity
- * @return GNUNET_YES on success, GNUNET_NO on failure.
+ * @param[in/out] room Room
+ * @param[in/out] handle Handle
+ * @param[in] door Peer identity
+ * @return #GNUNET_YES on success, #GNUNET_NO on failure.
  */
 int
-entry_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+enter_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
                const struct GNUNET_PeerIdentity *door);
 
-/**
- * Returns a tunnel granting a direct connection to a specific member in a 
<i>room</i>. The member gets identified
- * by an <i>id</i>. If no tunnel has been linked to the selected id, NULL gets 
returned.
- *
- * @param room Room
- * @param contact_id Member id
- * @return Tunnel to the member or NULL
- */
-struct GNUNET_MESSENGER_SrvTunnel*
-find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *contact_id);
-
 /**
  * Packs a <i>message</i> depending on the selected <i>mode</i> into a newly 
allocated envelope. It will set the
  * timestamp of the message, the sender id and the previous messages hash 
automatically before packing. The message
@@ -213,18 +179,18 @@ find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom 
*room, const struct GNUNET_
  *
  * If the optional <i>hash</i> parameter is a valid pointer, its value will be 
overriden by the signed messages hash.
  *
- * If <i>mode</i> is set to GNUNET_MESSENGER_PACK_MODE_ENVELOPE, the function 
returns a valid envelope to send
+ * If <i>mode</i> is set to #GNUNET_MESSENGER_PACK_MODE_ENVELOPE, the function 
returns a valid envelope to send
  * through a message queue, otherwise NULL.
  *
- * @param room Room
- * @param handle Handle
- * @param message Message
+ * @param[in] room Room
+ * @param[in] handle Handle
+ * @param[in/out] message Message
  * @param[out] hash Hash of message
- * @param mode Packing mode
+ * @param[in] mode Packing mode
  * @return New envelope or NULL
  */
 struct GNUNET_MQ_Envelope*
-pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+pack_room_message (const struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_MESSENGER_SrvHandle *handle,
                    struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash, int mode);
 
 /**
@@ -234,59 +200,69 @@ pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, 
struct GNUNET_MESSENGE
  * The function handles packing the message automatically and will call linked 
message-events locally even if
  * the message won't be sent to another peer.
  *
- * @param room Room
- * @param handle Handle
- * @param message Message
- * @param[out] hash Hash of message
+ * The function returns #GNUNET_YES on success, #GNUNET_NO if message is null 
and
+ * #GNUNET_SYSERR if the message was known already.
+ *
+ * @param[in/out] room Room
+ * @param[in/out] handle Handle
+ * @param[in/out] message Message
+ * @return #GNUNET_YES on success, #GNUNET_NO or #GNUNET_SYSERR otherwise.
  */
-void
+int
 send_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                   struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash);
+                   struct GNUNET_MESSENGER_Message *message);
 
 /**
- * Sends a <i>message</i> from a given <i>handle</i> into a <i>room</i> 
excluding one specific <i>tunnel</i>.
- * The <i>hash</i> parameter will be updated with the hash-value resulting 
from the sent message.
- *
- * The function handles packing the message automatically and will call linked 
message-events locally even if
- * the message won't be sent to another peer.
+ * Forwards a <i>message</i> with a given <i>hash</i> to a specific 
<i>tunnel</i> inside of a <i>room</i>.
  *
- * @param room Room
- * @param handle Handle
- * @param message Message
- * @param[out] hash Hash of message
- * @param tunnel Tunnel
+ * @param[in/out] room Room
+ * @param[in/out] tunnel Tunnel
+ * @param[in/out] message Message
+ * @param[in] hash Hash of message
  */
 void
-send_room_message_ext (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
-                       struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash,
-                       struct GNUNET_MESSENGER_SrvTunnel *tunnel);
+forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
 
 /**
- * Forwards a <i>message</i> with a given <i>hash</i> to a specific 
<i>tunnel</i> inside of a <i>room</i>.
+ * Checks the current state of opening a given <i>room</i> from this peer and 
re-publishes it
+ * if necessary to a selected <i>tunnel</i> or to all connected tunnels if 
necessary or if the
+ * selected tunnel is NULL.
  *
- * @param room Room
- * @param tunnel Tunnel
- * @param message Message
- * @param hash Hash of message
+ * @param[in/out] room Room
+ * @param[in/out] tunnel Tunnel
  */
 void
-forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
-                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
+check_room_peer_status (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel);
 
 /**
  * Reduces all current forks inside of the message history of a <i>room</i> to 
one remaining last message
  * by merging them down. All merge messages will be sent from a given 
<i>handle</i>.
  *
- * @param room Room
- * @param handle Handle
+ * @param[in/out] room Room
+ * @param[in/out] handle Handle
  */
 void
 merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle);
 
+/**
+ * Deletes a message from the <i>room</i> with a given <i>hash</i> in a 
specific <i>delay</i> if
+ * the provided member by its session is permitted to do so.
+ *
+ * @param[in/out] room Room
+ * @param[in/out] session Member session
+ * @param[in] hash Hash of message
+ * @param[in] delay Delay of deletion
+ * @return #GNUNET_YES on success, #GNUNET_NO if permission gets denied, 
#GNUNET_SYSERR on operation failure
+ */
+int
+delete_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_MemberSession *session,
+                     const struct GNUNET_HashCode *hash, const struct 
GNUNET_TIME_Relative delay);
+
 /**
  * Returns the CADET handle from a rooms service.
  *
- * @param room Room
+ * @param[in/out] room Room
  * @return CADET handle
  */
 struct GNUNET_CADET_Handle*
@@ -295,26 +271,26 @@ get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room);
 /**
  * Returns the shared secret you need to access a <i>room</i>.
  *
- * @param room Room
+ * @param[in] room Room
  * @return Shared secret
  */
-struct GNUNET_HashCode*
-get_room_key (struct GNUNET_MESSENGER_SrvRoom *room);
+const struct GNUNET_HashCode*
+get_room_key (const struct GNUNET_MESSENGER_SrvRoom *room);
 
 /**
  * Returns a tunnel inside of a <i>room</i> leading towards a given 
<i>peer</i> if such a tunnel exists,
  * otherwise NULL.
  *
- * @param room Room
- * @param peer Peer identity
+ * @param[in] room Room
+ * @param[in] peer Peer identity
  * @return Tunnel or NULL
  */
 const struct GNUNET_MESSENGER_SrvTunnel*
-get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *peer);
+get_room_tunnel (const struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *peer);
 
 /**
  * Returns a message from a <i>room</i> identified by a given <i>hash</i>. If 
no matching message is
- * found and <i>request</i> is set to GNUNET_YES, the <i>handle</i> will 
request the missing message
+ * found and <i>request</i> is set to #GNUNET_YES, the <i>handle</i> will 
request the missing message
  * automatically.
  *
  * The function uses the optimized check for a message via its hash from the 
message store.
@@ -323,10 +299,10 @@ get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, 
const struct GNUNET_Peer
  * If a message is missing independent of the following request, NULL gets 
returned instead of the
  * matching message.
  *
- * @param room Room
- * @param handle Handle
- * @param hash Hash of message
- * @param request Flag to request a message
+ * @param[in/out] room Room
+ * @param[in/out] handle Handle
+ * @param[in] hash Hash of message
+ * @param[in] request Flag to request a message
  * @return Message or NULL
  */
 const struct GNUNET_MESSENGER_Message*
@@ -334,45 +310,30 @@ get_room_message (struct GNUNET_MESSENGER_SrvRoom *room, 
struct GNUNET_MESSENGER
                   const struct GNUNET_HashCode *hash, int request);
 
 /**
- * Updates the last messages of a <i>room</i> by replacing them if the 
previous hash of a given <i>message</i>
- * matches with one of the latest messages.
- *
- * @param room Room
- * @param message Message
- * @param hash Hash of message
- */
-void
-update_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_MESSENGER_Message *message,
-                           const struct GNUNET_HashCode *hash);
-
-/**
- * Changes an id of a current member from an old id to a new one and adds 
optionally the <i>hash</i> of an
- * id message to the members information.
+ * Rebuilds the decentralized structure for a <i>room</i> by ensuring all 
required connections are made
+ * depending on the amount of peers and this peers index in the list of them.
  *
- * @param room Room
- * @param old_id Old member id
- * @param new_id New member id
- * @param hash Hash of id message
+ * @param[in/out] room Room
  */
 void
-switch_room_member_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *old_id,
-                       const struct GNUNET_ShortHashCode *new_id, const struct 
GNUNET_HashCode *hash);
+rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room);
 
 /**
- * Rebuilds the decentralized structure for a <i>room</i> by ensuring all 
required connections are made
- * depending on the amount of peers and this peers index in the list of them.
+ * Loads the local configuration for a given <i>room</i> of a service which 
contains the last messages hash
+ * and the ruleset for general access of new members.
  *
- * @param room Room
+ * @param[out] room Room
  */
 void
-rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room);
+load_room (struct GNUNET_MESSENGER_SrvRoom *room);
 
 /**
- * Handles all queued up messages of a room to handle in correct order.
+ * Saves the configuration for a given <i>room</i> of a service which contains 
the last messages hash
+ * and the ruleset for general access of new members locally.
  *
- * @param room Room
+ * @param[in] room Room
  */
 void
-handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room);
+save_room (struct GNUNET_MESSENGER_SrvRoom *room);
 
 #endif //GNUNET_SERVICE_MESSENGER_ROOM_H
diff --git a/src/messenger/gnunet-service-messenger_service.c 
b/src/messenger/gnunet-service-messenger_service.c
index 963314fd8..8c63e9bf4 100644
--- a/src/messenger/gnunet-service-messenger_service.c
+++ b/src/messenger/gnunet-service-messenger_service.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -24,11 +24,8 @@
  */
 
 #include "gnunet-service-messenger_service.h"
-
 #include "gnunet-service-messenger_message_kind.h"
-
 #include "gnunet-service-messenger.h"
-#include "gnunet-service-messenger_util.h"
 
 static void
 callback_shutdown_service (void *cls)
@@ -43,23 +40,11 @@ callback_shutdown_service (void *cls)
   }
 }
 
-static void
-callback_update_ego (void *cls,
-                     struct GNUNET_IDENTITY_Ego *ego,
-                     void **ctx,
-                     const char *identifier)
-{
-  if ((!ego) || (!identifier))
-    return;
-
-  struct GNUNET_MESSENGER_Service *service = cls;
-
-  update_service_ego(service, identifier, 
GNUNET_IDENTITY_ego_get_private_key(ego));
-}
-
 struct GNUNET_MESSENGER_Service*
 create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct 
GNUNET_SERVICE_Handle *service_handle)
 {
+  GNUNET_assert((config) && (service_handle));
+
   struct GNUNET_MESSENGER_Service *service = GNUNET_new(struct 
GNUNET_MESSENGER_Service);
 
   service->config = config;
@@ -90,24 +75,16 @@ create_service (const struct GNUNET_CONFIGURATION_Handle 
*config, struct GNUNET_
   }
 
   service->cadet = GNUNET_CADET_connect (service->config);
-  service->identity = GNUNET_IDENTITY_connect (service->config, 
&callback_update_ego, service);
 
-  service->egos = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  init_ego_store(get_service_ego_store(service), service->config);
 
   init_list_handles (&(service->handles));
 
-  service->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
   service->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
 
-  return service;
-}
+  init_contact_store(get_service_contact_store(service));
 
-static int
-iterate_destroy_egos (void *cls, const struct GNUNET_HashCode *key, void 
*value)
-{
-  struct GNUNET_MESSENGER_Ego *ego = value;
-  GNUNET_free(ego);
-  return GNUNET_YES;
+  return service;
 }
 
 static int
@@ -118,17 +95,11 @@ iterate_destroy_rooms (void *cls, const struct 
GNUNET_HashCode *key, void *value
   return GNUNET_YES;
 }
 
-static int
-iterate_destroy_contacts (void *cls, const struct GNUNET_HashCode *key, void 
*value)
-{
-  struct GNUNET_MESSENGER_SrvContact *contact = value;
-  destroy_contact (contact);
-  return GNUNET_YES;
-}
-
 void
 destroy_service (struct GNUNET_MESSENGER_Service *service)
 {
+  GNUNET_assert(service);
+
   if (service->shutdown)
   {
     GNUNET_SCHEDULER_cancel (service->shutdown);
@@ -136,16 +107,13 @@ destroy_service (struct GNUNET_MESSENGER_Service *service)
     service->shutdown = NULL;
   }
 
-  GNUNET_CONTAINER_multihashmap_iterate (service->egos, iterate_destroy_egos, 
NULL);
-
+  clear_ego_store(get_service_ego_store(service));
   clear_list_handles (&(service->handles));
 
   GNUNET_CONTAINER_multihashmap_iterate (service->rooms, 
iterate_destroy_rooms, NULL);
-  GNUNET_CONTAINER_multihashmap_iterate (service->contacts, 
iterate_destroy_contacts, NULL);
-
-  GNUNET_CONTAINER_multihashmap_destroy (service->egos);
   GNUNET_CONTAINER_multihashmap_destroy (service->rooms);
-  GNUNET_CONTAINER_multihashmap_destroy (service->contacts);
+
+  clear_contact_store(get_service_contact_store(service));
 
   if (service->cadet)
   {
@@ -154,13 +122,6 @@ destroy_service (struct GNUNET_MESSENGER_Service *service)
     service->cadet = NULL;
   }
 
-  if (service->identity)
-  {
-    GNUNET_IDENTITY_disconnect (service->identity);
-
-    service->identity = NULL;
-  }
-
   if (service->dir)
   {
     GNUNET_free(service->dir);
@@ -173,44 +134,27 @@ destroy_service (struct GNUNET_MESSENGER_Service *service)
   GNUNET_free(service);
 }
 
-struct GNUNET_MESSENGER_Ego*
-lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier)
+struct GNUNET_MESSENGER_EgoStore*
+get_service_ego_store (struct GNUNET_MESSENGER_Service *service)
 {
-  GNUNET_assert(identifier);
+  GNUNET_assert(service);
 
-  struct GNUNET_HashCode hash;
-
-  GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash);
-  return GNUNET_CONTAINER_multihashmap_get(service->egos, &hash);
+  return &(service->ego_store);
 }
 
-void
-update_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier,
-                    const struct GNUNET_IDENTITY_PrivateKey* key)
+struct GNUNET_MESSENGER_ContactStore*
+get_service_contact_store (struct GNUNET_MESSENGER_Service *service)
 {
-  GNUNET_assert((identifier) && (key));
-
-  struct GNUNET_HashCode hash;
-
-  GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash);
+  GNUNET_assert(service);
 
-  struct GNUNET_MESSENGER_Ego* ego = 
GNUNET_CONTAINER_multihashmap_get(service->egos, &hash);
-
-  if (!ego)
-  {
-    ego = GNUNET_new(struct GNUNET_MESSENGER_Ego);
-    GNUNET_CONTAINER_multihashmap_put(service->egos, &hash, ego, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
-  }
-
-  GNUNET_memcpy(&(ego->priv), key, sizeof(*key));
-
-  if (GNUNET_OK != GNUNET_IDENTITY_key_get_public(key, &(ego->pub)))
-    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Updating invalid ego key 
failed!\n");
+  return &(service->contact_store);
 }
 
 struct GNUNET_MESSENGER_SrvHandle*
 add_service_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MQ_Handle *mq)
 {
+  GNUNET_assert((service) && (mq));
+
   struct GNUNET_MESSENGER_SrvHandle *handle = create_handle (service, mq);
 
   if (handle)
@@ -224,6 +168,8 @@ add_service_handle (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_MQ_H
 void
 remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle)
 {
+  GNUNET_assert((service) && (handle));
+
   if (!handle)
     return;
 
@@ -234,68 +180,16 @@ remove_service_handle (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_M
 int
 get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_PeerIdentity *peer)
 {
-  return GNUNET_CRYPTO_get_peer_identity (service->config, peer);
-}
+  GNUNET_assert((service) && (peer));
 
-struct GNUNET_MESSENGER_SrvContact*
-get_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, const 
struct GNUNET_IDENTITY_PublicKey *pubkey)
-{
-  struct GNUNET_HashCode hash;
-
-  GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash);
-
-  struct GNUNET_MESSENGER_SrvContact *contact = 
GNUNET_CONTAINER_multihashmap_get (service->contacts, &hash);
-
-  if (contact)
-    return contact;
-
-  contact = create_contact (pubkey);
-
-  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (service->contacts, 
&hash, contact,
-                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
-    return contact;
-
-  destroy_contact (contact);
-  return NULL;
-}
-
-void
-swap_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvContact *contact,
-                                const struct GNUNET_IDENTITY_PublicKey *pubkey)
-{
-  const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact);
-
-  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (service->contacts, 
hash, contact))
-  {
-    GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey));
-
-    hash = get_contact_id_from_key (contact);
-
-    GNUNET_CONTAINER_multihashmap_put (service->contacts, hash, contact,
-                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
-  }
-}
-
-struct GNUNET_ShortHashCode*
-generate_service_new_member_id (struct GNUNET_MESSENGER_Service *service, 
const struct GNUNET_HashCode *key)
-{
-  struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
-
-  if (room)
-  {
-    return generate_room_member_id (room);
-  }
-  else
-  {
-    struct GNUNET_ShortHashCode *random_id = GNUNET_new(struct 
GNUNET_ShortHashCode);
-    generate_free_member_id (random_id, NULL);
-    return random_id;
-  }
+  return GNUNET_CRYPTO_get_peer_identity (service->config, peer);
 }
 
 struct GNUNET_MESSENGER_SrvRoom*
-get_service_room (struct GNUNET_MESSENGER_Service *service, const struct 
GNUNET_HashCode *key)
+get_service_room (const struct GNUNET_MESSENGER_Service *service, const struct 
GNUNET_HashCode *key)
 {
+  GNUNET_assert((service) && (key));
+
   return GNUNET_CONTAINER_multihashmap_get (service->rooms, key);
 }
 
@@ -303,6 +197,8 @@ int
 open_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
                    const struct GNUNET_HashCode *key)
 {
+  GNUNET_assert((service) && (handle) && (key));
+
   struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
 
   if (room)
@@ -310,8 +206,10 @@ open_service_room (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_MESSE
 
   room = create_room (handle, key);
 
-  if ((GNUNET_YES == open_room (room, handle)) && (GNUNET_OK
-      == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+  if ((GNUNET_YES == open_room (room, handle)) &&
+      (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (service->rooms,
+                                                       key, room,
+                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
     return GNUNET_YES;
 
   destroy_room (room);
@@ -322,11 +220,13 @@ int
 entry_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
                     const struct GNUNET_PeerIdentity *door, const struct 
GNUNET_HashCode *key)
 {
+  GNUNET_assert((service) && (handle) && (door) && (key));
+
   struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
 
   if (room)
   {
-    if (GNUNET_YES == entry_room_at (room, handle, door))
+    if (GNUNET_YES == enter_room_at (room, handle, door))
       return GNUNET_YES;
     else
       return GNUNET_NO;
@@ -334,8 +234,10 @@ entry_service_room (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_MESS
 
   room = create_room (handle, key);
 
-  if ((GNUNET_YES == entry_room_at (room, handle, door)) && (GNUNET_OK
-      == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+  if ((GNUNET_YES == enter_room_at (room, handle, door)) &&
+      (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (service->rooms,
+                                                       key, room,
+                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
   {
     return GNUNET_YES;
   }
@@ -351,20 +253,14 @@ int
 close_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
                     const struct GNUNET_HashCode *key)
 {
+  GNUNET_assert((service) && (handle) && (key));
+
   struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
 
   if (!room)
     return GNUNET_NO;
 
-  struct GNUNET_MESSENGER_Message *message = create_message_leave ();
-
-  if (message)
-  {
-    struct GNUNET_HashCode hash;
-
-    send_room_message (room, handle, message, &hash);
-    destroy_message (message);
-  }
+  send_room_message (room, handle, create_message_leave ());
 
   const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key);
 
@@ -393,124 +289,18 @@ close_service_room (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_MESS
   return GNUNET_YES;
 }
 
-static void
-get_room_data_subdir (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvRoom *room, char **dir)
-{
-  GNUNET_asprintf (dir, "%s%s%c%s%c", service->dir, "rooms", DIR_SEPARATOR, 
GNUNET_h2s (&(room->key)), DIR_SEPARATOR);
-}
-
-void
-load_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room)
-{
-  char *room_dir;
-  get_room_data_subdir (service, room, &room_dir);
-
-  if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES))
-  {
-    load_message_store (&room->store, room_dir);
-
-    char *config_file;
-    GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg");
-
-    struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
-
-    if ((GNUNET_YES == GNUNET_DISK_file_test (config_file)) && (GNUNET_OK
-        == GNUNET_CONFIGURATION_parse (cfg, config_file)))
-    {
-      unsigned long long access;
-
-      if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "room", 
"access-rule", &access))
-        room->strict_access = (int) (access);
-
-      char *message_string;
-
-      if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "room", 
"last-message", &message_string)) && (message_string))
-      {
-        struct GNUNET_HashCode hash;
-
-        GNUNET_CRYPTO_hash_from_string(message_string, &hash);
-
-        const struct GNUNET_MESSENGER_Message *message = get_room_message 
(room, room->host, &hash, GNUNET_NO);
-
-        if (message)
-          update_room_last_messages (room, message, &hash);
-
-        GNUNET_free(message_string);
-      }
-    }
-
-    GNUNET_CONFIGURATION_destroy (cfg);
-
-    GNUNET_free(config_file);
-  }
-
-  GNUNET_free(room_dir);
-}
-
-void
-save_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room)
-{
-  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (service->rooms, 
&(room->key)))
-  {
-    return;
-  }
-
-  char *room_dir;
-  get_room_data_subdir (service, room, &room_dir);
-
-  if ((GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_NO)) || 
(GNUNET_OK
-      == GNUNET_DISK_directory_create (room_dir)))
-  {
-    save_message_store (&room->store, room_dir);
-
-    char *config_file;
-    GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg");
-
-    struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
-
-    GNUNET_CONFIGURATION_set_value_number (cfg, "room", "access-rule", 
room->strict_access);
-
-    if (room->last_messages.head)
-      GNUNET_CONFIGURATION_set_value_string (cfg, "room", "last-message",
-                                             GNUNET_h2s_full 
(&(room->last_messages.head->hash)));
-
-    GNUNET_CONFIGURATION_write (cfg, config_file);
-    GNUNET_CONFIGURATION_destroy (cfg);
-
-    GNUNET_free(config_file);
-  }
-
-  GNUNET_free(room_dir);
-}
-
 void
 handle_service_message (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvRoom *room,
+                        const struct GNUNET_MESSENGER_MemberSession *session,
                         const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_ListHandle *element = service->handles.head;
+  GNUNET_assert((service) && (room) && (session) && (message) && (hash));
 
-  const uint16_t length = get_message_size (message);
+  struct GNUNET_MESSENGER_ListHandle *element = service->handles.head;
 
   while (element)
   {
-    struct GNUNET_MESSENGER_SrvHandle *handle = (struct 
GNUNET_MESSENGER_SrvHandle*) element->handle;
-
-    if ((handle->mq) && (get_handle_member_id (handle, &(room->key))))
-    {
-      struct GNUNET_MESSENGER_RecvMessage *msg;
-      struct GNUNET_MQ_Envelope *env;
-
-      env = GNUNET_MQ_msg_extra(msg, length, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE);
-
-      GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
-      GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash));
-
-      char *buffer = ((char*) msg) + sizeof(*msg);
-      encode_message (message, length, buffer);
-
-      GNUNET_MQ_send (handle->mq, env);
-    }
-
+    notify_handle_message (element->handle, get_room_key(room), session, 
message, hash);
     element = element->next;
   }
 }
diff --git a/src/messenger/gnunet-service-messenger_service.h 
b/src/messenger/gnunet-service-messenger_service.h
index 246c74771..aa43fa457 100644
--- a/src/messenger/gnunet-service-messenger_service.h
+++ b/src/messenger/gnunet-service-messenger_service.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -33,13 +33,14 @@
 #include "gnunet_disk_lib.h"
 #include "gnunet_identity_service.h"
 
-#include "messenger_api_ego.h"
-
+#include "gnunet-service-messenger_ego_store.h"
 #include "gnunet-service-messenger_list_handles.h"
 
-#include "gnunet-service-messenger_contact.h"
+#include "messenger_api_contact_store.h"
 #include "gnunet-service-messenger_room.h"
 
+#include "gnunet-service-messenger_member_session.h"
+
 struct GNUNET_MESSENGER_Service
 {
   const struct GNUNET_CONFIGURATION_Handle *config;
@@ -50,21 +51,20 @@ struct GNUNET_MESSENGER_Service
   char *dir;
 
   struct GNUNET_CADET_Handle *cadet;
-  struct GNUNET_IDENTITY_Handle *identity;
 
-  struct GNUNET_CONTAINER_MultiHashMap *egos;
+  struct GNUNET_MESSENGER_EgoStore ego_store;
+  struct GNUNET_MESSENGER_ContactStore contact_store;
 
   struct GNUNET_MESSENGER_ListHandles handles;
 
-  struct GNUNET_CONTAINER_MultiHashMap *contacts;
   struct GNUNET_CONTAINER_MultiHashMap *rooms;
 };
 
 /**
  * Creates and allocates a new service using a given <i>config</i> and a 
GNUnet service handle.
  *
- * @param config Configuration
- * @param service_handle GNUnet service handle
+ * @param[in] config Configuration
+ * @param[in/out] service_handle GNUnet service handle
  * @return New service
  */
 struct GNUNET_MESSENGER_Service*
@@ -73,39 +73,34 @@ create_service (const struct GNUNET_CONFIGURATION_Handle 
*config, struct GNUNET_
 /**
  * Destroys a <i>service</i> and frees its memory fully.
  *
- * @param service Service
+ * @param[in/out] service Service
  */
 void
 destroy_service (struct GNUNET_MESSENGER_Service *service);
 
 /**
- * Lookups an EGO which was registered to a <i>service</i> under
- * a specific <i>identifier</i>.
+ * Returns the used EGO-store of a given <i>service</i>.
  *
- * @param service Service
- * @param identifier Identifier string
- * @return EGO or NULL
+ * @param[in/out] service Service
+ * @return EGO-store
  */
-struct GNUNET_MESSENGER_Ego*
-lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier);
+struct GNUNET_MESSENGER_EgoStore*
+get_service_ego_store (struct GNUNET_MESSENGER_Service *service);
 
 /**
- * Updates the registration of an EGO to a <i>service</i> under
- * a specific <i>identifier</i> with a new <i>key</i>.
+ * Returns the used contact store of a given <i>service</i>.
  *
- * @param service Service
- * @param identifier Identifier string
- * @param key Private EGO key
+ * @param[in/out] service Service
+ * @return Contact store
  */
-void
-update_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier,
-                    const struct GNUNET_IDENTITY_PrivateKey* key);
+struct GNUNET_MESSENGER_ContactStore*
+get_service_contact_store (struct GNUNET_MESSENGER_Service *service);
 
 /**
  * Creates and adds a new handle to a <i>service</i> using a given message 
queue.
  *
- * @param service Service
- * @param mq Message queue
+ * @param[in/out] service Service
+ * @param[in/out] mq Message queue
  * @return New handle
  */
 struct GNUNET_MESSENGER_SrvHandle*
@@ -114,77 +109,42 @@ add_service_handle (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_MQ_H
 /**
  * Removes a <i>handle</i> from a <i>service</i> and destroys it.
  *
- * @param service Service
- * @param handle Handle
+ * @param[in/out] service Service
+ * @param[in/out] handle Handle
  */
 void
 remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle);
 
 /**
  * Tries to write the peer identity of the peer running a <i>service</i> on to 
the <i>peer</i>
- * parameter. The functions returns GNUNET_OK on success, otherwise 
GNUNET_SYSERR.
+ * parameter. The functions returns #GNUNET_OK on success, otherwise 
#GNUNET_SYSERR.
  *
- * @param service Service
+ * @param[in] service Service
  * @param[out] peer Peer identity
- * @return GNUNET_OK on success, otherwise GNUNET_SYSERR
+ * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR
  */
 int
 get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_PeerIdentity *peer);
 
-/**
- * Returns a contact of a <i>service</i> identified by a given public key. If 
no matching contact exists,
- * it will tried to create one with the specific public key. If the function 
still fails to do so,
- * NULL gets returned.
- *
- * @param service Service
- * @param pubkey Public key of EGO
- * @return Contact
- */
-struct GNUNET_MESSENGER_SrvContact*
-get_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, const 
struct GNUNET_IDENTITY_PublicKey *pubkey);
-
-/**
- * Changes the public key for a <i>contact</i> known to a <i>service</i> to a 
specific public key and
- * updates local map entries to access the contact by its updated key.
- *
- * @param service Service
- * @param contact Contact
- * @param pubkey Public key of EGO
- */
-void
-swap_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvContact *contact,
-                                const struct GNUNET_IDENTITY_PublicKey 
*pubkey);
-
-/**
- * Tries to generate and allocate a new unique member id for a given room of a 
service identified by its <i>key</i>.
- * If the generation fails caused by too many tries of duplicates, it returns 
NULL.
- *
- * @param service Service
- * @param key Key of room
- * @return Newly generated member id or NULL
- */
-struct GNUNET_ShortHashCode*
-generate_service_new_member_id (struct GNUNET_MESSENGER_Service *service, 
const struct GNUNET_HashCode *key);
-
 /**
  * Returns the room identified by a given <i>key</i> for a <i>service</i>. If 
the service doesn't know any room
  * using the given key, NULL gets returned.
  *
- * @param service Service
- * @param key Key of room
+ * @param[in] service Service
+ * @param[in] key Key of room
  * @return Room or NULL
  */
 struct GNUNET_MESSENGER_SrvRoom*
-get_service_room (struct GNUNET_MESSENGER_Service *service, const struct 
GNUNET_HashCode *key);
+get_service_room (const struct GNUNET_MESSENGER_Service *service, const struct 
GNUNET_HashCode *key);
 
 /**
  * Tries to open a room using a given <i>key</i> for a <i>service</i> by a 
specific <i>handle</i>. The room will be
- * created if necessary. If the function is successful, it returns GNUNET_YES, 
otherwise GNUNET_NO.
+ * created if necessary. If the function is successful, it returns 
#GNUNET_YES, otherwise #GNUNET_NO.
  *
- * @param service Service
- * @param handle Handle
- * @param key Key of room
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] service Service
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 open_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
@@ -192,16 +152,16 @@ open_service_room (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_MESSE
 
 /**
  * Tries to enter a room using a given <i>key</i> for a <i>service</i> by a 
specific <i>handle</i>. The room will
- * be created if necessary. If the function is successful, it returns 
GNUNET_YES, otherwise GNUNET_NO.
+ * be created if necessary. If the function is successful, it returns 
#GNUNET_YES, otherwise #GNUNET_NO.
  *
  * The room will be entered through the peer identitied by the peer identity 
provided as <i>door</i> parameter and
  * a new connection will be made.
  *
- * @param service Service
- * @param handle Handle
- * @param door Peer identity
- * @param key Key of room
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] service Service
+ * @param[in/out] handle Handle
+ * @param[in] door Peer identity
+ * @param[in] key Key of room
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 entry_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
@@ -209,51 +169,33 @@ entry_service_room (struct GNUNET_MESSENGER_Service 
*service, struct GNUNET_MESS
 
 /**
  * Tries to close a room using a given <i>key</i> for a <i>service</i> by a 
specific <i>handle</i>. The room will
- * be created if necessary. If the function is successful, it returns 
GNUNET_YES, otherwise GNUNET_NO.
+ * be created if necessary. If the function is successful, it returns 
#GNUNET_YES, otherwise #GNUNET_NO.
  *
  * If the specific handle is currently the host of the room for this service, 
a new handle which is a member will
  * take its place. Otherwise the room will be destroyed for this service.
  *
- * @param service Service
- * @param handle Handle
- * @param key Key of room
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] service Service
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 close_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
                     const struct GNUNET_HashCode *key);
 
-/**
- * Loads the local configuration for a given <i>room</i> of a <i>service</i> 
which contains the last messages hash
- * and the ruleset for general access of new members.
- *
- * @param service Service
- * @param room Room
- */
-void
-load_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room);
-
-/**
- * Saves the configuration for a given <i>room</i> of a <i>service</i> which 
contains the last messages hash
- * and the ruleset for general access of new members locally.
- *
- * @param service Service
- * @param room Room
- */
-void
-save_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room);
-
 /**
  * Sends a received or sent <i>message</i> with a given <i>hash</i> to each 
handle of a <i>service</i> which
  * is currently member of a specific <i>room</i> for handling it in the client 
API.
  *
- * @param service Service
- * @param room Room
- * @param message Message
- * @param hash Hash of message
+ * @param[in/out] service Service
+ * @param[in/out] room Room
+ * @param[in] session Member session
+ * @param[in] message Message
+ * @param[in] hash Hash of message
  */
 void
 handle_service_message (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvRoom *room,
+                        const struct GNUNET_MESSENGER_MemberSession *session,
                         const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
 
 #endif //GNUNET_SERVICE_MESSENGER_SERVICE_H
diff --git a/src/messenger/gnunet-service-messenger_tunnel.c 
b/src/messenger/gnunet-service-messenger_tunnel.c
index df9e5c4c7..f7e8713c6 100644
--- a/src/messenger/gnunet-service-messenger_tunnel.c
+++ b/src/messenger/gnunet-service-messenger_tunnel.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -26,7 +26,8 @@
 #include "gnunet-service-messenger_tunnel.h"
 
 #include "gnunet-service-messenger_handle.h"
-#include "gnunet-service-messenger_util.h"
+#include "gnunet-service-messenger_message_recv.h"
+#include "messenger_api_util.h"
 
 struct GNUNET_MESSENGER_SrvTunnel*
 create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *door)
@@ -39,7 +40,8 @@ create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const 
struct GNUNET_PeerId
   tunnel->channel = NULL;
 
   tunnel->peer = GNUNET_PEER_intern (door);
-  tunnel->contact_id = NULL;
+
+  tunnel->messenger_version = 0;
 
   tunnel->peer_message = NULL;
   tunnel->last_message = NULL;
@@ -57,9 +59,6 @@ destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
 
   GNUNET_PEER_change_rc (tunnel->peer, -1);
 
-  if (tunnel->contact_id)
-    GNUNET_free(tunnel->contact_id);
-
   if (tunnel->peer_message)
     GNUNET_free(tunnel->peer_message);
 
@@ -69,22 +68,15 @@ destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
   GNUNET_free(tunnel);
 }
 
-int
+void
 bind_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_CADET_Channel *channel)
 {
   GNUNET_assert(tunnel);
 
   if (tunnel->channel)
-  {
-    if (tunnel->contact_id)
-      return GNUNET_NO;
-
     delayed_disconnect_channel (tunnel->channel);
-  }
 
   tunnel->channel = channel;
-
-  return GNUNET_YES;
 }
 
 extern void
@@ -113,52 +105,49 @@ check_tunnel_message (void *cls, const struct 
GNUNET_MessageHeader *header)
   struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
 
   if (!tunnel)
-    return GNUNET_NO;
+    return GNUNET_SYSERR;
 
   const uint16_t length = ntohs (header->size) - sizeof(*header);
   const char *buffer = (const char*) &header[1];
 
   struct GNUNET_MESSENGER_Message message;
 
-  if (length < sizeof(message.header))
-    return GNUNET_NO;
-
-  if (GNUNET_YES != decode_message (&message, length, buffer))
-    return GNUNET_NO;
-
-  struct GNUNET_HashCode hash;
-  hash_message (length, buffer, &hash);
+  if (length < get_message_kind_size(GNUNET_MESSENGER_KIND_UNKNOWN))
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Tunnel error: Message too short! 
(%d)\n", length);
+    return GNUNET_SYSERR;
+  }
 
-  int result = callback_verify_room_message (tunnel->room, cls, &message, 
&hash);
+  uint16_t padding = 0;
 
-  if (GNUNET_MESSENGER_KIND_PEER == message.header.kind)
+  if (GNUNET_YES != decode_message (&message, length, buffer, GNUNET_YES, 
&padding))
   {
-    struct GNUNET_PeerIdentity identity;
-
-    GNUNET_PEER_resolve (tunnel->peer, &identity);
-
-    if (0 == GNUNET_memcmp(&(message.body.peer.peer), &(identity)))
-    {
-      if (tunnel->contact_id)
-      {
-        if (0 != GNUNET_memcmp(tunnel->contact_id, 
&(message.header.sender_id)))
-          result = GNUNET_SYSERR;
-      }
-      else
-      {
-        tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
-
-        GNUNET_memcpy(tunnel->contact_id, &(message.header.sender_id), 
sizeof(struct GNUNET_ShortHashCode));
-      }
-    }
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Tunnel error: Decoding failed!\n");
+    return GNUNET_SYSERR;
   }
 
-  return (result == GNUNET_YES ? GNUNET_OK : GNUNET_NO);
+  struct GNUNET_HashCode hash;
+  hash_message (&message, length - padding, buffer, &hash);
+
+  return callback_verify_room_message (tunnel->room, cls, &message, &hash);
 }
 
+extern int
+update_room_message (struct GNUNET_MESSENGER_SrvRoom *room,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
 extern void
-callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct 
GNUNET_MESSENGER_Message *message,
-                    const struct GNUNET_HashCode *hash);
+callback_room_handle_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                              const struct GNUNET_MESSENGER_Message *message, 
const struct GNUNET_HashCode *hash);
+
+static void
+update_tunnel_last_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const 
struct GNUNET_HashCode *hash)
+{
+  if (!tunnel->last_message)
+    tunnel->last_message = GNUNET_new(struct GNUNET_HashCode);
+
+  GNUNET_memcpy(tunnel->last_message, hash, sizeof(*hash));
+}
 
 void
 handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header)
@@ -171,19 +160,48 @@ handle_tunnel_message (void *cls, const struct 
GNUNET_MessageHeader *header)
   struct GNUNET_MESSENGER_Message message;
   struct GNUNET_HashCode hash;
 
-  decode_message (&message, length, buffer);
-  hash_message (length, buffer, &hash);
+  uint16_t padding = 0;
 
-  if (tunnel)
-  {
-    if (!tunnel->last_message)
-      tunnel->last_message = GNUNET_new(struct GNUNET_HashCode);
+  decode_message (&message, length, buffer, GNUNET_YES, &padding);
+  hash_message (&message, length - padding, buffer, &hash);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got message of kind: %s!\n",
+             GNUNET_MESSENGER_name_of_kind(message.header.kind));
+
+  if (!tunnel)
+    return;
+
+  const int new_message = update_room_message (tunnel->room, copy_message 
(&message), &hash);
+
+  if (GNUNET_YES != new_message)
+    goto receive_done;
 
-    GNUNET_memcpy(tunnel->last_message, &hash, sizeof(struct GNUNET_HashCode));
+  update_tunnel_last_message (tunnel, &hash);
 
-    callback_room_recv (tunnel->room, cls, copy_message (&message), &hash);
+  int forward_message = GNUNET_YES;
+
+  switch (message.header.kind)
+  {
+  case GNUNET_MESSENGER_KIND_INFO:
+    forward_message = recv_message_info (tunnel->room, tunnel, &message, 
&hash);
+    break;
+  case GNUNET_MESSENGER_KIND_PEER:
+    forward_message = recv_message_peer (tunnel->room, tunnel, &message, 
&hash);
+    break;
+  case GNUNET_MESSENGER_KIND_REQUEST:
+    forward_message = recv_message_request (tunnel->room, tunnel, &message, 
&hash);
+    break;
+  default:
+    break;
+  }
+
+  if (GNUNET_YES == forward_message)
+  {
+    forward_room_message (tunnel->room, tunnel, &message, &hash);
+    callback_room_handle_message (tunnel->room, NULL, &message, &hash);
   }
 
+receive_done:
   GNUNET_CADET_receive_done (tunnel->channel);
 }
 
@@ -198,7 +216,7 @@ connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
   const struct GNUNET_PeerIdentity *door = GNUNET_PEER_resolve2 (tunnel->peer);
 
   struct GNUNET_CADET_Handle *cadet = get_room_cadet (tunnel->room);
-  struct GNUNET_HashCode *key = get_room_key (tunnel->room);
+  const struct GNUNET_HashCode *key = get_room_key (tunnel->room);
 
   struct GNUNET_MQ_MessageHandler handlers[] = { 
GNUNET_MQ_hd_var_size(tunnel_message, GNUNET_MESSAGE_TYPE_CADET_CLI,
                                                                        struct 
GNUNET_MessageHeader, NULL),
@@ -212,6 +230,8 @@ connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
 void
 disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
 {
+  GNUNET_assert(tunnel);
+
   if (tunnel->channel)
   {
     delayed_disconnect_channel (tunnel->channel);
@@ -223,6 +243,8 @@ disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel 
*tunnel)
 int
 is_tunnel_connected (const struct GNUNET_MESSENGER_SrvTunnel *tunnel)
 {
+  GNUNET_assert(tunnel);
+
   return (tunnel->channel ? GNUNET_YES : GNUNET_NO);
 }
 
@@ -232,30 +254,23 @@ struct GNUNET_MESSENGER_MessageSent
   struct GNUNET_HashCode hash;
 };
 
-extern void
-callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle, void *cls,
-                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
-
 static void
 callback_tunnel_sent (void *cls)
 {
   struct GNUNET_MESSENGER_MessageSent *sent = cls;
 
   if (sent->tunnel)
-  {
-    if (!sent->tunnel->last_message)
-      sent->tunnel->last_message = GNUNET_new(struct GNUNET_HashCode);
-
-    GNUNET_memcpy(sent->tunnel->last_message, &(sent->hash), sizeof(struct 
GNUNET_HashCode));
-  }
+    update_tunnel_last_message (sent->tunnel, &(sent->hash));
 
   GNUNET_free(sent);
 }
 
 void
-send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MQ_Envelope *env,
-                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MQ_Envelope *env,
+                      const struct GNUNET_HashCode *hash)
 {
+  GNUNET_assert((tunnel) && (env) && (hash));
+
   struct GNUNET_MQ_Handle *mq = GNUNET_CADET_get_mq (tunnel->channel);
 
   struct GNUNET_MESSENGER_MessageSent *sent = GNUNET_new(struct 
GNUNET_MESSENGER_MessageSent);
@@ -266,35 +281,91 @@ send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel 
*tunnel, void *handle, s
 
   GNUNET_MQ_notify_sent (env, callback_tunnel_sent, sent);
   GNUNET_MQ_send (mq, env);
-
-  callback_room_sent (tunnel->room, (struct GNUNET_MESSENGER_SrvHandle*) 
handle, tunnel, message, hash);
 }
 
-void
-send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MESSENGER_Message *message,
-                     struct GNUNET_HashCode *hash)
+int
+send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MESSENGER_Message *message)
 {
-  struct GNUNET_MQ_Envelope *env = pack_room_message (tunnel->room, (struct 
GNUNET_MESSENGER_SrvHandle*) handle,
-                                                      message, hash,
-                                                      
GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
+  GNUNET_assert((tunnel) && (handle));
+
+  if (!message)
+    return GNUNET_NO;
+
+  struct GNUNET_HashCode hash;
+  struct GNUNET_MQ_Envelope *env = pack_room_message (
+      tunnel->room, (struct GNUNET_MESSENGER_SrvHandle*) handle,
+      message, &hash, GNUNET_MESSENGER_PACK_MODE_ENVELOPE
+  );
+
+  destroy_message(message);
+
+  if (!env)
+    return GNUNET_NO;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending tunnel message: %s\n",
+             GNUNET_h2s(&hash));
 
-  if (env)
-    send_tunnel_envelope (tunnel, handle, env, copy_message (message), hash);
+  send_tunnel_envelope (tunnel, env, &hash);
+  return GNUNET_YES;
 }
 
 void
 forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const 
struct GNUNET_MESSENGER_Message *message,
                         const struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_Message *clone = copy_message (message);
-  struct GNUNET_MQ_Envelope *env = pack_message (clone, NULL, NULL, 
GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
+  if (!message)
+    return;
+
+  GNUNET_assert((tunnel) && (message) && (hash));
+
+  struct GNUNET_MESSENGER_Message *copy = copy_message(message);
+  struct GNUNET_MQ_Envelope *env = pack_message (copy, NULL, NULL, 
GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
+
+  destroy_message(copy);
+
+  if (!env)
+    return;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Forwarding tunnel message: %s\n",
+             GNUNET_h2s(hash));
 
-  if (env)
-    send_tunnel_envelope (tunnel, NULL, env, clone, hash);
+  send_tunnel_envelope (tunnel, env, hash);
 }
 
 const struct GNUNET_HashCode*
 get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel)
 {
+  GNUNET_assert(tunnel);
+
   return tunnel->peer_message;
 }
+
+void
+get_tunnel_peer_identity (const struct GNUNET_MESSENGER_SrvTunnel *tunnel, 
struct GNUNET_PeerIdentity *peer)
+{
+  GNUNET_assert(tunnel);
+
+  GNUNET_PEER_resolve(tunnel->peer, peer);
+}
+
+uint32_t
+get_tunnel_messenger_version (const struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+  GNUNET_assert(tunnel);
+
+  return tunnel->messenger_version;
+}
+
+int
+update_tunnel_messenger_version (struct GNUNET_MESSENGER_SrvTunnel *tunnel, 
uint32_t version)
+{
+  GNUNET_assert(tunnel);
+
+  if (version != GNUNET_MESSENGER_VERSION)
+    return GNUNET_SYSERR;
+
+  if (version > tunnel->messenger_version)
+    tunnel->messenger_version = version;
+
+  return GNUNET_OK;
+}
diff --git a/src/messenger/gnunet-service-messenger_tunnel.h 
b/src/messenger/gnunet-service-messenger_tunnel.h
index e6efb226d..51c5d32c1 100644
--- a/src/messenger/gnunet-service-messenger_tunnel.h
+++ b/src/messenger/gnunet-service-messenger_tunnel.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -39,17 +39,18 @@ struct GNUNET_MESSENGER_SrvTunnel
   struct GNUNET_CADET_Channel *channel;
 
   GNUNET_PEER_Id peer;
-  struct GNUNET_ShortHashCode *contact_id;
+
+  uint32_t messenger_version;
 
   struct GNUNET_HashCode *peer_message;
   struct GNUNET_HashCode *last_message;
 };
 
 /**
- * Creates and allocates a tunnel of a <i>room</i> to a specific peer identity.
+ * Creates and allocates a tunnel of a <i>room</i> to a specific peer identity 
(called <i>door</i>).
  *
- * @param room Room
- * @param door Peer identity
+ * @param[in/out] room Room
+ * @param[in] door Peer identity
  * @return New tunnel
  */
 struct GNUNET_MESSENGER_SrvTunnel*
@@ -58,28 +59,27 @@ create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const 
struct GNUNET_PeerId
 /**
  * Destroys a <i>tunnel</i> and frees its memory fully.
  *
- * @param tunnel
+ * @param[in/out] tunnel
  */
 void
 destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel);
 
 /**
- * Binds a CADET <i>channel</i> to a <i>tunnel</i> on returns GNUNET_YES only 
if
- * the bounds channel was replaced successfully, otherwise GNUNET_NO gets 
returned.
+ * Binds a CADET <i>channel</i> to a <i>tunnel</i> and replaces its channel
+ * the tunnel is currently bound to if necessary.
  *
- * @param tunnel Tunnel
- * @param channel CADET channel
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] tunnel Tunnel
+ * @param[in/out] channel CADET channel
  */
-int
+void
 bind_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_CADET_Channel *channel);
 
 /**
  * Tries to connect a <i>tunnel</i> by creating a new CADET channel and 
binding it.
- * The function returns GNUNET_YES on success, otherwise GNUNET_NO.
+ * The function returns #GNUNET_YES on success, otherwise #GNUNET_NO.
  *
- * @param tunnel Tunnel
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] tunnel Tunnel
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel);
@@ -88,7 +88,7 @@ connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel);
  * Disconnects and unbinds a channel from a <i>tunnel</i>. The actual 
disconnection
  * will be asynchronous.
  *
- * @param tunnel Tunnel
+ * @param[in/out] tunnel Tunnel
  */
 void
 disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel);
@@ -96,46 +96,43 @@ disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel 
*tunnel);
 /**
  * Returns the status of a currently bound channel of a <i>tunnel</i>.
  *
- * @param tunnel Tunnel
- * @return GNUNET_YES or GNUNET_NO
+ * @param[in] tunnel Tunnel
+ * @return #GNUNET_YES or #GNUNET_NO
  */
 int
 is_tunnel_connected (const struct GNUNET_MESSENGER_SrvTunnel *tunnel);
 
 /**
  * Sends an envelope containing a <i>message</i> with a given <i>hash</i> 
through
- * a <i>tunnel</i> by a given <i>handle</i>.
+ * a <i>tunnel</i>.
  *
- * @param tunnel Tunnel
- * @param handle Handle
- * @param env Envelope
- * @param message Message
- * @param hash Hash of message
+ * @param[in/out] tunnel Tunnel
+ * @param[in/out] env Envelope
+ * @param[in] hash Hash of message
  */
 void
-send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MQ_Envelope *env,
-                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MQ_Envelope *env,
+                      const struct GNUNET_HashCode *hash);
 
 /**
  * Sends a <i>message</i> by packing it automatically into an envelope and 
passing it
  * through the <i>tunnel</i>. The used <i>handle</i> will sign the message and
  * the <i>hash</i> will be calculated and stored.
  *
- * @param tunnel Tunnel
- * @param handle Handle
- * @param[out] message Message
- * @param[out] hash Hash of message
+ * @param[in/out] tunnel Tunnel
+ * @param[in/out] handle Handle
+ * @param[in/out] message Message
+ * @return #GNUNET_YES on success, GNUNET_NO otherwise
  */
-void
-send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MESSENGER_Message *message,
-                     struct GNUNET_HashCode *hash);
+int
+send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MESSENGER_Message *message);
 
 /**
  * Forwards a given <i>message</i> with a known <i>hash</i> through a 
<i>tunnel</i>.
  *
- * @param tunnel Tunnel
- * @param message Message
- * @param hash Hash of message
+ * @param[in/out] tunnel Tunnel
+ * @param[in] message Message
+ * @param[in] hash Hash of message
  */
 void
 forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const 
struct GNUNET_MESSENGER_Message *message,
@@ -146,10 +143,43 @@ forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel 
*tunnel, const struct
  * and matching the tunnels peer identity. If no peer message has been linked 
to the tunnel
  * yet, NULL gets returned.
  *
- * @param tunnel Tunnel
+ * @param[in] tunnel Tunnel
  * @return Hash of peer message or NULL
  */
 const struct GNUNET_HashCode*
 get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel);
 
+/**
+ * Writes the peer identity of the peer connected via <i>tunnel</i> to this 
peer into
+ * the <i>peer</i> parameter.
+ *
+ * @param[in] tunnel Tunnel
+ * @param[out] peer Peer identity
+ */
+void
+get_tunnel_peer_identity (const struct GNUNET_MESSENGER_SrvTunnel *tunnel, 
struct GNUNET_PeerIdentity *peer);
+
+/**
+ * Returns the current messenger version the peer connected via a given 
<i>tunnel</i>
+ * has reported to be using if it was compatible during updating.
+ *
+ * @see update_tunnel_messenger_version
+ *
+ * @param[in] tunnel Tunnel
+ * @return Version of messenger
+ */
+uint32_t
+get_tunnel_messenger_version (const struct GNUNET_MESSENGER_SrvTunnel *tunnel);
+
+/**
+ * Updates the messenger version of the <i>tunnel</i> to a given 
<i>version</i> if
+ * it is compatible to the running peer of the service. Depending on success it
+ * returns #GNUNET_OK or #GNUNET_SYSERR on failure.
+ *
+ * @param[in/out] tunnel Tunnel
+ * @param[in] version Version of messenger
+ */
+int
+update_tunnel_messenger_version (struct GNUNET_MESSENGER_SrvTunnel *tunnel, 
uint32_t version);
+
 #endif //GNUNET_SERVICE_MESSENGER_TUNNEL_H
diff --git a/src/messenger/messenger_api.c b/src/messenger/messenger_api.c
index 6401b18d7..dc6d11aaf 100644
--- a/src/messenger/messenger_api.c
+++ b/src/messenger/messenger_api.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -29,6 +29,7 @@
 
 #include "messenger_api_handle.h"
 #include "messenger_api_message.h"
+#include "messenger_api_util.h"
 
 const char*
 GNUNET_MESSENGER_name_of_kind (enum GNUNET_MESSENGER_MessageKind kind)
@@ -61,6 +62,8 @@ GNUNET_MESSENGER_name_of_kind (enum 
GNUNET_MESSENGER_MessageKind kind)
     return "TEXT";
   case GNUNET_MESSENGER_KIND_FILE:
     return "FILE";
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    return "PRIVATE";
   default:
     return "UNKNOWN";
   }
@@ -82,7 +85,25 @@ handle_get_name (void *cls, const struct 
GNUNET_MESSENGER_NameMessage *msg)
 
   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set name of handle: %s\n", name);
 
-  set_handle_name (handle, strlen(name) > 0? name : NULL);
+  set_handle_name (handle, strlen (name) > 0 ? name : NULL);
+}
+
+static int
+check_get_key (void *cls, const struct GNUNET_MESSENGER_KeyMessage *msg)
+{
+  const uint16_t full_length = ntohs (msg->header.size);
+
+  if (full_length < sizeof(*msg))
+    return GNUNET_NO;
+
+  const uint16_t length = full_length - sizeof(*msg);
+  const char *buffer = ((const char*) msg) + sizeof(*msg);
+
+  struct GNUNET_IDENTITY_PublicKey pubkey;
+  if (GNUNET_IDENTITY_read_key_from_buffer(&pubkey, buffer, length) < 0)
+    return GNUNET_NO;
+
+  return GNUNET_OK;
 }
 
 static void
@@ -90,11 +111,18 @@ handle_get_key (void *cls, const struct 
GNUNET_MESSENGER_KeyMessage *msg)
 {
   struct GNUNET_MESSENGER_Handle *handle = cls;
 
-  const struct GNUNET_IDENTITY_PublicKey *pubkey = &(msg->pubkey);
+  const uint16_t length = ntohs (msg->header.size) - sizeof(*msg);
+  const char *buffer = ((const char*) msg) + sizeof(*msg);
+
+  struct GNUNET_IDENTITY_PublicKey pubkey;
+  if (GNUNET_IDENTITY_read_key_from_buffer(&pubkey, buffer, length) < 0)
+    return;
 
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set key of handle: %s\n", 
GNUNET_IDENTITY_public_key_to_string (pubkey));
+  char* str = GNUNET_IDENTITY_public_key_to_string (&pubkey);
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set key of handle: %s\n", str);
+  GNUNET_free(str);
 
-  set_handle_key (handle, pubkey);
+  set_handle_key (handle, &pubkey);
 
   if (handle->identity_callback)
     handle->identity_callback (handle->identity_cls, handle);
@@ -161,12 +189,12 @@ handle_room_close (void *cls, const struct 
GNUNET_MESSENGER_RoomMessage *msg)
 static int
 check_recv_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg)
 {
-  const uint16_t full_length = ntohs (msg->header.size) - sizeof(msg->header);
+  const uint16_t full_length = ntohs (msg->header.size);
 
-  if (full_length < sizeof(msg->hash))
+  if (full_length < sizeof(*msg))
     return GNUNET_NO;
 
-  const uint16_t length = full_length - sizeof(msg->hash);
+  const uint16_t length = full_length - sizeof(*msg);
   const char *buffer = ((const char*) msg) + sizeof(*msg);
 
   struct GNUNET_MESSENGER_Message message;
@@ -174,7 +202,7 @@ check_recv_message (void *cls, const struct 
GNUNET_MESSENGER_RecvMessage *msg)
   if (length < sizeof(message.header))
     return GNUNET_NO;
 
-  if (GNUNET_YES != decode_message (&message, length, buffer))
+  if (GNUNET_YES != decode_message (&message, length, buffer, GNUNET_YES, 
NULL))
     return GNUNET_NO;
 
   return GNUNET_OK;
@@ -186,14 +214,18 @@ handle_recv_message (void *cls, const struct 
GNUNET_MESSENGER_RecvMessage *msg)
   struct GNUNET_MESSENGER_Handle *handle = cls;
 
   const struct GNUNET_HashCode *key = &(msg->key);
+  const struct GNUNET_HashCode *sender = &(msg->sender);
+  const struct GNUNET_HashCode *context = &(msg->context);
   const struct GNUNET_HashCode *hash = &(msg->hash);
-
-  const char *buffer = ((const char*) msg) + sizeof(*msg);
+  const enum GNUNET_MESSENGER_MessageFlags flags = (
+      (enum GNUNET_MESSENGER_MessageFlags) (msg->flags)
+  );
 
   const uint16_t length = ntohs (msg->header.size) - sizeof(*msg);
+  const char *buffer = ((const char*) msg) + sizeof(*msg);
 
   struct GNUNET_MESSENGER_Message message;
-  decode_message (&message, length, buffer);
+  decode_message (&message, length, buffer, GNUNET_YES, NULL);
 
   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving message: %s\n", 
GNUNET_MESSENGER_name_of_kind (message.header.kind));
 
@@ -201,13 +233,22 @@ handle_recv_message (void *cls, const struct 
GNUNET_MESSENGER_RecvMessage *msg)
 
   if (room)
   {
-    handle_room_message (room, &message, hash);
+    struct GNUNET_MESSENGER_ContactStore *store = 
get_handle_contact_store(handle);
+
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Raw contact from sender and context: 
(%s : %s)\n",
+               GNUNET_h2s(sender), GNUNET_h2s_full(context));
+
+    struct GNUNET_MESSENGER_Contact *contact = get_store_contact_raw(
+        store, context, sender
+    );
+
+    handle_room_message (room, contact, &message, hash);
 
     if (handle->msg_callback)
-      handle->msg_callback (handle->msg_cls, room, &message, hash);
+      handle->msg_callback (handle->msg_cls, room, contact, &message, hash, 
flags);
   }
   else
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "MESSENGER ERROR: Room not found\n");
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Room not found\n");
 }
 
 static void
@@ -220,12 +261,12 @@ send_open_room (struct GNUNET_MESSENGER_Handle *handle, 
struct GNUNET_MESSENGER_
   struct GNUNET_MQ_Envelope *env;
 
   env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN);
-  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(msg->key));
   GNUNET_MQ_send (handle->mq, env);
 }
 
 static void
-send_entry_room (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Room *room,
+send_enter_room (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Room *room,
                  const struct GNUNET_PeerIdentity *door)
 {
   struct GNUNET_MESSENGER_RoomMessage *msg;
@@ -233,7 +274,7 @@ send_entry_room (struct GNUNET_MESSENGER_Handle *handle, 
struct GNUNET_MESSENGER
 
   env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY);
   GNUNET_memcpy(&(msg->door), door, sizeof(*door));
-  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(msg->key));
   GNUNET_MQ_send (handle->mq, env);
 }
 
@@ -244,7 +285,7 @@ send_close_room (struct GNUNET_MESSENGER_Handle *handle, 
struct GNUNET_MESSENGER
   struct GNUNET_MQ_Envelope *env;
 
   env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE);
-  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(msg->key));
   GNUNET_MQ_send (handle->mq, env);
 }
 
@@ -265,7 +306,7 @@ iterate_reset_room (void *cls, const struct GNUNET_HashCode 
*key, void *value)
   {
     GNUNET_PEER_resolve (entry->peer, &door);
 
-    send_entry_room (handle, room, &door);
+    send_enter_room (handle, room, &door);
 
     entry = entry->next;
   }
@@ -303,7 +344,7 @@ callback_mq_error (void *cls, enum GNUNET_MQ_Error error)
 {
   struct GNUNET_MESSENGER_Handle *handle = cls;
 
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %u\n", error);
+  GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "MQ_Error: %u\n", error);
 
   GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, iterate_close_room, 
handle);
 
@@ -319,36 +360,45 @@ callback_mq_error (void *cls, enum GNUNET_MQ_Error error)
 static void
 reconnect (struct GNUNET_MESSENGER_Handle *handle)
 {
-  const struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_var_size(
-      get_name, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_NAME, struct 
GNUNET_MESSENGER_NameMessage, handle),
-                                                       GNUNET_MQ_hd_fixed_size(
-                                                           get_key, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY,
-                                                           struct 
GNUNET_MESSENGER_KeyMessage, handle),
-                                                       GNUNET_MQ_hd_fixed_size(
-                                                           member_id,
-                                                           
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID,
-                                                           struct 
GNUNET_MESSENGER_MemberMessage, handle),
-                                                       
GNUNET_MQ_hd_fixed_size(room_open,
-                                                                               
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN,
-                                                                               
struct GNUNET_MESSENGER_RoomMessage,
-                                                                               
handle),
-                                                       
GNUNET_MQ_hd_fixed_size(room_entry,
-                                                                               
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY,
-                                                                               
struct GNUNET_MESSENGER_RoomMessage,
-                                                                               
handle),
-                                                       
GNUNET_MQ_hd_fixed_size(room_close,
-                                                                               
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE,
-                                                                               
struct GNUNET_MESSENGER_RoomMessage,
-                                                                               
handle),
-                                                       GNUNET_MQ_hd_var_size(
-                                                           recv_message,
-                                                           
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE,
-                                                           struct 
GNUNET_MESSENGER_RecvMessage, handle),
-                                                       GNUNET_MQ_handler_end() 
};
-
-  handle->mq = GNUNET_CLIENT_connect (handle->cfg,
-  GNUNET_MESSENGER_SERVICE_NAME,
-                                      handlers, &callback_mq_error, handle);
+  const struct GNUNET_MQ_MessageHandler handlers[] =
+  {
+   GNUNET_MQ_hd_var_size(
+        get_name, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_NAME,
+        struct GNUNET_MESSENGER_NameMessage, handle
+   ),
+   GNUNET_MQ_hd_var_size(
+       get_key, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY,
+       struct GNUNET_MESSENGER_KeyMessage, handle
+   ),
+   GNUNET_MQ_hd_fixed_size(
+       member_id,
+       GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID,
+       struct GNUNET_MESSENGER_MemberMessage, handle
+   ),
+   GNUNET_MQ_hd_fixed_size(
+       room_open,
+       GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN,
+       struct GNUNET_MESSENGER_RoomMessage, handle
+   ),
+   GNUNET_MQ_hd_fixed_size(
+       room_entry,
+       GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY,
+       struct GNUNET_MESSENGER_RoomMessage, handle
+   ),
+   GNUNET_MQ_hd_fixed_size(
+       room_close,
+       GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE,
+       struct GNUNET_MESSENGER_RoomMessage, handle
+   ),
+   GNUNET_MQ_hd_var_size(
+       recv_message,
+       GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE,
+       struct GNUNET_MESSENGER_RecvMessage, handle
+   ),
+   GNUNET_MQ_handler_end()
+  };
+
+  handle->mq = GNUNET_CLIENT_connect (handle->cfg, 
GNUNET_MESSENGER_SERVICE_NAME, handlers, &callback_mq_error, handle);
 }
 
 struct GNUNET_MESSENGER_Handle*
@@ -389,7 +439,7 @@ GNUNET_MESSENGER_connect (const struct 
GNUNET_CONFIGURATION_Handle *cfg, const c
 int
 GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle)
 {
-  if ((!handle) || (!get_handle_name(handle)))
+  if ((!handle) || (!get_handle_name (handle)))
     return GNUNET_SYSERR;
 
   struct GNUNET_MESSENGER_UpdateMessage *msg;
@@ -448,18 +498,30 @@ GNUNET_MESSENGER_set_name (struct GNUNET_MESSENGER_Handle 
*handle, const char *n
   return GNUNET_YES;
 }
 
+static const struct GNUNET_IDENTITY_PublicKey*
+get_non_anonymous_key (const struct GNUNET_IDENTITY_PublicKey* public_key)
+{
+  if (0 == GNUNET_memcmp(public_key, get_anonymous_public_key()))
+    return NULL;
+
+  return public_key;
+}
+
 const struct GNUNET_IDENTITY_PublicKey*
 GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle)
 {
   if (!handle)
     return NULL;
 
-  return get_handle_key (handle);
+  return get_non_anonymous_key (get_handle_key (handle));
 }
 
 struct GNUNET_MESSENGER_Room*
 GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_HashCode *key)
 {
+  if ((!handle) || (!key))
+    return NULL;
+
   struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
 
   if (!room)
@@ -479,9 +541,12 @@ GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle 
*handle, const struct
 }
 
 struct GNUNET_MESSENGER_Room*
-GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_PeerIdentity *door,
+GNUNET_MESSENGER_enter_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_PeerIdentity *door,
                              const struct GNUNET_HashCode *key)
 {
+  if ((!handle) || (!door) || (!key))
+    return NULL;
+
   struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
 
   if (!room)
@@ -496,20 +561,26 @@ GNUNET_MESSENGER_entry_room (struct 
GNUNET_MESSENGER_Handle *handle, const struc
     }
   }
 
-  send_entry_room (handle, room, door);
+  send_enter_room (handle, room, door);
   return room;
 }
 
 void
 GNUNET_MESSENGER_close_room (struct GNUNET_MESSENGER_Room *room)
 {
+  if (!room)
+    return;
+
   send_close_room (room->handle, room);
 }
 
 struct GNUNET_MESSENGER_Contact*
-GNUNET_MESSENGER_get_member (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_ShortHashCode *id)
+GNUNET_MESSENGER_get_sender (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_HashCode *hash)
 {
-  return GNUNET_CONTAINER_multishortmap_get (room->members, id);
+  if ((!room) || (!hash))
+    return NULL;
+
+  return get_room_sender(room, hash);
 }
 
 const char*
@@ -527,23 +598,73 @@ GNUNET_MESSENGER_contact_get_key (const struct 
GNUNET_MESSENGER_Contact *contact
   if (!contact)
     return NULL;
 
-  return get_contact_key (contact);
+  return get_non_anonymous_key (get_contact_key (contact));
 }
 
 void
-GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_MESSENGER_Message *message)
+GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_MESSENGER_Message *message,
+                               const struct GNUNET_MESSENGER_Contact *contact)
 {
-  const uint16_t length = get_message_size (message);
+  if ((!room) || (!message))
+    return;
+
+  switch (filter_message_sending (message))
+  {
+  case GNUNET_SYSERR:
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sending message aborted: This kind of 
message is reserved for the service!\n");
+    return;
+  case GNUNET_NO:
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sending message aborted: This kind 
of message could cause issues!\n");
+    return;
+  default:
+    break;
+  }
+
+  ssize_t key_length = 0;
+
+  if (contact)
+  {
+    const struct GNUNET_IDENTITY_PublicKey *public_key = get_non_anonymous_key 
(
+        get_contact_key(contact)
+    );
+
+    if (public_key)
+      key_length = GNUNET_IDENTITY_key_get_length(public_key);
+    else
+      key_length = -1;
+  }
+
+  if (key_length < 0)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sending message aborted: Invalid 
key!\n");
+    return;
+  }
+
+  const uint16_t msg_length = get_message_size (message, GNUNET_NO);
 
   struct GNUNET_MESSENGER_SendMessage *msg;
   struct GNUNET_MQ_Envelope *env;
 
-  env = GNUNET_MQ_msg_extra(msg, length, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE);
+  const uint16_t length = (uint16_t) key_length + msg_length;
+
+  env = GNUNET_MQ_msg_extra(
+      msg, length,
+      GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE
+  );
 
-  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(msg->key));
+
+  msg->flags = (uint32_t) (
+      contact? GNUNET_MESSENGER_FLAG_PRIVATE : GNUNET_MESSENGER_FLAG_NONE
+  );
 
   char *buffer = ((char*) msg) + sizeof(*msg);
-  encode_message (message, length, buffer);
+  char *msg_buffer = buffer + key_length;
+
+  if (key_length > 0)
+    GNUNET_IDENTITY_write_key_to_buffer(get_contact_key(contact), buffer, 
key_length);
+
+  encode_message (message, msg_length, msg_buffer, GNUNET_NO);
 
   GNUNET_MQ_send (room->handle->mq, env);
 }
@@ -551,18 +672,31 @@ GNUNET_MESSENGER_send_message (struct 
GNUNET_MESSENGER_Room *room, const struct
 const struct GNUNET_MESSENGER_Message*
 GNUNET_MESSENGER_get_message (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_HashCode *hash)
 {
+  if ((!room) || (!hash))
+    return NULL;
+
   const struct GNUNET_MESSENGER_Message *message = get_room_message (room, 
hash);
 
   if (!message)
   {
-    struct GNUNET_MESSENGER_RecvMessage *msg;
+    struct GNUNET_MESSENGER_GetMessage *msg;
     struct GNUNET_MQ_Envelope *env;
 
     env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE);
-    GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+    GNUNET_memcpy(&(msg->key), &(room->key), sizeof(msg->key));
     GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash));
     GNUNET_MQ_send (room->handle->mq, env);
   }
 
   return message;
 }
+
+int
+GNUNET_MESSENGER_iterate_members (struct GNUNET_MESSENGER_Room *room, 
GNUNET_MESSENGER_MemberCallback callback,
+                                  void* cls)
+{
+  if (!room)
+    return GNUNET_SYSERR;
+
+  return iterate_room_members(room, callback, cls);
+}
diff --git a/src/messenger/messenger_api_contact.c 
b/src/messenger/messenger_api_contact.c
index 9a242aa00..04e1f60c1 100644
--- a/src/messenger/messenger_api_contact.c
+++ b/src/messenger/messenger_api_contact.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -28,9 +28,12 @@
 struct GNUNET_MESSENGER_Contact*
 create_contact (const struct GNUNET_IDENTITY_PublicKey *key)
 {
+  GNUNET_assert(key);
+
   struct GNUNET_MESSENGER_Contact *contact = GNUNET_new(struct 
GNUNET_MESSENGER_Contact);
 
   contact->name = NULL;
+  contact->rc = 0;
 
   GNUNET_memcpy(&(contact->public_key), key, sizeof(contact->public_key));
 
@@ -40,6 +43,8 @@ create_contact (const struct GNUNET_IDENTITY_PublicKey *key)
 void
 destroy_contact (struct GNUNET_MESSENGER_Contact *contact)
 {
+  GNUNET_assert(contact);
+
   if (contact->name)
     GNUNET_free(contact->name);
 
@@ -49,30 +54,55 @@ destroy_contact (struct GNUNET_MESSENGER_Contact *contact)
 const char*
 get_contact_name (const struct GNUNET_MESSENGER_Contact *contact)
 {
+  GNUNET_assert(contact);
+
   return contact->name;
 }
 
 void
 set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name)
 {
+  GNUNET_assert(contact);
+
   if (contact->name)
     GNUNET_free(contact->name);
 
-  contact->name = name? GNUNET_strdup(name) : NULL;
+  contact->name = name ? GNUNET_strdup(name) : NULL;
 }
 
 const struct GNUNET_IDENTITY_PublicKey*
 get_contact_key (const struct GNUNET_MESSENGER_Contact *contact)
 {
+  GNUNET_assert(contact);
+
   return &(contact->public_key);
 }
 
-const struct GNUNET_HashCode*
-get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact)
+void
+increase_contact_rc (struct GNUNET_MESSENGER_Contact *contact)
 {
-  static struct GNUNET_HashCode id;
+  GNUNET_assert(contact);
+
+  contact->rc++;
+}
 
-  GNUNET_CRYPTO_hash (&(contact->public_key), sizeof(contact->public_key), 
&id);
+int
+decrease_contact_rc (struct GNUNET_MESSENGER_Contact *contact)
+{
+  GNUNET_assert(contact);
+
+  if (contact->rc > 0)
+    contact->rc--;
+
+  return contact->rc ? GNUNET_NO : GNUNET_YES;
+}
+
+void
+get_context_from_member (const struct GNUNET_HashCode *key, const struct 
GNUNET_ShortHashCode *id,
+                         struct GNUNET_HashCode *context)
+{
+  GNUNET_assert((key) && (id) && (context));
 
-  return &id;
+  GNUNET_CRYPTO_hash (id, sizeof(*id), context);
+  GNUNET_CRYPTO_hash_xor (key, context, context);
 }
diff --git a/src/messenger/messenger_api_contact.h 
b/src/messenger/messenger_api_contact.h
index 0673b9b85..e94d1fcd0 100644
--- a/src/messenger/messenger_api_contact.h
+++ b/src/messenger/messenger_api_contact.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -33,6 +33,7 @@
 struct GNUNET_MESSENGER_Contact
 {
   char *name;
+  size_t rc;
 
   struct GNUNET_IDENTITY_PublicKey public_key;
 };
@@ -40,7 +41,7 @@ struct GNUNET_MESSENGER_Contact
 /**
  * Creates and allocates a new contact with a given public <i>key</i> from an 
EGO.
  *
- * @param key Public key
+ * @param[in] key Public key
  * @return New contact
  */
 struct GNUNET_MESSENGER_Contact*
@@ -49,7 +50,7 @@ create_contact (const struct GNUNET_IDENTITY_PublicKey *key);
 /**
  * Destroys a contact and frees its memory fully.
  *
- * @param contact Contact
+ * @param[in/out] contact Contact
  */
 void
 destroy_contact (struct GNUNET_MESSENGER_Contact *contact);
@@ -57,7 +58,7 @@ destroy_contact (struct GNUNET_MESSENGER_Contact *contact);
 /**
  * Returns the current name of a given <i>contact</i> or NULL if no valid name 
was assigned yet.
  *
- * @param contact Contact
+ * @param[in] contact Contact
  * @return Name of the contact or NULL
  */
 const char*
@@ -66,8 +67,8 @@ get_contact_name (const struct GNUNET_MESSENGER_Contact 
*contact);
 /**
  * Changes the current name of a given <i>contact</i> by copying it from the 
parameter <i>name</i>.
  *
- * @param contact Contact
- * @param name Valid name (may not be NULL!)
+ * @param[in/out] contact Contact
+ * @param[in] name Name
  */
 void
 set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name);
@@ -75,19 +76,39 @@ set_contact_name (struct GNUNET_MESSENGER_Contact *contact, 
const char *name);
 /**
  * Returns the public key of a given <i>contact</i>.
  *
- * @param contact Contact
+ * @param[in] contact Contact
  * @return Public key of the contact
  */
 const struct GNUNET_IDENTITY_PublicKey*
 get_contact_key (const struct GNUNET_MESSENGER_Contact *contact);
 
 /**
- * Returns the resulting hashcode of the public key from a given 
<i>contact</i>.
+ * Increases the reference counter of a given <i>contact</i> which is zero as 
default.
  *
- * @param contact Contact
- * @return Hash of the contacts public key
+ * @param[in/out] contact Contact
  */
-const struct GNUNET_HashCode*
-get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact);
+void
+increase_contact_rc (struct GNUNET_MESSENGER_Contact *contact);
+
+/**
+ * Decreases the reference counter if possible (can not underflow!) of a given 
<i>contact</i>
+ * and returns #GNUNET_YES if the counter is equal to zero, otherwise 
#GNUNET_NO.
+ *
+ * @param[in/out] contact Contact
+ * @return #GNUNET_YES or #GNUNET_NO depending on the reference counter
+ */
+int
+decrease_contact_rc (struct GNUNET_MESSENGER_Contact *contact);
+
+/**
+ * Calculates the context <i>hash</i> of a member in a room and returns it.
+ *
+ * @param[in] key Key of room
+ * @param[in] id Member id
+ * @param[out] hash Member context
+ */
+void
+get_context_from_member (const struct GNUNET_HashCode *key, const struct 
GNUNET_ShortHashCode *id,
+                         struct GNUNET_HashCode *context);
 
 #endif //GNUNET_MESSENGER_API_CONTACT_H
diff --git a/src/messenger/messenger_api_contact_store.c 
b/src/messenger/messenger_api_contact_store.c
new file mode 100644
index 000000000..5238b2c58
--- /dev/null
+++ b/src/messenger/messenger_api_contact_store.c
@@ -0,0 +1,182 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/messenger_api_contact_store.c
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#include "messenger_api_contact_store.h"
+
+#include "messenger_api_contact.h"
+#include "messenger_api_util.h"
+
+void
+init_contact_store (struct GNUNET_MESSENGER_ContactStore *store)
+{
+  GNUNET_assert (store);
+
+  store->anonymous = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  store->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+}
+
+static int
+iterate_destroy_contacts (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Contact *contact = value;
+  destroy_contact (contact);
+  return GNUNET_YES;
+}
+
+void
+clear_contact_store (struct GNUNET_MESSENGER_ContactStore *store)
+{
+  GNUNET_assert ((store) && (store->contacts));
+
+  GNUNET_CONTAINER_multihashmap_iterate (store->anonymous, 
iterate_destroy_contacts, NULL);
+  GNUNET_CONTAINER_multihashmap_iterate (store->contacts, 
iterate_destroy_contacts, NULL);
+
+  GNUNET_CONTAINER_multihashmap_destroy (store->anonymous);
+  GNUNET_CONTAINER_multihashmap_destroy (store->contacts);
+}
+
+static struct GNUNET_CONTAINER_MultiHashMap*
+select_store_contact_map (struct GNUNET_MESSENGER_ContactStore *store, const 
struct GNUNET_HashCode *context,
+                          struct GNUNET_HashCode *hash)
+{
+  const struct GNUNET_IDENTITY_PublicKey *anonymous = get_anonymous_public_key 
();
+
+  struct GNUNET_HashCode anonHash;
+  GNUNET_CRYPTO_hash (anonymous, sizeof(*anonymous), &anonHash);
+
+  if ((context) && (0 == GNUNET_CRYPTO_hash_cmp(hash, &anonHash)))
+  {
+    GNUNET_memcpy(hash, context, sizeof(*context));
+    return store->anonymous;
+  }
+  else
+    return store->contacts;
+}
+
+struct GNUNET_MESSENGER_Contact*
+get_store_contact_raw (struct GNUNET_MESSENGER_ContactStore *store, const 
struct GNUNET_HashCode *context,
+                       const struct GNUNET_HashCode *key_hash)
+{
+  GNUNET_assert ((store) && (store->contacts) && (context) && (key_hash));
+
+  struct GNUNET_HashCode hash;
+  GNUNET_memcpy(&hash, key_hash, sizeof(*key_hash));
+
+  struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map (
+      store, context, &hash
+  );
+
+  return GNUNET_CONTAINER_multihashmap_get (map, &hash);
+}
+
+struct GNUNET_MESSENGER_Contact*
+get_store_contact (struct GNUNET_MESSENGER_ContactStore *store, const struct 
GNUNET_HashCode *context,
+                   const struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  GNUNET_assert ((store) && (store->contacts) && (context) && (pubkey));
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map (
+      store, context, &hash
+  );
+
+  struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multihashmap_get 
(map, &hash);
+
+  if (contact)
+  {
+    if (0 != GNUNET_memcmp(pubkey, get_contact_key(contact)))
+    {
+      char* str = GNUNET_IDENTITY_public_key_to_string 
(get_contact_key(contact));
+      GNUNET_log (GNUNET_ERROR_TYPE_INVALID, "Contact in store uses wrong key: 
%s\n", str);
+      GNUNET_free (str);
+      return NULL;
+    }
+
+    return contact;
+  }
+
+  contact = create_contact (pubkey);
+
+  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (map, &hash, contact,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    return contact;
+
+  destroy_contact (contact);
+  return NULL;
+}
+
+void
+update_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct 
GNUNET_MESSENGER_Contact* contact,
+                      const struct GNUNET_HashCode *context, const struct 
GNUNET_HashCode *next_context,
+                      const struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  GNUNET_assert ((store) && (store->contacts) && (contact) && (pubkey));
+
+  const struct GNUNET_IDENTITY_PublicKey* oldkey = get_contact_key (contact);
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash (oldkey, sizeof(*oldkey), &hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map (
+      store, context, &hash
+  );
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (map, &hash, contact))
+  {
+    GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey));
+
+    GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash);
+
+    map = select_store_contact_map (
+        store, next_context, &hash
+    );
+
+    GNUNET_CONTAINER_multihashmap_put (map, &hash, contact,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+}
+
+void
+remove_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct 
GNUNET_MESSENGER_Contact* contact,
+                      const struct GNUNET_HashCode *context)
+{
+  GNUNET_assert ((store) && (store->contacts) && (contact));
+
+  const struct GNUNET_IDENTITY_PublicKey* pubkey = get_contact_key(contact);
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map (
+      store, context, &hash
+  );
+
+  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (map, &hash, contact))
+    return;
+
+  destroy_contact (contact);
+}
diff --git a/src/messenger/messenger_api_contact_store.h 
b/src/messenger/messenger_api_contact_store.h
new file mode 100644
index 000000000..966f6d962
--- /dev/null
+++ b/src/messenger/messenger_api_contact_store.h
@@ -0,0 +1,122 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/messenger_api_contact_store.h
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_MESSENGER_API_CONTACT_STORE_H
+#define GNUNET_MESSENGER_API_CONTACT_STORE_H
+
+#include "platform.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+
+struct GNUNET_MESSENGER_Contact;
+
+struct GNUNET_MESSENGER_ContactStore
+{
+  struct GNUNET_CONTAINER_MultiHashMap *anonymous;
+  struct GNUNET_CONTAINER_MultiHashMap *contacts;
+};
+
+/**
+ * Initializes a contact store as fully empty.
+ *
+ * @param[out] store Contact store
+ */
+void
+init_contact_store (struct GNUNET_MESSENGER_ContactStore *store);
+
+/**
+ * Clears a contact store, wipes its content and deallocates its memory.
+ *
+ * @param[in/out] store Contact store
+ */
+void
+clear_contact_store (struct GNUNET_MESSENGER_ContactStore *store);
+
+/**
+ * Returns a contact using the hash of a specific public key. In case the 
anonymous
+ * key gets used by the requested contact, it will use its provided member
+ * <i>context</i> to select the matching contact from the <i>store</i>.
+ *
+ * In case there is no contact stored which uses the given key or context,
+ * NULL gets returned.
+ *
+ * @param[in/out] store Contact store
+ * @param[in] context Member context
+ * @param[in] key_hash Hash of public key
+ */
+struct GNUNET_MESSENGER_Contact*
+get_store_contact_raw (struct GNUNET_MESSENGER_ContactStore *store, const 
struct GNUNET_HashCode *context,
+                       const struct GNUNET_HashCode *key_hash);
+
+/**
+ * Returns a contact using a specific public key. In case the anonymous
+ * key gets used by the requested contact, it will use its provided member
+ * <i>context</i> to select the matching contact from the <i>store</i>.
+ *
+ * In case there is no contact stored which uses the given key or context,
+ * a new contact will be created automatically.
+ *
+ * The function returns NULL if an error occures during allocation
+ * or validation of the contacts key.
+ *
+ * @param[in/out] store Contact store
+ * @param[in] context Member context
+ * @param[in] pubkey Public key of EGO
+ */
+struct GNUNET_MESSENGER_Contact*
+get_store_contact (struct GNUNET_MESSENGER_ContactStore *store, const struct 
GNUNET_HashCode *context,
+                   const struct GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Moves a <i>contact</i> from the <i>store</i> to another location
+ * matching a given public key and member <i>context</i>.
+ *
+ * This function allows changes of keys or changes of member contexts!
+ *
+ * @param[in/out] store Contact store
+ * @param[in/out] contact Contact
+ * @param[in] context Member context
+ * @param[in] next_context Member context
+ * @param[in] pubkey Public key of EGO
+ */
+void
+update_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct 
GNUNET_MESSENGER_Contact* contact,
+                      const struct GNUNET_HashCode *context, const struct 
GNUNET_HashCode *next_context,
+                      const struct GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Removes a <i>contact</i> from the <i>store</i> which uses
+ * a given member <i>context</i>.
+ *
+ * @param[in/out] store Contact store
+ * @param[in/out] contact Contact
+ * @param[in] context Member context
+ */
+void
+remove_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct 
GNUNET_MESSENGER_Contact* contact,
+                      const struct GNUNET_HashCode *context);
+
+#endif //GNUNET_MESSENGER_API_CONTACT_STORE_H
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/messenger_api_ego.h
index c60eeac50..b52b895f2 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/messenger_api_ego.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
diff --git a/src/messenger/messenger_api_handle.c 
b/src/messenger/messenger_api_handle.c
index 20ef77254..ab57f82cc 100644
--- a/src/messenger/messenger_api_handle.c
+++ b/src/messenger/messenger_api_handle.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -25,10 +25,14 @@
 
 #include "messenger_api_handle.h"
 
+#include "messenger_api_util.h"
+
 struct GNUNET_MESSENGER_Handle*
 create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, 
GNUNET_MESSENGER_IdentityCallback identity_callback,
                void *identity_cls, GNUNET_MESSENGER_MessageCallback 
msg_callback, void *msg_cls)
 {
+  GNUNET_assert(cfg);
+
   struct GNUNET_MESSENGER_Handle *handle = GNUNET_new(struct 
GNUNET_MESSENGER_Handle);
 
   handle->cfg = cfg;
@@ -47,7 +51,8 @@ create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, 
GNUNET_MESSENGER_I
   handle->reconnect_task = NULL;
 
   handle->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
-  handle->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  init_contact_store(get_handle_contact_store(handle));
 
   return handle;
 }
@@ -62,19 +67,11 @@ iterate_destroy_room (void *cls, const struct 
GNUNET_HashCode *key, void *value)
   return GNUNET_YES;
 }
 
-static int
-iterate_destroy_contact (void *cls, const struct GNUNET_HashCode *key, void 
*value)
-{
-  struct GNUNET_MESSENGER_Contact *contact = value;
-
-  destroy_contact (contact);
-
-  return GNUNET_YES;
-}
-
 void
 destroy_handle (struct GNUNET_MESSENGER_Handle *handle)
 {
+  GNUNET_assert(handle);
+
   if (handle->reconnect_task)
     GNUNET_SCHEDULER_cancel (handle->reconnect_task);
 
@@ -94,12 +91,7 @@ destroy_handle (struct GNUNET_MESSENGER_Handle *handle)
     GNUNET_CONTAINER_multihashmap_destroy (handle->rooms);
   }
 
-  if (handle->contacts)
-  {
-    GNUNET_CONTAINER_multihashmap_iterate (handle->contacts, 
iterate_destroy_contact, NULL);
-
-    GNUNET_CONTAINER_multihashmap_destroy (handle->contacts);
-  }
+  clear_contact_store(get_handle_contact_store(handle));
 
   GNUNET_free(handle->name);
 }
@@ -107,21 +99,27 @@ destroy_handle (struct GNUNET_MESSENGER_Handle *handle)
 void
 set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name)
 {
+  GNUNET_assert(handle);
+
   if (handle->name)
     GNUNET_free(handle->name);
 
-  handle->name = name? GNUNET_strdup(name) : NULL;
+  handle->name = name ? GNUNET_strdup(name) : NULL;
 }
 
 const char*
 get_handle_name (const struct GNUNET_MESSENGER_Handle *handle)
 {
+  GNUNET_assert(handle);
+
   return handle->name;
 }
 
 void
 set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_IDENTITY_PublicKey *pubkey)
 {
+  GNUNET_assert(handle);
+
   if (!handle->pubkey)
     handle->pubkey = GNUNET_new(struct GNUNET_IDENTITY_PublicKey);
 
@@ -131,62 +129,43 @@ set_handle_key (struct GNUNET_MESSENGER_Handle *handle, 
const struct GNUNET_IDEN
 const struct GNUNET_IDENTITY_PublicKey*
 get_handle_key (const struct GNUNET_MESSENGER_Handle *handle)
 {
-  if (!handle->pubkey)
-  {
-    struct GNUNET_IDENTITY_Ego *anonymous = GNUNET_IDENTITY_ego_get_anonymous 
();
-    static struct GNUNET_IDENTITY_PublicKey pubkey;
+  GNUNET_assert(handle);
 
-    GNUNET_IDENTITY_ego_get_public_key (anonymous, &pubkey);
-
-    return &pubkey;
-  }
+  if (handle->pubkey)
+    return handle->pubkey;
 
-  return handle->pubkey;
+  return get_anonymous_public_key ();
 }
 
-struct GNUNET_MESSENGER_Contact*
-get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle,
-                              const struct GNUNET_IDENTITY_PublicKey *pubkey)
+struct GNUNET_MESSENGER_ContactStore*
+get_handle_contact_store (struct GNUNET_MESSENGER_Handle *handle)
 {
-  struct GNUNET_HashCode hash;
-
-  GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash);
-
-  struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multihashmap_get 
(handle->contacts, &hash);
-
-  if (contact)
-    return contact;
-
-  contact = create_contact (pubkey);
-
-  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (handle->contacts, &hash, 
contact,
-                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
-    return contact;
+  GNUNET_assert(handle);
 
-  destroy_contact (contact);
-  return NULL;
+  return &(handle->contact_store);
 }
 
-void
-swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Contact *contact,
-                               const struct GNUNET_IDENTITY_PublicKey *pubkey)
+struct GNUNET_MESSENGER_Contact*
+get_handle_contact (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key)
 {
-  const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact);
+  GNUNET_assert((handle) && (key));
 
-  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (handle->contacts, 
hash, contact))
-  {
-    GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey));
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
 
-    hash = get_contact_id_from_key (contact);
+  if ((!room) || (!(room->contact_id)))
+    return NULL;
 
-    GNUNET_CONTAINER_multihashmap_put (handle->contacts, hash, contact,
-                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
-  }
+  struct GNUNET_HashCode context;
+  get_context_from_member (key, room->contact_id, &context);
+
+  return get_store_contact(get_handle_contact_store(handle), &context, 
get_handle_key(handle));
 }
 
 void
 open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (key));
+
   struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
 
   if (room)
@@ -197,6 +176,8 @@ void
 entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_PeerIdentity *door,
                       const struct GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (door) && (key));
+
   struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
 
   if (room)
@@ -206,6 +187,8 @@ entry_handle_room_at (struct GNUNET_MESSENGER_Handle 
*handle, const struct GNUNE
 void
 close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (key));
+
   struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
 
   if ((room) && (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove 
(handle->rooms, key, room)))
diff --git a/src/messenger/messenger_api_handle.h 
b/src/messenger/messenger_api_handle.h
index d6cde0106..e6ca474f2 100644
--- a/src/messenger/messenger_api_handle.h
+++ b/src/messenger/messenger_api_handle.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -35,7 +35,7 @@
 
 #include "gnunet_messenger_service.h"
 
-#include "messenger_api_contact.h"
+#include "messenger_api_contact_store.h"
 #include "messenger_api_room.h"
 
 struct GNUNET_MESSENGER_Handle
@@ -56,17 +56,18 @@ struct GNUNET_MESSENGER_Handle
   struct GNUNET_TIME_Relative reconnect_time;
   struct GNUNET_SCHEDULER_Task *reconnect_task;
 
+  struct GNUNET_MESSENGER_ContactStore contact_store;
+
   struct GNUNET_CONTAINER_MultiHashMap *rooms;
-  struct GNUNET_CONTAINER_MultiHashMap *contacts;
 };
 
 /**
  * Creates and allocates a new handle using a given configuration and a custom 
message callback
  * with a given closure for the client API.
  *
- * @param cfg Configuration
- * @param msg_callback Message callback
- * @param msg_cls Closure
+ * @param[in] cfg Configuration
+ * @param[in] msg_callback Message callback
+ * @param[in/out] msg_cls Closure
  * @return New handle
  */
 struct GNUNET_MESSENGER_Handle*
@@ -76,7 +77,7 @@ create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, 
GNUNET_MESSENGER_I
 /**
  * Destroys a <i>handle</i> and frees its memory fully from the client API.
  *
- * @param handle Handle
+ * @param[in/out] handle Handle
  */
 void
 destroy_handle (struct GNUNET_MESSENGER_Handle *handle);
@@ -84,8 +85,8 @@ destroy_handle (struct GNUNET_MESSENGER_Handle *handle);
 /**
  * Sets the name of a <i>handle</i> to a specific <i>name</i>.
  *
- * @param handle Handle
- * @param name New name
+ * @param[in/out] handle Handle
+ * @param[in] name New name
  */
 void
 set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name);
@@ -93,7 +94,7 @@ set_handle_name (struct GNUNET_MESSENGER_Handle *handle, 
const char *name);
 /**
  * Returns the current name of a given <i>handle</i> or NULL if no valid name 
was assigned yet.
  *
- * @param handle Handle
+ * @param[in] handle Handle
  * @return Name of the handle or NULL
  */
 const char*
@@ -102,8 +103,8 @@ get_handle_name (const struct GNUNET_MESSENGER_Handle 
*handle);
 /**
  * Sets the public key of a given <i>handle</i> to a specific public key.
  *
- * @param handle Handle
- * @param pubkey Public key
+ * @param[in/out] handle Handle
+ * @param[in] pubkey Public key
  */
 void
 set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_IDENTITY_PublicKey *pubkey);
@@ -111,41 +112,37 @@ set_handle_key (struct GNUNET_MESSENGER_Handle *handle, 
const struct GNUNET_IDEN
 /**
  * Returns the public key of a given <i>handle</i>.
  *
- * @param handle Handle
+ * @param[in] handle Handle
  * @return Public key of the handle
  */
 const struct GNUNET_IDENTITY_PublicKey*
 get_handle_key (const struct GNUNET_MESSENGER_Handle *handle);
 
 /**
- * Returns a contact known to a <i>handle</i> identified by a given public 
key. If not matching
- * contact is found, NULL gets returned.
+ * Returns the used contact store of a given <i>handle</i>.
  *
- * @param handle Handle
- * @param pubkey Public key of EGO
- * @return Contact or NULL
+ * @param[in/out] handle Handle
+ * @return Contact store
  */
-struct GNUNET_MESSENGER_Contact*
-get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle,
-                              const struct GNUNET_IDENTITY_PublicKey *pubkey);
+struct GNUNET_MESSENGER_ContactStore*
+get_handle_contact_store (struct GNUNET_MESSENGER_Handle *handle);
 
 /**
- * Changes the public key for a <i>contact</i> known to a <i>handle</i> to a 
specific public key and
- * updates local map entries to access the contact by its updated key.
+ * Returns the contact of a given <i>handle</i> in a room identified by a
+ * given <i>key</i>.
  *
- * @param handle Handle
- * @param contact Contact
- * @param pubkey Public key of EGO
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
+ * @return Contact
  */
-void
-swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Contact *contact,
-                               const struct GNUNET_IDENTITY_PublicKey *pubkey);
+struct GNUNET_MESSENGER_Contact*
+get_handle_contact (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key);
 
 /**
  * Marks a room known to a <i>handle</i> identified by a given <i>key</i> as 
open.
  *
- * @param handle Handle
- * @param key Key of room
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
  */
 void
 open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key);
@@ -154,9 +151,9 @@ open_handle_room (struct GNUNET_MESSENGER_Handle *handle, 
const struct GNUNET_Ha
  * Adds a tunnel for a room known to a <i>handle</i> identified by a given 
<i>key</i> to a
  * list of opened connections.
  *
- * @param handle Handle
- * @param door Peer identity
- * @param key Key of room
+ * @param[in/out] handle Handle
+ * @param[in] door Peer identity
+ * @param[in] key Key of room
  */
 void
 entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_PeerIdentity *door,
@@ -165,8 +162,8 @@ entry_handle_room_at (struct GNUNET_MESSENGER_Handle 
*handle, const struct GNUNE
 /**
  * Destroys and so implicitly closes a room known to a <i>handle</i> 
identified by a given <i>key</i>.
  *
- * @param handle Handle
- * @param key Key of room
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
  */
 void
 close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key);
diff --git a/src/messenger/messenger_api_list_tunnels.c 
b/src/messenger/messenger_api_list_tunnels.c
index 13d8c1906..990e36878 100644
--- a/src/messenger/messenger_api_list_tunnels.c
+++ b/src/messenger/messenger_api_list_tunnels.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -62,6 +62,8 @@ compare_list_tunnels (void *cls, struct 
GNUNET_MESSENGER_ListTunnel *element0,
 void
 add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer)
 {
+  GNUNET_assert((tunnels) && (peer));
+
   struct GNUNET_MESSENGER_ListTunnel *element = GNUNET_new(struct 
GNUNET_MESSENGER_ListTunnel);
 
   element->peer = GNUNET_PEER_intern (peer);
@@ -73,6 +75,8 @@ add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels 
*tunnels, const struct
 struct GNUNET_MESSENGER_ListTunnel*
 find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct 
GNUNET_PeerIdentity *peer, size_t *index)
 {
+  GNUNET_assert((tunnels) && (peer));
+
   struct GNUNET_MESSENGER_ListTunnel *element;
   struct GNUNET_PeerIdentity pid;
 
@@ -96,12 +100,16 @@ find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels 
*tunnels, const struct GN
 int
 contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer)
 {
+  GNUNET_assert((tunnels) && (peer));
+
   return find_list_tunnels (tunnels, peer, NULL) != NULL ? GNUNET_YES : 
GNUNET_NO;
 }
 
 struct GNUNET_MESSENGER_ListTunnel*
 remove_from_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, struct 
GNUNET_MESSENGER_ListTunnel *element)
 {
+  GNUNET_assert((tunnels) && (element));
+
   struct GNUNET_MESSENGER_ListTunnel *next = element->next;
 
   GNUNET_CONTAINER_DLL_remove(tunnels->head, tunnels->tail, element);
@@ -110,3 +118,67 @@ remove_from_list_tunnels (struct 
GNUNET_MESSENGER_ListTunnels *tunnels, struct G
 
   return next;
 }
+
+void
+load_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char 
*path)
+{
+  GNUNET_assert((tunnels) && (path));
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (path))
+    return;
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
+      path, GNUNET_DISK_OPEN_READ, permission
+  );
+
+  if (!handle)
+    return;
+
+  GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
+
+  struct GNUNET_PeerIdentity peer;
+  ssize_t len;
+
+  do {
+    len = GNUNET_DISK_file_read(handle, &peer, sizeof(peer));
+
+    if (len != sizeof(peer))
+      break;
+
+    add_to_list_tunnels(tunnels, &peer);
+  } while (len == sizeof(peer));
+
+  GNUNET_DISK_file_close(handle);
+}
+
+void
+save_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char 
*path)
+{
+  GNUNET_assert((tunnels) && (path));
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
+      path, GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE, permission
+  );
+
+  if (!handle)
+    return;
+
+  GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
+
+  struct GNUNET_MESSENGER_ListTunnel *element;
+  struct GNUNET_PeerIdentity pid;
+
+  for (element = tunnels->head; element; element = element->next)
+  {
+    GNUNET_PEER_resolve (element->peer, &pid);
+
+    GNUNET_DISK_file_write(handle, &pid, sizeof(pid));
+  }
+
+  GNUNET_DISK_file_sync(handle);
+  GNUNET_DISK_file_close(handle);
+}
diff --git a/src/messenger/messenger_api_list_tunnels.h 
b/src/messenger/messenger_api_list_tunnels.h
index 0240fceb8..bd0a42e41 100644
--- a/src/messenger/messenger_api_list_tunnels.h
+++ b/src/messenger/messenger_api_list_tunnels.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -47,7 +47,7 @@ struct GNUNET_MESSENGER_ListTunnels
 /**
  * Initializes list of tunnels peer identities as empty list.
  *
- * @param tunnels List of peer identities
+ * @param[out] tunnels List of peer identities
  */
 void
 init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels);
@@ -55,7 +55,7 @@ init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels 
*tunnels);
 /**
  * Clears the list of tunnels peer identities.
  *
- * @param tunnels List of peer identities
+ * @param[in/out] tunnels List of peer identities
  */
 void
 clear_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels);
@@ -63,8 +63,8 @@ clear_list_tunnels (struct GNUNET_MESSENGER_ListTunnels 
*tunnels);
 /**
  * Adds a specific <i>peer</i> from a tunnel to the end of the list.
  *
- * @param tunnels List of peer identities
- * @param peer Peer identity of tunnel
+ * @param[in/out] tunnels List of peer identities
+ * @param[in] peer Peer identity of tunnel
  */
 void
 add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer);
@@ -79,8 +79,8 @@ add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels 
*tunnels, const struct
  * the found element in the list. If no matching element is found, 
<i>index</i> will
  * contain the total amount of elements in the list.
  *
- * @param tunnels List of peer identities
- * @param peer Peer identity of tunnel
+ * @param[in/out] tunnels List of peer identities
+ * @param[in] peer Peer identity of tunnel
  * @param[out] index Index of found element (optional)
  * @return Element in the list with matching peer identity
  */
@@ -89,11 +89,11 @@ find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels 
*tunnels, const struct GN
 
 /**
  * Tests linearly if the list of tunnels peer identities contains a specific
- * <i>peer</i> identity and returns GNUNET_YES on success, otherwise GNUNET_NO.
+ * <i>peer</i> identity and returns #GNUNET_YES on success, otherwise 
#GNUNET_NO.
  *
- * @param tunnels List of peer identities
- * @param peer Peer identity of tunnel
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] tunnels List of peer identities
+ * @param[in] peer Peer identity of tunnel
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer);
@@ -102,11 +102,29 @@ contains_list_tunnels (struct 
GNUNET_MESSENGER_ListTunnels *tunnels, const struc
  * Removes a specific <i>element</i> from the list of tunnels peer identities 
and returns
  * the next element in the list.
  *
- * @param tunnels List of peer identities
- * @param element Element of the list
+ * @param[in/out] tunnels List of peer identities
+ * @param[in/out] element Element of the list
  * @return Next element in the list
  */
 struct GNUNET_MESSENGER_ListTunnel*
 remove_from_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, struct 
GNUNET_MESSENGER_ListTunnel *element);
 
+/**
+ * Loads the list of tunnels peer identities from a file under a given 
<i>path</i>.
+ *
+ * @param[out] messages List of hashes
+ * @param[in] path Path of file
+ */
+void
+load_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char 
*path);
+
+/**
+ * Saves the list of tunnels peer identities to a file under a given 
<i>path</i>.
+ *
+ * @param[in] messages List of hashes
+ * @param[in] path Path of file
+ */
+void
+save_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char 
*path);
+
 #endif //GNUNET_MESSENGER_API_LIST_TUNNELS_H
diff --git a/src/messenger/messenger_api_message.c 
b/src/messenger/messenger_api_message.c
index fdab60eef..d88859186 100644
--- a/src/messenger/messenger_api_message.c
+++ b/src/messenger/messenger_api_message.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -69,6 +69,8 @@ create_message (enum GNUNET_MESSENGER_MessageKind kind)
 struct GNUNET_MESSENGER_Message*
 copy_message (const struct GNUNET_MESSENGER_Message *message)
 {
+  GNUNET_assert(message);
+
   struct GNUNET_MESSENGER_Message *copy = GNUNET_new(struct 
GNUNET_MESSENGER_Message);
 
   GNUNET_memcpy(copy, message, sizeof(struct GNUNET_MESSENGER_Message));
@@ -125,11 +127,28 @@ destroy_message_body (enum GNUNET_MESSENGER_MessageKind 
kind, struct GNUNET_MESS
 void
 destroy_message (struct GNUNET_MESSENGER_Message *message)
 {
+  GNUNET_assert(message);
+
   destroy_message_body (message->header.kind, &(message->body));
 
   GNUNET_free(message);
 }
 
+int
+is_message_session_bound (const struct GNUNET_MESSENGER_Message *message)
+{
+  GNUNET_assert(message);
+
+  if ((GNUNET_MESSENGER_KIND_JOIN == message->header.kind) ||
+      (GNUNET_MESSENGER_KIND_LEAVE == message->header.kind) ||
+      (GNUNET_MESSENGER_KIND_NAME == message->header.kind) ||
+      (GNUNET_MESSENGER_KIND_KEY == message->header.kind) ||
+      (GNUNET_MESSENGER_KIND_ID == message->header.kind))
+    return GNUNET_YES;
+  else
+    return GNUNET_NO;
+}
+
 static void
 fold_short_message (const struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_MESSENGER_ShortMessage *shortened)
 {
@@ -158,18 +177,7 @@ get_message_body_kind_size (enum 
GNUNET_MESSENGER_MessageKind kind)
   switch (kind)
   {
   case GNUNET_MESSENGER_KIND_INFO:
-    length += member_size(struct GNUNET_MESSENGER_Message, body.info.host_key);
-    length += member_size(struct GNUNET_MESSENGER_Message, 
body.info.unique_id);
-    break;
-  case GNUNET_MESSENGER_KIND_JOIN:
-    length += member_size(struct GNUNET_MESSENGER_Message, body.join.key);
-    break;
-  case GNUNET_MESSENGER_KIND_LEAVE:
-    break;
-  case GNUNET_MESSENGER_KIND_NAME:
-    break;
-  case GNUNET_MESSENGER_KIND_KEY:
-    length += member_size(struct GNUNET_MESSENGER_Message, body.key.key);
+    length += member_size(struct GNUNET_MESSENGER_Message, 
body.info.messenger_version);
     break;
   case GNUNET_MESSENGER_KIND_PEER:
     length += member_size(struct GNUNET_MESSENGER_Message, body.peer.peer);
@@ -195,7 +203,7 @@ get_message_body_kind_size (enum 
GNUNET_MESSENGER_MessageKind kind)
   case GNUNET_MESSENGER_KIND_FILE:
     length += member_size(struct GNUNET_MESSENGER_Message, body.file.key);
     length += member_size(struct GNUNET_MESSENGER_Message, body.file.hash);
-    length += NAME_MAX;
+    length += member_size(struct GNUNET_MESSENGER_Message, body.file.name);
     break;
   case GNUNET_MESSENGER_KIND_PRIVATE:
     length += member_size(struct GNUNET_MESSENGER_Message, body.private.key);
@@ -207,16 +215,17 @@ get_message_body_kind_size (enum 
GNUNET_MESSENGER_MessageKind kind)
   return length;
 }
 
+typedef uint32_t kind_t;
+
 uint16_t
 get_message_kind_size (enum GNUNET_MESSENGER_MessageKind kind)
 {
   uint16_t length = 0;
 
-  length += member_size(struct GNUNET_MESSENGER_Message, header.signature);
   length += member_size(struct GNUNET_MESSENGER_Message, header.timestamp);
   length += member_size(struct GNUNET_MESSENGER_Message, header.sender_id);
   length += member_size(struct GNUNET_MESSENGER_Message, header.previous);
-  length += member_size(struct GNUNET_MESSENGER_Message, header.kind);
+  length += sizeof(kind_t);
 
   return length + get_message_body_kind_size (kind);
 }
@@ -228,8 +237,17 @@ get_message_body_size (enum GNUNET_MESSENGER_MessageKind 
kind, const struct GNUN
 
   switch (kind)
   {
+  case GNUNET_MESSENGER_KIND_INFO:
+    length += GNUNET_IDENTITY_key_get_length(&(body->info.host_key));
+    break;
+  case GNUNET_MESSENGER_KIND_JOIN:
+    length += GNUNET_IDENTITY_key_get_length(&(body->join.key));
+    break;
   case GNUNET_MESSENGER_KIND_NAME:
-    length += (body->name.name? strlen (body->name.name) : 0);
+    length += (body->name.name ? strlen (body->name.name) : 0);
+    break;
+  case GNUNET_MESSENGER_KIND_KEY:
+    length += GNUNET_IDENTITY_key_get_length(&(body->key.key));
     break;
   case GNUNET_MESSENGER_KIND_TEXT:
     length += strlen (body->text.text);
@@ -248,19 +266,76 @@ get_message_body_size (enum GNUNET_MESSENGER_MessageKind 
kind, const struct GNUN
 }
 
 uint16_t
-get_message_size (const struct GNUNET_MESSENGER_Message *message)
+get_message_size (const struct GNUNET_MESSENGER_Message *message,
+                  int include_signature)
 {
-  return get_message_kind_size (message->header.kind) + get_message_body_size 
(message->header.kind, &(message->body));
+  GNUNET_assert(message);
+
+  uint16_t length = 0;
+
+  if (GNUNET_YES == include_signature)
+    length += 
GNUNET_IDENTITY_signature_get_length(&(message->header.signature));
+
+  length += get_message_kind_size (message->header.kind);
+  length += get_message_body_size (message->header.kind, &(message->body));
+
+  return length;
 }
 
 static uint16_t
-get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message)
+get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message, 
int include_body)
 {
+  const uint16_t minimum_size = sizeof(struct GNUNET_HashCode) + 
sizeof(kind_t);
+
   if (message)
-    return sizeof(message->kind) + get_message_body_kind_size (message->kind)
-           + get_message_body_size (message->kind, &(message->body));
+    return minimum_size + get_message_body_kind_size (message->kind)
+           + (include_body == GNUNET_YES? get_message_body_size 
(message->kind, &(message->body)) : 0);
   else
-    return sizeof(message->kind);
+    return minimum_size;
+}
+
+static uint16_t
+calc_usual_padding ()
+{
+  uint16_t padding = 0;
+  uint16_t kind_size;
+
+  for (int i = 0; i <= GNUNET_MESSENGER_KIND_MAX; i++) {
+    kind_size = get_message_kind_size ((enum GNUNET_MESSENGER_MessageKind) i);
+
+    if (kind_size > padding)
+      padding = kind_size;
+  }
+
+  return padding + GNUNET_MESSENGER_PADDING_MIN;
+}
+
+#define max(x, y) (x > y? x : y)
+
+static uint16_t
+calc_padded_length (uint16_t length)
+{
+  static uint16_t usual_padding = 0;
+
+  if (!usual_padding)
+    usual_padding = calc_usual_padding();
+
+  const uint16_t padded_length = max(
+      length + GNUNET_MESSENGER_PADDING_MIN,
+      usual_padding
+  );
+
+  if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL0)
+    return GNUNET_MESSENGER_PADDING_LEVEL0;
+
+  if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL1)
+    return GNUNET_MESSENGER_PADDING_LEVEL1;
+
+  if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL2)
+    return GNUNET_MESSENGER_PADDING_LEVEL2;
+
+  return GNUNET_MESSENGER_MAX_MESSAGE_SIZE;
+
 }
 
 #define min(x, y) (x < y? x : y)
@@ -272,7 +347,27 @@ get_short_message_size (const struct 
GNUNET_MESSENGER_ShortMessage *message)
 
 #define encode_step(dst, offset, src) do {         \
   encode_step_ext(dst, offset, src, sizeof(*src)); \
-} while(0)
+} while (0)
+
+#define encode_step_key(dst, offset, src, length) do {  \
+  ssize_t result = GNUNET_IDENTITY_write_key_to_buffer( \
+      src, dst + offset, length - offset                \
+  );                                                    \
+  if (result < 0)                                       \
+    GNUNET_break (0);                                   \
+  else                                                  \
+    offset += result;                                   \
+} while (0)
+
+#define encode_step_signature(dst, offset, src, length) do {  \
+  ssize_t result = GNUNET_IDENTITY_write_signature_to_buffer( \
+      src, dst + offset, length - offset                      \
+  );                                                          \
+  if (result < 0)                                             \
+    GNUNET_break (0);                                         \
+  else                                                        \
+    offset += result;                                         \
+} while (0)
 
 static void
 encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct 
GNUNET_MESSENGER_MessageBody *body,
@@ -281,20 +376,18 @@ encode_message_body (enum GNUNET_MESSENGER_MessageKind 
kind, const struct GNUNET
   switch (kind)
   {
   case GNUNET_MESSENGER_KIND_INFO:
-    encode_step(buffer, offset, &(body->info.host_key));
-    encode_step(buffer, offset, &(body->info.unique_id));
+    encode_step_key(buffer, offset, &(body->info.host_key), length);
+    encode_step(buffer, offset, &(body->info.messenger_version));
     break;
   case GNUNET_MESSENGER_KIND_JOIN:
-    encode_step(buffer, offset, &(body->join.key));
-    break;
-  case GNUNET_MESSENGER_KIND_LEAVE:
+    encode_step_key(buffer, offset, &(body->join.key), length);
     break;
   case GNUNET_MESSENGER_KIND_NAME:
     if (body->name.name)
       encode_step_ext(buffer, offset, body->name.name, min(length - offset, 
strlen(body->name.name)));
     break;
   case GNUNET_MESSENGER_KIND_KEY:
-    encode_step(buffer, offset, &(body->key.key));
+    encode_step_key(buffer, offset, &(body->key.key), length);
     break;
   case GNUNET_MESSENGER_KIND_PEER:
     encode_step(buffer, offset, &(body->peer.peer));
@@ -321,7 +414,7 @@ encode_message_body (enum GNUNET_MESSENGER_MessageKind 
kind, const struct GNUNET
   case GNUNET_MESSENGER_KIND_FILE:
     encode_step(buffer, offset, &(body->file.key));
     encode_step(buffer, offset, &(body->file.hash));
-    encode_step_ext(buffer, offset, body->file.name, NAME_MAX);
+    encode_step_ext(buffer, offset, body->file.name, sizeof(body->file.name));
     encode_step_ext(buffer, offset, body->file.uri, min(length - offset, 
strlen(body->file.uri)));
     break;
   case GNUNET_MESSENGER_KIND_PRIVATE:
@@ -331,18 +424,40 @@ encode_message_body (enum GNUNET_MESSENGER_MessageKind 
kind, const struct GNUNET
   default:
     break;
   }
+
+  if (offset >= length)
+    return;
+
+  const uint16_t padding = length - offset;
+  const uint16_t used_padding = sizeof(padding) + sizeof(char);
+
+  GNUNET_assert(padding >= used_padding);
+
+  buffer[offset++] = '\0';
+
+  if (padding > used_padding)
+    GNUNET_CRYPTO_random_block(GNUNET_CRYPTO_QUALITY_WEAK, buffer + offset, 
padding - used_padding);
+
+  GNUNET_memcpy(buffer + length - sizeof(padding), &padding, sizeof(padding));
 }
 
 void
-encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t 
length, char *buffer)
+encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t 
length, char *buffer,
+                int include_signature)
 {
+  GNUNET_assert((message) && (buffer));
+
   uint16_t offset = 0;
 
-  encode_step(buffer, offset, &(message->header.signature));
+  if (GNUNET_YES == include_signature)
+    encode_step_signature(buffer, offset, &(message->header.signature), 
length);
+
+  const kind_t kind = (kind_t) message->header.kind;
+
   encode_step(buffer, offset, &(message->header.timestamp));
   encode_step(buffer, offset, &(message->header.sender_id));
   encode_step(buffer, offset, &(message->header.previous));
-  encode_step(buffer, offset, &(message->header.kind));
+  encode_step(buffer, offset, &kind);
 
   encode_message_body (message->header.kind, &(message->body), length, buffer, 
offset);
 }
@@ -350,11 +465,22 @@ encode_message (const struct GNUNET_MESSENGER_Message 
*message, uint16_t length,
 static void
 encode_short_message (const struct GNUNET_MESSENGER_ShortMessage *message, 
uint16_t length, char *buffer)
 {
-  uint16_t offset = 0;
+  struct GNUNET_HashCode hash;
+  uint16_t offset = sizeof(hash);
 
-  encode_step(buffer, offset, &(message->kind));
+  const kind_t kind = (kind_t) message->kind;
+
+  encode_step(buffer, offset, &kind);
 
   encode_message_body (message->kind, &(message->body), length, buffer, 
offset);
+
+  GNUNET_CRYPTO_hash(
+      buffer + sizeof(hash),
+      length - sizeof(hash),
+      &hash
+  );
+
+  GNUNET_memcpy(buffer, &hash, sizeof(hash));
 }
 
 #define decode_step_ext(src, offset, dst, size) do { \
@@ -372,29 +498,51 @@ encode_short_message (const struct 
GNUNET_MESSENGER_ShortMessage *message, uint1
        decode_step_ext(src, offset, dst, size);                                
                  \
 } while (0)
 
-static void
+#define decode_step_key(src, offset, dst, length) do {   \
+  ssize_t result = GNUNET_IDENTITY_read_key_from_buffer( \
+    dst, src + offset, length - offset                   \
+  );                                                     \
+  if (result < 0)                                        \
+    GNUNET_break(0);                                     \
+  else                                                   \
+    offset += result;                                    \
+} while (0)
+
+static uint16_t
 decode_message_body (enum GNUNET_MESSENGER_MessageKind *kind, struct 
GNUNET_MESSENGER_MessageBody *body,
                      uint16_t length, const char *buffer, uint16_t offset)
 {
+  uint16_t padding = 0;
+
+  GNUNET_memcpy(&padding, buffer + length - sizeof(padding), sizeof(padding));
+
+  if (padding > length - offset)
+    padding = 0;
+
+  const uint16_t end_zero = length - padding;
+
+  if ((padding) && (buffer[end_zero] != '\0'))
+    padding = 0;
+
+  length -= padding;
+
   switch (*kind)
   {
-  case GNUNET_MESSENGER_KIND_INFO:
-    decode_step(buffer, offset, &(body->info.host_key));
-    decode_step(buffer, offset, &(body->info.unique_id));
+  case GNUNET_MESSENGER_KIND_INFO: {
+    decode_step_key(buffer, offset, &(body->info.host_key), length);
+    decode_step(buffer, offset, &(body->info.messenger_version));
     break;
-  case GNUNET_MESSENGER_KIND_JOIN:
-    decode_step(buffer, offset, &(body->join.key));
-    break;
-  case GNUNET_MESSENGER_KIND_LEAVE:
+  } case GNUNET_MESSENGER_KIND_JOIN: {
+    decode_step_key(buffer, offset, &(body->join.key), length);
     break;
-  case GNUNET_MESSENGER_KIND_NAME:
+  } case GNUNET_MESSENGER_KIND_NAME:
     if (length - offset > 0)
       decode_step_malloc(buffer, offset, body->name.name, length - offset, 1);
     else
       body->name.name = NULL;
     break;
   case GNUNET_MESSENGER_KIND_KEY:
-    decode_step(buffer, offset, &(body->key.key));
+    decode_step_key(buffer, offset, &(body->key.key), length);
     break;
   case GNUNET_MESSENGER_KIND_PEER:
     decode_step(buffer, offset, &(body->peer.peer));
@@ -421,7 +569,7 @@ decode_message_body (enum GNUNET_MESSENGER_MessageKind 
*kind, struct GNUNET_MESS
   case GNUNET_MESSENGER_KIND_FILE:
     decode_step(buffer, offset, &(body->file.key));
     decode_step(buffer, offset, &(body->file.hash));
-    decode_step_ext(buffer, offset, body->file.name, NAME_MAX);
+    decode_step_ext(buffer, offset, body->file.name, sizeof(body->file.name));
     decode_step_malloc(buffer, offset, body->file.uri, length - offset, 1);
     break;
   case GNUNET_MESSENGER_KIND_PRIVATE:
@@ -434,26 +582,51 @@ decode_message_body (enum GNUNET_MESSENGER_MessageKind 
*kind, struct GNUNET_MESS
     *kind = GNUNET_MESSENGER_KIND_UNKNOWN;
     break;
   }
+
+  return padding;
 }
 
 int
-decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer)
+decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer,
+                int include_signature, uint16_t *padding)
 {
+  GNUNET_assert((message) && (buffer));
+
   uint16_t offset = 0;
 
-  if (length < get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN))
+  if (GNUNET_YES == include_signature)
+  {
+    ssize_t result = GNUNET_IDENTITY_read_signature_from_buffer(
+        &(message->header.signature), buffer, length - offset
+    );
+
+    if (result < 0)
+      return GNUNET_NO;
+    else
+      offset += result;
+  }
+
+  const uint16_t count = length - offset;
+
+  if (count < get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN))
     return GNUNET_NO;
 
-  decode_step(buffer, offset, &(message->header.signature));
+  kind_t kind;
+
   decode_step(buffer, offset, &(message->header.timestamp));
   decode_step(buffer, offset, &(message->header.sender_id));
   decode_step(buffer, offset, &(message->header.previous));
-  decode_step(buffer, offset, &(message->header.kind));
+  decode_step(buffer, offset, &kind);
 
-  if (length < get_message_kind_size (message->header.kind))
+  message->header.kind = (enum GNUNET_MESSENGER_MessageKind) kind;
+
+  if (count < get_message_kind_size (message->header.kind))
     return GNUNET_NO;
 
-  decode_message_body (&(message->header.kind), &(message->body), length, 
buffer, offset);
+  const uint16_t result = decode_message_body (&(message->header.kind), 
&(message->body), length, buffer, offset);
+
+  if (padding)
+    *padding = result;
 
   return GNUNET_YES;
 }
@@ -461,47 +634,80 @@ decode_message (struct GNUNET_MESSENGER_Message *message, 
uint16_t length, const
 static int
 decode_short_message (struct GNUNET_MESSENGER_ShortMessage *message, uint16_t 
length, const char *buffer)
 {
-  uint16_t offset = 0;
+  struct GNUNET_HashCode expected, hash;
+  uint16_t offset = sizeof(hash);
+
+  if (length < get_short_message_size (NULL, GNUNET_NO))
+    return GNUNET_NO;
+
+  GNUNET_memcpy(&hash, buffer, sizeof(hash));
+
+  GNUNET_CRYPTO_hash(
+      buffer + sizeof(hash),
+      length - sizeof(hash),
+      &expected
+  );
 
-  if (length < get_short_message_size (NULL))
+  if (0 != GNUNET_CRYPTO_hash_cmp(&hash, &expected))
     return GNUNET_NO;
 
-  decode_step(buffer, offset, &(message->kind));
+  kind_t kind;
 
-  if (length < get_short_message_size (message))
+  decode_step(buffer, offset, &kind);
+
+  message->kind = (enum GNUNET_MESSENGER_MessageKind) kind;
+
+  if (length < get_short_message_size (message, GNUNET_NO))
     return GNUNET_NO;
 
   decode_message_body (&(message->kind), &(message->body), length, buffer, 
offset);
 
+  if (GNUNET_MESSENGER_KIND_UNKNOWN == message->kind)
+    return GNUNET_NO;
+
   return GNUNET_YES;
 }
 
 void
-hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode 
*hash)
+hash_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer,
+              struct GNUNET_HashCode *hash)
 {
-  GNUNET_CRYPTO_hash (buffer + sizeof(struct GNUNET_CRYPTO_EcdsaSignature),
-                      length - sizeof(struct GNUNET_CRYPTO_EcdsaSignature), 
hash);
+  GNUNET_assert((message) && (buffer) && (hash));
+
+  const ssize_t offset = GNUNET_IDENTITY_signature_get_length(
+      &(message->header.signature)
+  );
+
+  GNUNET_CRYPTO_hash (buffer + offset, length - offset, hash);
 }
 
 void
 sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char 
*buffer,
               const struct GNUNET_HashCode *hash, const struct 
GNUNET_MESSENGER_Ego *ego)
 {
+  GNUNET_assert((message) && (buffer) && (hash) && (ego));
+
   struct GNUNET_MESSENGER_MessageSignature signature;
 
   signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE);
   signature.purpose.size = htonl (sizeof(signature));
 
   GNUNET_memcpy(&(signature.hash), hash, sizeof(struct GNUNET_HashCode));
-
   GNUNET_IDENTITY_sign(&(ego->priv), &signature, &(message->header.signature));
-  GNUNET_memcpy(buffer, &(message->header.signature), sizeof(struct 
GNUNET_CRYPTO_EcdsaSignature));
+
+  uint16_t offset = 0;
+  encode_step_signature(buffer, offset, &(message->header.signature), length);
 }
 
 int
 verify_message (const struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash,
                 const struct GNUNET_IDENTITY_PublicKey *key)
 {
+  GNUNET_assert((message) && (hash) && (key));
+
+  if (ntohl (key->type) != ntohl (message->header.signature.type))
+    return GNUNET_SYSERR;
+
   struct GNUNET_MESSENGER_MessageSignature signature;
 
   signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE);
@@ -516,26 +722,32 @@ verify_message (const struct GNUNET_MESSENGER_Message 
*message, const struct GNU
 int
 encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PublicKey *key)
 {
+  GNUNET_assert((message) && (key));
+
   struct GNUNET_MESSENGER_ShortMessage shortened;
 
   fold_short_message (message, &shortened);
 
-  const uint16_t length = get_short_message_size (&shortened);
+  const uint16_t length = get_short_message_size (&shortened, GNUNET_YES);
+  const uint16_t padded_length = calc_padded_length(length);
 
   message->header.kind = GNUNET_MESSENGER_KIND_PRIVATE;
-  message->body.private.data = GNUNET_malloc(length);
+  message->body.private.data = GNUNET_malloc(padded_length);
+  message->body.private.length = padded_length;
 
-  encode_short_message (&shortened, length, message->body.private.data);
+  encode_short_message (&shortened, padded_length, message->body.private.data);
 
-  if (GNUNET_IDENTITY_encrypt (message->body.private.data, length, key, 
&(message->body.private.key),
-                               message->body.private.data)
-      == length)
+  if (padded_length == GNUNET_IDENTITY_encrypt (message->body.private.data, 
padded_length, key,
+                                                &(message->body.private.key),
+                                                message->body.private.data))
   {
     destroy_message_body (shortened.kind, &(shortened.body));
     return GNUNET_YES;
   }
   else
   {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Encrypting message failed!\n");
+
     unfold_short_message (&shortened, message);
     return GNUNET_NO;
   }
@@ -544,18 +756,28 @@ encrypt_message (struct GNUNET_MESSENGER_Message 
*message, const struct GNUNET_I
 int
 decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PrivateKey *key)
 {
-  if (message->body.private.length != GNUNET_IDENTITY_decrypt 
(message->body.private.data,
-                                                               
message->body.private.length, key,
-                                                               
&(message->body.private.key),
+  GNUNET_assert((message) && (key));
+
+  if (message->body.private.length != GNUNET_IDENTITY_decrypt 
(message->body.private.data, message->body.private.length,
+                                                               key, 
&(message->body.private.key),
                                                                
message->body.private.data))
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Decrypting message failed!\n");
+
     return GNUNET_NO;
+  }
 
   struct GNUNET_MESSENGER_ShortMessage shortened;
 
   if (GNUNET_YES != decode_short_message (&shortened, 
message->body.private.length, message->body.private.data))
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Decoding decrypted message 
failed!\n");
+
     return GNUNET_NO;
+  }
 
   unfold_short_message (&shortened, message);
+
   return GNUNET_YES;
 }
 
@@ -563,18 +785,25 @@ struct GNUNET_MQ_Envelope*
 pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode 
*hash,
               const struct GNUNET_MESSENGER_Ego *ego, int mode)
 {
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Packing message: %u\n", 
message->header.kind);
+  GNUNET_assert(message);
+
+  if (ego)
+    message->header.signature.type = ego->priv.type;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Packing message kind=%u and sender: 
%s\n",
+             message->header.kind, GNUNET_sh2s(&(message->header.sender_id)));
 
   struct GNUNET_MessageHeader *header;
 
-  uint16_t length = get_message_size (message);
+  const uint16_t length = get_message_size (message, GNUNET_YES);
+  const uint16_t padded_length = calc_padded_length(length);
 
   struct GNUNET_MQ_Envelope *env;
   char *buffer;
 
   if (GNUNET_MESSENGER_PACK_MODE_ENVELOPE == mode)
   {
-    env = GNUNET_MQ_msg_extra(header, length, GNUNET_MESSAGE_TYPE_CADET_CLI);
+    env = GNUNET_MQ_msg_extra(header, padded_length, 
GNUNET_MESSAGE_TYPE_CADET_CLI);
 
     buffer = (char*) &(header[1]);
   }
@@ -582,14 +811,14 @@ pack_message (struct GNUNET_MESSENGER_Message *message, 
struct GNUNET_HashCode *
   {
     env = NULL;
 
-    buffer = GNUNET_malloc(length);
+    buffer = GNUNET_malloc(padded_length);
   }
 
-  encode_message (message, length, buffer);
+  encode_message (message, padded_length, buffer, GNUNET_YES);
 
   if (hash)
   {
-    hash_message (length, buffer, hash);
+    hash_message (message, length, buffer, hash);
 
     if (ego)
       sign_message (message, length, buffer, hash, ego);
@@ -600,3 +829,43 @@ pack_message (struct GNUNET_MESSENGER_Message *message, 
struct GNUNET_HashCode *
 
   return env;
 }
+
+int
+filter_message_sending (const struct GNUNET_MESSENGER_Message *message)
+{
+  switch (message->header.kind)
+  {
+  case GNUNET_MESSENGER_KIND_INFO:
+    return GNUNET_SYSERR; // Reserved for connection handling only!
+  case GNUNET_MESSENGER_KIND_JOIN:
+    return GNUNET_NO; // Use #GNUNET_MESSENGER_enter_room(...) instead!
+  case GNUNET_MESSENGER_KIND_LEAVE:
+    return GNUNET_NO; // Use #GNUNET_MESSENGER_close_room(...) instead!
+  case GNUNET_MESSENGER_KIND_NAME:
+    return GNUNET_YES;
+  case GNUNET_MESSENGER_KIND_KEY:
+    return GNUNET_NO; // Use #GNUNET_MESSENGER_update(...) instead!
+  case GNUNET_MESSENGER_KIND_PEER:
+    return GNUNET_NO; // Use #GNUNET_MESSENGER_open_room(...) instead!
+  case GNUNET_MESSENGER_KIND_ID:
+    return GNUNET_SYSERR; // Reserved for member id handling only!
+  case GNUNET_MESSENGER_KIND_MISS:
+    return GNUNET_SYSERR; // Reserved for connection handling only!
+  case GNUNET_MESSENGER_KIND_MERGE:
+    return GNUNET_YES;
+  case GNUNET_MESSENGER_KIND_REQUEST:
+    return GNUNET_YES;
+  case GNUNET_MESSENGER_KIND_INVITE:
+    return GNUNET_YES;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    return GNUNET_YES;
+  case GNUNET_MESSENGER_KIND_FILE:
+    return GNUNET_YES;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    return GNUNET_NO; // Use #GNUNET_MESSENGER_send_message(...) with a 
contact instead!
+  case GNUNET_MESSENGER_KIND_DELETE:
+    return GNUNET_YES;
+  default:
+    return GNUNET_SYSERR;
+  }
+}
diff --git a/src/messenger/messenger_api_message.h 
b/src/messenger/messenger_api_message.h
index 0f0a97e9c..7ce30dc92 100644
--- a/src/messenger/messenger_api_message.h
+++ b/src/messenger/messenger_api_message.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -36,10 +36,17 @@
 
 #include "messenger_api_ego.h"
 
+#define GNUNET_MESSENGER_MAX_MESSAGE_SIZE (GNUNET_MAX_MESSAGE_SIZE - 
GNUNET_MIN_MESSAGE_SIZE)
+
+#define GNUNET_MESSENGER_PADDING_MIN (sizeof(uint16_t) + sizeof(char))
+#define GNUNET_MESSENGER_PADDING_LEVEL0 (512)
+#define GNUNET_MESSENGER_PADDING_LEVEL1 (4096)
+#define GNUNET_MESSENGER_PADDING_LEVEL2 (32768)
+
 /**
  * Creates and allocates a new message with a specific <i>kind</i>.
  *
- * @param kind Kind of message
+ * @param[in] kind Kind of message
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
@@ -48,7 +55,7 @@ create_message (enum GNUNET_MESSENGER_MessageKind kind);
 /**
  * Creates and allocates a copy of a given <i>message</i>.
  *
- * @param message Message
+ * @param[in] message Message
  * @return New message
  */
 struct GNUNET_MESSENGER_Message*
@@ -57,15 +64,24 @@ copy_message (const struct GNUNET_MESSENGER_Message 
*message);
 /**
  * Destroys a message and frees its memory fully.
  *
- * @param message Message
+ * @param[in/out] message Message
  */
 void
 destroy_message (struct GNUNET_MESSENGER_Message *message);
 
+/**
+ * Returns if the message should be bound to a member session.
+ *
+ * @param[in] message Message
+ * @return #GNUNET_YES or #GNUNET_NO
+ */
+int
+is_message_session_bound (const struct GNUNET_MESSENGER_Message *message);
+
 /**
  * Returns the minimal size in bytes to encode a message of a specific 
<i>kind</i>.
  *
- * @param kind Kind of message
+ * @param[in] kind Kind of message
  * @return Minimal size to encode
  */
 uint16_t
@@ -74,57 +90,66 @@ get_message_kind_size (enum GNUNET_MESSENGER_MessageKind 
kind);
 /**
  * Returns the exact size in bytes to encode a given <i>message</i>.
  *
- * @param message Message
+ * @param[in] message Message
+ * @param[in] encode_signature Flag to include signature
  * @return Size to encode
  */
 uint16_t
-get_message_size (const struct GNUNET_MESSENGER_Message *message);
+get_message_size (const struct GNUNET_MESSENGER_Message *message,
+                  int include_signature);
 
 /**
  * Encodes a given <i>message</i> into a <i>buffer</i> of a maximal 
<i>length</i> in bytes.
  *
- * @param message Message
- * @param length Maximal length to encode
+ * @param[in] message Message
+ * @param[in] length Maximal length to encode
  * @param[out] buffer Buffer
+ * @param[in] encode_signature Flag to include signature
  */
 void
-encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t 
length, char *buffer);
+encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t 
length, char *buffer,
+                int include_signature);
 
 /**
  * Decodes a <i>message</i> from a given <i>buffer</i> of a maximal 
<i>length</i> in bytes.
  *
  * If the buffer is too small for a message of its decoded kind the function 
fails with
- * resulting GNUNET_NO after decoding only the messages header.
+ * resulting #GNUNET_NO after decoding only the messages header.
  *
- * On success the function returns GNUNET_YES.
+ * On success the function returns #GNUNET_YES.
  *
  * @param[out] message Message
- * @param length Maximal length to decode
- * @param buffer Buffer
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in] length Maximal length to decode
+ * @param[in] buffer Buffer
+ * @param[out] padding Padding
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
-decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer);
+decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer,
+                int include_signature, uint16_t *padding);
 
 /**
- * Calculates a <i>hash</i> of a given <i>buffer</i> of a <i>length</i> in 
bytes.
+ * Calculates a <i>hash</i> of a given <i>buffer</i> with a <i>length</i> in 
bytes
+ * from a <i>message</i>.
  *
- * @param length Length of buffer
- * @param buffer Buffer
+ * @param[in] message Message
+ * @param[in] length Length of buffer
+ * @param[in] buffer Buffer
  * @param[out] hash Hash
  */
 void
-hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode 
*hash);
+hash_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer,
+              struct GNUNET_HashCode *hash);
 
 /**
  * Signs the <i>hash</i> of a <i>message</i> with a given <i>ego</i> and 
writes the signature
  * into the <i>buffer</i> as well.
  *
- * @param[out] message Message
- * @param length Length of buffer
+ * @param[in/out] message Message
+ * @param[in] length Length of buffer
  * @param[out] buffer Buffer
- * @param hash Hash of message
- * @param ego EGO
+ * @param[in] hash Hash of message
+ * @param[in] ego EGO
  */
 void
 sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char 
*buffer,
@@ -132,13 +157,13 @@ sign_message (struct GNUNET_MESSENGER_Message *message, 
uint16_t length, char *b
 
 /**
  * Verifies the signature of a given <i>message</i> and its <i>hash</i> with a 
specific
- * public key. The function returns GNUNET_OK if the signature was valid, 
otherwise
- * GNUNET_SYSERR.
+ * public key. The function returns #GNUNET_OK if the signature was valid, 
otherwise
+ * #GNUNET_SYSERR.
  *
- * @param message Message
- * @param hash Hash of message
- * @param key Public key of EGO
- * @return GNUNET_OK on success, otherwise GNUNET_SYSERR
+ * @param[in] message Message
+ * @param[in] hash Hash of message
+ * @param[in] key Public key of EGO
+ * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR
  */
 int
 verify_message (const struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash,
@@ -147,23 +172,23 @@ verify_message (const struct GNUNET_MESSENGER_Message 
*message, const struct GNU
 /**
  * Encrypts a <i>message</i> using a given public <i>key</i> and replaces its 
body
  * and kind with the now private encrypted <i>message</i>. The function returns
- * GNUNET_YES if the operation succeeded, otherwise GNUNET_NO.
+ * #GNUNET_YES if the operation succeeded, otherwise #GNUNET_NO.
  *
- * @param message Message
- * @param key Public key of EGO
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] message Message
+ * @param[in] key Public key of EGO
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PublicKey *key);
 
 /**
  * Decrypts a private <i>message</i> using a given private <i>key</i> and 
replaces its body
- * and kind with the inner encrypted message. The function returns GNUNET_YES 
if the
- * operation succeeded, otherwise GNUNET_NO.
+ * and kind with the inner encrypted message. The function returns #GNUNET_YES 
if the
+ * operation succeeded, otherwise #GNUNET_NO.
  *
- * @param message Message
- * @param key Private key of EGO
- * @return GNUNET_YES on success, otherwise GNUNET_NO
+ * @param[in/out] message Message
+ * @param[in] key Private key of EGO
+ * @return #GNUNET_YES on success, otherwise #GNUNET_NO
  */
 int
 decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PrivateKey *key);
@@ -173,18 +198,28 @@ decrypt_message (struct GNUNET_MESSENGER_Message 
*message, const struct GNUNET_I
 
 /**
  * Encodes the <i>message</i> to pack it into a newly allocated envelope if 
<i>mode</i>
- * is equal to GNUNET_MESSENGER_PACK_MODE_ENVELOPE. Independent of the mode 
the message
+ * is equal to #GNUNET_MESSENGER_PACK_MODE_ENVELOPE. Independent of the mode 
the message
  * will be hashed if <i>hash</i> is not NULL and it will be signed if the 
<i>ego</i> is
  * not NULL.
  *
  * @param[out] message Message
  * @param[out] hash Hash of message
- * @param ego EGO to sign
- * @param mode Mode of packing
+ * @param[in] ego EGO to sign
+ * @param[in] mode Mode of packing
  * @return Envelope or NULL
  */
 struct GNUNET_MQ_Envelope*
 pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode 
*hash,
               const struct GNUNET_MESSENGER_Ego *ego, int mode);
 
+/**
+ * Returns if a specific kind of message should be sent by a client. The 
function returns
+ * #GNUNET_YES or #GNUNET_NO for recommendations and #GNUNET_SYSERR for 
specific kinds
+ * of messages which should not be sent manually at all.
+ *
+ * @param[in] message Message
+ */
+int
+filter_message_sending (const struct GNUNET_MESSENGER_Message *message);
+
 #endif //GNUNET_MESSENGER_API_MESSAGE_H
diff --git a/src/messenger/messenger_api_room.c 
b/src/messenger/messenger_api_room.c
index 5fedf1a78..df141ca12 100644
--- a/src/messenger/messenger_api_room.c
+++ b/src/messenger/messenger_api_room.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -30,6 +30,8 @@
 struct GNUNET_MESSENGER_Room*
 create_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key)
 {
+  GNUNET_assert((handle) && (key));
+
   struct GNUNET_MESSENGER_Room *room = GNUNET_new(struct 
GNUNET_MESSENGER_Room);
 
   room->handle = handle;
@@ -38,11 +40,10 @@ create_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_HashCod
   room->opened = GNUNET_NO;
   room->contact_id = NULL;
 
-  room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO);
-
   init_list_tunnels (&(room->entries));
 
   room->messages = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO);
 
   return room;
 }
@@ -50,9 +51,10 @@ create_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_HashCod
 static int
 iterate_destroy_message (void *cls, const struct GNUNET_HashCode *key, void 
*value)
 {
-  struct GNUNET_MESSENGER_Message *message = value;
+  struct GNUNET_MESSENGER_RoomMessageEntry *entry = value;
 
-  destroy_message (message);
+  destroy_message (entry->message);
+  GNUNET_free(entry);
 
   return GNUNET_YES;
 }
@@ -60,8 +62,7 @@ iterate_destroy_message (void *cls, const struct 
GNUNET_HashCode *key, void *val
 void
 destroy_room (struct GNUNET_MESSENGER_Room *room)
 {
-  if (room->members)
-    GNUNET_CONTAINER_multishortmap_destroy (room->members);
+  GNUNET_assert(room);
 
   clear_list_tunnels (&(room->entries));
 
@@ -72,6 +73,9 @@ destroy_room (struct GNUNET_MESSENGER_Room *room)
     GNUNET_CONTAINER_multihashmap_destroy (room->messages);
   }
 
+  if (room->members)
+    GNUNET_CONTAINER_multishortmap_destroy (room->members);
+
   if (room->contact_id)
     GNUNET_free(room->contact_id);
 
@@ -81,65 +85,111 @@ destroy_room (struct GNUNET_MESSENGER_Room *room)
 const struct GNUNET_MESSENGER_Message*
 get_room_message (const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_HashCode *hash)
 {
-  return GNUNET_CONTAINER_multihashmap_get (room->messages, hash);
+  GNUNET_assert((room) && (hash));
+
+  struct GNUNET_MESSENGER_RoomMessageEntry *entry = 
GNUNET_CONTAINER_multihashmap_get (
+      room->messages, hash
+  );
+
+  return (entry? entry->message : NULL);
+}
+
+struct GNUNET_MESSENGER_Contact*
+get_room_sender (const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_HashCode *hash)
+{
+  GNUNET_assert((room) && (hash));
+
+  struct GNUNET_MESSENGER_RoomMessageEntry *entry = 
GNUNET_CONTAINER_multihashmap_get (
+      room->messages, hash
+  );
+
+  return (entry? entry->sender : NULL);
 }
 
 static void
-handle_join_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                     const struct GNUNET_HashCode *hash)
+handle_join_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_Contact *contact = get_handle_contact_by_pubkey 
(room->handle, &(message->body.join.key));
+  if (!sender)
+  {
+    struct GNUNET_MESSENGER_ContactStore *store = 
get_handle_contact_store(room->handle);
+    struct GNUNET_HashCode context;
+
+    get_context_from_member(&(room->key), &(message->header.sender_id), 
&context);
+
+    sender = get_store_contact(store, &context, &(message->body.join.key));
+  }
 
-  if (contact)
-    GNUNET_CONTAINER_multishortmap_put (room->members, 
&(message->header.sender_id), contact,
-                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  if ((GNUNET_YES != 
GNUNET_CONTAINER_multishortmap_contains_value(room->members, 
&(message->header.sender_id), sender)) &&
+      (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put(room->members, 
&(message->header.sender_id), sender,
+                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)))
+    increase_contact_rc(sender);
 }
 
 static void
-handle_leave_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                      const struct GNUNET_HashCode *hash)
+handle_leave_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                      const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  GNUNET_CONTAINER_multishortmap_remove_all (room->members, 
&(message->header.sender_id));
+  if ((!sender) ||
+      (GNUNET_YES != GNUNET_CONTAINER_multishortmap_remove(room->members, 
&(message->header.sender_id), sender)))
+    return;
+
+  struct GNUNET_HashCode context;
+  get_context_from_member(&(room->key), &(message->header.sender_id), 
&context);
+
+  struct GNUNET_MESSENGER_ContactStore *store = 
get_handle_contact_store(room->handle);
+
+  if (GNUNET_YES == decrease_contact_rc(sender))
+    remove_store_contact(store, sender, &context);
 }
 
 static void
-handle_name_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                     const struct GNUNET_HashCode *hash)
+handle_name_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_Contact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members,
-                                                                               
  &(message->header.sender_id));
+  if (!sender)
+    return;
 
-  if (contact)
-    set_contact_name (contact, message->body.name.name);
+  set_contact_name (sender, message->body.name.name);
 }
 
 static void
-handle_key_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                    const struct GNUNET_HashCode *hash)
+handle_key_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                    const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_Contact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members,
-                                                                               
  &(message->header.sender_id));
+  if (!sender)
+    return;
+
+  struct GNUNET_HashCode context;
+  get_context_from_member(&(room->key), &(message->header.sender_id), 
&context);
 
-  if (contact)
-    swap_handle_contact_by_pubkey (room->handle, contact, 
&(message->body.key.key));
+  struct GNUNET_MESSENGER_ContactStore *store = 
get_handle_contact_store(room->handle);
+
+  update_store_contact(store, sender, &context, &context, 
&(message->body.key.key));
 }
 
 static void
-handle_id_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                   const struct GNUNET_HashCode *hash)
+handle_id_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                   const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
-  struct GNUNET_MESSENGER_Contact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members,
-                                                                               
  &(message->header.sender_id));
+  if ((!sender) ||
+      (GNUNET_YES != GNUNET_CONTAINER_multishortmap_remove(room->members, 
&(message->header.sender_id), sender)) ||
+      (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(room->members, 
&(message->body.id.id), sender,
+                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)))
+    return;
+
+  struct GNUNET_HashCode context, next_context;
+  get_context_from_member(&(room->key), &(message->header.sender_id), 
&context);
+  get_context_from_member(&(room->key), &(message->body.id.id), &next_context);
 
-  if ((contact) && (GNUNET_OK
-      == GNUNET_CONTAINER_multishortmap_put (room->members, 
&(message->body.id.id), contact,
-                                             
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
-    GNUNET_CONTAINER_multishortmap_remove (room->members, 
&(message->header.sender_id), contact);
+  struct GNUNET_MESSENGER_ContactStore *store = 
get_handle_contact_store(room->handle);
+
+  update_store_contact(store, sender, &context, &next_context, 
get_contact_key(sender));
 }
 
 static void
-handle_miss_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                     const struct GNUNET_HashCode *hash)
+handle_miss_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
   if ((room->contact_id) && (0 == GNUNET_memcmp(&(message->header.sender_id), 
room->contact_id)))
   {
@@ -150,9 +200,25 @@ handle_miss_message (struct GNUNET_MESSENGER_Room *room, 
const struct GNUNET_MES
   }
 }
 
+static void
+handle_delete_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_RoomMessageEntry *entry = 
GNUNET_CONTAINER_multihashmap_get (
+      room->messages, &(message->body.delete.hash)
+  );
+
+  if ((entry) && ((entry->sender == sender) || (get_handle_contact 
(room->handle, &(room->key)) == sender)) &&
+      (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (room->messages, 
&(message->body.delete.hash), entry)))
+  {
+    destroy_message (entry->message);
+    GNUNET_free(entry);
+  }
+}
+
 void
-handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                     const struct GNUNET_HashCode *hash)
+handle_room_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
 {
   if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (room->messages, 
hash))
     return;
@@ -160,30 +226,78 @@ handle_room_message (struct GNUNET_MESSENGER_Room *room, 
const struct GNUNET_MES
   switch (message->header.kind)
   {
   case GNUNET_MESSENGER_KIND_JOIN:
-    handle_join_message (room, message, hash);
+    handle_join_message (room, sender, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_LEAVE:
-    handle_leave_message (room, message, hash);
+    handle_leave_message (room, sender, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_NAME:
-    handle_name_message (room, message, hash);
+    handle_name_message (room, sender, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_KEY:
-    handle_key_message (room, message, hash);
+    handle_key_message (room, sender, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_ID:
-    handle_id_message (room, message, hash);
+    handle_id_message (room, sender, message, hash);
     break;
   case GNUNET_MESSENGER_KIND_MISS:
-    handle_miss_message (room, message, hash);
+    handle_miss_message (room, sender, message, hash);
+    break;
+  case GNUNET_MESSENGER_KIND_DELETE:
+    handle_delete_message (room, sender, message, hash);
     break;
   default:
     break;
   }
 
-  struct GNUNET_MESSENGER_Message *clone = copy_message (message);
+  struct GNUNET_MESSENGER_RoomMessageEntry *entry = GNUNET_new(struct 
GNUNET_MESSENGER_RoomMessageEntry);
+
+  if (!entry)
+    return;
+
+  entry->sender = sender;
+  entry->message = copy_message (message);
 
-  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (room->messages, hash, 
clone,
+  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (room->messages, hash, 
entry,
                                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
-    destroy_message (clone);
+  {
+    destroy_message (entry->message);
+    GNUNET_free(entry);
+  }
+}
+
+struct GNUNET_MESSENGER_MemberCall
+{
+  struct GNUNET_MESSENGER_Room *room;
+  GNUNET_MESSENGER_MemberCallback callback;
+  void *cls;
+};
+
+static int
+iterate_local_members (void* cls, const struct GNUNET_ShortHashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_MemberCall *call = cls;
+  struct GNUNET_MESSENGER_Contact *contact = value;
+
+  return call->callback(call->cls, call->room, contact);
+}
+
+int
+iterate_room_members (struct GNUNET_MESSENGER_Room *room, 
GNUNET_MESSENGER_MemberCallback callback,
+                      void* cls)
+{
+  GNUNET_assert(room);
+
+  if (!callback)
+    return GNUNET_CONTAINER_multishortmap_iterate(room->members, NULL, NULL);
+
+  struct GNUNET_MESSENGER_MemberCall call;
+
+  call.room = room;
+  call.callback = callback;
+  call.cls = cls;
+
+  GNUNET_assert(callback);
+
+  return GNUNET_CONTAINER_multishortmap_iterate(room->members, 
iterate_local_members, &call);
 }
diff --git a/src/messenger/messenger_api_room.h 
b/src/messenger/messenger_api_room.h
index 0038128d8..9455fd43b 100644
--- a/src/messenger/messenger_api_room.h
+++ b/src/messenger/messenger_api_room.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -36,6 +36,11 @@
 #include "messenger_api_contact.h"
 #include "messenger_api_message.h"
 
+struct GNUNET_MESSENGER_RoomMessageEntry {
+  struct GNUNET_MESSENGER_Contact* sender;
+  struct GNUNET_MESSENGER_Message* message;
+};
+
 struct GNUNET_MESSENGER_Room
 {
   struct GNUNET_MESSENGER_Handle *handle;
@@ -45,17 +50,17 @@ struct GNUNET_MESSENGER_Room
 
   struct GNUNET_ShortHashCode *contact_id;
 
-  struct GNUNET_CONTAINER_MultiShortmap *members;
   struct GNUNET_MESSENGER_ListTunnels entries;
 
   struct GNUNET_CONTAINER_MultiHashMap *messages;
+  struct GNUNET_CONTAINER_MultiShortmap *members;
 };
 
 /**
  * Creates and allocates a new room for a <i>handle</i> with a given 
<i>key</i> for the client API.
  *
- * @param handle Handle
- * @param key Key of room
+ * @param[in/out] handle Handle
+ * @param[in] key Key of room
  * @return New room
  */
 struct GNUNET_MESSENGER_Room*
@@ -64,7 +69,7 @@ create_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_HashCod
 /**
  * Destroys a room and frees its memory fully from the client API.
  *
- * @param room Room
+ * @param[in/out] room Room
  */
 void
 destroy_room (struct GNUNET_MESSENGER_Room *room);
@@ -73,23 +78,48 @@ destroy_room (struct GNUNET_MESSENGER_Room *room);
  * Returns a message locally stored from a map for a given <i>hash</i> in a 
<i>room</i>. If no matching
  * message is found, NULL gets returned.
  *
- * @param room Room
- * @param hash Hash of message
+ * @param[in] room Room
+ * @param[in] hash Hash of message
  * @return Message or NULL
  */
 const struct GNUNET_MESSENGER_Message*
 get_room_message (const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_HashCode *hash);
 
+/**
+ * Returns a messages sender locally stored from a map for a given <i>hash</i> 
in a <i>room</i>. If no
+ * matching message is found, NULL gets returned.
+ *
+ * @param[in] room Room
+ * @param[in] hash Hash of message
+ * @return Contact of sender or NULL
+ */
+struct GNUNET_MESSENGER_Contact*
+get_room_sender (const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_HashCode *hash);
+
 /**
  * Handles a <i>message</i> with a given <i>hash</i> in a <i>room</i> for the 
client API to update
  * members and its information. The function also stores the message in map 
locally for access afterwards.
  *
- * @param room Room
- * @param message Message
- * @param hash Hash of message
+ * @param[in/out] room Room
+ * @param[in/out] sender Contact of sender
+ * @param[in] message Message
+ * @param[in] hash Hash of message
  */
 void
-handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-                     const struct GNUNET_HashCode *hash);
+handle_room_message (struct GNUNET_MESSENGER_Room *room, struct 
GNUNET_MESSENGER_Contact *sender,
+                     const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
+
+/**
+ * Iterates through all members of a given <i>room</i> to forward each of them 
to a selected
+ * <i>callback</i> with a custom closure.
+ *
+ * @param[in/out] room Room
+ * @param[in] callback Function called for each member
+ * @param[in/out] cls Closure
+ * @return Amount of members iterated
+ */
+int
+iterate_room_members (struct GNUNET_MESSENGER_Room *room, 
GNUNET_MESSENGER_MemberCallback callback,
+                      void* cls);
 
 #endif //GNUNET_MESSENGER_API_ROOM_H
diff --git a/src/messenger/gnunet-service-messenger_util.c 
b/src/messenger/messenger_api_util.c
similarity index 71%
rename from src/messenger/gnunet-service-messenger_util.c
rename to src/messenger/messenger_api_util.c
index 94fc9469d..68e15d789 100644
--- a/src/messenger/gnunet-service-messenger_util.c
+++ b/src/messenger/messenger_api_util.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -19,11 +19,11 @@
  */
 /**
  * @author Tobias Frisch
- * @file src/messenger/gnunet-service-messenger_util.c
- * @brief GNUnet MESSENGER service
+ * @file src/messenger/messenger_api_util.c
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
  */
 
-#include "gnunet-service-messenger_util.h"
+#include "messenger_api_util.h"
 
 static void
 callback_close_channel (void *cls)
@@ -37,13 +37,18 @@ callback_close_channel (void *cls)
 void
 delayed_disconnect_channel (struct GNUNET_CADET_Channel *channel)
 {
-  GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_zero_ 
(), GNUNET_SCHEDULER_PRIORITY_URGENT,
+  GNUNET_assert(channel);
+
+  GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_zero_ 
(),
+                                              GNUNET_SCHEDULER_PRIORITY_URGENT,
                                               callback_close_channel, channel);
 }
 
 int
 generate_free_member_id (struct GNUNET_ShortHashCode *id, const struct 
GNUNET_CONTAINER_MultiShortmap *members)
 {
+  GNUNET_assert(id);
+
   size_t counter = 1 + (members ? GNUNET_CONTAINER_multishortmap_size 
(members) : 0);
 
   do
@@ -62,3 +67,18 @@ generate_free_member_id (struct GNUNET_ShortHashCode *id, 
const struct GNUNET_CO
 
   return GNUNET_NO;
 }
+
+const struct GNUNET_IDENTITY_PublicKey*
+get_anonymous_public_key ()
+{
+  static struct GNUNET_IDENTITY_PublicKey public_key;
+  static struct GNUNET_IDENTITY_Ego* ego = NULL;
+
+  if (!ego)
+  {
+    ego = GNUNET_IDENTITY_ego_get_anonymous();
+    GNUNET_IDENTITY_ego_get_public_key(ego, &public_key);
+  }
+
+  return &public_key;
+}
diff --git a/src/messenger/gnunet-service-messenger_util.h 
b/src/messenger/messenger_api_util.h
similarity index 67%
rename from src/messenger/gnunet-service-messenger_util.h
rename to src/messenger/messenger_api_util.h
index 20f8f0afe..c70a3601f 100644
--- a/src/messenger/gnunet-service-messenger_util.h
+++ b/src/messenger/messenger_api_util.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -19,8 +19,8 @@
  */
 /**
  * @author Tobias Frisch
- * @file src/messenger/gnunet-service-messenger_util.h
- * @brief GNUnet MESSENGER service
+ * @file src/messenger/messenger_api_util.h
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
  */
 
 #ifndef GNUNET_SERVICE_MESSENGER_UTIL_H
@@ -30,24 +30,35 @@
 #include "gnunet_cadet_service.h"
 #include "gnunet_container_lib.h"
 #include "gnunet_crypto_lib.h"
+#include "gnunet_disk_lib.h"
+#include "gnunet_identity_service.h"
 
 /**
  * Starts an urgent task to close a CADET channel asynchronously.
  *
- * @param channel Channel
+ * @param[in/out] channel Channel
  */
 void
 delayed_disconnect_channel (struct GNUNET_CADET_Channel *channel);
 
 /**
- * Tries to generate an unused member id and store it into the <i>id</i> 
parameter. A map containing all currently
- * used member ids is used to check against.
+ * Tries to generate an unused member id and store it into the <i>id</i> 
parameter.
+ * A map containing all currently used member ids is used to check against.
  *
  * @param[out] id New member id
- * @param members Map of member ids
- * @return GNUNET_YES on success, GNUNET_NO on failure
+ * @param[in] members Map of member ids
+ * @return #GNUNET_YES on success, #GNUNET_NO on failure
  */
 int
 generate_free_member_id (struct GNUNET_ShortHashCode *id, const struct 
GNUNET_CONTAINER_MultiShortmap *members);
 
+/**
+ * Returns the public identity key of #GNUNET_IDENTITY_ego_get_anonymous() 
without
+ * recalculating it every time.
+ *
+ * @return anonymous public key
+ */
+const struct GNUNET_IDENTITY_PublicKey*
+get_anonymous_public_key ();
+
 #endif //GNUNET_SERVICE_MESSENGER_UTIL_H
diff --git a/src/messenger/test_messenger.c b/src/messenger/test_messenger.c
index b42dfe6d9..fb3e3e1bc 100644
--- a/src/messenger/test_messenger.c
+++ b/src/messenger/test_messenger.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -61,14 +61,13 @@ end (void *cls)
 
   if (messenger)
   {
-    GNUNET_MESSENGER_disconnect(messenger);
+    GNUNET_MESSENGER_disconnect (messenger);
     messenger = NULL;
   }
 
   status = 0;
 }
 
-
 static void
 end_badly (void *cls)
 {
@@ -83,7 +82,7 @@ end_operation (void *cls)
 {
   op_task = NULL;
 
-  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) 
cls : "unknown");
+  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls ? (const char*) 
cls : "unknown");
 
   if (die_task)
     GNUNET_SCHEDULER_cancel (die_task);
@@ -109,30 +108,25 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
     op_task = NULL;
   }
 
-  const char* name = GNUNET_MESSENGER_get_name(handle);
+  const char *name = GNUNET_MESSENGER_get_name (handle);
 
-  if (0 != strcmp(name, TESTER_NAME))
+  if (0 != strcmp (name, TESTER_NAME))
   {
     op_task = GNUNET_SCHEDULER_add_now (&end_operation, "name");
     return;
   }
 
-  struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous();
-  struct GNUNET_IDENTITY_PublicKey anonymous_key;
-
-  GNUNET_IDENTITY_ego_get_public_key(ego, &anonymous_key);
+  const struct GNUNET_IDENTITY_PublicKey *key = GNUNET_MESSENGER_get_key 
(handle);
 
-  const struct GNUNET_IDENTITY_PublicKey* key = 
GNUNET_MESSENGER_get_key(handle);
-
-  if (((!identity_counter) && (0 != GNUNET_memcmp(key, (&anonymous_key)))) ||
-      ((identity_counter) && (0 == GNUNET_memcmp(key, (&anonymous_key)))))
+  if (((!identity_counter) && (key)) || ((identity_counter) && (!key)))
   {
     op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key");
     return;
   }
 
-  if (identity_counter) {
-    GNUNET_MESSENGER_disconnect(handle);
+  if (identity_counter)
+  {
+    GNUNET_MESSENGER_disconnect (handle);
 
     op_task = NULL;
     messenger = NULL;
@@ -144,7 +138,7 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
     return;
   }
 
-  GNUNET_MESSENGER_update(messenger);
+  GNUNET_MESSENGER_update (messenger);
   identity_counter++;
 }
 
@@ -156,16 +150,14 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
  * @param peer Peer for testing
  */
 static void
-run (void *cls,
-     const struct GNUNET_CONFIGURATION_Handle *cfg,
-     struct GNUNET_TESTING_Peer *peer)
+run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, struct 
GNUNET_TESTING_Peer *peer)
 {
   die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL);
 
   identity_counter = 0;
 
   op_task = GNUNET_SCHEDULER_add_delayed (BASE_TIMEOUT, &end_operation, 
"connect");
-  messenger = GNUNET_MESSENGER_connect(cfg, TESTER_NAME, &on_identity, NULL, 
NULL, NULL);
+  messenger = GNUNET_MESSENGER_connect (cfg, TESTER_NAME, &on_identity, NULL, 
NULL, NULL);
 }
 
 /**
@@ -176,11 +168,9 @@ run (void *cls,
  * @return 0 ok, 1 on error
  */
 int
-main(int argc, char **argv)
+main (int argc, char **argv)
 {
-  if (0 != GNUNET_TESTING_peer_run("test-messenger",
-                                   "test_messenger_api.conf",
-                                   &run, NULL))
+  if (0 != GNUNET_TESTING_peer_run ("test-messenger", 
"test_messenger_api.conf", &run, NULL))
     return 1;
 
   return status;
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_adapt.c
similarity index 53%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_adapt.c
index c60eeac50..90e8ac28d 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_adapt.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_adapt.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    5,    1,    2,    3,    6,    7,    8,    4 };
+  unsigned int stages [] = { 0x21, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x21 };
+
+  struct test_configuration cfg;
+  cfg.count = 8;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_adapt", &cfg);
+}
diff --git a/src/messenger/test_messenger_anonymous.c 
b/src/messenger/test_messenger_anonymous.c
index e2057acc4..a70121a30 100644
--- a/src/messenger/test_messenger_anonymous.c
+++ b/src/messenger/test_messenger_anonymous.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2020--2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -59,14 +59,13 @@ end (void *cls)
 
   if (messenger)
   {
-    GNUNET_MESSENGER_disconnect(messenger);
+    GNUNET_MESSENGER_disconnect (messenger);
     messenger = NULL;
   }
 
   status = 0;
 }
 
-
 static void
 end_badly (void *cls)
 {
@@ -81,7 +80,7 @@ end_operation (void *cls)
 {
   op_task = NULL;
 
-  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) 
cls : "unknown");
+  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls ? (const char*) 
cls : "unknown");
 
   if (die_task)
     GNUNET_SCHEDULER_cancel (die_task);
@@ -105,7 +104,7 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
     op_task = NULL;
   }
 
-  const char* name = GNUNET_MESSENGER_get_name(handle);
+  const char *name = GNUNET_MESSENGER_get_name (handle);
 
   if (NULL != name)
   {
@@ -113,26 +112,21 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
     return;
   }
 
-  if (GNUNET_SYSERR != GNUNET_MESSENGER_update(handle))
+  if (GNUNET_SYSERR != GNUNET_MESSENGER_update (handle))
   {
     op_task = GNUNET_SCHEDULER_add_now (&end_operation, "update-fail");
     return;
   }
 
-  struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous();
-  struct GNUNET_IDENTITY_PublicKey anonymous_key;
-
-  GNUNET_IDENTITY_ego_get_public_key(ego, &anonymous_key);
-
-  const struct GNUNET_IDENTITY_PublicKey* key = 
GNUNET_MESSENGER_get_key(handle);
+  const struct GNUNET_IDENTITY_PublicKey *key = GNUNET_MESSENGER_get_key 
(handle);
 
-  if (0 != GNUNET_memcmp(key, (&anonymous_key)))
+  if (key)
   {
     op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key-anonymous");
     return;
   }
 
-  GNUNET_MESSENGER_disconnect(handle);
+  GNUNET_MESSENGER_disconnect (handle);
 
   messenger = NULL;
 
@@ -150,14 +144,12 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle 
*handle)
  * @param peer Peer for testing
  */
 static void
-run (void *cls,
-     const struct GNUNET_CONFIGURATION_Handle *cfg,
-     struct GNUNET_TESTING_Peer *peer)
+run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, struct 
GNUNET_TESTING_Peer *peer)
 {
   die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL);
 
   op_task = GNUNET_SCHEDULER_add_delayed (BASE_TIMEOUT, &end_operation, 
"connect");
-  messenger = GNUNET_MESSENGER_connect(cfg, NULL, &on_identity, NULL, NULL, 
NULL);
+  messenger = GNUNET_MESSENGER_connect (cfg, NULL, &on_identity, NULL, NULL, 
NULL);
 }
 
 /**
@@ -168,11 +160,9 @@ run (void *cls,
  * @return 0 ok, 1 on error
  */
 int
-main(int argc, char **argv)
+main (int argc, char **argv)
 {
-  if (0 != GNUNET_TESTING_peer_run("test-messenger",
-                                   "test_messenger_api.conf",
-                                   &run, NULL))
+  if (0 != GNUNET_TESTING_peer_run ("test-messenger", 
"test_messenger_api.conf", &run, NULL))
     return 1;
 
   return status;
diff --git a/src/messenger/test_messenger_api.conf 
b/src/messenger/test_messenger_api.conf
index f5837392e..968f56f6d 100644
--- a/src/messenger/test_messenger_api.conf
+++ b/src/messenger/test_messenger_api.conf
@@ -1,11 +1,39 @@
 @INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
 @INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
 
+[testbed]
+HOSTNAME = localhost
+OVERLAY_TOPOLOGY = CLIQUE
+
+[arm]
+GLOBAL_POSTFIX = -l $GNUNET_CACHE_HOME/{}-logs -L verbose
+
+[transport]
+IMMEDIATE_START = YES
+
+[core]
+START_ON_DEMAND = YES
+IMMEDIATE_START = YES
+USE_EPHEMERAL_KEYS = NO
+
 [PATHS]
 GNUNET_TEST_HOME = $GNUNET_TMP/test-messenger-api/
 
+[peerinfo]
+NO_IO = YES
+
 [cadet]
 START_ON_DEMAND = YES
+REFRESH_CONNECTION_TIME = 1 s
+ID_ANNOUNCE_TIME = 5 s
+CONNECT_TIMEOUT = 30 s
+DEFAULT_TTL = 16
+DHT_REPLICATION_LEVEL = 10
+MAX_TUNNELS = 10
+MAX_CONNECTIONS = 10
+MAX_MSGS_QUEUE = 20
+DISABLE_TRY_CONNECT = YES
+REKEY_PERIOD = 2 s
 
 [identity]
 START_ON_DEMAND = YES
@@ -14,4 +42,6 @@ START_ON_DEMAND = YES
 START_ON_DEMAND = YES
 
 [nat]
-ENABLE_UPNP = NO
\ No newline at end of file
+ENABLE_UPNP = NO
+RETURN_LOCAL_ADDRESSES = YES
+IMMEDIATE_START = NO
\ No newline at end of file
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_async_client.c
similarity index 55%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_async_client.c
index c60eeac50..1067b9a6d 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_async_client.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_async_client.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    0,    1 };
+  unsigned int stages [] = { 0x10, 0x20 };
+
+  struct test_configuration cfg;
+  cfg.count = 2;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_async_client", &cfg);
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_async_p2p.c
similarity index 55%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_async_p2p.c
index c60eeac50..d827aae16 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_async_p2p.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_async_p2p.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    2,    1 };
+  unsigned int stages [] = { 0x30, 0x30 };
+
+  struct test_configuration cfg;
+  cfg.count = 2;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_async_p2p", &cfg);
+}
diff --git a/src/messenger/test_messenger_comm0.c 
b/src/messenger/test_messenger_comm0.c
deleted file mode 100644
index 631b5b2c9..000000000
--- a/src/messenger/test_messenger_comm0.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
-   This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
-
-   GNUnet is free software: you can redistribute it and/or modify it
-   under the terms of the GNU Affero General Public License as published
-   by the Free Software Foundation, either version 3 of the License,
-   or (at your option) any later version.
-
-   GNUnet is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Affero General Public License for more details.
-
-   You should have received a copy of the GNU Affero General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-   SPDX-License-Identifier: AGPL3.0-or-later
- */
-/**
- * @file messenger/test_messenger_comm0.c
- * @author Tobias Frisch
- * @brief Test for the messenger service using cadet API.
- */
-#include <stdio.h>
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_testbed_logger_service.h"
-#include "gnunet_testbed_service.h"
-#include "gnunet_testing_lib.h"
-#include "gnunet_messenger_service.h"
-
-/**
- * How long until we really give up on a particular testcase portion?
- */
-#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
\
-                                                     60)
-
-/**
- * How long until we give up on any particular operation (and retry)?
- */
-#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
5)
-
-static int status = 1;
-
-static struct GNUNET_SCHEDULER_Task *die_task = NULL;
-static struct GNUNET_SCHEDULER_Task *op_task = NULL;
-
-static void
-end (void *cls)
-{
-  die_task = NULL;
-
-  if (op_task)
-  {
-    GNUNET_SCHEDULER_cancel (op_task);
-    op_task = NULL;
-  }
-
-  GNUNET_SCHEDULER_shutdown ();
-  status = 0;
-}
-
-
-static void
-end_badly (void *cls)
-{
-  fprintf (stderr, "Testcase failed (timeout).\n");
-
-  end (NULL);
-  status = 1;
-}
-
-static void
-end_operation (void *cls)
-{
-  op_task = NULL;
-
-  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) 
cls : "unknown");
-
-  if (die_task)
-    GNUNET_SCHEDULER_cancel (die_task);
-
-  end (NULL);
-  status = 1;
-}
-
-static void
-end_error (void *cls)
-{
-  op_task = NULL;
-
-  fprintf (stderr, "Testcase failed (error: '%s').\n", cls? (const char*) cls 
: "unknown");
-  GNUNET_free(cls);
-
-  if (die_task)
-    GNUNET_SCHEDULER_cancel (die_task);
-
-  end (NULL);
-  status = 1;
-}
-
-/**
- * Function called whenever a message is received or sent.
- *
- * @param cls Closure
- * @param room Room
- * @param message Message
- * @param hash Hash of message
- */
-static void
-on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
-            const struct GNUNET_HashCode *hash)
-{
-  // TODO
-}
-
-/**
- * Function called when an identity is retrieved.
- *
- * @param cls Closure
- * @param handle Handle of messenger service
- */
-static void
-on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle)
-{
-  // TODO
-}
-
-static void
-on_peer (void *cb_cls, struct GNUNET_TESTBED_Operation *op,
-         const struct GNUNET_TESTBED_PeerInformation *pinfo,
-         const char *emsg)
-{
-  if (emsg)
-  {
-    op_task = GNUNET_SCHEDULER_add_now (&end_error, GNUNET_strdup(emsg));
-    return;
-  }
-
-  if (pinfo->pit != GNUNET_TESTBED_PIT_CONFIGURATION)
-  {
-    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "config");
-    return;
-  }
-
-  struct GNUNET_MESSENGER_Handle *handle;
-  struct GNUNET_MESSENGER_Room *room;
-
-  fprintf (stderr, "MSG: connect\n");
-
-  handle = GNUNET_MESSENGER_connect(pinfo->result.cfg, "tester", &on_identity, 
NULL, &on_message, NULL);
-
-  struct GNUNET_HashCode hash;
-  GNUNET_CRYPTO_hash("test", 4, &hash);
-
-  fprintf (stderr, "MSG: open\n");
-
-  room = GNUNET_MESSENGER_open_room(handle, &hash);
-
-  fprintf (stderr, "MSG: close\n");
-
-  GNUNET_MESSENGER_close_room(room);
-
-  fprintf (stderr, "MSG: disconnect\n");
-
-  GNUNET_MESSENGER_disconnect(handle);
-
-  GNUNET_TESTBED_operation_done(op);
-
-}
-
-/**
- * Main function for a peer of the testcase.
- *
- * @param cls Closure
- * @param event Information about the event
- */
-static void
-run (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
-{
-  if (GNUNET_TESTBED_ET_PEER_START != event->type)
-  {
-    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "start");
-    return;
-  }
-
-  GNUNET_TESTBED_peer_get_information(event->details.peer_start.peer,
-                                      GNUNET_TESTBED_PIT_CONFIGURATION,
-                                      on_peer, event->details.peer_start.peer);
-
-  fprintf (stderr, "MSG: barrier\n");
-
-  GNUNET_TESTBED_barrier_wait("exit", NULL, NULL);
-
-  fprintf (stderr, "MSG: exit\n");
-}
-
-static void
-exit_status (void *cls, const char *name,
-             struct GNUNET_TESTBED_Barrier *barrier,
-             enum GNUNET_TESTBED_BarrierStatus status,
-             const char *emsg)
-{
-  if (emsg)
-  {
-    op_task = GNUNET_SCHEDULER_add_now (&end_error, GNUNET_strdup(emsg));
-    return;
-  }
-
-  if (GNUNET_TESTBED_BARRIERSTATUS_ERROR == status)
-  {
-    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "exit");
-    return;
-  }
-  else if (GNUNET_TESTBED_BARRIERSTATUS_CROSSED == status)
-    GNUNET_SCHEDULER_add_now(&end, NULL);
-}
-
-static void
-init (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num_peers,
-      struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded,
-      unsigned int links_failed)
-{
-  die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL);
-
-  struct GNUNET_TESTBED_Controller *controller;
-
-  controller = GNUNET_TESTBED_run_get_controller_handle(h);
-
-  GNUNET_TESTBED_barrier_init(controller, "exit", num_peers, exit_status, 
NULL);
-}
-
-/**
- * The main function.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main(int argc, char **argv)
-{
-  if (GNUNET_OK != GNUNET_TESTBED_test_run("test-messenger-comm0",
-                                           "test_messenger_api.conf",
-                                           2, 0,
-                                           &run, NULL,
-                                           &init, NULL))
-    return 1;
-
-  return status;
-}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_growth.c
similarity index 53%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_growth.c
index c60eeac50..3781f3ee5 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_growth.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_growth.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    0,    1,    1,    1,    1,    1,    1,    1 };
+  unsigned int stages [] = { 0x01, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21 };
+
+  struct test_configuration cfg;
+  cfg.count = 8;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_growth", &cfg);
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_ring.c
similarity index 53%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_ring.c
index c60eeac50..07cfd0c47 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_ring.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_ring.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    8,    1,    2,    3,    4,    5,    6,    7 };
+  unsigned int stages [] = { 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21 };
+
+  struct test_configuration cfg;
+  cfg.count = 8;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_ring", &cfg);
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_server.c
similarity index 53%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_server.c
index c60eeac50..1cf2fcc27 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_server.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_server.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    0,    1,    1,    1,    1,    1,    1,    1 };
+  unsigned int stages [] = { 0x01, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
+
+  struct test_configuration cfg;
+  cfg.count = 8;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_server", &cfg);
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_sync_client.c
similarity index 55%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_sync_client.c
index c60eeac50..99f26b018 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_sync_client.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_sync_client.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    0,    1 };
+  unsigned int stages [] = { 0x01, 0x20 };
+
+  struct test_configuration cfg;
+  cfg.count = 2;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_sync_client", &cfg);
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_sync_p2p.c
similarity index 55%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_sync_p2p.c
index c60eeac50..77ce280a1 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_sync_p2p.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_sync_p2p.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    2,    1 };
+  unsigned int stages [] = { 0x21, 0x21 };
+
+  struct test_configuration cfg;
+  cfg.count = 2;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_sync_p2p", &cfg);
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_worst_client.c
similarity index 55%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_worst_client.c
index c60eeac50..63826631c 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_worst_client.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_worst_client.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    0,    1 };
+  unsigned int stages [] = { 0x10, 0x02 };
+
+  struct test_configuration cfg;
+  cfg.count = 2;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_worst_client", &cfg);
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/test_messenger_worst_p2p.c
similarity index 55%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/test_messenger_worst_p2p.c
index c60eeac50..c89288eea 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/test_messenger_worst_p2p.c
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,30 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/test_messenger_worst_p2p.c
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief Test for the messenger service using cadet API.
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#include "testing_messenger_setup.h"
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
-};
+  unsigned int doors  [] = {    2,    1 };
+  unsigned int stages [] = { 0x12, 0x12 };
+
+  struct test_configuration cfg;
+  cfg.count = 2;
+  cfg.doors = doors;
+  cfg.stages = stages;
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+  return GNUNET_run_messenger_setup ("test_messenger_worst_p2p", &cfg);
+}
diff --git a/src/messenger/testing_messenger_barrier.c 
b/src/messenger/testing_messenger_barrier.c
new file mode 100644
index 000000000..618d255b7
--- /dev/null
+++ b/src/messenger/testing_messenger_barrier.c
@@ -0,0 +1,170 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @file messenger/testing_messenger_barrier.c
+ * @author Tobias Frisch
+ * @brief Pseudo-barriers for simple event handling
+ */
+
+#include "testing_messenger_barrier.h"
+
+struct GNUNET_BarrierHandle
+{
+  unsigned int requirement;
+  GNUNET_BarrierStatusCallback cb;
+  void *cls;
+
+  struct GNUNET_BarrierWaitHandle *head;
+  struct GNUNET_BarrierWaitHandle *tail;
+
+  struct GNUNET_SCHEDULER_Task* task;
+};
+
+struct GNUNET_BarrierHandle*
+GNUNET_init_barrier (unsigned int requirement,
+                     GNUNET_BarrierStatusCallback cb,
+                     void *cb_cls)
+{
+  if (0 == requirement)
+    return NULL;
+
+  struct GNUNET_BarrierHandle *barrier = GNUNET_new(struct 
GNUNET_BarrierHandle);
+
+  if (!barrier)
+    return NULL;
+
+  barrier->requirement = requirement;
+  barrier->cb = cb;
+  barrier->cls = cb_cls;
+  barrier->head = NULL;
+  barrier->tail = NULL;
+  barrier->task = NULL;
+
+  return barrier;
+}
+
+static void
+exit_status (struct GNUNET_BarrierHandle *barrier, int status);
+
+static void
+cancel_barrier (void *cls)
+{
+  exit_status ((struct GNUNET_BarrierHandle*) cls, GNUNET_SYSERR);
+}
+
+static void
+complete_barrier (void *cls)
+{
+  exit_status ((struct GNUNET_BarrierHandle*) cls, GNUNET_OK);
+}
+
+void
+GNUNET_cancel_barrier (struct GNUNET_BarrierHandle *barrier)
+{
+  if ((!barrier) || (barrier->task))
+    return;
+
+  barrier->task = GNUNET_SCHEDULER_add_now(cancel_barrier, barrier);
+}
+
+struct GNUNET_BarrierWaitHandle
+{
+  GNUNET_BarrierWaitStatusCallback cb;
+  void *cls;
+
+  struct GNUNET_BarrierWaitHandle *prev;
+  struct GNUNET_BarrierWaitHandle *next;
+
+  struct GNUNET_BarrierHandle *barrier;
+};
+
+static void
+exit_status (struct GNUNET_BarrierHandle *barrier, int status)
+{
+  struct GNUNET_BarrierWaitHandle *waiting = barrier->head;
+  while (waiting)
+  {
+    struct GNUNET_BarrierWaitHandle *current = waiting;
+
+    if (current->cb)
+      current->cb(current->cls, current, status);
+
+    waiting = waiting->next;
+
+    GNUNET_CONTAINER_DLL_remove(barrier->head, barrier->tail, current);
+    GNUNET_free(current);
+  }
+
+  if (barrier->cb)
+    barrier->cb(barrier->cls, barrier, status);
+
+  GNUNET_free(barrier);
+}
+
+struct GNUNET_BarrierWaitHandle*
+GNUNET_wait_barrier (struct GNUNET_BarrierHandle *barrier,
+                     GNUNET_BarrierWaitStatusCallback cb,
+                     void *cb_cls)
+{
+  if ((!barrier) || (0 == barrier->requirement))
+    return NULL;
+
+  struct GNUNET_BarrierWaitHandle *waiting = GNUNET_new(struct 
GNUNET_BarrierWaitHandle);
+
+  if (!waiting)
+    return NULL;
+
+  waiting->cb = cb;
+  waiting->cls = cb_cls;
+  waiting->prev = NULL;
+  waiting->next = NULL;
+  waiting->barrier = barrier;
+
+  GNUNET_CONTAINER_DLL_insert_tail(barrier->head, barrier->tail, waiting);
+  barrier->requirement--;
+
+  if ((barrier->requirement == 0) && (!barrier->task))
+    barrier->task = GNUNET_SCHEDULER_add_now(complete_barrier, barrier);
+
+  return waiting;
+}
+
+void
+GNUNET_cancel_wait_barrier (struct GNUNET_BarrierWaitHandle *waiting)
+{
+  if (!waiting)
+    return;
+
+  struct GNUNET_BarrierHandle *barrier = waiting->barrier;
+
+  if (!barrier)
+    return;
+
+  if ((barrier->requirement == 0) && (barrier->task))
+  {
+    GNUNET_SCHEDULER_cancel(barrier->task);
+    barrier->task = NULL;
+  }
+
+  barrier->requirement++;
+  GNUNET_CONTAINER_DLL_remove(barrier->head, barrier->tail, waiting);
+
+  GNUNET_free(waiting);
+}
diff --git a/src/messenger/testing_messenger_barrier.h 
b/src/messenger/testing_messenger_barrier.h
new file mode 100644
index 000000000..3062a393a
--- /dev/null
+++ b/src/messenger/testing_messenger_barrier.h
@@ -0,0 +1,131 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @file messenger/testing_messenger_barrier.h
+ * @author Tobias Frisch
+ * @brief Pseudo-barriers for simple event handling
+ */
+
+#ifndef GNUNET_TESTING_MESSENGER_BARRIER_H_
+#define GNUNET_TESTING_MESSENGER_BARRIER_H_
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+/**
+ * Handle for pseudo-barrier
+ */
+struct GNUNET_BarrierHandle;
+
+
+/**
+ * Functions of this type are to be given as callback argument to
+ * GNUNET_init_barrier(). The callback will be called when status
+ * information is available for the pseudo-barrier.
+ *
+ * @param cls the closure given to GNUNET_init_barrier()
+ * @param barrier the pseudo-barrier handle
+ * @param status status of the pseudo-barrier. The pseudo-barrier is removed
+ *          once it has been crossed or an error occurs while processing it.
+ *          Therefore it is invalid to call GNUNET_cancel_barrier() on a
+ *          crossed or errored pseudo-barrier.
+ */
+typedef void
+(*GNUNET_BarrierStatusCallback) (void *cls,
+                                 struct GNUNET_BarrierHandle *barrier,
+                                 int status);
+
+
+/**
+ * Initialise a pseudo-barrier and call the given callback when the required
+ * amount of peers (requirement) reach the pseudo-barrier OR upon error.
+ *
+ * @param requirement the amount of peers that is required to reach the
+ *   pseudo-barrier. Peers signal reaching a pseudo-barrier by calling
+ *   GNUNET_wait_barrier().
+ * @param cb the callback to call when the pseudo-barrier is reached or upon
+ *   error. Can be NULL.
+ * @param cls closure for the above callback
+ * @return pseudo-barrier handle; NULL upon error
+ */
+struct GNUNET_BarrierHandle*
+GNUNET_init_barrier (unsigned int requirement,
+                     GNUNET_BarrierStatusCallback cb,
+                     void *cb_cls);
+
+
+/**
+ * Cancel a pseudo-barrier.
+ *
+ * @param barrier the pseudo-barrier handle
+ */
+void
+GNUNET_cancel_barrier (struct GNUNET_BarrierHandle *barrier);
+
+
+/**
+ * Handle for pseudo-barrier wait
+ */
+struct GNUNET_BarrierWaitHandle;
+
+
+/**
+ * Functions of this type are to be given as acallback argument to
+ * GNUNET_wait_barrier(). The callback will be called when the pseudo-barrier
+ * corresponding given in GNUNET_wait_barrier() is crossed or cancelled.
+ *
+ * @param cls closure pointer given to GNUNET_wait_barrier()
+ * @param waiting the pseudo-barrier wait handle
+ * @param status #GNUNET_SYSERR in case of error while waiting for the
+ *   pseudo-barrier; #GNUNET_OK if the pseudo-barrier is crossed
+ */
+typedef void
+(*GNUNET_BarrierWaitStatusCallback) (void *cls,
+                                     struct GNUNET_BarrierWaitHandle *waiting,
+                                     int status);
+
+
+/**
+ * Wait for a pseudo-barrier to be crossed. This function should be called for
+ * the peers which have been started by the testbed.
+ *
+ * @param barrier the pseudo-barrier handle
+ * @param cb the pseudo-barrier wait callback
+ * @param cls the closure for the above callback
+ * @return pseudo-barrier wait handle which can be used to cancel the waiting
+ *   at anytime before the callback is called. NULL upon error.
+ */
+struct GNUNET_BarrierWaitHandle*
+GNUNET_wait_barrier (struct GNUNET_BarrierHandle *barrier,
+                     GNUNET_BarrierWaitStatusCallback cb,
+                     void *cb_cls);
+
+
+/**
+ * Cancel a pseudo-barrier wait handle. Should not be called in or after the
+ * callback given to GNUNET_wait_barrier() has been called.
+ *
+ * @param waiting the pseudo-barrier wait handle
+ */
+void
+GNUNET_cancel_wait_barrier (struct GNUNET_BarrierWaitHandle *waiting);
+
+
+#endif /* GNUNET_TESTING_MESSENGER_BARRIER_H_ */
diff --git a/src/messenger/testing_messenger_setup.c 
b/src/messenger/testing_messenger_setup.c
new file mode 100644
index 000000000..98241fa08
--- /dev/null
+++ b/src/messenger/testing_messenger_setup.c
@@ -0,0 +1,528 @@
+/*
+   This file is part of GNUnet.
+   Copyright (C) 2020--2021 GNUnet e.V.
+
+   GNUnet is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Affero General Public License as published
+   by the Free Software Foundation, either version 3 of the License,
+   or (at your option) any later version.
+
+   GNUnet is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Affero General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @file messenger/testing_messenger_barrier.c
+ * @author Tobias Frisch
+ * @brief A simple test-case setup for the messenger service
+ */
+
+#include "testing_messenger_setup.h"
+
+#include <stdio.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_logger_service.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_messenger_service.h"
+#include "testing_messenger_barrier.h"
+
+#define TEST_ROOM "test"
+#define TEST_NAME "tester"
+
+struct test_properties;
+
+struct test_peer {
+  struct test_properties *props;
+  unsigned int num;
+
+  struct GNUNET_SCHEDULER_Task *op_task;
+  struct GNUNET_TESTBED_Operation *op;
+
+  struct GNUNET_TESTBED_Peer *peer;
+  struct GNUNET_PeerIdentity peer_id;
+  struct GNUNET_BarrierWaitHandle *wait;
+
+  struct GNUNET_MESSENGER_Handle *handle;
+  struct GNUNET_MESSENGER_Room *room;
+
+  unsigned int peer_messages;
+
+  const char *message;
+};
+
+struct test_properties {
+  const struct test_configuration *cfg;
+
+  unsigned int num_hosts;
+
+  struct GNUNET_SCHEDULER_Task *die_task;
+  struct GNUNET_SCHEDULER_Task *end_task;
+
+  struct GNUNET_BarrierHandle *barrier;
+
+  struct test_peer *peers;
+  unsigned int num_peer;
+
+  int status;
+};
+
+static void
+shutdown_cb (void *cls)
+{
+  struct test_properties *properties = cls;
+
+
+  for (unsigned int i = 0; i < properties->num_peer; i++)
+  {
+    struct test_peer *peer = &properties->peers[i];
+
+    GNUNET_assert(peer != NULL);
+
+    if (peer->op_task)
+      GNUNET_SCHEDULER_cancel(peer->op_task);
+
+    peer->op_task = NULL;
+
+    if (peer->op)
+      GNUNET_TESTBED_operation_done (peer->op);
+
+    peer->op = NULL;
+
+    if (peer->wait)
+      GNUNET_cancel_wait_barrier(peer->wait);
+
+    peer->wait = NULL;
+
+    if (peer->room)
+      GNUNET_MESSENGER_close_room (peer->room);
+
+    peer->room = NULL;
+
+    if (peer->handle)
+      GNUNET_MESSENGER_disconnect (peer->handle);
+
+    peer->handle = NULL;
+  }
+
+  if (properties->die_task)
+    GNUNET_SCHEDULER_cancel(properties->die_task);
+
+  properties->die_task = NULL;
+  properties->end_task = NULL;
+
+  if (properties->barrier)
+    GNUNET_cancel_barrier(properties->barrier);
+
+  properties->barrier = NULL;
+}
+
+
+
+static void
+end_cb (void *cls)
+{
+  struct test_properties *properties = cls;
+
+  GNUNET_assert(properties != NULL);
+
+  properties->die_task = NULL;
+
+  int status = 0;
+
+  for (unsigned int i = 0; i < properties->num_peer; i++)
+  {
+    struct test_peer *peer = &properties->peers[i];
+
+    GNUNET_assert(peer != NULL);
+
+    const int members = GNUNET_MESSENGER_iterate_members(peer->room, NULL, 
NULL);
+
+    GNUNET_assert (members >= 0);
+
+    if (peer->props->num_peer != (unsigned int) members)
+    {
+      fprintf (stderr, "Testcase failed (members: %d/%u).\n", members, 
peer->props->num_peer);
+      status = 1;
+      break;
+    }
+  }
+
+  GNUNET_SCHEDULER_shutdown ();
+
+  properties->status = status;
+}
+
+static void
+end_badly_cb (void *cls)
+{
+  struct test_properties *properties = cls;
+
+  GNUNET_assert(properties != NULL);
+
+  fprintf (stderr, "Testcase failed (timeout).\n");
+
+  end_cb (properties);
+
+  properties->status = 1;
+}
+
+static void
+end_operation_cb (void *cls)
+{
+  struct test_peer *peer = cls;
+
+  GNUNET_assert(peer != NULL);
+
+  peer->op_task = NULL;
+
+  fprintf (stderr, "Testcase failed (operation: '%s').\n", peer->message);
+
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+static void
+end_error_cb (void *cls)
+{
+  struct test_peer *peer = cls;
+
+  GNUNET_assert(peer != NULL);
+
+  peer->op_task = NULL;
+
+  fprintf (stderr, "Testcase failed (error: '%s').\n", peer->message);
+  GNUNET_free (peer);
+
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+static void
+barrier2_wait_cb (void *cls, struct GNUNET_BarrierWaitHandle *waiting, int 
status)
+{
+  struct test_peer *peer = cls;
+
+  GNUNET_assert(peer != NULL);
+
+  if (peer->wait == waiting)
+    peer->wait = NULL;
+}
+
+static void
+barrier_wait_cb (void *cls, struct GNUNET_BarrierWaitHandle *waiting, int 
status)
+{
+  struct test_peer *peer = cls;
+
+  GNUNET_assert(peer != NULL);
+
+  if (peer->wait == waiting)
+    peer->wait = NULL;
+
+  if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x02))
+  {
+    unsigned int door = peer->props->cfg->doors[peer->num - 1];
+
+    if (door == 0)
+      door = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, 
peer->props->cfg->count);
+    else
+      door = door - 1;
+
+    struct GNUNET_HashCode hash;
+    GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash);
+
+    struct GNUNET_MESSENGER_Room *room;
+    room = GNUNET_MESSENGER_enter_room(peer->handle, 
&(peer->props->peers[door].peer_id), &hash);
+
+    if (peer->room)
+      GNUNET_assert(room == peer->room);
+    else
+      GNUNET_assert(room != NULL);
+
+    peer->room = room;
+  }
+}
+
+/**
+ * Function called whenever a message is received or sent.
+ *
+ * @param cls Closure
+ * @param room Room
+ * @param sender Sender
+ * @param message Message
+ * @param hash Hash of message
+ * @param flags Flags of message
+ */
+static void
+on_message (void *cls, struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Contact *sender,
+            const struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash,
+            enum GNUNET_MESSENGER_MessageFlags flags)
+{
+  struct test_peer *peer = cls;
+
+  GNUNET_assert(peer != NULL);
+
+  fprintf (stderr, "Peer: %s; [%s] Message: %s (%s)\n",
+           GNUNET_i2s(&(peer->peer_id)),
+           GNUNET_sh2s(&(message->header.sender_id)),
+           GNUNET_MESSENGER_name_of_kind(message->header.kind),
+           GNUNET_h2s(hash));
+
+  if (GNUNET_MESSENGER_KIND_PEER == message->header.kind)
+    peer->peer_messages++;
+
+  if (peer->props->num_hosts == peer->peer_messages)
+    peer->wait = GNUNET_wait_barrier (peer->props->barrier, &barrier2_wait_cb, 
peer);
+  else if (peer->props->num_hosts < peer->peer_messages)
+  {
+    if (peer->wait)
+      GNUNET_cancel_wait_barrier(peer->wait);
+
+    peer->wait = NULL;
+
+    if (peer->op_task)
+      GNUNET_SCHEDULER_cancel(peer->op_task);
+
+    peer->message = "peer";
+    peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer);
+  }
+}
+
+static void
+second_stage (void *cls)
+{
+  struct test_peer *peer = cls;
+
+  GNUNET_assert(peer != NULL);
+
+  peer->op_task = NULL;
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash);
+
+  if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x10))
+  {
+    struct GNUNET_MESSENGER_Room *room;
+    room = GNUNET_MESSENGER_open_room (peer->handle, &hash);
+
+    if (peer->room)
+      GNUNET_assert(room == peer->room);
+    else
+      GNUNET_assert(room != NULL);
+
+    peer->room = room;
+  }
+
+  if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x20))
+  {
+    unsigned int door = peer->props->cfg->doors[peer->num - 1];
+
+    if (door == 0)
+      door = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, 
peer->props->cfg->count);
+    else
+      door = door - 1;
+
+    struct GNUNET_MESSENGER_Room *room;
+    room = GNUNET_MESSENGER_enter_room(peer->handle, 
&(peer->props->peers[door].peer_id), &hash);
+
+    if (peer->room)
+      GNUNET_assert(room == peer->room);
+    else
+      GNUNET_assert(room != NULL);
+
+    peer->room = room;
+  }
+}
+
+static void
+on_peer (void *cb_cls, struct GNUNET_TESTBED_Operation *op, const struct 
GNUNET_TESTBED_PeerInformation *pinfo,
+         const char *emsg)
+{
+  struct test_peer *peer = cb_cls;
+
+  GNUNET_assert(peer != NULL);
+
+  if (emsg)
+  {
+    peer->message = GNUNET_strdup(emsg);
+    peer->op_task = GNUNET_SCHEDULER_add_now (&end_error_cb, peer);
+    return;
+  }
+
+  if (!pinfo)
+  {
+    peer->message = "info";
+    peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer);
+    return;
+  }
+
+  if (pinfo->pit != GNUNET_TESTBED_PIT_CONFIGURATION)
+  {
+    peer->message = "config";
+    peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer);
+    return;
+  }
+
+  peer->handle = GNUNET_MESSENGER_connect (pinfo->result.cfg, TEST_NAME, NULL, 
NULL, &on_message, peer);
+
+  GNUNET_assert(GNUNET_OK == GNUNET_CRYPTO_get_peer_identity(
+      pinfo->result.cfg, &(peer->peer_id)
+  ));
+
+  if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x01))
+  {
+    struct GNUNET_HashCode hash;
+    GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash);
+
+    peer->room = GNUNET_MESSENGER_open_room (peer->handle, &hash);
+
+    GNUNET_assert(peer->room != NULL);
+  }
+  else
+    peer->room = NULL;
+
+  peer->wait = GNUNET_wait_barrier (peer->props->barrier, &barrier_wait_cb, 
peer);
+}
+
+/**
+ * Main function for a peer of the testcase.
+ *
+ * @param cls Closure
+ * @param event Information about the event
+ */
+static void
+run (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
+{
+  struct test_properties *properties = cls;
+
+  GNUNET_assert(properties != NULL);
+
+  if (GNUNET_TESTBED_ET_PEER_START != event->type)
+  {
+    fprintf (stderr, "Testcase failed (operation: 'start').\n");
+
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  struct test_peer *peer = &(properties->peers[properties->num_peer++]);
+
+  peer->props = properties;
+  peer->num = properties->num_peer;
+
+  peer->peer = event->details.peer_start.peer;
+  peer->op = GNUNET_TESTBED_peer_get_information (peer->peer, 
GNUNET_TESTBED_PIT_CONFIGURATION, on_peer, peer);
+}
+
+static void
+barrier2_cb (void *cls, struct GNUNET_BarrierHandle *barrier, int status)
+{
+  struct test_properties *properties = cls;
+
+  GNUNET_assert(properties != NULL);
+
+  if (properties->barrier == barrier)
+    properties->barrier = NULL;
+
+  if (GNUNET_SYSERR == status)
+  {
+    fprintf (stderr, "Testcase failed (operation: 'barrier2').\n");
+
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  else if (GNUNET_OK == status)
+  {
+    if (properties->die_task)
+      GNUNET_SCHEDULER_cancel(properties->die_task);
+
+    properties->die_task = GNUNET_SCHEDULER_add_delayed (
+        GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
properties->cfg->count),
+        &end_cb, properties
+    );
+  }
+}
+
+static void
+barrier_cb (void *cls, struct GNUNET_BarrierHandle *barrier, int status)
+{
+  struct test_properties *properties = cls;
+
+  GNUNET_assert(properties != NULL);
+
+  if (properties->barrier == barrier)
+    properties->barrier = NULL;
+  else if (!properties->barrier)
+    return;
+
+  if (properties->num_peer != properties->cfg->count)
+  {
+    fprintf (stderr, "Testcase failed (operation: 'process').\n");
+
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  if (GNUNET_SYSERR == status)
+  {
+    fprintf (stderr, "Testcase failed (operation: 'barrier').\n");
+
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  else if (GNUNET_OK == status)
+  {
+    properties->barrier = GNUNET_init_barrier (properties->num_peer, 
&barrier2_cb, properties);
+
+    for (unsigned int i = 0; i < properties->num_peer; i++)
+      properties->peers[i].op_task = GNUNET_SCHEDULER_add_now (&second_stage, 
&(properties->peers[i]));
+  }
+}
+
+static void
+init (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num_peers, 
struct GNUNET_TESTBED_Peer **peers,
+      unsigned int links_succeeded, unsigned int links_failed)
+{
+  struct test_properties *properties = cls;
+
+  GNUNET_assert(properties != NULL);
+
+  properties->end_task = GNUNET_SCHEDULER_add_shutdown(&shutdown_cb, 
properties);
+  properties->die_task = GNUNET_SCHEDULER_add_delayed (
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
properties->cfg->count * 5),
+      &end_badly_cb, properties
+  );
+}
+
+int
+GNUNET_run_messenger_setup (const char* test_name, const struct 
test_configuration *cfg)
+{
+  struct test_properties properties;
+  memset(&properties, 0, sizeof(properties));
+
+  properties.cfg = cfg;
+  properties.peers = GNUNET_new_array(cfg->count, struct test_peer);
+
+  for (unsigned int i = 0; i < cfg->count; i++)
+    if (0 != (cfg->stages[i] & 0x11))
+      properties.num_hosts++;
+
+  properties.status = 1;
+  properties.barrier = GNUNET_init_barrier (cfg->count, &barrier_cb, 
&properties);
+
+  if (GNUNET_OK != GNUNET_TESTBED_test_run (test_name, 
"test_messenger_api.conf",
+                                            cfg->count,
+                                            (1LL << 
GNUNET_TESTBED_ET_PEER_START),
+                                            &run, &properties,
+                                            &init, &properties))
+    return 1;
+
+  GNUNET_free(properties.peers);
+
+  return properties.status;
+}
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/testing_messenger_setup.h
similarity index 62%
copy from src/messenger/messenger_api_ego.h
copy to src/messenger/testing_messenger_setup.h
index c60eeac50..5e6b5d461 100644
--- a/src/messenger/messenger_api_ego.h
+++ b/src/messenger/testing_messenger_setup.h
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2020 GNUnet e.V.
+   Copyright (C) 2021 GNUnet e.V.
 
    GNUnet is free software: you can redistribute it and/or modify it
    under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,22 @@
    SPDX-License-Identifier: AGPL3.0-or-later
  */
 /**
+ * @file messenger/testing_messenger_setup.h
  * @author Tobias Frisch
- * @file src/messenger/messenger_api_ego.h
- * @brief GNUnet MESSENGER service
+ * @brief A simple test-case setup for the messenger service
  */
 
-#ifndef GNUNET_MESSENGER_API_EGO_H
-#define GNUNET_MESSENGER_API_EGO_H
+#ifndef GNUNET_TESTING_MESSENGER_SETUP_H_
+#define GNUNET_TESTING_MESSENGER_SETUP_H_
 
-#include "platform.h"
-#include "gnunet_identity_service.h"
-
-struct GNUNET_MESSENGER_Ego
+struct test_configuration
 {
-  struct GNUNET_IDENTITY_PrivateKey priv;
-  struct GNUNET_IDENTITY_PublicKey pub;
+  unsigned int count;
+  unsigned int *doors;
+  unsigned int *stages;
 };
 
-#endif //GNUNET_MESSENGER_API_EGO_H
+int
+GNUNET_run_messenger_setup (const char* test_name, const struct 
test_configuration *cfg);
+
+#endif /* GNUNET_TESTING_MESSENGER_SETUP_H_ */

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]