diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1d17dae13b53adda563547053eb79233b236f797
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.venv
diff --git a/dns_unbound_cache_reader/__init__.py b/dns_unbound_cache_reader/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e2ad801199630d9e1715a3b94b7640cfb8f5843
--- /dev/null
+++ b/dns_unbound_cache_reader/__init__.py
@@ -0,0 +1 @@
+from dns_unbound_cache_reader import read_unbound_cache
diff --git a/dns_unbound_cache_reader/dns_unbound_cache_reader.py b/dns_unbound_cache_reader/dns_unbound_cache_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8ce74a1b76f09bafe1a0e86f27166ce067c1e04
--- /dev/null
+++ b/dns_unbound_cache_reader/dns_unbound_cache_reader.py
@@ -0,0 +1,38 @@
+import subprocess
+from fabric import Connection, Config
+
+
+## Global variables
+localhost = [
+    "localhost",
+    "127.0.0.1"
+]
+cmd = "unbound-control dump_cache"
+
+
+def read_unbound_cache(host: str = "127.0.0.1"):
+    """
+    Read the Unbound DNS cache and return it as a dictionary.
+
+    Args:
+        host (str): IP address of the Unbound DNS server. Default is localhost.
+    """
+    if host in localhost:
+
+        ## Unbound runs on localhost
+        proc = subprocess.run(cmd.split(), capture_output=True)
+        dns_cache = proc.stdout.decode().strip().split("\n")
+    
+
+    else:
+
+        ## Unbound runs on a remote host
+        
+        # SSH connection with remote host
+        ssh_config = Config(overrides={"run": {"hide": True}})
+        remote = Connection(host, config=ssh_config)
+        result = remote.run(cmd)
+        dns_cache = result.stdout.strip().split("\n")
+    
+
+    return dns_cache
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..93dd5f6f7f6ce640b165459a48821c8b77aca321
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,55 @@
+[build-system]
+requires = ["setuptools", "wheel"]
+build-backend = "setuptools.build_meta"
+
+
+[project]
+name = "dns-unbound-cache-reader"
+version = "0.1.0"
+description = "Read DNS cache from unbound"
+readme = "README.md"
+requires-python = ">=3.8"
+license = {file = "LICENSE"}
+keywords = ["network", "dns", "unbound"]
+authors = [
+  {name = "François De Keersmaeker", email = "francois.dekeersmaeker@uclouvain.be" }
+]
+maintainers = [
+  {name = "François De Keersmaeker", email = "francois.dekeersmaeker@uclouvain.be" }
+]
+
+# Classifiers help users find your project by categorizing it.
+#
+# For a list of valid classifiers, see https://pypi.org/classifiers/
+classifiers = [
+  # How mature is this project? Common values are
+  #   3 - Alpha
+  #   4 - Beta
+  #   5 - Production/Stable
+  "Development Status :: 3 - Alpha",
+
+  # Pick your license as you wish
+  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
+
+  # Specify the Python versions you support here. In particular, ensure
+  # that you indicate you support Python 3. These classifiers are *not*
+  # checked by "pip install". See instead "requires-python" key in this file.
+  "Programming Language :: Python :: 3",
+  "Programming Language :: Python :: 3.8",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
+  "Programming Language :: Python :: 3 :: Only",
+
+  "Operating System :: OS Independent"
+]
+
+# TODO: update dependencies
+dependencies = [
+    "fabric"
+]
+
+[project.urls]
+"Homepage" = "https://forge.uclouvain.be/smart-home-network-security/dns-unbound-cache-reader"
+"Source" = "https://forge.uclouvain.be/smart-home-network-security/dns-unbound-cache-reader"
diff --git a/test.py b/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b6aa9a69764ae0f6ec03fc0ddf234c18c4bae54
--- /dev/null
+++ b/test.py
@@ -0,0 +1,3 @@
+from dns_unbound_cache_reader import read_unbound_cache
+
+read_unbound_cache