diff --git a/cubicweb-rodolf/.gitlab-ci.yml b/cubicweb-rodolf/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmLy5naXRsYWItY2kueW1s
--- /dev/null
+++ b/cubicweb-rodolf/.gitlab-ci.yml
@@ -0,0 +1,24 @@
+---
+default:
+  image: python:3.7
+
+include:
+  - project: "open-source/gitlab-ci-templates"
+    ref: "branch/default"
+    file:
+      - "templates/no-duplicated-ci-pipelines.yml"  # use workflow to avoid duplicated pipelines
+      - "templates/lint/flake8.yml"  # will do the equivalent of 'tox -e flake8'
+      - "templates/lint/black.yml"  # will do the equivalent of 'tox -e black'
+      - "templates/lint/check-manifest.yml"  # will do the equivalent of 'tox -e check-manifest'
+      - "templates/lint/yamllint.yml"  # will do the equivalent of 'tox -e yamllint'
+      - "templates/tests/py3.yml"  # will do the equivalent of 'tox -e py3'
+      - "templates/create-release-on-heptapod.yml"  # this will create a release on heptapod
+      - "templates/upload-to-pypi.yml"  # on a new mercurial tag (expected to be done with release-new), will push a release on pypi
+      # - "templates/lint/mypy.yml"  # will do the equivalent of 'tox -e mypy'
+      # - "templates/upload-python-package-to-heptapod.yml"  # upload the package to heptapod registry on new tag
+
+stages:
+  - lint
+  - tests
+  - release
+  - publish
diff --git a/cubicweb-rodolf/.hgignore b/cubicweb-rodolf/.hgignore
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmLy5oZ2lnbm9yZQ==
--- /dev/null
+++ b/cubicweb-rodolf/.hgignore
@@ -0,0 +1,28 @@
+syntax: glob
+*~
+#*
+*.bak
+*.old
+*.pyo
+*.pyc
+*.cache
+*.swp
+*.swo
+*.mo
+*.lock
+*.sqlite
+__pycache__
+*.egg-info
+.tox/
+.pybuild
+.cache
+.coverage
+.pytest-cache
+build/
+dist/
+node_modules/
+.mypy_cache/
+test/data/database/
+test/data/ldapdb/
+test/data/uicache/
+docs/_build
diff --git a/cubicweb-rodolf/.yamllint b/cubicweb-rodolf/.yamllint
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmLy55YW1sbGludA==
--- /dev/null
+++ b/cubicweb-rodolf/.yamllint
@@ -0,0 +1,9 @@
+ignore: |
+  .tox/
+  node_modules/
+
+extends: default
+
+rules:
+  document-start: disable
+  line-length: disable
diff --git a/cubicweb-rodolf/Dockerfile b/cubicweb-rodolf/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL0RvY2tlcmZpbGU=
--- /dev/null
+++ b/cubicweb-rodolf/Dockerfile
@@ -0,0 +1,9 @@
+FROM logilab/cubicweb-base:latest
+
+COPY --chown=cubicweb . /src
+# If you pinned dependencies in a file (requirements.txt), add it here with
+# -r /src/requirements.txt
+RUN pip install -e /src
+
+USER cubicweb
+RUN docker-cubicweb-helper create-instance
diff --git a/cubicweb-rodolf/MANIFEST.in b/cubicweb-rodolf/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL01BTklGRVNULmlu
--- /dev/null
+++ b/cubicweb-rodolf/MANIFEST.in
@@ -0,0 +1,11 @@
+include *.py
+include */*.py
+recursive-include cubicweb_rodolf *.py
+recursive-include cubicweb_rodolf/data *.gif *.png *.ico *.css *.js
+recursive-include cubicweb_rodolf/i18n *.po
+recursive-include cubicweb_rodolf/wdoc *
+recursive-include test/data bootstrap_cubes *.py
+include *.ini
+exclude .gitlab-ci.yml
+exclude .yamllint
+exclude Dockerfile
diff --git a/cubicweb-rodolf/README.rst b/cubicweb-rodolf/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL1JFQURNRS5yc3Q=
--- /dev/null
+++ b/cubicweb-rodolf/README.rst
@@ -0,0 +1,57 @@
+rodolf
+=============================================================
+
+RDF data production monitoring (RODOLF)
+
+Installation
+------------
+
+Open the project in a terminal and run::
+
+    pip install -e .
+
+This will install the cube in your active virtual environment
+as ``cubicweb-rodolf``.
+
+The following sections indicate additional steps when you
+install this cube as a dependency or as an instance.
+
+As a dependency
+~~~~~~~~~~~~~~~
+
+If you plan to use this cube as a dependency for your own cube,
+add it to your ``__pkginfo__.py`` as follows::
+
+    __depends__ = {
+        # ... Your previous dependencies
+        "cubicweb-rodolf": None,
+    }
+
+If the target cube is already used as an instance, you need to migrate it
+with the help of its python shell (replace ``YOUR_INSTANCE_NAME`` by your instance name)::
+
+    cubicweb-ctl shell YOUR_INSTANCE_NAME
+
+In the python prompt, enter the following command::
+
+    add_cube("rodolf")
+
+Press ``Ctrl-D`` then restart your instance.
+The cube should now be available in your instance.
+
+As an instance
+~~~~~~~~~~~~~~
+
+If you plan to use this cube directly as an instance, create and start
+your instance with the following commands (replace ``rodolf-instance``
+by the name of your choice)::
+
+    cubicweb-ctl create rodolf rodolf-instance
+    cubicweb-ctl start -D rodolf-instance
+
+
+Learn More
+----------
+
+Visit the `official documentation <https://cubicweb.readthedocs.io/en/4.5.2>`_
+to learn more about CubicWeb.
diff --git a/cubicweb-rodolf/cubicweb_rodolf/__init__.py b/cubicweb-rodolf/cubicweb_rodolf/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/__init__.py
@@ -0,0 +1,8 @@
+"""cubicweb-rodolf application package
+
+RDF data production monitoring (RODOLF)
+"""
+
+
+def includeme(config):
+    pass
diff --git a/cubicweb-rodolf/cubicweb_rodolf/__pkginfo__.py b/cubicweb-rodolf/cubicweb_rodolf/__pkginfo__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9fX3BrZ2luZm9fXy5weQ==
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/__pkginfo__.py
@@ -0,0 +1,24 @@
+"""cubicweb-rodolf application packaging information"""
+
+
+modname = "cubicweb_rodolf"
+distname = "cubicweb-rodolf"
+
+numversion = (0, 1, 0)
+version = ".".join(str(num) for num in numversion)
+
+license = "LGPL"
+author = "LOGILAB S.A. (Paris, FRANCE)"
+author_email = "contact@logilab.fr"
+description = "RDF data production monitoring (RODOLF)"
+web = "https://forge.extranet.logilab.fr/cubicweb/cubes/rodolf"
+
+__depends__ = {"cubicweb": ">= 4.5.2, < 5.0.0"}
+__recommends__ = {}
+
+classifiers = [
+    "Environment :: Web Environment",
+    "Framework :: CubicWeb",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: JavaScript",
+]
diff --git a/cubicweb-rodolf/cubicweb_rodolf/entities.py b/cubicweb-rodolf/cubicweb_rodolf/entities.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9lbnRpdGllcy5weQ==
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/entities.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+# copyright 2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact https://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""cubicweb-rodolf entity's classes"""
+
+from datetime import datetime, timedelta
+import pytz
+from cubicweb import NoResultError
+
+from cubicweb.entities import AnyEntity
+
+
+TIMEDELTA_REFRESH = {
+    "daily": timedelta(days=1),
+    "weekly": timedelta(weeks=1),
+    "monthly": timedelta(days=30),
+}
+
+
+class DataService(AnyEntity):
+    __regid__ = "DataService"
+
+    @property
+    def refresh_timedelta(self):
+        return TIMEDELTA_REFRESH[self.refresh_period]
+
+
+class ImportProcedure(AnyEntity):
+    __regid__ = "ImportProcedure"
+
+    @property
+    def import_recipe_to_launch(self):
+        for recipe in self.import_recipes:
+            if not recipe.must_be_launched(self):
+                continue
+            yield recipe
+
+    def create_needed_import_process(self):
+        for recipe in self.import_recipe_to_launch:
+            import_process = self._cw.create_entity(
+                "ImportProcess",
+                import_recipe=(recipe,),
+                import_procedure=(self,),
+            )
+            print(
+                f"ImportProcess for {self.sparql_endpoint} (recipe : {recipe.name})"
+                f" created ({import_process.eid})"
+            )
+            yield import_process
+
+
+class ImportRecipe(AnyEntity):
+    __regid__ = "ImportRecipe"
+
+    def must_be_launched(self, import_procedure):
+        try:
+            import_process = self._cw.execute(
+                "Any I ORDERBY D DESC LIMIT 1 WHERE "
+                "I is ImportProcess, "
+                "I import_recipe %(recipe_eid)s, "
+                "I import_procedure %(import_procedure)s, "
+                "I in_state S, S name 'successful', "
+                "I creation_date D",
+                {
+                    "recipe_eid": self.eid,
+                    "import_procedure": import_procedure.eid,
+                },
+            ).one()
+            dataservice = import_process.import_recipe[0].dataservice[0]
+            if (
+                datetime.now(tz=pytz.utc) - import_process.creation_date
+                > dataservice.refresh_timedelta
+            ):
+                return True
+        except NoResultError:
+            return True
+        return False
diff --git a/cubicweb-rodolf/cubicweb_rodolf/hooks.py b/cubicweb-rodolf/cubicweb_rodolf/hooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9ob29rcy5weQ==
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/hooks.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+# copyright 2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact https://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""cubicweb-rodolf specific hooks and operations"""
diff --git a/cubicweb-rodolf/cubicweb_rodolf/i18n/en.po b/cubicweb-rodolf/cubicweb_rodolf/i18n/en.po
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9pMThuL2VuLnBv
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/i18n/en.po
@@ -0,0 +1,9 @@
+msgid ""
+msgstr ""
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: pygettext.py 1.5\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
+
diff --git a/cubicweb-rodolf/cubicweb_rodolf/i18n/es.po b/cubicweb-rodolf/cubicweb_rodolf/i18n/es.po
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9pMThuL2VzLnBv
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/i18n/es.po
@@ -0,0 +1,9 @@
+msgid ""
+msgstr ""
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: pygettext.py 1.5\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
+
diff --git a/cubicweb-rodolf/cubicweb_rodolf/i18n/fr.po b/cubicweb-rodolf/cubicweb_rodolf/i18n/fr.po
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9pMThuL2ZyLnBv
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/i18n/fr.po
@@ -0,0 +1,9 @@
+msgid ""
+msgstr ""
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: pygettext.py 1.5\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
+
diff --git a/cubicweb-rodolf/cubicweb_rodolf/migration/postcreate.py b/cubicweb-rodolf/cubicweb_rodolf/migration/postcreate.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9taWdyYXRpb24vcG9zdGNyZWF0ZS5weQ==
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/migration/postcreate.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# copyright 2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact https://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""cubicweb-rodolf postcreate script, executed at instance creation time or when
+the cube is added to an existing instance.
+
+You could setup site properties or a workflow here for example.
+"""
+
+# Example of site property change
+# set_property('ui.site-title', "<sitename>")
+
+from cubicweb_rodolf.workflows import create_import_process_workflow
+
+
+create_import_process_workflow(add_workflow)
diff --git a/cubicweb-rodolf/cubicweb_rodolf/schema.py b/cubicweb-rodolf/cubicweb_rodolf/schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi9zY2hlbWEucHk=
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/schema.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+# copyright 2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact https://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""cubicweb-rodolf schema"""
+
+from yams.buildobjs import Boolean, EntityType, String, SubjectRelation
+from cubicweb.schema import WorkflowableEntityType
+from cubicweb_file.schema import File
+
+
+class SHACLFile(EntityType):
+    file = File()
+    name = String()
+
+
+class ImportProcedure(EntityType):
+    name = String()
+    sparql_endpoint = String(required=True)
+    ontology_file = File()
+    shacl_files = SubjectRelation("SHACLFile", cardinality="**")
+    import_recipes = SubjectRelation("ImportRecipe", cardinality="**")
+    activated = Boolean(required=True, default=True)
+
+
+class DataService(EntityType):
+    name = String(required=True)
+    data_url = String(required=True)
+    refresh_period = String(
+        required=True,
+        vocabulary=["daily", "weekly", "monthly"],
+        default="daily",
+    )
+    description = String()
+
+
+class ImportRecipe(EntityType):
+    name = String(required=True)
+    dataservice = SubjectRelation("DataService", cardinality="1*")
+    process_type = String(
+        required=True,
+        vocabulary=["default", "default-dryrun"],
+        default="default",
+    )
+
+
+class ImportProcess(WorkflowableEntityType):
+    import_recipe = SubjectRelation("ImportRecipe", cardinality="1*")
+    import_procedure = SubjectRelation("ImportProcedure", cardinality="1*")
+    log = File()
+    has_output_dataset = File()
+    import_report = File()
diff --git a/cubicweb-rodolf/cubicweb_rodolf/views.py b/cubicweb-rodolf/cubicweb_rodolf/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi92aWV3cy5weQ==
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/views.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+# copyright 2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact https://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""cubicweb-rodolf views/forms/actions/components for web ui"""
diff --git a/cubicweb-rodolf/cubicweb_rodolf/workflows.py b/cubicweb-rodolf/cubicweb_rodolf/workflows.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL2N1Ymljd2ViX3JvZG9sZi93b3JrZmxvd3MucHk=
--- /dev/null
+++ b/cubicweb-rodolf/cubicweb_rodolf/workflows.py
@@ -0,0 +1,12 @@
+
+def create_import_process_workflow(add_workflow):
+    workflow = add_workflow("import process workflow", "ImportProcess")
+    waiting = workflow.add_state("waiting", initial=True)
+    ongoing = workflow.add_state("ongoing")
+    error = workflow.add_state("error")
+    successful = workflow.add_state("successful")
+
+    workflow.add_transition("starts", (waiting,), ongoing)
+    workflow.add_transition("fails", (ongoing,), error)
+    workflow.add_transition("success", (ongoing,), successful)
+    return workflow
diff --git a/cubicweb-rodolf/setup.py b/cubicweb-rodolf/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL3NldHVwLnB5
--- /dev/null
+++ b/cubicweb-rodolf/setup.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# pylint: disable=W0142,W0403,W0404,W0613,W0622,W0622,W0704,R0904,C0103,E0611
+#
+# copyright 2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact https://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This file is part of a cubicweb-rodolf.
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""cubicweb_rodolf setup module using data from
+cubicweb_rodolf/__pkginfo__.py file
+"""
+
+from os.path import join, dirname
+
+from setuptools import find_packages, setup
+
+here = dirname(__file__)
+
+# load metadata from the __pkginfo__.py file so there is no risk of conflict
+# see https://packaging.python.org/en/latest/single_source_version.html
+pkginfo = join(here, "cubicweb_rodolf", "__pkginfo__.py")
+__pkginfo__ = {}
+with open(pkginfo) as f:
+    exec(f.read(), __pkginfo__)
+
+# get required metadatas
+distname = __pkginfo__["distname"]
+version = __pkginfo__["version"]
+license = __pkginfo__["license"]
+description = __pkginfo__["description"]
+web = __pkginfo__["web"]
+author = __pkginfo__["author"]
+author_email = __pkginfo__["author_email"]
+classifiers = __pkginfo__["classifiers"]
+
+with open(join(here, "README.rst")) as f:
+    long_description = f.read()
+
+# get optional metadatas
+data_files = __pkginfo__.get("data_files", None)
+dependency_links = __pkginfo__.get("dependency_links", ())
+
+requires = {}
+for entry in ("__depends__",):  # "__recommends__"):
+    requires.update(__pkginfo__.get(entry, {}))
+install_requires = [
+    "{0} {1}".format(d, v and v or "").strip() for d, v in requires.items()
+]
+
+
+setup(
+    name=distname,
+    version=version,
+    license=license,
+    description=description,
+    long_description=long_description,
+    long_description_content_type="text/x-rst",
+    author=author,
+    author_email=author_email,
+    url=web,
+    classifiers=classifiers,
+    packages=find_packages(exclude=["test"]),
+    install_requires=install_requires,
+    include_package_data=True,
+    entry_points={
+        "cubicweb.cubes": [
+            "rodolf=cubicweb_rodolf",
+        ],
+    },
+    zip_safe=False,
+)
diff --git a/cubicweb-rodolf/test/data/bootstrap_cubes b/cubicweb-rodolf/test/data/bootstrap_cubes
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL3Rlc3QvZGF0YS9ib290c3RyYXBfY3ViZXM=
--- /dev/null
+++ b/cubicweb-rodolf/test/data/bootstrap_cubes
@@ -0,0 +1,1 @@
+rodolf
diff --git a/cubicweb-rodolf/test/test_rodolf.py b/cubicweb-rodolf/test/test_rodolf.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL3Rlc3QvdGVzdF9yb2RvbGYucHk=
--- /dev/null
+++ b/cubicweb-rodolf/test/test_rodolf.py
@@ -0,0 +1,29 @@
+# copyright 2023 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact https://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+from cubicweb.devtools import testlib
+
+
+class DefaultTC(testlib.CubicWebTC):
+    def test_something(self):
+        self.skipTest("this cube has no test")
+
+
+if __name__ == "__main__":
+    from unittest import main
+
+    main()
diff --git a/cubicweb-rodolf/tox.ini b/cubicweb-rodolf/tox.ini
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_Y3ViaWN3ZWItcm9kb2xmL3RveC5pbmk=
--- /dev/null
+++ b/cubicweb-rodolf/tox.ini
@@ -0,0 +1,77 @@
+[tox]
+envlist = py3,flake8,check-manifest,black
+
+[testenv]
+deps =
+  pytest
+commands =
+  {envpython} -m pytest {posargs:test}
+
+[testenv:flake8]
+basepython = python3
+skip_install = true
+deps =
+  flake8 >= 3.6
+commands = flake8
+
+[testenv:check-manifest]
+skip_install = true
+deps =
+  check-manifest
+commands =
+  {envpython} -m check_manifest {toxinidir}
+
+[testenv:mypy]
+deps =
+  mypy >= 0.761
+commands = mypy --ignore-missing-imports cubicweb_rodolf
+
+[testenv:black]
+basepython = python3
+skip_install = true
+deps =
+  black >= 22.12
+commands = black --check .
+
+[testenv:black-run]
+basepython = python3
+skip_install = true
+deps =
+  black >= 22.12
+commands = black .
+
+[testenv:pypi-publish]
+basepython = python3
+skip_install = true
+allowlist_externals = rm
+deps =
+  twine
+passenv =
+  TWINE_USERNAME
+  TWINE_PASSWORD
+commands =
+  rm -rf build dist .egg .egg-info
+  python3 setup.py sdist bdist_wheel
+  twine check dist/*
+  twine upload --skip-existing dist/*
+
+[testenv:yamllint]
+skip_install = true
+deps = yamllint
+commands =
+  yamllint .
+
+[testenv:release-new]
+basepython = python3
+skip_install = true
+passenv =
+  EDITOR
+deps =
+  release-new
+commands = release-new {posargs:-r auto}
+
+[flake8]
+basepython = python3
+ignore = W503, E203, E731, E231
+max-line-length = 100
+exclude = cubicweb_rodolf/migration/*,test/data/*,.tox/*
diff --git a/requirement.txt b/requirement.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ec90aa5c440f84108e37ead3f96740ca8c2e903f_cmVxdWlyZW1lbnQudHh0
--- /dev/null
+++ b/requirement.txt
@@ -0,0 +1,2 @@
+cubicweb[postgresql]
+cubicweb-file