diff --git a/deploy/tfs.sh b/deploy/tfs.sh
index 3fdbe77fb502c42aaf7dd507ab239f6b3bb20056..285acc46532f0c38917268750438e3af7cc6ba93 100755
--- a/deploy/tfs.sh
+++ b/deploy/tfs.sh
@@ -46,6 +46,26 @@ export TFS_GRAFANA_PASSWORD=${TFS_GRAFANA_PASSWORD:-"admin123+"}
 export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-""}
 
 
+# ----- App CockroachDB --------------------------------------------------------
+
+# If not already set, set the namespace where CockroackDB will be deployed.
+export APP_CRDB_NAMESPACE=${APP_CRDB_NAMESPACE:-"app-crdb"}
+
+# If not already set, set the external port CockroackDB Postgre SQL interface will be exposed to.
+export APP_CRDB_EXT_PORT_SQL=${APP_CRDB_EXT_PORT_SQL:-"26257"}
+
+# If not already set, set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to.
+export APP_CRDB_EXT_PORT_HTTP=${APP_CRDB_EXT_PORT_HTTP:-"8081"}
+
+# If not already set, set the database username to be used by Context.
+export APP_CRDB_USERNAME=${APP_CRDB_USERNAME:-"tfs"}
+
+# If not already set, set the database user's password to be used by Context.
+export APP_CRDB_PASSWORD=${APP_CRDB_PASSWORD:-"tfs123"}
+
+# If not already set, set the database name to be used by Context.
+export APP_CRDB_DATABASE=${APP_CRDB_DATABASE:-"tfs"}
+
 # ----- CockroachDB ------------------------------------------------------------
 
 # If not already set, set the namespace where CockroackDB will be deployed.
@@ -141,6 +161,7 @@ kubectl create secret generic crdb-data --namespace ${TFS_K8S_NAMESPACE} --type=
     --from-literal=CRDB_NAMESPACE=${CRDB_NAMESPACE} \
     --from-literal=CRDB_SQL_PORT=${CRDB_SQL_PORT} \
     --from-literal=CRDB_DATABASE=${CRDB_DATABASE} \
+    --from-literal=CRDB_DATABASE_APP=${CRDB_DATABASE_APP} \
     --from-literal=CRDB_USERNAME=${CRDB_USERNAME} \
     --from-literal=CRDB_PASSWORD=${CRDB_PASSWORD} \
     --from-literal=CRDB_SSLMODE=require
diff --git a/manifests/appservice.yaml b/manifests/appservice.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..da0a073538ecd1a194a4b356b248c5c70a18f974
--- /dev/null
+++ b/manifests/appservice.yaml
@@ -0,0 +1,83 @@
+# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: appservice
+spec:
+  selector:
+    matchLabels:
+      app: appservice
+  #replicas: 1
+  template:
+    metadata:
+      labels:
+        app: appservice
+    spec:
+      terminationGracePeriodSeconds: 5
+      containers:
+      - name: server
+        image: labs.etsi.org:5050/tfs/controller/app:latest
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 10060
+        - containerPort: 9192
+        - containerPort: 8005
+        env:
+        - name: MB_BACKEND
+          value: "nats"
+        - name: LOG_LEVEL
+          value: "INFO"
+        envFrom:
+        - secretRef:
+            name: crdb-data
+        - secretRef:
+            name: nats-data
+        readinessProbe:
+          exec:
+            command: ["/bin/grpc_health_probe", "-addr=:10060"]
+        livenessProbe:
+          exec:
+            command: ["/bin/grpc_health_probe", "-addr=:10060"]
+        resources:
+          requests:
+            cpu: 150m
+            memory: 128Mi
+          limits:
+            cpu: 500m
+            memory: 512Mi
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: appservice
+  labels:
+    app: appservice
+spec:
+  type: ClusterIP
+  selector:
+    app: appservice
+  ports:
+  - name: grpc
+    protocol: TCP
+    port: 10060
+    targetPort: 10060
+  - name: metrics
+    protocol: TCP
+    port: 9192
+    targetPort: 9192
+  - name: app
+    port: 8005
+    targetPort: 8005
diff --git a/manifests/nginx_ingress_http.yaml b/manifests/nginx_ingress_http.yaml
index 0892f0c9b790b936df5540ac5fe1aed0270b91a5..249501afade8f4dc5f6c9eb632602c1d0fefb530 100644
--- a/manifests/nginx_ingress_http.yaml
+++ b/manifests/nginx_ingress_http.yaml
@@ -57,3 +57,10 @@ spec:
                 name: nbiservice
                 port:
                   number: 8080
+                    - path: /()(app/.*)
+          - pathType: Prefix
+            backend:
+              service:
+                name: appservice
+                port:
+                  number: 8005
diff --git a/my_deploy.sh b/my_deploy.sh
index 8417f6eae510391e65d5f91202e59cccf32e1f98..b52434d33d58029335e24952c63b65befb0be0e1 100755
--- a/my_deploy.sh
+++ b/my_deploy.sh
@@ -20,13 +20,13 @@
 export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/"
 
 # Set the list of components, separated by spaces, you want to build images for, and deploy.
-export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_generator"
+export TFS_COMPONENTS="context device app pathcomp service slice nbi webui load_generator"
 
 # Uncomment to activate Monitoring
 #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring"
 
 # Uncomment to activate BGP-LS Speaker
-#export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker"
+export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker"
 
 # Uncomment to activate Optical Controller
 #   To manage optical connections, "service" requires "opticalcontroller" to be deployed
@@ -100,6 +100,7 @@ export CRDB_PASSWORD="tfs123"
 
 # Set the database name to be used by Context.
 export CRDB_DATABASE="tfs"
+export CRDB_DATABASE_APP="tfs_app"
 
 # Set CockroachDB installation mode to 'single'. This option is convenient for development and testing.
 # See ./deploy/all.sh or ./deploy/crdb.sh for additional details
diff --git a/proto/app.proto b/proto/app.proto
new file mode 100644
index 0000000000000000000000000000000000000000..d2ed33500da412fad1e8af3ebc709136e4cf46a8
--- /dev/null
+++ b/proto/app.proto
@@ -0,0 +1,53 @@
+syntax = "proto3";
+package app;
+
+import "context.proto";
+
+// Optare: Change this if you want to change App's structure or enums. 
+// Optare: If a message (structure) is changed it must be changed in src/app/service/database
+
+enum QKDAppStatusEnum {
+  QKDAPPSTATUS_ON = 0;
+  QKDAPPSTATUS_DISCONNECTED = 1;
+  QKDAPPSTATUS_OUT_OF_TIME = 2;
+  QKDAPPSTATUS_ZOMBIE = 3;
+}
+
+enum QKDAppTypesEnum {
+  QKDAPPTYPES_INTERNAL = 0;
+  QKDAPPTYPES_CLIENT = 1;
+}
+
+message QKDLId {
+  context.Uuid qkdl_uuid = 1;
+}
+
+
+message App {
+  AppId app_id = 1;
+  QKDAppStatusEnum app_status = 2;
+  QKDAppTypesEnum app_type = 3;
+  string server_app_id = 4;
+  repeated string client_app_id = 5;
+  repeated QKDLId backing_qkdl_id = 6;
+  context.DeviceId local_device_id = 7;
+  context.DeviceId remote_device_id = 8;
+}
+
+
+message AppId {
+  context.ContextId context_id = 1;
+  context.Uuid app_uuid = 2;
+}
+
+
+service AppService {
+  rpc RegisterApp(App) returns (context.Empty) {}
+  rpc ListApps       (context.ContextId     ) returns (       AppList     ) {}
+ }
+ 
+
+
+ message AppList {
+  repeated App apps = 1;
+}
diff --git a/src/app/client/AppClient b/src/app/client/AppClient.py
similarity index 100%
rename from src/app/client/AppClient
rename to src/app/client/AppClient.py
diff --git a/src/common/Constants.py b/src/common/Constants.py
index de9ac45a4089a7847c37ceeeeab000f51566a3a3..511735ea4b03cce00eb91242975983d6b58c5986 100644
--- a/src/common/Constants.py
+++ b/src/common/Constants.py
@@ -61,6 +61,7 @@ class ServiceNameEnum(Enum):
     E2EORCHESTRATOR        = 'e2eorchestrator'
     OPTICALCONTROLLER      = 'opticalcontroller'
     BGPLS                  = 'bgpls-speaker'
+    APP                    = 'app'
 
     # Used for test and debugging only
     DLT_GATEWAY    = 'dltgateway'
@@ -89,6 +90,7 @@ DEFAULT_SERVICE_GRPC_PORTS = {
     ServiceNameEnum.FORECASTER             .value : 10040,
     ServiceNameEnum.E2EORCHESTRATOR        .value : 10050,
     ServiceNameEnum.OPTICALCONTROLLER      .value : 10060,
+    ServiceNameEnum.APP                    .value : 10070,
     ServiceNameEnum.BGPLS                  .value : 20030,
 
     # Used for test and debugging only
@@ -101,10 +103,12 @@ DEFAULT_SERVICE_HTTP_PORTS = {
     ServiceNameEnum.CONTEXT   .value : 8080,
     ServiceNameEnum.NBI       .value : 8080,
     ServiceNameEnum.WEBUI     .value : 8004,
+    ServiceNameEnum.APP       .value : 8005,
 }
 
 # Default HTTP/REST-API service base URLs
 DEFAULT_SERVICE_HTTP_BASEURLS = {
     ServiceNameEnum.NBI       .value : None,
     ServiceNameEnum.WEBUI     .value : None,
+    ServiceNameEnum.APP       .value : None,
 }
diff --git a/src/service/Dockerfile b/src/service/Dockerfile
index a847ae762d1303dda852d7f3d8200d3db3ef53f7..cfb9900ae000ef8e542fc9bb4a208b5609057164 100644
--- a/src/service/Dockerfile
+++ b/src/service/Dockerfile
@@ -70,6 +70,8 @@ COPY src/pathcomp/frontend/__init__.py pathcomp/frontend/__init__.py
 COPY src/pathcomp/frontend/client/. pathcomp/frontend/client/
 COPY src/e2e_orchestrator/__init__.py e2e_orchestrator/__init__.py
 COPY src/e2e_orchestrator/client/. e2e_orchestrator/client/
+COPY src/app/__init__.py app/__init__.py
+COPY src/app/client/. app/client/
 COPY src/service/. service/
 
 # Start the service
diff --git a/src/service/service/__main__.py b/src/service/service/__main__.py
index ae8a9e960cf6a0d720b508fe3ea5592632420c18..9e7e7b46bb2e8bc4242e795250b6162c730951d5 100644
--- a/src/service/service/__main__.py
+++ b/src/service/service/__main__.py
@@ -44,6 +44,9 @@ def main():
         get_env_var_name(ServiceNameEnum.DEVICE,   ENVVAR_SUFIX_SERVICE_PORT_GRPC),
         get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_HOST     ),
         get_env_var_name(ServiceNameEnum.PATHCOMP, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
+        get_env_var_name(ServiceNameEnum.APP, ENVVAR_SUFIX_SERVICE_HOST     ),
+        get_env_var_name(ServiceNameEnum.APP, ENVVAR_SUFIX_SERVICE_PORT_GRPC),
+
     ])
 
     signal.signal(signal.SIGINT,  signal_handler)
@@ -72,4 +75,4 @@ def main():
     return 0
 
 if __name__ == '__main__':
-    sys.exit(main())
+    sys.exit(main())
\ No newline at end of file
diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py
index cd20faad23a06678be39dbacc476a0ea25d4d540..487fe5b8548bfe1ea3cb1a4b4e9849234ba8de58 100644
--- a/src/service/service/task_scheduler/TaskExecutor.py
+++ b/src/service/service/task_scheduler/TaskExecutor.py
@@ -20,6 +20,7 @@ from common.proto.context_pb2 import (
     Connection, ConnectionId, Device, DeviceDriverEnum, DeviceId, Service, ServiceId,
     OpticalConfig, OpticalConfigId
 )
+from common.proto.app_pb2 import App
 from common.tools.context_queries.Connection import get_connection_by_id
 from common.tools.context_queries.Device import get_device
 from common.tools.context_queries.Service import get_service_by_id
@@ -27,11 +28,12 @@ from common.tools.grpc.Tools import grpc_message_to_json_string
 from common.tools.object_factory.Device import json_device_id
 from context.client.ContextClient import ContextClient
 from device.client.DeviceClient import DeviceClient
+from app.client.AppClient import AppClient
 from service.service.service_handler_api.Exceptions import (
     UnsatisfiedFilterException, UnsupportedFilterFieldException, UnsupportedFilterFieldValueException
 )
 from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory, get_service_handler_class
-from service.service.tools.ObjectKeys import get_connection_key, get_device_key, get_service_key
+from service.service.tools.ObjectKeys import get_connection_key, get_device_key, get_service_key, get_app_key
 
 if TYPE_CHECKING:
     from service.service.service_handler_api._ServiceHandler import _ServiceHandler
@@ -44,11 +46,14 @@ class CacheableObjectType(Enum):
     CONNECTION = 'connection'
     DEVICE     = 'device'
     SERVICE    = 'service'
+    APP        = 'app'
 
 class TaskExecutor:
     def __init__(self, service_handler_factory : ServiceHandlerFactory) -> None:
         self._service_handler_factory = service_handler_factory
         self._context_client = ContextClient()
+        # DEPENDENCY QKD
+        self._app_client = AppClient()
         self._device_client = DeviceClient()
         self._grpc_objects_cache : Dict[str, CacheableObject] = dict()
 
@@ -220,3 +225,12 @@ class TaskExecutor:
                     str(dict_connection_devices)
                 )
             )
+
+
+    # ----- App-related methods ---------------------------------------------------------------------------------------
+
+    def register_app(self, app: App) -> None:
+        app_key = get_app_key(app.app_id)
+        self._app_client.RegisterApp(app)
+        LOGGER.info("reg registered")
+        self._store_grpc_object(CacheableObjectType.APP, app_key, app)
\ No newline at end of file
diff --git a/src/service/service/tools/ObjectKeys.py b/src/service/service/tools/ObjectKeys.py
index f45126e07df6a74a20e507fd51d08f1a32de7f98..50129c704afbd1aff90913b56872d98f6943d39e 100644
--- a/src/service/service/tools/ObjectKeys.py
+++ b/src/service/service/tools/ObjectKeys.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 from common.proto.context_pb2 import ConnectionId, DeviceId, ServiceId
+from common.proto.app_pb2 import AppId
 
 def get_connection_key(connection_id : ConnectionId) -> str:
     return connection_id.connection_uuid.uuid
@@ -24,3 +25,7 @@ def get_service_key(service_id : ServiceId) -> str:
     context_uuid = service_id.context_id.context_uuid.uuid
     service_uuid = service_id.service_uuid.uuid
     return '{:s}/{:s}'.format(context_uuid, service_uuid)
+
+def get_app_key(app_id : AppId) -> str:
+    return app_id.app_uuid.uuid
+