diff --git a/openapi b/openapi
new file mode 160000
index 0000000000000000000000000000000000000000..073fd7213fd9e6ebc2f8a47d628a650de30c8bc4
--- /dev/null
+++ b/openapi
@@ -0,0 +1 @@
+Subproject commit 073fd7213fd9e6ebc2f8a47d628a650de30c8bc4
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Dockerfile b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Dockerfile
index 1203a89ba3579798b30bb4b67e45538b2c42d7b5..76f44e25130c34acda31ffe342b389729b530da1 100644
--- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Dockerfile
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Dockerfile
@@ -1,21 +1,21 @@
 #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
 
 # Container we use for final publish
-FROM mcr.microsoft.com/dotnet/core/aspnet:5.0-buster-slim AS base
+FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
 WORKDIR /app
 EXPOSE 80
 EXPOSE 443
 
 # Build container
-FROM mcr.microsoft.com/dotnet/core/sdk:5.0-buster AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
 
 # Copy the code into the container
 WORKDIR /src
-COPY ["src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI.ARF.OpenAPI.WorldAnalysis.csproj", "ETSI.ARF.OpenAPI.WorldAnalysis/"]
+COPY ["ETSI.ARF.OpenAPI.WorldAnalysis.csproj", "ETSI.ARF.OpenAPI.WorldAnalysis/"]
 
 # NuGet restore
 RUN dotnet restore "ETSI.ARF.OpenAPI.WorldAnalysis/ETSI.ARF.OpenAPI.WorldAnalysis.csproj"
-COPY ["src/ETSI.ARF.OpenAPI.WorldAnalysis/", "ETSI.ARF.OpenAPI.WorldAnalysis/"]
+COPY [".", "ETSI.ARF.OpenAPI.WorldAnalysis/"]
 
 # Build the API
 WORKDIR "ETSI.ARF.OpenAPI.WorldAnalysis"
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs
index 3529f665f4d9f1d980521bc88507f96fb613d453..4638d5c7df96786128abcc49291f699338163b95 100644
--- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs
@@ -53,7 +53,22 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
         /// </summary>
         public override IActionResult GetCapabilities([FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // Get all capabilities from all anchors/trackables
+            // Info: Capabilities are collected after a module is connected via websockets
+
+            // Create list
+            List<Capability> capabilitiesList = new List<Capability>();
+            capabilitiesList.AddRange(WorldAnalysisConnections.Singleton.GetCapabilities());
+
+            // Create response object
+            GetCapabilities200Response response = new GetCapabilities200Response();
+            response.Capabilities = capabilitiesList;
+            return new ObjectResult(response);
+            //return StatusCode(405, "Not supported yet!");
         }
 
         /// <summary>
@@ -61,7 +76,19 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
         /// </summary>
         public override IActionResult GetSupport([FromRoute (Name = "trackableOrAnchorUUID")][Required]Guid trackableOrAnchorUUID, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // Create list
+            List<Capability> capabilitiesList = new List<Capability>();
+            capabilitiesList.AddRange(WorldAnalysisConnections.Singleton.GetCapabilitiesFromUuid(trackableOrAnchorUUID));
+
+            // Create response object
+            GetSupport200Response response = new GetSupport200Response();
+            response.Capabilities = capabilitiesList;
+            return new ObjectResult(response); 
+            //return StatusCode(405, "Not supported yet!");
         }
     }
 }
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs
index 93f84bf5e40afd68daafb623344f0a90954c507e..44606416aab46dfd8512ffebd5068f2ada13de84 100644
--- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs
@@ -41,66 +41,212 @@ using ETSI.ARF.OpenAPI.WorldAnalysis.Attributes;
 using ETSI.ARF.OpenAPI.WorldAnalysis.Models;
 
 namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
-{ 
+{
     /// <summary>
     /// 
     /// </summary>
     [ApiController]
     public class PoseApiControlleImpl : PoseApiController
-    { 
+    {
         /// <summary>
         /// Specify the a minimum frame rate for pose estimation for Trackable types
         /// </summary>
-        public override IActionResult ConfigureFramerate([FromBody]PoseConfiguration poseConfiguration, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
+        public override IActionResult ConfigureFramerate([FromBody] PoseConfiguration poseConfiguration, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // Notify the modules that the client need a new framerate
+            bool result = WorldAnalysisConnections.Singleton.ConfigureFramerate(poseConfiguration);
+            return result ? StatusCode(200, "Ok.") : StatusCode(405, "Not supported.");
         }
 
         /// <summary>
         /// Request the last pose of a single Anchor or Trackable
         /// </summary>
-        public override IActionResult GetPose([FromRoute (Name = "trackableOrAnchorUUID")][Required]Guid trackableOrAnchorUUID, [FromQuery (Name = "mode")][Required()]ModeWorldAnalysis mode, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
+        public override IActionResult GetPose([FromRoute(Name = "trackableOrAnchorUUID")][Required] Guid trackableOrAnchorUUID, [FromQuery(Name = "mode")][Required()] ModeWorldAnalysis mode, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // Request from the modules a new pose of a single UUID
+            Pose result;
+            result = WorldAnalysisConnections.Singleton.GetPose(trackableOrAnchorUUID, mode);
+            return new ObjectResult(result);
         }
 
         /// <summary>
         /// Request the last pose of a batch of Anchor or Trackable
         /// </summary>
-        public override IActionResult GetPoses([FromQuery (Name = "uuid")][Required()]List<GetPosesUuidParameterInner> uuid, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
+        public override IActionResult GetPoses([FromQuery(Name = "uuid")][Required()] List<UuidAndMode> uuid, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // Request from the modules new poses from all UUIDs
+            GetPoses200Response result = new GetPoses200Response();
+            foreach (var item in uuid)
+            {
+                result.Poses.Add(WorldAnalysisConnections.Singleton.GetPose(item.Uuid, item.Mode));
+            }
+            return new ObjectResult(result);
         }
 
+#pragma warning disable CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element
+
+        //
+        // Management of subscriptions
+        //
+        /// <summary>
+        /// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable)
+        /// </summary>
+        private Dictionary<Guid, SubscriptionInfo> m_subscriptionsPoses = new Dictionary<Guid, SubscriptionInfo>();
+
+        public struct SubscriptionInfo
+        {
+            public Guid uuidSub;           // id of subscription (id is defined by the WA server)
+
+            public SubscribeToPoseRequest subscription;
+            public string webSocket;
+            public Pose pose;
+            public DateTime timeValidity;
+
+            //public PoseCallback callback;
+        }
+#pragma warning restore CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element
+
         /// <summary>
         /// Get information about a subscription
         /// </summary>
-        public override IActionResult GetSubscription([FromRoute (Name = "subscriptionUUID")][Required]Guid subscriptionUUID, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
+        public override IActionResult GetSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // todo: search for a subscription and send it back
+            foreach (var item in m_subscriptionsPoses)
+            {
+                if (item.Key == subscriptionUUID)
+                {
+                    SubscriptionSingle response = new SubscriptionSingle();
+                    response.Uuid = subscriptionUUID;
+                    response.Target = item.Value.subscription.Target;
+                    response.Mode = item.Value.pose.Mode;
+                    response.Validity = item.Value.timeValidity.Millisecond;
+                    response.WebhookUrl = item.Value.subscription.WebhookUrl;
+                    response.WebsocketUrl = item.Value.webSocket;
+                    return new ObjectResult(response);
+                }
+            }
+            return StatusCode(404, "Not found.");
         }
 
         /// <summary>
         /// Subscribe to collect the pose of an AR device, an Anchor or a Trackable
         /// </summary>
-        public override IActionResult SubscribeToPose([FromBody]SubscribeToPoseRequest subscribeToPoseRequest, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
+        public override IActionResult SubscribeToPose([FromBody] SubscribeToPoseRequest subscribeToPoseRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            // Simulation ON for STF669 pose calcualtion
+            string _token = token;
+            Guid parentUUID = Guid.Empty;
+            if (token.Contains(","))
+            {
+                _token = token.Split(',')[0];
+                parentUUID = new Guid(token.Split(',')[1]);
+            }
+
+            if (!Startup.IsAccessGranted(_token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            if (subscribeToPoseRequest.Targets != null && subscribeToPoseRequest.Targets.Count > 0)
+            {
+                return StatusCode(404, "Multiple subscriptions are not implemented (targets).");
+            }
+            else if (subscribeToPoseRequest.Modes != null && subscribeToPoseRequest.Modes.Count > 0)
+            {
+                return StatusCode(404, "Multiple subscriptions are not implemented (modes).");
+            }
+
+            int validity = subscribeToPoseRequest.Validity; // todo: is to handle here or by the client?
+
+            SubscribeToPose200Response response = new SubscribeToPose200Response();
+            response.Validity = validity;
+            response.Uuid = Guid.NewGuid();
+            response.Target = subscribeToPoseRequest.Target;
+            response.Mode = subscribeToPoseRequest.Mode;
+            response.WebhookUrl = subscribeToPoseRequest.WebhookUrl;
+            response.WebsocketUrl = "";
+
+            // Send the websocket connection URL
+            if (string.IsNullOrEmpty(response.WebhookUrl))
+            {
+                // Notice: starting websocket server is done autom. by the client, when calling "URL:xxx/ws"
+                // Registering the client is done in the analysis module, so the websocket can send to it pose updates
+                response.WebsocketUrl = "wss://" + Request.Host.ToString() + "/ws";
+            }
+
+            // We add the subscription
+            SubscriptionInfo info = new SubscriptionInfo();
+            info.uuidSub = response.Uuid;
+            info.webSocket = response.WebsocketUrl;
+            info.timeValidity = DateTime.Now.AddMilliseconds(validity / 1000.0f);
+
+            info.pose = new Pose();
+            info.pose.Mode = response.Mode;
+
+            info.subscription = new SubscribeToPoseRequest();
+            info.subscription.Target = response.Target;
+            info.subscription.Mode = response.Mode;
+            info.subscription.Validity = response.Validity;
+            info.subscription.WebhookUrl = response.WebhookUrl;
+
+            m_subscriptionsPoses.Add(info.uuidSub, info);
+            
+            // todo: inform the module(s) that the client will track an anchor/trackable and need the pose
+            // todo: has the module to call GetRelocalizationInformation() then?!?
+            WorldAnalysisConnections.Singleton.SubscribeToPose(info, parentUUID);
+
+            return new ObjectResult(response);
         }
 
         /// <summary>
         /// Remove a subscription to a given pose
         /// </summary>
-        public override IActionResult UnsubscribeFromPose([FromRoute (Name = "subscriptionUUID")][Required]Guid subscriptionUUID, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
+        public override IActionResult UnsubscribeFromPose([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
         {
-            return StatusCode(405, "Not supported yet!");
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // Remove the subscription from the list?
+            if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) m_subscriptionsPoses.Remove(subscriptionUUID);
+            else
+            {
+                //return StatusCode(404, new Error() { Message = "Unsubscribe UUID not found!" });
+            }
+
+            // Inform the module(s) that the subscription ended
+            WorldAnalysisConnections.Singleton.UnsubscribeFromPose(subscriptionUUID);
+
+            return StatusCode(200, new Success() { Message = "Unsubscription successfull." });
         }
 
         /// <summary>
         /// Update a subscription
         /// </summary>
-        public override IActionResult UpdateSubscription([FromRoute (Name = "subscriptionUUID")][Required]Guid subscriptionUUID, [FromBody]UpdateSubscriptionRequest updateSubscriptionRequest, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
+        public override IActionResult UpdateSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromBody] UpdateSubscriptionRequest updateSubscriptionRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
         {
+            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });
+
+            // todo: compare sessionID
+
+            // todo: inform the module(s) that the subscription changed
+
             return StatusCode(405, "Not supported yet!");
         }
     }
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs
index 6e80b54d679b0ac6e03429de8246451ca5e6ab9d..47315344237a9b087410b0bca2610e1fdb2a7444 100644
--- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs
@@ -30,9 +30,12 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Http;
 using Swashbuckle.AspNetCore.Annotations;
 using Swashbuckle.AspNetCore.SwaggerGen;
+using Newtonsoft.Json;
+
+using ETSI.ARF.OpenAPI.WorldAnalysis.Models;
 
 #pragma warning disable CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element
-namespace ETSI.ARF.OpenAPI.WorldStorage.Services
+namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
 {
     //
     // ETSI-ARF World Analysis WebSocket implementation
@@ -40,6 +43,14 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Services
     //
     public class WebSocketController : ControllerBase
     {
+        private WebSocket websocket;
+        static public int WebSocketControllerInstanceCount = 0;
+
+        private string currentName = "";
+        private bool registered = false;
+        private bool firstTime = true;
+        private int timeCnt = 3;
+
         [HttpGet("/ws")]
         public async Task Get()
         {
@@ -47,12 +58,15 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Services
             {
                 using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
 
-                if (webSocket.State == WebSocketState.Open)
+                if (webSocket.State == WebSocketState.Connecting)
                 {
-                    // Response an OK message
-                    await SendText(webSocket, "Hello, here is the ARF World Analysis services!");
+                    // Response an OK message?
+                }
+                else if (webSocket.State == WebSocketState.Open)
+                {
+                    // Go to the loop...
+                    await WebSocketServer_Loop(HttpContext, webSocket);
                 }
-                await Echo(HttpContext, webSocket);
             }
             else
             {
@@ -64,59 +78,298 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Services
         //
         // Send a line of text
         //
-        private async Task SendText(WebSocket webSocket, string text)
+        static private async Task SendText(WebSocket ws, string msg)
         {
-            // Response an OK message
-            var message = text;
+            var message = msg;
             var bytes = Encoding.UTF8.GetBytes(message);
             var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length);
-            await webSocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
+            await ws.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
+        }
+
+        public void SendText(string msg)
+        {
+            var message = msg;
+            var bytes = Encoding.UTF8.GetBytes(message);
+            var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length);
+            websocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
         }
 
         //
-        // Send the time all seconds
+        // Send a demo pose all seconds
         //
-        private async Task SendTime(WebSocket webSocket)
+        float rotInc = 0;
+        private async Task SendDemoPose(WebSocket ws)
         {
             while (true)
             {
-                var message = "Hello, my time is: " + DateTime.Now.ToLocalTime();
-                var bytes = Encoding.UTF8.GetBytes(message);
-                var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length);
-
-                if (webSocket.State == WebSocketState.Open)
+                if (ws.State == WebSocketState.Open)
                 {
-                    await webSocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
+                    timeCnt--;
+                    if (timeCnt < 0)
+                    {
+                        await SendText(ws, "PoseStop");
+                        break;
+                    }
+                    else
+                    {
+                        PoseValue val = new PoseValue();
+                        val.Unit = UnitSystem.MEnum;
+                        val.Position = new List<float>() { 1, 2, 3 };
+                        val.Rotation = new List<float>() { rotInc, 0, 0, 0 };
+                        rotInc += 0.01f;
+
+                        Pose pose = new Pose();
+                        pose.Uuid = new Guid();
+                        pose.EstimationState = Pose.EstimationStateEnum.OKEnum;
+                        //pose.SubscriptionUrl = "";
+                        pose.Value = val;
+                        pose.Timestamp = (int)DateTime.Now.ToFileTime();
+                        pose.Confidence = 1;
+                        pose.Mode = ModeWorldAnalysis.DEVICETOTRACKABLESEnum;
+
+                        string json = pose.ToJson();
+
+                        await SendText(ws, "NewPose=" + json);
+                    }
                 }
-                else if (webSocket.State == WebSocketState.Closed || webSocket.State == WebSocketState.Aborted)
+                else if (ws.State == WebSocketState.Closed || ws.State == WebSocketState.Aborted)
                 {
+                    if (WorldAnalysisConnections.Singleton.modules.Contains(currentModule)) WorldAnalysisConnections.Singleton.modules.Remove(currentModule);
+                    if (WorldAnalysisConnections.Singleton.clients.Contains(currentClient)) WorldAnalysisConnections.Singleton.clients.Remove(currentClient);
+                    currentClient = null;
+                    currentModule = null;
                     break;
                 }
-                Thread.Sleep(1000);
+                Thread.Sleep(250);
             }
         }
 
+        private Module currentModule;
+        private Client currentClient;
+
+        private async void OnReceiveText(string msg)
+        {
+            #region Register the client/module
+            if (firstTime)
+            {
+                if (msg.StartsWith("RegisterModule="))
+                {
+                    registered = true;
+                    firstTime = false;
+                    currentName = msg.Split('=')[1];
+
+                    // If module exist already reuse it
+                    Module module = null;
+                    foreach (var item in WorldAnalysisConnections.Singleton.modules) if (item.name == currentName) module = item;
+                    if (module != null)
+                    {
+                        currentModule = module;
+                        currentModule.name = currentName;
+                        currentModule.websockets = this;
+                        currentModule.capabilities.Clear();
+                    }
+
+                    if (currentModule == null && currentClient == null)
+                    {
+                        currentModule = new Module();
+                        currentModule.name = currentName;
+                        currentModule.websockets = this;
+                        WorldAnalysisConnections.Singleton.modules.Add(currentModule);
+                    }
+                    SendText($"ARF World Analysis Server: #{ WebSocketControllerInstanceCount } You are now registered as module: { currentName }");
+                }
+                else if (msg.StartsWith("RegisterClient="))
+                {
+                    registered = true;
+                    firstTime = false;
+                    currentName = msg.Split('=')[1];
+                    if (currentModule == null && currentClient == null)
+                    {
+                        currentClient = new Client();
+                        currentClient.name = currentName;
+                        currentClient.websockets = this;
+                        WorldAnalysisConnections.Singleton.clients.Add(currentClient);
+                        SendText($"ARF World Analysis Server: #{ WebSocketControllerInstanceCount } You are now registered as client: { currentName }");
+                    }
+                }
+                else
+                {
+                    registered = false;
+                    SendText("ARF World Analysis Server: Cannot register " + msg);
+                }
+                return;
+            }
+            #endregion
+
+            if (registered)
+            {
+                //
+                // Some admin stuffs
+                //
+                if (msg == "Idle")
+                {
+                    SendText("Idle");
+                }
+                else if (msg == "Busy")
+                {
+                    SendText("Busy");
+                }
+                else if (msg.StartsWith("UnregisterModule="))
+                {
+                    string name = msg.Split('=')[1];
+
+                    // Unregister a client (e.g. Unity client)
+                    Module module = null;
+                    foreach (var item in WorldAnalysisConnections.Singleton.modules) if (item.name == name) module = item;
+                    if (module != null)
+                    {
+                        WorldAnalysisConnections.Singleton.modules.Remove(module);
+                        SendText("UnregisterModuleOK");
+                    }
+                    currentName = "";
+                    firstTime = true;
+                    registered = false;
+                }
+                else if (msg.StartsWith("UnregisterClient="))
+                {
+                    string name = msg.Split('=')[1];
+
+                    // Unregister a client (e.g. Unity client)
+                    Client client = null;
+                    foreach (var item in WorldAnalysisConnections.Singleton.clients) if (item.name == name) client = item;
+                    if (client != null)
+                    {
+                        WorldAnalysisConnections.Singleton.clients.Remove(client);
+                        SendText("UnregisterClientOK");
+                    }
+                    currentName = "";
+                    firstTime = true;
+                    registered = false;
+                }
+                //
+                // OpenAPI
+                //
+                else if (msg == "PoseIsNowSubscribed")
+                {
+                    SendText("RequestNextPose");
+                }
+
+                //
+                // Messages from a module (Analysis)
+                //
+                else if (msg.StartsWith("Capabilities="))  // Receive capab. from a module
+                {
+                    // Module is sending their capabilities
+                    string[] str_cap = msg.Split('=');
+                    string moduleName = str_cap[1];
+                    Capability _c = JsonConvert.DeserializeObject<Capability>(str_cap[2]);
+
+                    // Has the module already send their capabilities?
+                    Module module = null;
+                    foreach (var item in WorldAnalysisConnections.Singleton.modules) if (item.name == moduleName) module = item;
+                    if (module != null) module.capabilities.Add(_c);
+                }
+                else if (msg.StartsWith("NewPose="))
+                {
+                    string[] str_pose = msg.Split('=');
+
+                    // Send the pose to the client(s)
+                    Pose pose = JsonConvert.DeserializeObject<Pose>(str_pose[1]);
+
+                    //WorldAnalysisConnections.Singleton.SendPoseToClients(pose);
+                    WorldAnalysisConnections.Singleton.SendPoseToClients(str_pose[1]);
+
+                    // todo: if there are some subscription ask modules for new pose
+                    SendText("RequestNextPose");
+                }
+                else if (msg.StartsWith("Error="))
+                {
+                    string errorMsg = msg.Split('=')[1];
+                    foreach (var item in WorldAnalysisConnections.Singleton.clients)
+                    {
+                        await SendText(item.websockets.websocket, errorMsg);
+                    }
+                }
+
+                //
+                // Messages from a client (Unity)
+                //
+                else if (msg.StartsWith("PoseStart")) // send some fake poses to Unity clients
+                {
+                    await SendDemoPose(websocket);
+                }
+                else if (msg.StartsWith("CurrentUserPose="))
+                {
+                    // A client (Unity) is sending the current user posistion
+
+                    // Send new user pose to all modules
+                    foreach (var item in WorldAnalysisConnections.Singleton.modules)
+                    {
+                        await SendText(item.websockets.websocket, msg);
+                    }
+
+                    //// Test: send user pose back to clients
+                    //string[] str_pose = msg.Split('=');
+                    //Pose pose = JsonConvert.DeserializeObject<Pose>(str_pose[1]);
+                    //pose.Value.Position[2] += 1f;
+                    //WorldAnalysisConnections.Singleton.SendPoseToClients(pose);
+                }
+
+                //
+                // Messages from modules and clients
+                //
+                else
+                {
+                    // Send a response
+                    SendText("ARF World Analysis Server: I got this unknown message: " + msg);
+                }
+            }
+        }
+
+        private async Task WebSocketServer_Loop(HttpContext context, WebSocket ws)
+        {
+            websocket = ws;
+            WebSocketControllerInstanceCount++;
+
+            var buffer = new byte[1024 * 4];
+
+            // Read/get the first data block
+            WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
+            if (result.MessageType == WebSocketMessageType.Text)
+            {
+                string getMsg = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
+                OnReceiveText(getMsg);
+            }
+
+            // Entering the loop
+            while (!result.CloseStatus.HasValue)
+            {
+                // Read/get the next block
+                result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
+                if (result.MessageType == WebSocketMessageType.Text)
+                {
+                    string getMsg = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
+                    OnReceiveText(getMsg);
+                }
+            }
+            await ws.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
+            WebSocketControllerInstanceCount--;
+        }
+
+
         //
         // Echo incoming messages
         //
-        private async Task Echo(HttpContext context, WebSocket webSocket)
+        private async Task Echo(WebSocketReceiveResult context, WebSocket webSocket)
         {
             var buffer = new byte[1024 * 4];
 
-            // get the first data block
+            // Read/get the first data block
             WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
-            while (!result.CloseStatus.HasValue)
-            {
-                // test
-                await SendText(webSocket, "Thanks, I got this message:");
 
-                // echo the message back to the client
-                await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
-
-                // get the next block
-                result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
-            }
-            await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
+            // Send/echo the message back to the client
+            await SendText(webSocket, "ARF World Analysis Server: I got this (raw) message: ");
+            await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
         }
     }
 }
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ModelsExt/NotNeeded.txt b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ModelsExt/NotNeeded.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/NoMongoDBneeded.txt b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/NoMongoDBneeded.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/Services/NotNeeded.txt b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/Services/NotNeeded.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/WorldAnalysisConnections.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/WorldAnalysisConnections.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c864547b3750de69a96ae41035c7a6f3c4788622
--- /dev/null
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/WorldAnalysisConnections.cs
@@ -0,0 +1,134 @@
+using ETSI.ARF.OpenAPI.WorldAnalysis.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using static ETSI.ARF.OpenAPI.WorldAnalysis.Controllers.PoseApiControlleImpl;
+using ETSI.ARF.OpenAPI.WorldAnalysis.Controllers;
+
+#pragma warning disable CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element
+namespace ETSI.ARF.OpenAPI.WorldAnalysis
+{
+    // For management of WA modules
+    public class Module
+    {
+        public string name;
+        public List<Capability> capabilities = new List<Capability>();
+        public WebSocketController websockets;
+    }
+
+    // For management of WA client (e.g. Unity client)
+    public class Client
+    {
+        public string name;
+        public string session;
+        public VectorQuaternionPoseValue currentUserPose;
+        public WebSocketController websockets;
+    }
+
+    public class WorldAnalysisConnections
+    {
+        static public WorldAnalysisConnections Singleton = new WorldAnalysisConnections();
+
+        public List<Module> modules = new List<Module>();
+        public List<Client> clients = new List<Client>();   // e.g. Unity clients
+
+        // todo
+        // Manage the sessions and modules
+        // get capabilities of all modules
+        public List<Capability> GetCapabilities()
+        {
+            List<Capability> list = new List<Capability>();
+            foreach (var item in modules)
+            {
+                list.AddRange(item.capabilities);
+            }
+            return list;
+        }
+
+        public List<Capability> GetCapabilitiesFromUuid(Guid trackableOrAnchorUUID)
+        {
+            List<Capability> list = new List<Capability>();
+            foreach (var item in modules)
+            {
+                // todo: Check if uuid has the capability?
+                // Get the world object from the world storage via the module?
+                // Use GetRelocalisation() ?
+                list.AddRange(item.capabilities);
+            }
+            return list;
+        }
+
+        public bool ConfigureFramerate(PoseConfiguration poseConfiguration)
+        {
+            bool ok = false;
+            foreach (var m in modules)
+            {
+                bool moduleHasCap = false;
+                foreach (var c in m.capabilities)
+                {
+                    if ((int)poseConfiguration.TrackableType == (int)c.TrackableType)
+                    {
+                        moduleHasCap = true;
+                        break;
+                    }
+                }
+                // Configure the module via websocket
+                if (moduleHasCap) m.websockets.SendText("ConfigureFramerate=" + poseConfiguration.ToJson());
+            }
+            return ok;
+        }
+
+        public Pose GetPose(Guid trackableOrAnchorUUID, ModeWorldAnalysis mode)
+        {
+            Pose result = new Pose();
+            foreach (var item in modules)
+            {
+                // todo: get the pose via websocket
+                item.websockets.SendText("GetPose=" + trackableOrAnchorUUID + "=" + mode.ToString());
+
+                // todo: find the pose with the best confidence?
+                // How to get the results !?!?! (List with results, per Modules)
+            }
+            return result;
+        }
+        
+        public void SubscribeToPose(SubscriptionInfo info, Guid parentTrackableUUID)
+        {
+            // Send to all modules a request of subscription
+            foreach (var item in modules)
+            {
+                if (parentTrackableUUID == Guid.Empty) item.websockets.SendText("SubscribePose=" + info.uuidSub + "=" + info.subscription.ToJson());
+                else item.websockets.SendText("SubscribeSimulatedPose=" + info.uuidSub + "=" + info.subscription.ToJson() + "=" + parentTrackableUUID.ToString());
+            }
+        }
+
+        public void UnsubscribeFromPose(Guid subscriptionUUID)
+        {
+            // Send to all modules a request of subscription
+            foreach (var item in modules)
+            {
+                item.websockets.SendText("UnsubscribePose=" + subscriptionUUID.ToString());
+            }
+        }
+
+        public void SendPoseToClients(Pose pose)
+        {
+            // Send to all clients with valid subscription the new pose
+            foreach (var item in clients)
+            {
+                item.websockets.SendText("NewPose=" + pose.ToJson());
+            }
+        }
+
+        public void SendPoseToClients(string str_pose)
+        {
+            // Send to all clients with valid subscription the new pose
+            foreach (var item in clients)
+            {
+                item.websockets.SendText("NewPose=" + str_pose);
+            }
+        }
+    }
+}
+#pragma warning restore CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Program.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Program.cs
index 95e22062027cf8674a23366f548a3e07c48c5d15..1fba73140c56dfc242fd22c8b795c3bf7a51801f 100644
--- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Program.cs
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Program.cs
@@ -27,7 +27,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
                 .ConfigureWebHostDefaults(webBuilder =>
                 {
                    webBuilder.UseStartup<Startup>()
-                             .UseUrls("http://0.0.0.0:8080/");
+                             .UseUrls("http://0.0.0.0:44301/");     // SylR: Wichtig!!!
                 });
     }
 }
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs
index 48be55250c2022d50514a393758b36234e0102d7..2a3f67c8c81faf23840f8f5e81c90a997d5cb9ba 100644
--- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs
@@ -15,7 +15,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-// Last change: June 2024
+// Last change: September 2024
 //
 
 /*
@@ -23,7 +23,7 @@
  *
  * API ensuring interoperability between Scene Management and a World Analysis service
  *
- * The version of the OpenAPI document: 2.0.1
+ * The version of the OpenAPI document: 2.0.2
  * 
  * Generated by: https://openapi-generator.tech
  */
@@ -64,17 +64,18 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
         /// <summary>
         /// The API version. (how to read it from the yaml?)
         /// </summary>
-        static public string apiVersion = "2.0.1";
+        static public string apiVersion = "2.0.0";
 
         /// <summary>
         /// Demo access key
         /// </summary>
-        static public string accessKey = "My!Key.ETSI";
+        static public string accessKey = "ARF";
 
         /// <summary>
         /// Demo secret key
         /// </summary>
-        static public string secretKey = "GW0Wae1t4Cs5rAqEbPYFWO9J5nSbpJXxp1F3uv0J";
+        //static public string secretKey = "GW0Wae1t4Cs5rAqEbPYFWO9J5nSbpJXxp1F3uv0J";
+        static public string secretKey = "hhi";
 
         /// <summary>
         /// Constructor
@@ -90,6 +91,20 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
         /// </summary>
         public IConfiguration Configuration { get; }
 
+        /// <summary>
+        /// SylR: Check if the request is authorized
+        /// </summary>
+        /// <param name="token"></param>
+        /// <returns></returns>
+        static public bool IsAccessGranted(string token)
+        {
+            if (token == null) return false;
+            else if (token == "dev") return true;   // developermode
+
+            string[] t = token.Split('&');
+            return t[0] == accessKey && t[1] == secretKey;
+        }
+
         /// <summary>
         /// This method gets called by the runtime. Use this method to add services to the container.
         /// </summary>
@@ -116,7 +131,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
                 {
                     c.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true);
                     
-                    c.SwaggerDoc("2.0.1", new OpenApiInfo
+                    c.SwaggerDoc(apiVersion, new OpenApiInfo
                     {
                         Title = "World Analysis API",
                         Description = "World Analysis API (ASP.NET Core 5.0)",
@@ -132,7 +147,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
                             Name = "NoLicense",
                             Url = new Uri("https://opensource.org/licenses/BSD-3-Clause")
                         },
-                        Version = "2.0.1",
+                        Version = apiVersion,
                     });
                     c.CustomSchemaIds(type => type.FriendlyId(true));
                     c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{Assembly.GetEntryAssembly().GetName().Name}.xml");
@@ -161,14 +176,15 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
                 app.UseHsts();
             }
 
-            // ETSI-ARF Websocket implementation
+            // ETSI-ARF Websockets implementation
             var webSocketOptions = new WebSocketOptions()
             {
-                KeepAliveInterval = TimeSpan.FromSeconds(120),
+                //KeepAliveInterval = TimeSpan.FromSeconds(120),
+                //AllowedOrigins.Add("https://etsi.hhi.fraunhofer.de"),
+                //AllowedOrigins.Add("https://www.client.com")
             };
-            //webSocketOptions.AllowedOrigins.Add("https://etsi.hhi.fraunhofer.de");
-            //webSocketOptions.AllowedOrigins.Add("https://www.client.com");
-            app.UseWebSockets();
+            app.UseWebSockets(webSocketOptions);
+            //app.UseWebSockets();
 
             app.UseHttpsRedirection();
             app.UseDefaultFiles();
@@ -182,7 +198,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
                     // set route prefix to openapi, e.g. http://localhost:8080/openapi/index.html
                     c.RoutePrefix = "openapi";
                     //TODO: Either use the SwaggerGen generated OpenAPI contract (generated from C# classes)
-                    c.SwaggerEndpoint("/openapi/2.0.1/openapi.json", "World Analysis API");
+                    c.SwaggerEndpoint("/openapi/" + apiVersion  + "/openapi.json", "World Analysis API");
 
                     //TODO: Or alternatively use the original OpenAPI contract that's included in the static files
                     // c.SwaggerEndpoint("/openapi-original.json", "World Analysis API Original");
diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html
index bf3da4b51bc53cc4b3425f72d4da5860b2b24f25..cbc7959284563fd214e85b696773559cc48d3902 100644
--- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html
+++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html
@@ -71,6 +71,7 @@
         var port = document.location.port ? (":" + document.location.port) : "";
 
         connectionUrl.value = scheme + "://" + document.location.hostname + port + "/ws" ;
+        //connectionUrl.value = scheme + "://" + "etsi.hhi.fraunhofer.de" + "/ws" ;
 
         function updateState() {
             function disable() {