diff --git a/src/common/tools/object_factory/PolicyRule.py b/src/common/tools/object_factory/PolicyRule.py
new file mode 100644
index 0000000000000000000000000000000000000000..4157a3f0679efa37db72416c1fe2d7726c07355c
--- /dev/null
+++ b/src/common/tools/object_factory/PolicyRule.py
@@ -0,0 +1,45 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# 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.
+
+import logging
+LOGGER = logging.getLogger(__name__)
+
+def json_policy_rule_id(policy_rule_uuid: str):
+    result = {'uuid': {'uuid': policy_rule_uuid}}
+    return result
+
+
+def json_policy_rule(policy_rule_uuid: str, policy_rule_type: str):
+    result = {}
+    basic = {
+        "policyRuleId": json_policy_rule_id(policy_rule_uuid),
+        "priority": 1,
+        "conditionList": [],
+        "booleanOperator": bool(),
+        "actionList": []
+    }
+
+    if policy_rule_type == 'service':
+        result["service"] = {}
+        result["service"]["policyRuleBasic"] = basic
+        result["service"]["serviceId"] = {
+            "service_uuid": {"uuid": "uuid-service"},
+            "context_id": {"context_uuid": {"uuid": "uuid-context"}},
+        }
+
+    else:
+        result["device"] = {}
+        result["device"]["policyRuleBasic"] = basic
+
+    return result
diff --git a/src/context/client/ContextClient.py b/src/context/client/ContextClient.py
index da907341f799def94694817242c106a913e03327..7b7a039f3783fb166ba73641eeba79aec21bda54 100644
--- a/src/context/client/ContextClient.py
+++ b/src/context/client/ContextClient.py
@@ -28,6 +28,8 @@ from common.proto.context_pb2 import (
     Slice, SliceEvent, SliceId, SliceIdList, SliceList,
     Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList)
 from common.proto.context_pb2_grpc import ContextServiceStub
+from common.proto.context_policy_pb2_grpc import ContextPolicyServiceStub
+from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule)
 
 LOGGER = logging.getLogger(__name__)
 MAX_RETRIES = 15
@@ -42,17 +44,52 @@ class ContextClient:
         LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint)))
         self.channel = None
         self.stub = None
+        self.policy_stub = None
         self.connect()
         LOGGER.debug('Channel created')
 
     def connect(self):
         self.channel = grpc.insecure_channel(self.endpoint)
         self.stub = ContextServiceStub(self.channel)
+        self.policy_stub = ContextPolicyServiceStub(self.channel)
 
     def close(self):
         if self.channel is not None: self.channel.close()
         self.channel = None
         self.stub = None
+        self.policy_stub = None
+
+
+    @RETRY_DECORATOR
+    def ListPolicyRuleIds(self, request: Empty) -> PolicyRuleIdList:
+        LOGGER.debug('ListPolicyRuleIds request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.policy_stub.ListPolicyRuleIds(request)
+        LOGGER.debug('ListPolicyRuleIds result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def ListPolicyRules(self, request: Empty) -> PolicyRuleList:
+        LOGGER.debug('ListPolicyRules request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.policy_stub.ListPolicyRules(request)
+        LOGGER.debug('ListPolicyRules result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def GetPolicyRule(self, request: PolicyRuleId) -> PolicyRule:
+        LOGGER.info('GetPolicyRule request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.policy_stub.GetPolicyRule(request)
+        LOGGER.info('GetPolicyRule result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def SetPolicyRule(self, request: PolicyRule) -> PolicyRuleId:
+        LOGGER.debug('SetPolicyRule request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.policy_stub.SetPolicyRule(request)
+        LOGGER.debug('SetPolicyRule result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def RemovePolicyRule(self, request: PolicyRuleId) -> Empty:
+        LOGGER.debug('RemovePolicyRule request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.policy_stub.RemovePolicyRule(request)
+        LOGGER.debug('RemovePolicyRule result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
 
     @RETRY_DECORATOR
     def ListContextIds(self, request: Empty) -> ContextIdList:
diff --git a/src/context/service/database/PolicyRuleModel.py b/src/context/service/database/PolicyRuleModel.py
new file mode 100644
index 0000000000000000000000000000000000000000..02adbfa07f98e9fd02b6dd303cfe3159cbf23da6
--- /dev/null
+++ b/src/context/service/database/PolicyRuleModel.py
@@ -0,0 +1,35 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# 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.
+
+import logging
+import json
+from typing import Dict, List, Optional, Tuple
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+
+LOGGER = logging.getLogger(__name__)
+
+
+class PolicyRuleModel(Model):
+    pk = PrimaryKeyField()
+    value = StringField(required=True, allow_empty=False)
+
+    def dump_id(self) -> Dict:
+        return {
+            "uuid": {"uuid": self.pk}
+        }
+
+    def dump(self) -> Dict:
+        return json.loads(self.value)
diff --git a/src/context/service/grpc_server/Constants.py b/src/context/service/grpc_server/Constants.py
index 9d7c886c725d22308f33dc274234ad17f595633d..25a2fce40afab86beba2fdae1a8ce3f436011e07 100644
--- a/src/context/service/grpc_server/Constants.py
+++ b/src/context/service/grpc_server/Constants.py
@@ -19,7 +19,9 @@ TOPIC_DEVICE     = 'device'
 TOPIC_LINK       = 'link'
 TOPIC_SERVICE    = 'service'
 TOPIC_SLICE      = 'slice'
+TOPIC_POLICY     = 'policy'
 
-TOPICS = {TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_TOPOLOGY, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE}
+TOPICS = {TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_TOPOLOGY, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE,
+          TOPIC_POLICY}
 
 CONSUME_TIMEOUT = 0.5 # seconds
diff --git a/src/context/service/grpc_server/ContextService.py b/src/context/service/grpc_server/ContextService.py
index 1b54ec5400c93cba3882dccb197479b75bb699af..5d4dd8bb991ed64a970f9815bb302fd33d51cf34 100644
--- a/src/context/service/grpc_server/ContextService.py
+++ b/src/context/service/grpc_server/ContextService.py
@@ -17,6 +17,7 @@ from common.Settings import get_service_port_grpc
 from common.message_broker.MessageBroker import MessageBroker
 from common.orm.Database import Database
 from common.proto.context_pb2_grpc import add_ContextServiceServicer_to_server
+from common.proto.context_policy_pb2_grpc import add_ContextPolicyServiceServicer_to_server
 from common.tools.service.GenericGrpcService import GenericGrpcService
 from .ContextServiceServicerImpl import ContextServiceServicerImpl
 
@@ -31,3 +32,4 @@ class ContextService(GenericGrpcService):
 
     def install_servicers(self):
         add_ContextServiceServicer_to_server(self.context_servicer, self.server)
+        add_ContextPolicyServiceServicer_to_server(self.context_servicer, self.server)
diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py
index 88f7bd8af82009f1fc45bace87776d9cbc6d6543..3cd8aff5c0b5c77514e71ddc52ecef0f0d3fbb40 100644
--- a/src/context/service/grpc_server/ContextServiceServicerImpl.py
+++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py
@@ -28,13 +28,16 @@ from common.proto.context_pb2 import (
     Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList,
     Slice, SliceEvent, SliceId, SliceIdList, SliceList,
     Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList)
+from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule)
 from common.proto.context_pb2_grpc import ContextServiceServicer
+from common.proto.context_policy_pb2_grpc import ContextPolicyServiceServicer
 from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
 from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException
 from context.service.database.ConfigModel import update_config
 from context.service.database.ConnectionModel import ConnectionModel, set_path
 from context.service.database.ConstraintModel import set_constraints
 from context.service.database.ContextModel import ContextModel
+from context.service.database.PolicyRuleModel import PolicyRuleModel
 from context.service.database.DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers
 from context.service.database.EndPointModel import EndPointModel, set_kpi_sample_types
 from context.service.database.Events import notify_event
@@ -48,7 +51,8 @@ from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_
 from context.service.database.TopologyModel import TopologyModel
 from .Constants import (
     CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE,
-    TOPIC_TOPOLOGY)
+    TOPIC_TOPOLOGY, TOPIC_POLICY)
+from common.tools.grpc.Tools import grpc_message_to_json_string, grpc_message_to_json
 
 LOGGER = logging.getLogger(__name__)
 
@@ -61,11 +65,12 @@ METHOD_NAMES = [
     'ListLinkIds',       'ListLinks',       'GetLink',       'SetLink',       'RemoveLink',       'GetLinkEvents',
     'ListServiceIds',    'ListServices',    'GetService',    'SetService',    'RemoveService',    'GetServiceEvents',
     'ListSliceIds',      'ListSlices',      'GetSlice',      'SetSlice',      'RemoveSlice',      'GetSliceEvents',
+    'ListPolicyRuleIds', 'ListPolicyRules', 'GetPolicyRule', 'SetPolicyRule', 'RemovePolicyRule',
     'UnsetService',      'UnsetSlice',
 ]
 METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES)
 
-class ContextServiceServicerImpl(ContextServiceServicer):
+class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceServicer):
     def __init__(self, database : Database, messagebroker : MessageBroker):
         LOGGER.debug('Creating Servicer...')
         self.lock = threading.Lock()
@@ -811,3 +816,58 @@ class ContextServiceServicerImpl(ContextServiceServicer):
     def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]:
         for message in self.messagebroker.consume({TOPIC_CONNECTION}, consume_timeout=CONSUME_TIMEOUT):
             yield ConnectionEvent(**json.loads(message.content))
+
+
+    # ----- Policy ----------------------------------------------------------------------------------------------------
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def ListPolicyRuleIds(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleIdList:
+        with self.lock:
+            db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel)
+            db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk'))
+            return PolicyRuleIdList(policyRuleIdList=[db_policy_rule.dump_id() for db_policy_rule in db_policy_rules])
+
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def ListPolicyRules(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleList:
+        with self.lock:
+            db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel)
+            db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk'))
+            res = PolicyRuleList(policyRules=[db_policy_rule.dump_id() for db_policy_rule in db_policy_rules])
+            return res
+
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def GetPolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> PolicyRule:
+        with self.lock:
+            policy_rule_uuid = request.uuid.uuid
+            db_policy_rule: PolicyRuleModel = get_object(self.database, PolicyRuleModel, policy_rule_uuid)
+            return PolicyRule(**db_policy_rule.dump())
+
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def SetPolicyRule(self, request: PolicyRule, context: grpc.ServicerContext) -> PolicyRuleId:
+        with self.lock:
+            policy_rule_type = request.WhichOneof('policy_rule')
+            policy_rule_json = grpc_message_to_json(request)
+            policy_rule_uuid = policy_rule_json[policy_rule_type]["policyRuleBasic"]["policyRuleId"]["uuid"]["uuid"]
+            result: Tuple[PolicyRuleModel, bool] = update_or_create_object(
+                self.database, PolicyRuleModel, policy_rule_uuid, {"value": json.dumps(policy_rule_json)})
+            db_policy, updated = result
+
+            event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE
+            dict_policy_id = db_policy.dump_id()
+            notify_event(self.messagebroker, TOPIC_POLICY, event_type, {"policy_id": dict_policy_id})
+
+            return PolicyRuleId(**dict_policy_id)
+
+    @safe_and_metered_rpc_method(METRICS, LOGGER)
+    def RemovePolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> Empty:
+        with self.lock:
+            policy_uuid = request.uuid.uuid
+            db_policy = PolicyRuleModel(self.database, policy_uuid, auto_load=False)
+            found = db_policy.load()
+            if not found:
+                return Empty()
+
+            dict_policy_id = db_policy.dump_id()
+            db_policy.delete()
+            event_type = EventTypeEnum.EVENTTYPE_REMOVE
+            notify_event(self.messagebroker, TOPIC_POLICY, event_type, {"policy_id": dict_policy_id})
+            return Empty()
diff --git a/src/context/tests/Objects.py b/src/context/tests/Objects.py
index 140cbff686eaf5b430f23ee987a9335ecb04c0f5..4b435528bd927e1daf57f5b357759dd452cf3965 100644
--- a/src/context/tests/Objects.py
+++ b/src/context/tests/Objects.py
@@ -23,6 +23,7 @@ from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_id
 from common.tools.object_factory.Link import json_link, json_link_id
 from common.tools.object_factory.Service import json_service_id, json_service_l3nm_planned
 from common.tools.object_factory.Topology import json_topology, json_topology_id
+from common.tools.object_factory.PolicyRule import json_policy_rule, json_policy_rule_id
 
 
 # ----- Context --------------------------------------------------------------------------------------------------------
@@ -197,3 +198,10 @@ CONNECTION_R1_R3_SVCIDS = [SERVICE_R1_R2_ID, SERVICE_R2_R3_ID]
 CONNECTION_R1_R3        = json_connection(
     CONNECTION_R1_R3_UUID, service_id=SERVICE_R1_R3_ID, path_hops_endpoint_ids=CONNECTION_R1_R3_EPIDS,
     sub_service_ids=CONNECTION_R1_R3_SVCIDS)
+
+
+# ----- PolicyRule -------------------------------------------------------------------------------------------------------
+POLICY_RULE_UUID = '56380225-3e40-4f74-9162-529f8dcb96a1'
+POLICY_RULE_ID   = json_policy_rule_id(POLICY_RULE_UUID)
+POLICY_RULE      = json_policy_rule(POLICY_RULE_UUID, policy_rule_type='POLICYTYPE_DEVICE')
+
diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py
index 3109ef13dea98d4a56d661871b1c38ee2296f890..d37436677182a9ef4157c6c0e720925a3db7adb5 100644
--- a/src/context/tests/test_unitary.py
+++ b/src/context/tests/test_unitary.py
@@ -27,6 +27,7 @@ from common.proto.context_pb2 import (
     Connection, ConnectionEvent, ConnectionId, Context, ContextEvent, ContextId, Device, DeviceEvent, DeviceId,
     DeviceOperationalStatusEnum, Empty, EventTypeEnum, Link, LinkEvent, LinkId, Service, ServiceEvent, ServiceId,
     ServiceStatusEnum, ServiceTypeEnum, Topology, TopologyEvent, TopologyId)
+from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule)
 from common.type_checkers.Assertions import (
     validate_connection, validate_connection_ids, validate_connections, validate_context, validate_context_ids,
     validate_contexts, validate_device, validate_device_ids, validate_devices, validate_link, validate_link_ids,
@@ -44,7 +45,8 @@ from .Objects import (
     CONNECTION_R1_R3, CONNECTION_R1_R3_ID, CONNECTION_R1_R3_UUID, CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID,
     DEVICE_R1_UUID, DEVICE_R2, DEVICE_R2_ID, DEVICE_R2_UUID, DEVICE_R3, DEVICE_R3_ID, DEVICE_R3_UUID, LINK_R1_R2,
     LINK_R1_R2_ID, LINK_R1_R2_UUID, SERVICE_R1_R2, SERVICE_R1_R2_ID, SERVICE_R1_R2_UUID, SERVICE_R1_R3,
-    SERVICE_R1_R3_ID, SERVICE_R1_R3_UUID, SERVICE_R2_R3, SERVICE_R2_R3_ID, SERVICE_R2_R3_UUID, TOPOLOGY, TOPOLOGY_ID)
+    SERVICE_R1_R3_ID, SERVICE_R1_R3_UUID, SERVICE_R2_R3, SERVICE_R2_R3_ID, SERVICE_R2_R3_UUID, TOPOLOGY, TOPOLOGY_ID,
+    POLICY_RULE, POLICY_RULE_ID, POLICY_RULE_UUID)
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
@@ -68,8 +70,8 @@ REDIS_CONFIG = {
 }
 
 SCENARIOS = [
-    ('all_inmemory', DatabaseBackendEnum.INMEMORY, {},           MessageBrokerBackendEnum.INMEMORY, {}          ),
-    ('all_redis',    DatabaseBackendEnum.REDIS,    REDIS_CONFIG, MessageBrokerBackendEnum.REDIS,    REDIS_CONFIG),
+    ('all_inmemory', DatabaseBackendEnum.INMEMORY, {},           MessageBrokerBackendEnum.INMEMORY, {}          )
+#    ('all_redis',    DatabaseBackendEnum.REDIS,    REDIS_CONFIG, MessageBrokerBackendEnum.REDIS,    REDIS_CONFIG),
 ]
 
 @pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS)
@@ -1169,6 +1171,104 @@ def test_grpc_connection(
     assert len(db_entries) == 0
 
 
+def test_grpc_policy(
+    context_client_grpc : ContextClient,                # pylint: disable=redefined-outer-name
+    context_db_mb : Tuple[Database, MessageBroker]):    # pylint: disable=redefined-outer-name
+    context_database = context_db_mb[0]
+
+    # ----- Clean the database -----------------------------------------------------------------------------------------
+    context_database.clear_all()
+
+    # ----- Initialize the EventsCollector -----------------------------------------------------------------------------
+    events_collector = EventsCollector(context_client_grpc)
+    events_collector.start()
+
+    # ----- Get when the object does not exist -------------------------------------------------------------------------
+    POLICY_ID = 'no-uuid'
+    DEFAULT_POLICY_ID = {'uuid': {'uuid': POLICY_ID}}
+
+    with pytest.raises(grpc.RpcError) as e:
+        context_client_grpc.GetPolicyRule(PolicyRuleId(**DEFAULT_POLICY_ID))
+
+    assert e.value.code() == grpc.StatusCode.NOT_FOUND
+    assert e.value.details() == 'PolicyRule({:s}) not found'.format(POLICY_ID)
+
+    # ----- List when the object does not exist ------------------------------------------------------------------------
+    response = context_client_grpc.ListPolicyRuleIds(Empty())
+    assert len(response.policyRuleIdList) == 0
+
+    response = context_client_grpc.ListPolicyRules(Empty())
+    assert len(response.policyRules) == 0
+
+    # ----- Dump state of database before create the object ------------------------------------------------------------
+    db_entries = context_database.dump()
+    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
+    for db_entry in db_entries:
+        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry))  # pragma: no cover
+    LOGGER.info('-----------------------------------------------------------')
+    assert len(db_entries) == 0
+
+    # ----- Create the object ------------------------------------------------------------------------------------------
+    response = context_client_grpc.SetPolicyRule(PolicyRule(**POLICY_RULE))
+    assert response.uuid.uuid == POLICY_RULE_UUID
+
+    # ----- Check create event -----------------------------------------------------------------------------------------
+    # events = events_collector.get_events(block=True, count=2)
+    # assert isinstance(events[0], TopologyEvent)
+    # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
+    # assert events[0].policy_id.uuid.uuid == POLICY_RULE_UUID
+    #
+    # assert isinstance(events[1], ContextEvent)
+    # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE
+
+    # ----- Update the object ------------------------------------------------------------------------------------------
+    response = context_client_grpc.SetPolicyRule(PolicyRule(**POLICY_RULE))
+    assert response.uuid.uuid == POLICY_RULE_UUID
+
+    # ----- Dump state of database after create/update the object ------------------------------------------------------
+    db_entries = context_database.dump()
+    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
+    for db_entry in db_entries:
+        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
+    LOGGER.info('-----------------------------------------------------------')
+    assert len(db_entries) == 2
+
+    # ----- Get when the object exists ---------------------------------------------------------------------------------
+    response = context_client_grpc.GetPolicyRule(PolicyRuleId(**POLICY_RULE_ID))
+    assert response.device.policyRuleBasic.policyRuleId.uuid.uuid == POLICY_RULE_UUID
+
+    # ----- List when the object exists --------------------------------------------------------------------------------
+    response = context_client_grpc.ListPolicyRuleIds(Empty())
+    assert len(response.policyRuleIdList) == 1
+    assert response.policyRuleIdList[0].uuid.uuid == POLICY_RULE_UUID
+
+    response = context_client_grpc.ListPolicyRules(Empty())
+    assert len(response.policyRules) == 1
+
+    # ----- Remove the object ------------------------------------------------------------------------------------------
+    context_client_grpc.RemovePolicyRule(PolicyRuleId(**POLICY_RULE_ID))
+
+    # ----- Check remove event -----------------------------------------------------------------------------------------
+    # events = events_collector.get_events(block=True, count=2)
+    #
+    # assert isinstance(events[0], TopologyEvent)
+    # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE
+    # assert events[0].policy_id.uuid.uuid == POLICY_RULE_UUID
+
+
+    # ----- Stop the EventsCollector -----------------------------------------------------------------------------------
+    # events_collector.stop()
+
+    # ----- Dump state of database after remove the object -------------------------------------------------------------
+    db_entries = context_database.dump()
+    LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries)))
+    for db_entry in db_entries:
+        LOGGER.info('  [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover
+    LOGGER.info('-----------------------------------------------------------')
+    assert len(db_entries) == 0
+
+
+
 # ----- Test REST API methods ------------------------------------------------------------------------------------------
 
 def test_rest_populate_database(
diff --git a/src/policy/client/PolicyClient.py b/src/policy/client/PolicyClient.py
new file mode 100644
index 0000000000000000000000000000000000000000..2532a35caeccd182e5ffd1812ea99e1c41b95b4c
--- /dev/null
+++ b/src/policy/client/PolicyClient.py
@@ -0,0 +1,89 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# 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.
+
+import grpc, logging
+from common.Constants import ServiceNameEnum
+from common.Settings import get_service_host, get_service_port_grpc
+from common.tools.client.RetryDecorator import retry, delay_exponential
+from common.tools.grpc.Tools import grpc_message_to_json_string
+from common.proto.context_pb2 import (
+    Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList,
+    Context, ContextEvent, ContextId, ContextIdList, ContextList,
+    Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList,
+    Empty,
+    Link, LinkEvent, LinkId, LinkIdList, LinkList,
+    Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList,
+    Slice, SliceEvent, SliceId, SliceIdList, SliceList,
+    Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList)
+from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule)
+from common.proto.context_pb2_grpc import ContextServiceStub
+
+LOGGER = logging.getLogger(__name__)
+MAX_RETRIES = 15
+DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
+RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')
+
+class PolicyClient:
+    def __init__(self, host=None, port=None):
+        if not host:
+            host = get_service_host(ServiceNameEnum.POLICY)
+        if not port:
+            port = get_service_port_grpc(ServiceNameEnum.POLICY)
+        self.endpoint = '{:s}:{:s}'.format(str(host), str(port))
+        LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint)))
+        self.channel = None
+        self.stub = None
+        self.connect()
+        LOGGER.debug('Channel created')
+
+    def connect(self):
+        self.channel = grpc.insecure_channel(self.endpoint)
+        self.stub = ContextServiceStub(self.channel)
+
+    def close(self):
+        if self.channel is not None:
+            self.channel.close()
+        self.channel = None
+        self.stub = None
+
+    @RETRY_DECORATOR
+    def ListPolicyRuleIds(self, request: Empty) -> PolicyRuleIdList:
+        LOGGER.debug('ListPolicyRuleIds request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.ListPolicyRuleIds(request)
+        LOGGER.debug('ListPolicyRuleIds result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def ListPolicyRules(self, request: Empty) -> PolicyRuleList:
+        LOGGER.debug('ListPolicyRules request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.ListPolicyRules(request)
+        LOGGER.debug('ListPolicyRules result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def GetPolicyRule(self, request: PolicyRuleId) -> PolicyRule:
+        LOGGER.debug('GetPolicyRule request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.GetPolicyRule(request)
+        LOGGER.debug('GetPolicyRule result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def SetPolicyRule(self, request: PolicyRule) -> PolicyRuleId:
+        LOGGER.debug('SetPolicyRule request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.SetPolicyRule(request)
+        LOGGER.debug('SetPolicyRule result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
+    @RETRY_DECORATOR
+    def RemovePolicyRule(self, request: PolicyRuleId) -> Empty:
+        LOGGER.debug('RemovePolicyRule request: {:s}'.format(grpc_message_to_json_string(request)))
+        response = self.stub.RemovePolicyRule(request)
+        LOGGER.debug('RemovePolicyRule result: {:s}'.format(grpc_message_to_json_string(response)))
+        return response
diff --git a/src/policy/client/__init__.py b/src/policy/client/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7
--- /dev/null
+++ b/src/policy/client/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# 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.
+