diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 81bab136c4b98ad55c84b31e724968fea8597e26..f1d20736146d494afacbceb05a64ef8ee2ccd069 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,6 +1,6 @@
 FROM ros:jazzy-ros-base
 
-# Install some dependencies packages
+# Install dependencies for COWL and ROS2
 RUN apt update -q \
     && apt upgrade -q -y \
     && apt install -y --no-install-recommends \
@@ -8,11 +8,31 @@ RUN apt update -q \
     python3-pip \
     build-essential \
     libboost-all-dev \
-    xauth \
+    libxml2-dev \
+    libcurl4-openssl-dev \
+    libssl-dev \
+    cmake \
+    git \
+    flex \
+    bison \
+    doxygen \
     && apt clean \
     && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
 
-# Create workspace directory
+# Clone COWL repository with submodules
+RUN git clone --recursive https://github.com/sisinflab-swot/cowl.git /root/cowl
+
+# Build COWL
+WORKDIR /root/cowl
+RUN cmake -B cmake-build -DCMAKE_BUILD_TYPE=Release \
+    && cmake --build cmake-build --config Release \
+    && cmake --install cmake-build --prefix /root/cowl/install
+
+# Set environment variables to point to the COWL headers and libraries
+ENV COWL_INCLUDE_DIR=/root/cowl/install/include
+ENV COWL_LIB_DIR=/root/cowl/install/lib
+
+# Create workspace directory for ROS 2
 RUN mkdir -p /root/ros2_ws/src
 WORKDIR /root/ros2_ws
 
@@ -23,4 +43,4 @@ RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /root/.bashrc \
 # Setup entrypoint
 COPY ./ros_entrypoint.sh /
 ENTRYPOINT ["/ros_entrypoint.sh"]
-CMD ["bash"]
\ No newline at end of file
+CMD ["bash"]
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index b0cf01ca48c700124e9b27534e56f752a5db5d2f..047395f5922d3a5335276b9eb9eb4802fa856d4c 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -33,12 +33,9 @@
         }
     },
     "workspaceMount": "src=${localWorkspaceFolder},dst=/root/ros2_ws,type=bind",
-    //"workspaceMount": "src=/home/leolol/Code/dyknow-ros-2,dst=/root/ros2_ws,type=volume",
     "workspaceFolder": "/root/ros2_ws",
-    //"mounts": ["src=/home/leolol/Code/dyknow-ros-2,dst=/root/ros2_ws,type=bind"],
     "mounts": [],
     "runArgs": [
         "--net=host"
     ]
-  }
-  
\ No newline at end of file
+}
diff --git a/src/dyknow_cowl_watchdog/CMakeLists.txt b/src/dyknow_cowl_watchdog/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a5d16c7094c3b16115dd4a983057726bb1c24694
--- /dev/null
+++ b/src/dyknow_cowl_watchdog/CMakeLists.txt
@@ -0,0 +1,71 @@
+cmake_minimum_required(VERSION 3.8)
+project(dyknow_cowl_watchdog)
+
+find_package(ament_cmake REQUIRED)
+find_package(rclcpp REQUIRED)
+find_package(std_msgs REQUIRED)
+find_package(geometry_msgs REQUIRED)
+find_package(message_filters REQUIRED)
+find_package(rosidl_default_generators REQUIRED)
+
+set(msg_files
+    "msg/Lidar.msg"
+)
+
+rosidl_generate_interfaces(${PROJECT_NAME}
+    ${msg_files}
+    DEPENDENCIES std_msgs
+)
+
+ament_export_dependencies(rosidl_default_runtime)
+
+include_directories(
+    include
+    ${rclcpp_INCLUDE_DIRS}
+    ${std_msgs_INCLUDE_DIRS}
+    ${geometry_msgs_INCLUDE_DIRS}
+    ${message_filters_INCLUDE_DIRS}
+    ${rosidl_default_generators_INCLUDE_DIRS}
+    ${CMAKE_BINARY_DIR}/rosidl_generator_cpp  # Include path for generated message headers
+    /root/cowl/include
+    /root/cowl/lib/ulib/include
+)
+
+# Create the library
+add_library(dyknow_cowl_watchdog_lib SHARED
+    src/cowl_watcher.cpp
+)
+
+ament_target_dependencies(dyknow_cowl_watchdog_lib
+    rclcpp
+    std_msgs
+    geometry_msgs
+    message_filters
+)
+
+# Ensure that the generated message headers are available to this target
+add_dependencies(dyknow_cowl_watchdog_lib ${PROJECT_NAME}__rosidl_typesupport_cpp)
+
+install(TARGETS dyknow_cowl_watchdog_lib
+    EXPORT export_${PROJECT_NAME}
+    ARCHIVE DESTINATION lib
+    LIBRARY DESTINATION lib
+    RUNTIME DESTINATION bin
+)
+
+install(DIRECTORY include/
+    DESTINATION include/${PROJECT_NAME}/
+)
+
+# Install the CMake configuration files
+install(
+    FILES cmake/dyknow_cowl_watchdogConfig.cmake
+    DESTINATION share/${PROJECT_NAME}/cmake
+)
+
+install(
+    EXPORT export_${PROJECT_NAME}
+    DESTINATION share/${PROJECT_NAME}/cmake
+)
+
+ament_package()
diff --git a/src/dyknow_cowl_watchdog/cmake/dyknow_cowl_watchdogConfig.cmake b/src/dyknow_cowl_watchdog/cmake/dyknow_cowl_watchdogConfig.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..98c659f54e13bc5d7876cb813c3b2f85d8694aa8
--- /dev/null
+++ b/src/dyknow_cowl_watchdog/cmake/dyknow_cowl_watchdogConfig.cmake
@@ -0,0 +1,8 @@
+include(CMakeFindDependencyMacro)
+find_dependency(ament_cmake)
+find_dependency(rclcpp)
+find_dependency(std_msgs)
+find_dependency(geometry_msgs)
+find_dependency(message_filters)
+
+include("${CMAKE_CURRENT_LIST_DIR}/dyknow_cowl_watchdogTargets.cmake")
\ No newline at end of file
diff --git a/src/dyknow_cowl_watchdog/include/dyknow_cowl_watchdog/cowl_watcher.hpp b/src/dyknow_cowl_watchdog/include/dyknow_cowl_watchdog/cowl_watcher.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0600021821fe9d6927d630f199c6f9779131eff1
--- /dev/null
+++ b/src/dyknow_cowl_watchdog/include/dyknow_cowl_watchdog/cowl_watcher.hpp
@@ -0,0 +1,26 @@
+#ifndef INCLUDE_COWL_WATCHER_HPP_
+#define INCLUDE_COWL_WATCHER_HPP_
+
+#include "rclcpp/rclcpp.hpp"
+#include "std_msgs/msg/string.hpp"
+#include "cowl.h"
+
+class CowlWatcher : public rclcpp::Node {
+public:
+    CowlWatcher();
+    ~CowlWatcher();
+
+private:
+    CowlOntology *ontology_;
+    std::unordered_map<std::string, rclcpp::SubscriptionBase::SharedPtr> subscriptions_;
+    rclcpp::TimerBase::SharedPtr discovery_timer_;
+    rclcpp::CallbackGroup::SharedPtr callback_group_;
+    std::vector<std::string> active_topics_;
+    
+
+    void discoverTopics();
+    void subscribeToTopic(const std::string &topic_name);
+    void processMessage(const std::string &topic, const std::string &data);
+};
+
+#endif /*  INCLUDE_COWL_WATCHER_HPP_ */
\ No newline at end of file
diff --git a/src/dyknow_cowl_watchdog/msg/Lidar.msg b/src/dyknow_cowl_watchdog/msg/Lidar.msg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/dyknow_cowl_watchdog/ont/my_ontology.ttl b/src/dyknow_cowl_watchdog/ont/my_ontology.ttl
new file mode 100644
index 0000000000000000000000000000000000000000..c8f1675bc29e8ea97051c43008d9f77b8e25170e
--- /dev/null
+++ b/src/dyknow_cowl_watchdog/ont/my_ontology.ttl
@@ -0,0 +1,16 @@
+@prefix ex: <http://example.org#> .
+
+# Define sensor classes
+ex:LIDAR a rdfs:Class .
+ex:Gyroscope a rdfs:Class .
+ex:IR_Sensor a rdfs:Class .
+
+# Define properties (sensor data)
+ex:hasDistance a rdf:Property ; rdfs:domain ex:LIDAR ; rdfs:range xsd:float .
+ex:hasAngle a rdf:Property ; rdfs:domain ex:Gyroscope ; rdfs:range xsd:float .
+ex:hasTemperature a rdf:Property ; rdfs:domain ex:IR_Sensor ; rdfs:range xsd:float .
+
+# Instances of sensors
+ex:sensor1 a ex:LIDAR ; ex:hasDistance "10.5" .
+ex:sensor2 a ex:Gyroscope ; ex:hasAngle "45.0" .
+ex:sensor3 a ex:IR_Sensor ; ex:hasTemperature "22.5" .
diff --git a/src/dyknow_cowl_watchdog/package.xml b/src/dyknow_cowl_watchdog/package.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cf1d17ea438ac2110b58adfe207aa112ca8b1416
--- /dev/null
+++ b/src/dyknow_cowl_watchdog/package.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
+<package format="3">
+  <name>dyknow_cowl_watchdog</name>
+  <version>0.0.0</version>
+  <description>TODO: Package description</description>
+  <maintainer email="root@todo.todo">root</maintainer>
+  <license>TODO: License declaration</license>
+
+  <buildtool_depend>ament_cmake</buildtool_depend>
+
+  <test_depend>ament_lint_auto</test_depend>
+  <test_depend>ament_lint_common</test_depend>
+
+  <build_depend>rosidl_default_generators</build_depend>
+  <build_depend>std_msgs</build_depend>
+
+  <depend>builtin_interfaces</depend>
+  <exec_depend>rosidl_default_runtime</exec_depend>
+  <exec_depend>std_msgs</exec_depend>
+
+  <buildtool_depend>rosidl_default_generators</buildtool_depend>
+  <exec_depend>rosidl_default_runtime</exec_depend>
+  <member_of_group>rosidl_interface_packages</member_of_group>
+
+  <export>
+    <build_type>ament_cmake</build_type>
+  </export>
+</package>
diff --git a/src/dyknow_cowl_watchdog/src/cowl_watcher.cpp b/src/dyknow_cowl_watchdog/src/cowl_watcher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba36dda3fd9c23bf94d51b693b9875d872e07704
--- /dev/null
+++ b/src/dyknow_cowl_watchdog/src/cowl_watcher.cpp
@@ -0,0 +1,77 @@
+#include "dyknow_cowl_watchdog/cowl_watcher.hpp"
+#include <unordered_map>
+
+CowlWatcher::CowlWatcher() : Node("cowl_watcher") {
+    cowl_init();
+    ontology_ = cowl_ontology_new();
+
+    // Use Reentrant callback group for parallel processing
+    callback_group_ = this->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
+
+    // Timer to check for new topics every 5 seconds
+    discovery_timer_ = this->create_wall_timer(
+        std::chrono::seconds(5),
+        std::bind(&CowlWatcher::discoverTopics, this)
+    );
+
+    // Initial topic discovery
+    discoverTopics();
+}
+
+CowlWatcher::~CowlWatcher() {
+    cowl_ontology_free(ontology_);
+}
+
+void CowlWatcher::discoverTopics() {
+    auto topic_names_and_types = this->get_topic_names_and_types();
+    for (const auto &topic : topic_names_and_types) {
+        const std::string &topic_name = topic.first;
+        if(std::find(active_topics_.begin(), active_topics_.end(), topic_name) == active_topics_.end()) {
+            RCLCPP_INFO(this->get_logger(), "New topic detected: %s", topic_name.c_str());
+            subscribeToTopic(topic_name);
+            active_topics_.push_back(topic_name);
+        }
+    }
+}
+
+void CowlWatcher::subscribeToTopic(const std::string &topic_name) {
+    auto sub = this->create_subscription<std_msgs::msg::String>(
+        topic_name, 10, 
+        [this, topic_name](const std_msgs::msg::String::SharedPtr msg) {
+            processMessage(topic_name, msg->data);
+        },
+        rclcpp::SubscriptionOptions().callback_group(callback_group_)
+    );
+    subscriptions_[topic_name] = sub;
+}
+
+void CowlWatcher::processMessage(const std::string &topic, const std::string &data) {
+    // Convert message into RDF triple
+    std::string subject = "ex:" + topic;
+    std::string predicate = "ex::hasValue";
+    std::string object = '"' + data + '"';
+
+    // Create Cowl strings for subject, predicate, and object
+    CowlString *subject_str = cowl_string_new(subject.c_str());
+    CowlString *predicate_str = cowl_string_new(predicate.c_str());
+    CowlString *object_str = cowl_string_new(object.c_str());
+
+    CowlAxiom* axiom = cowl_axiom_new(subject_str, predicate_str, object_str);
+    cowl_ontology_add_axiom(ontology_, axiom);
+    RCLCPP_INFO(this->get_logger(), "Added RDF: (%s, %s, %s)", subject.c_str(), predicate.c_str(), object.c_str());
+
+    cowl_release(subject_str);
+    cowl_release(predicate_str);
+    cowl_release(object_str);
+}
+
+
+int main(int argc, char **argv) {
+    rclcpp::init(argc, argv);
+    auto node = std::make_shared<CowlWatcher>();
+    rclcpp::executors::MultiThreadedExecutor executor;
+    executor.add_node(node);
+    executor.spin();
+    rclcpp::shutdown();
+    return 0;
+}
\ No newline at end of file