diff --git a/.copier-answers.yml b/.copier-answers.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2c24d6fd9dbe74ab26e846eb8829435ef90257b4
--- /dev/null
+++ b/.copier-answers.yml
@@ -0,0 +1,11 @@
+# Changes here will be overwritten by Copier
+_commit: v0.1.6
+_src_path: https://gitlab.softwareheritage.org/swh/devel/swh-py-template.git
+description: Software Heritage deposit server
+distribution_name: swh-deposit
+have_cli: true
+have_workers: true
+package_root: swh/deposit
+project_name: swh.deposit
+python_minimal_version: '3.7'
+readme_format: rst
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index b29847ee828c3ba4c1b5887b6fceb350ef019f0a..7c2d23bed18b16832a1f7fa40f01912b7c66bdaf 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -1,5 +1,6 @@
-# Enable black
+# python: Reformat code with black
 f5426d6722826972e2d611d4e7040abbf40c49a1
-
-# python: Reformat code with black 22.3.0
 8a006aeebf7d0cf52abc71b07cd560cbd098349e
+7b0fac22d29db6ad27cb650f835cae2f8786ad70
+# isort
+9c0d0496369828c8fad882d5d676978fb76105f8
diff --git a/.gitignore b/.gitignore
index 52e95c367fba61a76449ebcafb36f31c15251786..d44f090faaff2efa7c3dfa14d2a402ae0d6b4eaf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,26 +1,16 @@
+*.egg-info/
 *.pyc
-*.sw?
-*~
-/.coverage
-/.coverage.*
+.coverage
 .eggs/
+.hypothesis
+.mypy_cache
+.tox
 __pycache__
-*.egg-info/
-version.txt
 build/
 dist/
-/analysis.org
-/swh/deposit/fixtures/private_data.yaml
-/swh/deposit.json
-/test.json
-/swh/test
-db.sqlite3
-/.noseids
-*.tgz
-*.zip
-*.tar.gz
-*.tar.bz2
-*.tar.lzma
-.tox/
-.mypy_cache/
-.hypothesis/
+# these are symlinks created by a hook in swh-docs' main sphinx conf.py
+docs/README.rst
+docs/README.md
+# this should be a symlink for people who want to build the sphinx doc
+# without using tox, generally created by the swh-env/bin/update script
+docs/Makefile.sphinx
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4b2ba24d114f9be8e37f46aa159fad822b018e94..8dab855808a735a52157a62526db89a7183b0163 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,14 +2,24 @@ exclude: ^swh/deposit/tests/data/atom/.*$
 
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.3.0
+    rev: v4.4.0
     hooks:
       - id: trailing-whitespace
       - id: check-json
       - id: check-yaml
 
+  - repo: https://github.com/python/black
+    rev: 23.1.0
+    hooks:
+      - id: black
+
+  - repo: https://github.com/PyCQA/isort
+    rev: 5.12.0
+    hooks:
+      - id: isort
+
   - repo: https://github.com/pycqa/flake8
-    rev: 5.0.4
+    rev: 6.0.0
     hooks:
       - id: flake8
         additional_dependencies: [flake8-bugbear==22.9.23]
@@ -21,6 +31,9 @@ repos:
         name: Check source code spelling
         args: [-L sur]
         stages: [commit]
+      - id: codespell
+        name: Check commit message spelling
+        stages: [commit-msg]
 
   - repo: local
     hooks:
@@ -32,12 +45,3 @@ repos:
         language: system
         types: [python]
 
-  - repo: https://github.com/PyCQA/isort
-    rev: 5.11.5
-    hooks:
-      - id: isort
-
-  - repo: https://github.com/python/black
-    rev: 22.10.0
-    hooks:
-      - id: black
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 03d5ef9def111754393c3c27a2ab92f76fd98fdb..0000000000000000000000000000000000000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,12 +0,0 @@
-include Makefile
-include requirements*.txt
-include version.txt
-recursive-include swh/deposit/static *
-recursive-include swh/deposit/fixtures *
-recursive-include swh/deposit/templates *
-recursive-include swh/deposit/tests/data *
-recursive-include swh/deposit/tests/*/data *
-recursive-include swh/deposit/xsd *
-recursive-include swh py.typed
-include tox.ini
-include pytest.ini
diff --git a/PKG-INFO b/PKG-INFO
index 32282eb0a867652f0d610bcc98e9b558f5c17634..06164c629b2823b365fdbb3483472ea9a044eb4e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,21 +1,20 @@
 Metadata-Version: 2.1
 Name: swh.deposit
-Version: 2.0.0
-Summary: Software Heritage Deposit Server
-Home-page: https://forge.softwareheritage.org/source/swh-deposit/
-Author: Software Heritage developers
-Author-email: swh-devel@inria.fr
-Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest
+Version: 2.1.1
+Summary: Software Heritage deposit server
+Author-email: Software Heritage developers <swh-devel@inria.fr>
+Project-URL: Homepage, https://gitlab.softwareheritage.org/swh/devel/swh-deposit
+Project-URL: Bug Reports, https://gitlab.softwareheritage.org/swh/devel/swh-deposit/-/issues
 Project-URL: Funding, https://www.softwareheritage.org/donate
-Project-URL: Source, https://forge.softwareheritage.org/source/swh-deposit
 Project-URL: Documentation, https://docs.softwareheritage.org/devel/swh-deposit/
+Project-URL: Source, https://gitlab.softwareheritage.org/swh/devel/swh-deposit.git
 Classifier: Programming Language :: Python :: 3
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
 Classifier: Operating System :: OS Independent
 Classifier: Development Status :: 5 - Production/Stable
 Requires-Python: >=3.7
-Description-Content-Type: text/markdown
+Description-Content-Type: text/x-rst
 License-File: LICENSE
 License-File: AUTHORS
 Requires-Dist: click
@@ -24,6 +23,21 @@ Requires-Dist: requests
 Requires-Dist: sentry-sdk
 Requires-Dist: swh.core[http]>=0.4
 Requires-Dist: swh.model>=4.4.0
+Provides-Extra: server
+Requires-Dist: django; extra == "server"
+Requires-Dist: djangorestframework; extra == "server"
+Requires-Dist: psycopg2; extra == "server"
+Requires-Dist: setuptools; extra == "server"
+Requires-Dist: xmlschema; extra == "server"
+Requires-Dist: pymemcache; extra == "server"
+Requires-Dist: swh.core[http]>=0.4; extra == "server"
+Requires-Dist: swh.loader.core>=0.0.71; extra == "server"
+Requires-Dist: swh.scheduler>=0.7.0; extra == "server"
+Requires-Dist: swh.model>=0.3.8; extra == "server"
+Requires-Dist: swh.auth[django]>=0.5.3; extra == "server"
+Requires-Dist: swh.storage>=0.28.0; extra == "server"
+Provides-Extra: azure
+Requires-Dist: django-storages[azure]; extra == "azure"
 Provides-Extra: testing
 Requires-Dist: pytest; extra == "testing"
 Requires-Dist: pytest-django; extra == "testing"
@@ -49,21 +63,7 @@ Requires-Dist: swh.scheduler>=0.7.0; extra == "testing"
 Requires-Dist: swh.model>=0.3.8; extra == "testing"
 Requires-Dist: swh.auth[django]>=0.5.3; extra == "testing"
 Requires-Dist: swh.storage>=0.28.0; extra == "testing"
-Provides-Extra: server
-Requires-Dist: django; extra == "server"
-Requires-Dist: djangorestframework; extra == "server"
-Requires-Dist: psycopg2; extra == "server"
-Requires-Dist: setuptools; extra == "server"
-Requires-Dist: xmlschema; extra == "server"
-Requires-Dist: pymemcache; extra == "server"
-Requires-Dist: swh.core[http]>=0.4; extra == "server"
-Requires-Dist: swh.loader.core>=0.0.71; extra == "server"
-Requires-Dist: swh.scheduler>=0.7.0; extra == "server"
-Requires-Dist: swh.model>=0.3.8; extra == "server"
-Requires-Dist: swh.auth[django]>=0.5.3; extra == "server"
-Requires-Dist: swh.storage>=0.28.0; extra == "server"
-Provides-Extra: azure
-Requires-Dist: django-storages[azure]; extra == "azure"
+Requires-Dist: django-storages[azure]; extra == "testing"
 
 Software Heritage - Deposit
 ===========================
@@ -81,8 +81,8 @@ Description
 -----------
 
 Most of the software source code artifacts present in the SWH Archive are gathered by
-the mean of :term:`loader <loader>` workers run by the SWH project from source code
-origins identified by :term:`lister <lister>` workers. This is a pull mechanism: it's
+the mean of `loader`_ workers run by the SWH project from source code
+origins identified by `lister`_ workers. This is a pull mechanism: it's
 the responsibility of the SWH project to gather and collect source code artifacts that
 way.
 
@@ -96,16 +96,16 @@ This mechanism is the ``deposit``.
 
 The main idea is the deposit is an authenticated access to an API allowing the user to
 provide source code artifacts -- with metadata -- to be ingested in the SWH Archive. The
-result of that is a :ref:`SWHID <persistent-identifiers>` that can be used to uniquely
+result of that is a `SWHID`_ that can be used to uniquely
 and persistently identify that very piece of source code.
 
 This unique identifier can then be used to `reference the source code
 <https://hal.archives-ouvertes.fr/hal-02446202>`_ (e.g. in a `scientific paper
 <https://www.softwareheritage.org/2020/05/26/citing-software-with-style/>`_) and
-retrieve it using the :ref:`vault <swh-vault>` feature of the SWH Archive platform.
+retrieve it using the `vault`_ feature of the SWH Archive platform.
 
 The differences between a piece of code uploaded using the deposit rather than simply
-asking SWH to archive a repository using the :swh_web:`save code now <save/>` feature
+asking SWH to archive a repository using the `save code now`_ feature
 are:
 
 - a deposited artifact is provided from one of the SWH partners which is regarded as a
@@ -119,20 +119,31 @@ are:
 - the deposit API uses the `SWORD v2`_ API, thus requires some tooling to send deposits
   to SWH. These tools are provided with this repository.
 
-See the :ref:`deposit-user-manual` page for more details on how to use the deposit client
+
+See the `User Manual`_ page for more details on how to use the deposit client
 command line tools to push a deposit in the SWH Archive.
 
-See the :ref:`deposit-api-specifications` reference pages of the SWORDv2 API implementation
-in `swh.deposit` if you want to do upload deposits using HTTP requests.
+See the `API Documentation`_ reference pages of the SWORDv2 API implementation
+in ``swh.deposit`` if you want to do upload deposits using HTTP requests.
 
-Read the :ref:`deposit-metadata` chapter to get more details on what metadata
+Read the `Deposit metadata`_ chapter to get more details on what metadata
 are supported when doing a deposit.
 
-See :ref:`swh-deposit-dev-env` if you want to hack the code of the ``swh.deposit`` module.
+See `Running swh-deposit locally`_ if you want to hack the code of the ``swh.deposit`` module.
 
-See :ref:`swh-deposit-prod-env` if you want to deploy your own copy of the
+See `Production deployment`_ if you want to deploy your own copy of the
 `swh.deposit` stack.
 
 
 .. _codemeta: https://codemeta.github.io/
 .. _SWORD v2: http://swordapp.org/sword-v2/
+.. _loader: https://docs.softwareheritage.org/devel/glossary.html#term-loader
+.. _lister: https://docs.softwareheritage.org/devel/glossary.html#term-lister
+.. _SWHID: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html#persistent-identifiers
+.. _vault: https://docs.softwareheritage.org/devel/swh-vault/index.html#swh-vault
+.. _save code now: https://archive.softwareheritage.org/save/
+.. _User Manual: https://docs.softwareheritage.org/devel/swh-deposit/api/user-manual.html#deposit-user-manual
+.. _API Documentation: https://docs.softwareheritage.org/devel/swh-deposit/api/api-documentation.html#deposit-api-specifications
+.. _Deposit metadata: https://docs.softwareheritage.org/devel/swh-deposit/api/metadata.html#deposit-metadata
+.. _Running swh-deposit locally: https://docs.softwareheritage.org/devel/swh-deposit/internals/dev-environment.html#swh-deposit-dev-env
+.. _Production deployment: https://docs.softwareheritage.org/devel/swh-deposit/internals/prod-environment.html#swh-deposit-prod-env
diff --git a/README.rst b/README.rst
index c9dc6efaca0b2a1abaa8b154dd9cbed1489c95a2..7a855b16c7392f64ae7bd52352df50f34f5412af 100644
--- a/README.rst
+++ b/README.rst
@@ -14,8 +14,8 @@ Description
 -----------
 
 Most of the software source code artifacts present in the SWH Archive are gathered by
-the mean of :term:`loader <loader>` workers run by the SWH project from source code
-origins identified by :term:`lister <lister>` workers. This is a pull mechanism: it's
+the mean of `loader`_ workers run by the SWH project from source code
+origins identified by `lister`_ workers. This is a pull mechanism: it's
 the responsibility of the SWH project to gather and collect source code artifacts that
 way.
 
@@ -29,16 +29,16 @@ This mechanism is the ``deposit``.
 
 The main idea is the deposit is an authenticated access to an API allowing the user to
 provide source code artifacts -- with metadata -- to be ingested in the SWH Archive. The
-result of that is a :ref:`SWHID <persistent-identifiers>` that can be used to uniquely
+result of that is a `SWHID`_ that can be used to uniquely
 and persistently identify that very piece of source code.
 
 This unique identifier can then be used to `reference the source code
 <https://hal.archives-ouvertes.fr/hal-02446202>`_ (e.g. in a `scientific paper
 <https://www.softwareheritage.org/2020/05/26/citing-software-with-style/>`_) and
-retrieve it using the :ref:`vault <swh-vault>` feature of the SWH Archive platform.
+retrieve it using the `vault`_ feature of the SWH Archive platform.
 
 The differences between a piece of code uploaded using the deposit rather than simply
-asking SWH to archive a repository using the :swh_web:`save code now <save/>` feature
+asking SWH to archive a repository using the `save code now`_ feature
 are:
 
 - a deposited artifact is provided from one of the SWH partners which is regarded as a
@@ -52,20 +52,31 @@ are:
 - the deposit API uses the `SWORD v2`_ API, thus requires some tooling to send deposits
   to SWH. These tools are provided with this repository.
 
-See the :ref:`deposit-user-manual` page for more details on how to use the deposit client
+
+See the `User Manual`_ page for more details on how to use the deposit client
 command line tools to push a deposit in the SWH Archive.
 
-See the :ref:`deposit-api-specifications` reference pages of the SWORDv2 API implementation
-in `swh.deposit` if you want to do upload deposits using HTTP requests.
+See the `API Documentation`_ reference pages of the SWORDv2 API implementation
+in ``swh.deposit`` if you want to do upload deposits using HTTP requests.
 
-Read the :ref:`deposit-metadata` chapter to get more details on what metadata
+Read the `Deposit metadata`_ chapter to get more details on what metadata
 are supported when doing a deposit.
 
-See :ref:`swh-deposit-dev-env` if you want to hack the code of the ``swh.deposit`` module.
+See `Running swh-deposit locally`_ if you want to hack the code of the ``swh.deposit`` module.
 
-See :ref:`swh-deposit-prod-env` if you want to deploy your own copy of the
+See `Production deployment`_ if you want to deploy your own copy of the
 `swh.deposit` stack.
 
 
 .. _codemeta: https://codemeta.github.io/
 .. _SWORD v2: http://swordapp.org/sword-v2/
+.. _loader: https://docs.softwareheritage.org/devel/glossary.html#term-loader
+.. _lister: https://docs.softwareheritage.org/devel/glossary.html#term-lister
+.. _SWHID: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html#persistent-identifiers
+.. _vault: https://docs.softwareheritage.org/devel/swh-vault/index.html#swh-vault
+.. _save code now: https://archive.softwareheritage.org/save/
+.. _User Manual: https://docs.softwareheritage.org/devel/swh-deposit/api/user-manual.html#deposit-user-manual
+.. _API Documentation: https://docs.softwareheritage.org/devel/swh-deposit/api/api-documentation.html#deposit-api-specifications
+.. _Deposit metadata: https://docs.softwareheritage.org/devel/swh-deposit/api/metadata.html#deposit-metadata
+.. _Running swh-deposit locally: https://docs.softwareheritage.org/devel/swh-deposit/internals/dev-environment.html#swh-deposit-dev-env
+.. _Production deployment: https://docs.softwareheritage.org/devel/swh-deposit/internals/prod-environment.html#swh-deposit-prod-env
\ No newline at end of file
diff --git a/docs/.gitignore b/docs/.gitignore
index e379dea113fe837073c036ddca9838eb9606a2a2..58a761ead8c3be489f0e4738fac8d2173656ea7f 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -1,4 +1,3 @@
 _build/
 apidoc/
 *-stamp
-
diff --git a/docs/Makefile b/docs/Makefile
index ac10d52b69f8982f77439a86ab82cc91562f3a41..ac4259f27d07102ce6166831b7bbed08c4f70923 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,4 +1,4 @@
-include ../../swh-docs/Makefile.sphinx
+include Makefile.sphinx
 
 APIDOC_EXCLUDES += ../swh/*/settings/*
 
diff --git a/docs/README.rst b/docs/README.rst
index c9dc6efaca0b2a1abaa8b154dd9cbed1489c95a2..7a855b16c7392f64ae7bd52352df50f34f5412af 100644
--- a/docs/README.rst
+++ b/docs/README.rst
@@ -14,8 +14,8 @@ Description
 -----------
 
 Most of the software source code artifacts present in the SWH Archive are gathered by
-the mean of :term:`loader <loader>` workers run by the SWH project from source code
-origins identified by :term:`lister <lister>` workers. This is a pull mechanism: it's
+the mean of `loader`_ workers run by the SWH project from source code
+origins identified by `lister`_ workers. This is a pull mechanism: it's
 the responsibility of the SWH project to gather and collect source code artifacts that
 way.
 
@@ -29,16 +29,16 @@ This mechanism is the ``deposit``.
 
 The main idea is the deposit is an authenticated access to an API allowing the user to
 provide source code artifacts -- with metadata -- to be ingested in the SWH Archive. The
-result of that is a :ref:`SWHID <persistent-identifiers>` that can be used to uniquely
+result of that is a `SWHID`_ that can be used to uniquely
 and persistently identify that very piece of source code.
 
 This unique identifier can then be used to `reference the source code
 <https://hal.archives-ouvertes.fr/hal-02446202>`_ (e.g. in a `scientific paper
 <https://www.softwareheritage.org/2020/05/26/citing-software-with-style/>`_) and
-retrieve it using the :ref:`vault <swh-vault>` feature of the SWH Archive platform.
+retrieve it using the `vault`_ feature of the SWH Archive platform.
 
 The differences between a piece of code uploaded using the deposit rather than simply
-asking SWH to archive a repository using the :swh_web:`save code now <save/>` feature
+asking SWH to archive a repository using the `save code now`_ feature
 are:
 
 - a deposited artifact is provided from one of the SWH partners which is regarded as a
@@ -52,20 +52,31 @@ are:
 - the deposit API uses the `SWORD v2`_ API, thus requires some tooling to send deposits
   to SWH. These tools are provided with this repository.
 
-See the :ref:`deposit-user-manual` page for more details on how to use the deposit client
+
+See the `User Manual`_ page for more details on how to use the deposit client
 command line tools to push a deposit in the SWH Archive.
 
-See the :ref:`deposit-api-specifications` reference pages of the SWORDv2 API implementation
-in `swh.deposit` if you want to do upload deposits using HTTP requests.
+See the `API Documentation`_ reference pages of the SWORDv2 API implementation
+in ``swh.deposit`` if you want to do upload deposits using HTTP requests.
 
-Read the :ref:`deposit-metadata` chapter to get more details on what metadata
+Read the `Deposit metadata`_ chapter to get more details on what metadata
 are supported when doing a deposit.
 
-See :ref:`swh-deposit-dev-env` if you want to hack the code of the ``swh.deposit`` module.
+See `Running swh-deposit locally`_ if you want to hack the code of the ``swh.deposit`` module.
 
-See :ref:`swh-deposit-prod-env` if you want to deploy your own copy of the
+See `Production deployment`_ if you want to deploy your own copy of the
 `swh.deposit` stack.
 
 
 .. _codemeta: https://codemeta.github.io/
 .. _SWORD v2: http://swordapp.org/sword-v2/
+.. _loader: https://docs.softwareheritage.org/devel/glossary.html#term-loader
+.. _lister: https://docs.softwareheritage.org/devel/glossary.html#term-lister
+.. _SWHID: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html#persistent-identifiers
+.. _vault: https://docs.softwareheritage.org/devel/swh-vault/index.html#swh-vault
+.. _save code now: https://archive.softwareheritage.org/save/
+.. _User Manual: https://docs.softwareheritage.org/devel/swh-deposit/api/user-manual.html#deposit-user-manual
+.. _API Documentation: https://docs.softwareheritage.org/devel/swh-deposit/api/api-documentation.html#deposit-api-specifications
+.. _Deposit metadata: https://docs.softwareheritage.org/devel/swh-deposit/api/metadata.html#deposit-metadata
+.. _Running swh-deposit locally: https://docs.softwareheritage.org/devel/swh-deposit/internals/dev-environment.html#swh-deposit-dev-env
+.. _Production deployment: https://docs.softwareheritage.org/devel/swh-deposit/internals/prod-environment.html#swh-deposit-prod-env
\ No newline at end of file
diff --git a/mypy.ini b/mypy.ini
index 360983431d8c48c127ac7d56efbb017a7a60fedf..e1cc453ce2dcfcd29aa4044e81b40b84be12860c 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,7 +1,9 @@
 [mypy]
 namespace_packages = True
 warn_unused_ignores = True
-
+explicit_package_bases = True
+# ^ Needed for mypy to detect py.typed from swh packages installed
+# in editable mode
 
 # support for django magic: https://github.com/typeddjango/django-stubs
 plugins = mypy_django_plugin.main
@@ -9,7 +11,6 @@ plugins = mypy_django_plugin.main
 [mypy.plugins.django-stubs]
 django_settings_module = swh.deposit.settings.testing
 
-
 # 3rd party libraries without stubs (yet)
 
 [mypy-celery.*]
@@ -24,9 +25,6 @@ ignore_missing_imports = True
 [mypy-psycopg2.*]
 ignore_missing_imports = True
 
-[mypy-pytest.*]
-ignore_missing_imports = True
-
 [tenacity.*]
 ignore_missing_imports = True
 
diff --git a/pyproject.toml b/pyproject.toml
index 69b8f4dd830abf638e624eeea85dbc580c862538..092bd34b0c8b33f5139181757d80fbb9497c1f01 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,57 @@
+[project]
+name = "swh.deposit"
+authors = [
+    {name="Software Heritage developers", email="swh-devel@inria.fr"},
+]
+
+description = "Software Heritage deposit server"
+readme = {file = "README.rst", content-type = "text/x-rst"}
+requires-python = ">=3.7"
+classifiers = [
+    "Programming Language :: Python :: 3",
+    "Intended Audience :: Developers",
+    "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+    "Operating System :: OS Independent",
+    "Development Status :: 5 - Production/Stable",
+]
+dynamic = ["version", "dependencies", "optional-dependencies"]
+
+[tool.setuptools.packages.find]
+include = ["swh.*"]
+
+[tool.setuptools.dynamic]
+dependencies = {file = ["requirements.txt", "requirements-swh.txt"]}
+
+[tool.setuptools.dynamic.optional-dependencies]
+server = {file = ["requirements-server.txt", "requirements-swh-server.txt"]}
+azure = {file = ["requirements-azure.txt"]}
+testing = {file = [
+    "requirements-test.txt",
+    "requirements-server.txt",
+    "requirements-swh-server.txt",
+    "requirements-azure.txt",
+    ]}
+
+[project.entry-points."swh.cli.subcommands"]
+"swh.deposit" = "swh.deposit.cli"
+
+[project.entry-points."swh.workers"]
+"swh.deposit" = "swh.deposit.loader:register"
+
+[project.urls]
+"Homepage" = "https://gitlab.softwareheritage.org/swh/devel/swh-deposit"
+"Bug Reports" = "https://gitlab.softwareheritage.org/swh/devel/swh-deposit/-/issues"
+"Funding" = "https://www.softwareheritage.org/donate"
+"Documentation" = "https://docs.softwareheritage.org/devel/swh-deposit/"
+"Source" = "https://gitlab.softwareheritage.org/swh/devel/swh-deposit.git"
+
+[build-system]
+requires = ["setuptools", "setuptools-scm"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools_scm]
+fallback_version = "0.0.1"
+
 [tool.black]
 target-version = ['py37']
 
@@ -9,3 +63,4 @@ use_parentheses = true
 ensure_newline_before_comments = true
 line_length = 88
 force_sort_within_sections = true
+known_first_party = ['swh']
diff --git a/pytest.ini b/pytest.ini
index 3b97c3651f31436f2d155ac1af94d2ef178277b1..04c78b48f82420a77215463237bd5709a201ece1 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,11 +1,11 @@
 [pytest]
 # Remove the pytest_swh_* entries when they stop getting imported automatically
-addopts = -p no:flask -p no:pytest_swh_scheduler -p no:pytest_swh_storage -p no:pytest_swh_core
+addopts = --import-mode=importlib --ignore=swh/deposit/settings -p no:flask -p no:pytest_swh_scheduler -p no:pytest_swh_storage -p no:pytest_swh_core
 norecursedirs = build docs .*
-DJANGO_SETTINGS_MODULE = swh.deposit.settings.testing
+asyncio_mode = strict
 
+DJANGO_SETTINGS_MODULE = swh.deposit.settings.testing
 markers =
     db: execute tests using a postgresql database
     fs: execute tests using the filesystem
 
-asyncio_mode = strict
diff --git a/setup.py b/setup.py
deleted file mode 100755
index 68d33153c732def5dff61c7fd8799755ffc0967f..0000000000000000000000000000000000000000
--- a/setup.py
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2015-2021  The Software Heritage developers
-# See the AUTHORS file at the top-level directory of this distribution
-# License: GNU General Public License version 3, or any later version
-# See top-level LICENSE file for more information
-
-from io import open
-from os import path
-
-from setuptools import find_packages, setup
-
-here = path.abspath(path.dirname(__file__))
-
-# Get the long description from the README file
-with open(path.join(here, "README.rst"), encoding="utf-8") as f:
-    long_description = f.read()
-
-
-def parse_requirements(*names):
-    requirements = []
-    for name in names:
-        if name:
-            reqf = "requirements-%s.txt" % name
-        else:
-            reqf = "requirements.txt"
-
-        if not path.exists(reqf):
-            return requirements
-
-        with open(reqf) as f:
-            for line in f.readlines():
-                line = line.strip()
-                if not line or line.startswith("#"):
-                    continue
-                requirements.append(line)
-    return requirements
-
-
-setup(
-    name="swh.deposit",
-    description="Software Heritage Deposit Server",
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    python_requires=">=3.7",
-    author="Software Heritage developers",
-    author_email="swh-devel@inria.fr",
-    url="https://forge.softwareheritage.org/source/swh-deposit/",
-    packages=find_packages(),
-    install_requires=parse_requirements(None, "swh"),
-    tests_require=parse_requirements("test"),
-    setup_requires=["setuptools-scm"],
-    use_scm_version=True,
-    extras_require={
-        "testing": parse_requirements("test", "server", "swh-server"),
-        "server": parse_requirements("server", "swh-server"),
-        "azure": parse_requirements("azure"),
-    },
-    include_package_data=True,
-    entry_points="""
-        [console_scripts]
-        swh-deposit=swh.deposit.cli:main
-        [swh.cli.subcommands]
-        deposit=swh.deposit.cli
-        [swh.workers]
-        deposit.worker=swh.deposit.loader:register
-    """,
-    classifiers=[
-        "Programming Language :: Python :: 3",
-        "Intended Audience :: Developers",
-        "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
-        "Operating System :: OS Independent",
-        "Development Status :: 5 - Production/Stable",
-    ],
-    project_urls={
-        "Bug Reports": "https://forge.softwareheritage.org/maniphest",
-        "Funding": "https://www.softwareheritage.org/donate",
-        "Source": "https://forge.softwareheritage.org/source/swh-deposit",
-        "Documentation": "https://docs.softwareheritage.org/devel/swh-deposit/",
-    },
-)
diff --git a/swh.deposit.egg-info/PKG-INFO b/swh.deposit.egg-info/PKG-INFO
index 32282eb0a867652f0d610bcc98e9b558f5c17634..06164c629b2823b365fdbb3483472ea9a044eb4e 100644
--- a/swh.deposit.egg-info/PKG-INFO
+++ b/swh.deposit.egg-info/PKG-INFO
@@ -1,21 +1,20 @@
 Metadata-Version: 2.1
 Name: swh.deposit
-Version: 2.0.0
-Summary: Software Heritage Deposit Server
-Home-page: https://forge.softwareheritage.org/source/swh-deposit/
-Author: Software Heritage developers
-Author-email: swh-devel@inria.fr
-Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest
+Version: 2.1.1
+Summary: Software Heritage deposit server
+Author-email: Software Heritage developers <swh-devel@inria.fr>
+Project-URL: Homepage, https://gitlab.softwareheritage.org/swh/devel/swh-deposit
+Project-URL: Bug Reports, https://gitlab.softwareheritage.org/swh/devel/swh-deposit/-/issues
 Project-URL: Funding, https://www.softwareheritage.org/donate
-Project-URL: Source, https://forge.softwareheritage.org/source/swh-deposit
 Project-URL: Documentation, https://docs.softwareheritage.org/devel/swh-deposit/
+Project-URL: Source, https://gitlab.softwareheritage.org/swh/devel/swh-deposit.git
 Classifier: Programming Language :: Python :: 3
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
 Classifier: Operating System :: OS Independent
 Classifier: Development Status :: 5 - Production/Stable
 Requires-Python: >=3.7
-Description-Content-Type: text/markdown
+Description-Content-Type: text/x-rst
 License-File: LICENSE
 License-File: AUTHORS
 Requires-Dist: click
@@ -24,6 +23,21 @@ Requires-Dist: requests
 Requires-Dist: sentry-sdk
 Requires-Dist: swh.core[http]>=0.4
 Requires-Dist: swh.model>=4.4.0
+Provides-Extra: server
+Requires-Dist: django; extra == "server"
+Requires-Dist: djangorestframework; extra == "server"
+Requires-Dist: psycopg2; extra == "server"
+Requires-Dist: setuptools; extra == "server"
+Requires-Dist: xmlschema; extra == "server"
+Requires-Dist: pymemcache; extra == "server"
+Requires-Dist: swh.core[http]>=0.4; extra == "server"
+Requires-Dist: swh.loader.core>=0.0.71; extra == "server"
+Requires-Dist: swh.scheduler>=0.7.0; extra == "server"
+Requires-Dist: swh.model>=0.3.8; extra == "server"
+Requires-Dist: swh.auth[django]>=0.5.3; extra == "server"
+Requires-Dist: swh.storage>=0.28.0; extra == "server"
+Provides-Extra: azure
+Requires-Dist: django-storages[azure]; extra == "azure"
 Provides-Extra: testing
 Requires-Dist: pytest; extra == "testing"
 Requires-Dist: pytest-django; extra == "testing"
@@ -49,21 +63,7 @@ Requires-Dist: swh.scheduler>=0.7.0; extra == "testing"
 Requires-Dist: swh.model>=0.3.8; extra == "testing"
 Requires-Dist: swh.auth[django]>=0.5.3; extra == "testing"
 Requires-Dist: swh.storage>=0.28.0; extra == "testing"
-Provides-Extra: server
-Requires-Dist: django; extra == "server"
-Requires-Dist: djangorestframework; extra == "server"
-Requires-Dist: psycopg2; extra == "server"
-Requires-Dist: setuptools; extra == "server"
-Requires-Dist: xmlschema; extra == "server"
-Requires-Dist: pymemcache; extra == "server"
-Requires-Dist: swh.core[http]>=0.4; extra == "server"
-Requires-Dist: swh.loader.core>=0.0.71; extra == "server"
-Requires-Dist: swh.scheduler>=0.7.0; extra == "server"
-Requires-Dist: swh.model>=0.3.8; extra == "server"
-Requires-Dist: swh.auth[django]>=0.5.3; extra == "server"
-Requires-Dist: swh.storage>=0.28.0; extra == "server"
-Provides-Extra: azure
-Requires-Dist: django-storages[azure]; extra == "azure"
+Requires-Dist: django-storages[azure]; extra == "testing"
 
 Software Heritage - Deposit
 ===========================
@@ -81,8 +81,8 @@ Description
 -----------
 
 Most of the software source code artifacts present in the SWH Archive are gathered by
-the mean of :term:`loader <loader>` workers run by the SWH project from source code
-origins identified by :term:`lister <lister>` workers. This is a pull mechanism: it's
+the mean of `loader`_ workers run by the SWH project from source code
+origins identified by `lister`_ workers. This is a pull mechanism: it's
 the responsibility of the SWH project to gather and collect source code artifacts that
 way.
 
@@ -96,16 +96,16 @@ This mechanism is the ``deposit``.
 
 The main idea is the deposit is an authenticated access to an API allowing the user to
 provide source code artifacts -- with metadata -- to be ingested in the SWH Archive. The
-result of that is a :ref:`SWHID <persistent-identifiers>` that can be used to uniquely
+result of that is a `SWHID`_ that can be used to uniquely
 and persistently identify that very piece of source code.
 
 This unique identifier can then be used to `reference the source code
 <https://hal.archives-ouvertes.fr/hal-02446202>`_ (e.g. in a `scientific paper
 <https://www.softwareheritage.org/2020/05/26/citing-software-with-style/>`_) and
-retrieve it using the :ref:`vault <swh-vault>` feature of the SWH Archive platform.
+retrieve it using the `vault`_ feature of the SWH Archive platform.
 
 The differences between a piece of code uploaded using the deposit rather than simply
-asking SWH to archive a repository using the :swh_web:`save code now <save/>` feature
+asking SWH to archive a repository using the `save code now`_ feature
 are:
 
 - a deposited artifact is provided from one of the SWH partners which is regarded as a
@@ -119,20 +119,31 @@ are:
 - the deposit API uses the `SWORD v2`_ API, thus requires some tooling to send deposits
   to SWH. These tools are provided with this repository.
 
-See the :ref:`deposit-user-manual` page for more details on how to use the deposit client
+
+See the `User Manual`_ page for more details on how to use the deposit client
 command line tools to push a deposit in the SWH Archive.
 
-See the :ref:`deposit-api-specifications` reference pages of the SWORDv2 API implementation
-in `swh.deposit` if you want to do upload deposits using HTTP requests.
+See the `API Documentation`_ reference pages of the SWORDv2 API implementation
+in ``swh.deposit`` if you want to do upload deposits using HTTP requests.
 
-Read the :ref:`deposit-metadata` chapter to get more details on what metadata
+Read the `Deposit metadata`_ chapter to get more details on what metadata
 are supported when doing a deposit.
 
-See :ref:`swh-deposit-dev-env` if you want to hack the code of the ``swh.deposit`` module.
+See `Running swh-deposit locally`_ if you want to hack the code of the ``swh.deposit`` module.
 
-See :ref:`swh-deposit-prod-env` if you want to deploy your own copy of the
+See `Production deployment`_ if you want to deploy your own copy of the
 `swh.deposit` stack.
 
 
 .. _codemeta: https://codemeta.github.io/
 .. _SWORD v2: http://swordapp.org/sword-v2/
+.. _loader: https://docs.softwareheritage.org/devel/glossary.html#term-loader
+.. _lister: https://docs.softwareheritage.org/devel/glossary.html#term-lister
+.. _SWHID: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html#persistent-identifiers
+.. _vault: https://docs.softwareheritage.org/devel/swh-vault/index.html#swh-vault
+.. _save code now: https://archive.softwareheritage.org/save/
+.. _User Manual: https://docs.softwareheritage.org/devel/swh-deposit/api/user-manual.html#deposit-user-manual
+.. _API Documentation: https://docs.softwareheritage.org/devel/swh-deposit/api/api-documentation.html#deposit-api-specifications
+.. _Deposit metadata: https://docs.softwareheritage.org/devel/swh-deposit/api/metadata.html#deposit-metadata
+.. _Running swh-deposit locally: https://docs.softwareheritage.org/devel/swh-deposit/internals/dev-environment.html#swh-deposit-dev-env
+.. _Production deployment: https://docs.softwareheritage.org/devel/swh-deposit/internals/prod-environment.html#swh-deposit-prod-env
diff --git a/swh.deposit.egg-info/SOURCES.txt b/swh.deposit.egg-info/SOURCES.txt
index fe119da85a1369869be7220917bb7610f3509d07..553dacd7bc2f69d00e8bf60f3527c2210ddb2028 100644
--- a/swh.deposit.egg-info/SOURCES.txt
+++ b/swh.deposit.egg-info/SOURCES.txt
@@ -1,3 +1,4 @@
+.copier-answers.yml
 .git-blame-ignore-revs
 .gitignore
 .pre-commit-config.yaml
@@ -5,7 +6,6 @@ AUTHORS
 CODE_OF_CONDUCT.md
 CONTRIBUTORS
 LICENSE
-MANIFEST.in
 Makefile
 Makefile.local
 README.rst
@@ -20,7 +20,6 @@ requirements-swh.txt
 requirements-test.txt
 requirements.txt
 setup.cfg
-setup.py
 tox.ini
 bin/Makefile
 bin/content.sh
@@ -81,7 +80,6 @@ docs/specs/protocol-reference.rst
 docs/specs/spec-loading.rst
 docs/specs/spec-meta-deposit.rst
 resources/deposit/server.yml
-swh/__init__.py
 swh.deposit.egg-info/PKG-INFO
 swh.deposit.egg-info/SOURCES.txt
 swh.deposit.egg-info/dependency_links.txt
diff --git a/swh.deposit.egg-info/entry_points.txt b/swh.deposit.egg-info/entry_points.txt
index ef89c380b0087bbc86f4637dbbafbadf8342de97..0e456f97f321135326af7e2736a60acf6ca2b6ac 100644
--- a/swh.deposit.egg-info/entry_points.txt
+++ b/swh.deposit.egg-info/entry_points.txt
@@ -1,8 +1,5 @@
-[console_scripts]
-swh-deposit = swh.deposit.cli:main
-
 [swh.cli.subcommands]
-deposit = swh.deposit.cli
+swh.deposit = swh.deposit.cli
 
 [swh.workers]
-deposit.worker = swh.deposit.loader:register
+swh.deposit = swh.deposit.loader:register
diff --git a/swh.deposit.egg-info/requires.txt b/swh.deposit.egg-info/requires.txt
index 1604e3f111c6357df34f3cfaf12cb3fb204ed98f..088a23a3232f37122f7030742693bbaf5d8192f6 100644
--- a/swh.deposit.egg-info/requires.txt
+++ b/swh.deposit.egg-info/requires.txt
@@ -47,3 +47,4 @@ swh.scheduler>=0.7.0
 swh.model>=0.3.8
 swh.auth[django]>=0.5.3
 swh.storage>=0.28.0
+django-storages[azure]
diff --git a/swh/__init__.py b/swh/__init__.py
deleted file mode 100644
index b36383a61027f0875a3cb103edc8f2a4528a3289..0000000000000000000000000000000000000000
--- a/swh/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from pkgutil import extend_path
-
-__path__ = extend_path(__path__, __name__)
diff --git a/swh/deposit/api/collection.py b/swh/deposit/api/collection.py
index e80c0bf0b5607583d2d64efd31af8e7740427d36..80c87a78021ff9e2136697d34a75f1c3aaafd084 100644
--- a/swh/deposit/api/collection.py
+++ b/swh/deposit/api/collection.py
@@ -9,22 +9,22 @@ from django.shortcuts import render
 from rest_framework import status
 from rest_framework.generics import ListAPIView
 
-from ..config import DEPOSIT_STATUS_LOAD_SUCCESS, EDIT_IRI
-from ..models import Deposit
-from ..parsers import (
-    SWHAtomEntryParser,
-    SWHFileUploadTarParser,
-    SWHFileUploadZipParser,
-    SWHMultiPartParser,
-)
-from .common import (
+from swh.deposit.api.common import (
     ACCEPT_ARCHIVE_CONTENT_TYPES,
     APIPost,
     ParsedRequestHeaders,
     Receipt,
     get_collection_by_name,
 )
-from .utils import DefaultPagination, DepositSerializer
+from swh.deposit.api.utils import DefaultPagination, DepositSerializer
+from swh.deposit.config import DEPOSIT_STATUS_LOAD_SUCCESS, EDIT_IRI
+from swh.deposit.models import Deposit
+from swh.deposit.parsers import (
+    SWHAtomEntryParser,
+    SWHFileUploadTarParser,
+    SWHFileUploadZipParser,
+    SWHMultiPartParser,
+)
 
 
 class CollectionAPI(ListAPIView, APIPost):
diff --git a/swh/deposit/api/common.py b/swh/deposit/api/common.py
index 7f82365e996ffd0effb64d9a0ccbf688365f1c03..adb478f71185f882045513adf26ac949477c583f 100644
--- a/swh/deposit/api/common.py
+++ b/swh/deposit/api/common.py
@@ -27,25 +27,7 @@ from rest_framework.views import APIView
 from swh.deposit.api.checks import check_metadata, check_url_match_provider
 from swh.deposit.api.converters import convert_status_detail
 from swh.deposit.auth import HasDepositPermission, KeycloakBasicAuthentication
-from swh.deposit.models import DEPOSIT_METADATA_ONLY, Deposit
-from swh.deposit.parsers import parse_xml
-from swh.deposit.utils import compute_metadata_context, parse_swh_metadata_provenance
-from swh.model import hashutil
-from swh.model.model import (
-    MetadataAuthority,
-    MetadataAuthorityType,
-    Origin,
-    RawExtrinsicMetadata,
-)
-from swh.model.swhids import (
-    ExtendedObjectType,
-    ExtendedSWHID,
-    QualifiedSWHID,
-    ValidationError,
-)
-from swh.scheduler.utils import create_oneshot_task_dict
-
-from ..config import (
+from swh.deposit.config import (
     ARCHIVE_KEY,
     ARCHIVE_TYPE,
     CONT_FILE_IRI,
@@ -60,7 +42,7 @@ from ..config import (
     STATE_IRI,
     APIConfig,
 )
-from ..errors import (
+from swh.deposit.errors import (
     BAD_REQUEST,
     CHECKSUM_MISMATCH,
     ERROR_CONTENT,
@@ -73,12 +55,35 @@ from ..errors import (
     DepositError,
     ParserError,
 )
-from ..models import DepositClient, DepositCollection, DepositRequest
-from ..utils import (
+from swh.deposit.models import (
+    DEPOSIT_METADATA_ONLY,
+    Deposit,
+    DepositClient,
+    DepositCollection,
+    DepositRequest,
+)
+from swh.deposit.parsers import parse_xml
+from swh.deposit.utils import (
+    compute_metadata_context,
     extended_swhid_from_qualified,
     parse_swh_deposit_origin,
+    parse_swh_metadata_provenance,
     parse_swh_reference,
 )
+from swh.model import hashutil
+from swh.model.model import (
+    MetadataAuthority,
+    MetadataAuthorityType,
+    Origin,
+    RawExtrinsicMetadata,
+)
+from swh.model.swhids import (
+    ExtendedObjectType,
+    ExtendedSWHID,
+    QualifiedSWHID,
+    ValidationError,
+)
+from swh.scheduler.utils import create_oneshot_task_dict
 
 ACCEPT_PACKAGINGS = ["http://purl.org/net/sword/package/SimpleZip"]
 ACCEPT_ARCHIVE_CONTENT_TYPES = ["application/zip", "application/x-tar"]
diff --git a/swh/deposit/api/content.py b/swh/deposit/api/content.py
index 7def160210febc7a6a7c4624a49067a80d5c1721..1b5e1fffcaf5bf9eaa6c28315ba0f4df742955f6 100644
--- a/swh/deposit/api/content.py
+++ b/swh/deposit/api/content.py
@@ -7,8 +7,8 @@ from django.http import HttpResponse
 from django.shortcuts import render
 from rest_framework import status
 
-from ..models import DEPOSIT_STATUS_DETAIL, DepositRequest
-from .common import APIBase, get_deposit_by_id
+from swh.deposit.api.common import APIBase, get_deposit_by_id
+from swh.deposit.models import DEPOSIT_STATUS_DETAIL, DepositRequest
 
 
 class ContentAPI(APIBase):
diff --git a/swh/deposit/api/edit.py b/swh/deposit/api/edit.py
index 67e8bf85a63c618589ff0df7cab7f7fe3018dcb4..0462d35a612f4040d9a4effa2a7e9a6a6698e598 100644
--- a/swh/deposit/api/edit.py
+++ b/swh/deposit/api/edit.py
@@ -5,14 +5,13 @@
 
 from rest_framework.request import Request
 
+from swh.deposit.api.common import APIDelete, APIPut, ParsedRequestHeaders
+from swh.deposit.config import DEPOSIT_STATUS_LOAD_SUCCESS
+from swh.deposit.errors import BAD_REQUEST, DepositError, ParserError
 from swh.deposit.models import Deposit
+from swh.deposit.parsers import SWHAtomEntryParser, SWHMultiPartParser
 from swh.model.swhids import QualifiedSWHID
 
-from ..config import DEPOSIT_STATUS_LOAD_SUCCESS
-from ..errors import BAD_REQUEST, DepositError, ParserError
-from ..parsers import SWHAtomEntryParser, SWHMultiPartParser
-from .common import APIDelete, APIPut, ParsedRequestHeaders
-
 
 class EditAPI(APIPut, APIDelete):
     """Deposit request class defining api endpoints for sword deposit.
diff --git a/swh/deposit/api/edit_media.py b/swh/deposit/api/edit_media.py
index b673ff759bedd410cdc498701e692009f34a08ea..e12b1ad7fd335cd2ef15544c76b882cb342a2f05 100644
--- a/swh/deposit/api/edit_media.py
+++ b/swh/deposit/api/edit_media.py
@@ -7,11 +7,7 @@ from typing import Optional, Tuple
 
 from rest_framework import status
 
-from ..config import CONT_FILE_IRI
-from ..errors import BAD_REQUEST, DepositError
-from ..models import Deposit
-from ..parsers import SWHFileUploadTarParser, SWHFileUploadZipParser
-from .common import (
+from swh.deposit.api.common import (
     ACCEPT_ARCHIVE_CONTENT_TYPES,
     APIDelete,
     APIPost,
@@ -19,6 +15,10 @@ from .common import (
     ParsedRequestHeaders,
     Receipt,
 )
+from swh.deposit.config import CONT_FILE_IRI
+from swh.deposit.errors import BAD_REQUEST, DepositError
+from swh.deposit.models import Deposit
+from swh.deposit.parsers import SWHFileUploadTarParser, SWHFileUploadZipParser
 
 
 class EditMediaAPI(APIPost, APIPut, APIDelete):
diff --git a/swh/deposit/api/private/__init__.py b/swh/deposit/api/private/__init__.py
index acf91c024a1dd1030289690ed1bdbaa7ceb56e2a..ab7217840eeb2f3ee233f832aaf916ff4f416351 100644
--- a/swh/deposit/api/private/__init__.py
+++ b/swh/deposit/api/private/__init__.py
@@ -8,8 +8,8 @@ from typing import Optional
 from rest_framework.permissions import AllowAny
 from rest_framework.views import APIView
 
-from ...config import METADATA_TYPE, APIConfig
-from ...models import Deposit, DepositRequest
+from swh.deposit.config import METADATA_TYPE, APIConfig
+from swh.deposit.models import Deposit, DepositRequest
 
 
 class DepositReadMixin:
diff --git a/swh/deposit/api/private/deposit_check.py b/swh/deposit/api/private/deposit_check.py
index e24310aa070ff9bfade754dd952549aaab5dba38..5f41bc5299eea2f5c2649bf1ee9cb636482ac99f 100644
--- a/swh/deposit/api/private/deposit_check.py
+++ b/swh/deposit/api/private/deposit_check.py
@@ -15,14 +15,17 @@ import zipfile
 from rest_framework import status
 from rest_framework.request import Request
 
+from swh.deposit.api.checks import check_metadata
+from swh.deposit.api.common import APIGet
+from swh.deposit.api.private import APIPrivateView, DepositReadMixin
+from swh.deposit.config import (
+    ARCHIVE_TYPE,
+    DEPOSIT_STATUS_REJECTED,
+    DEPOSIT_STATUS_VERIFIED,
+)
+from swh.deposit.models import Deposit, DepositRequest
 from swh.scheduler.utils import create_oneshot_task_dict
 
-from . import APIPrivateView, DepositReadMixin
-from ...config import ARCHIVE_TYPE, DEPOSIT_STATUS_REJECTED, DEPOSIT_STATUS_VERIFIED
-from ...models import Deposit, DepositRequest
-from ..checks import check_metadata
-from ..common import APIGet
-
 MANDATORY_ARCHIVE_UNREADABLE = (
     "At least one of its associated archives is not readable"  # noqa
 )
diff --git a/swh/deposit/api/private/deposit_list.py b/swh/deposit/api/private/deposit_list.py
index 30ca768e4305541bac6182258722793827893760..a1d2bdc6b597c3a48cea5455c7f88962fae9f153 100644
--- a/swh/deposit/api/private/deposit_list.py
+++ b/swh/deposit/api/private/deposit_list.py
@@ -20,13 +20,12 @@ from rest_framework.permissions import AllowAny
 from rest_framework.request import Request
 import sentry_sdk
 
+from swh.deposit.api.private import APIPrivateView
 from swh.deposit.api.utils import DefaultPagination, DepositSerializer
+from swh.deposit.models import Deposit
 from swh.deposit.utils import parse_swh_deposit_origin, parse_swh_metadata_provenance
 from swh.model.swhids import QualifiedSWHID
 
-from . import APIPrivateView
-from ...models import Deposit
-
 
 def _enrich_deposit_with_metadata(deposit: Deposit) -> Deposit:
     deposit_requests = deposit.depositrequest_set.filter(type="metadata")
diff --git a/swh/deposit/api/private/deposit_read.py b/swh/deposit/api/private/deposit_read.py
index 94ecb201a5eec0a7bad5857edd966150a7ec8f6b..e469b3944b31f8ec5ed48f5569abb6a78a68ae8e 100644
--- a/swh/deposit/api/private/deposit_read.py
+++ b/swh/deposit/api/private/deposit_read.py
@@ -14,16 +14,15 @@ from xml.etree import ElementTree
 from rest_framework import status
 
 from swh.core import tarball
+from swh.deposit.api.common import APIGet
+from swh.deposit.api.private import APIPrivateView, DepositReadMixin
+from swh.deposit.config import ARCHIVE_TYPE, SWH_PERSON
+from swh.deposit.models import Deposit
 from swh.deposit.utils import NAMESPACES, normalize_date
 from swh.model.hashutil import hash_to_hex
 from swh.model.model import MetadataAuthorityType
 from swh.model.swhids import CoreSWHID
 
-from . import APIPrivateView, DepositReadMixin
-from ...config import ARCHIVE_TYPE, SWH_PERSON
-from ...models import Deposit
-from ..common import APIGet
-
 
 @contextmanager
 def aggregate_tarballs(extraction_dir: str, archives: List) -> Iterator[str]:
diff --git a/swh/deposit/api/private/deposit_update_status.py b/swh/deposit/api/private/deposit_update_status.py
index 2ccc45e17337a50b4a8db0b15efa65d5a9c1dd34..34dea3fafd7c3ae4abff908ce5b7fb43ff91a62d 100644
--- a/swh/deposit/api/private/deposit_update_status.py
+++ b/swh/deposit/api/private/deposit_update_status.py
@@ -5,14 +5,17 @@
 
 from rest_framework.parsers import JSONParser
 
+from swh.deposit.api.common import APIPut, ParsedRequestHeaders
+from swh.deposit.api.private import APIPrivateView
+from swh.deposit.errors import BAD_REQUEST, DepositError
+from swh.deposit.models import (
+    DEPOSIT_STATUS_DETAIL,
+    DEPOSIT_STATUS_LOAD_SUCCESS,
+    Deposit,
+)
 from swh.model.hashutil import hash_to_bytes
 from swh.model.swhids import CoreSWHID, ObjectType, QualifiedSWHID
 
-from . import APIPrivateView
-from ...errors import BAD_REQUEST, DepositError
-from ...models import DEPOSIT_STATUS_DETAIL, DEPOSIT_STATUS_LOAD_SUCCESS, Deposit
-from ..common import APIPut, ParsedRequestHeaders
-
 MANDATORY_KEYS = ["origin_url", "release_id", "directory_id", "snapshot_id"]
 
 
diff --git a/swh/deposit/api/private/urls.py b/swh/deposit/api/private/urls.py
index a5d29b0ed166216c7d2f71b5cfdfb552aa2e3624..650480a892060a72e8cff7e31071146fc1cdcb52 100644
--- a/swh/deposit/api/private/urls.py
+++ b/swh/deposit/api/private/urls.py
@@ -5,7 +5,11 @@
 
 from django.urls import re_path as url
 
-from ...config import (
+from swh.deposit.api.private.deposit_check import APIChecks
+from swh.deposit.api.private.deposit_list import APIList, deposit_list_datatables
+from swh.deposit.api.private.deposit_read import APIReadArchives, APIReadMetadata
+from swh.deposit.api.private.deposit_update_status import APIUpdateStatus
+from swh.deposit.config import (
     PRIVATE_CHECK_DEPOSIT,
     PRIVATE_GET_DEPOSIT_METADATA,
     PRIVATE_GET_RAW_CONTENT,
@@ -13,10 +17,6 @@ from ...config import (
     PRIVATE_LIST_DEPOSITS_DATATABLES,
     PRIVATE_PUT_DEPOSIT,
 )
-from .deposit_check import APIChecks
-from .deposit_list import APIList, deposit_list_datatables
-from .deposit_read import APIReadArchives, APIReadMetadata
-from .deposit_update_status import APIUpdateStatus
 
 urlpatterns = [
     # Retrieve deposit's raw archives' content
diff --git a/swh/deposit/api/state.py b/swh/deposit/api/state.py
index c56782feb5d08ee3834c512e5219694ac40ca114..08f8b47081a3afa6ceef7c3a05dbd9d396687bd7 100644
--- a/swh/deposit/api/state.py
+++ b/swh/deposit/api/state.py
@@ -7,9 +7,9 @@ from django.http import HttpResponse
 from django.shortcuts import render
 from rest_framework import status
 
-from ..models import DEPOSIT_STATUS_DETAIL
-from .common import APIBase, get_deposit_by_id
-from .converters import convert_status_detail
+from swh.deposit.api.common import APIBase, get_deposit_by_id
+from swh.deposit.api.converters import convert_status_detail
+from swh.deposit.models import DEPOSIT_STATUS_DETAIL
 
 
 class StateAPI(APIBase):
diff --git a/swh/deposit/api/sword_edit.py b/swh/deposit/api/sword_edit.py
index 1378a5c1572e9fb18ec56b4d77040b9c9e0d20a2..b6267098df3acea420345e7f0250a5b61afce719 100644
--- a/swh/deposit/api/sword_edit.py
+++ b/swh/deposit/api/sword_edit.py
@@ -7,14 +7,13 @@ from typing import Optional, Tuple
 
 from rest_framework import status
 
+from swh.deposit.api.common import APIPost, ParsedRequestHeaders, Receipt
+from swh.deposit.config import EDIT_IRI, EM_IRI
+from swh.deposit.models import Deposit
+from swh.deposit.parsers import SWHAtomEntryParser, SWHMultiPartParser
 from swh.storage import get_storage
 from swh.storage.interface import StorageInterface
 
-from ..config import EDIT_IRI, EM_IRI
-from ..models import Deposit
-from ..parsers import SWHAtomEntryParser, SWHMultiPartParser
-from .common import APIPost, ParsedRequestHeaders, Receipt
-
 
 class SwordEditAPI(APIPost):
     """Deposit request class defining api endpoints for sword deposit.
diff --git a/swh/deposit/api/urls.py b/swh/deposit/api/urls.py
index c204c04d2f4869127665069d1c6d18b692ee3bd1..86b24a6b7dc2726e3d847aed306bf300b8f7b68c 100644
--- a/swh/deposit/api/urls.py
+++ b/swh/deposit/api/urls.py
@@ -10,14 +10,22 @@
 from django.shortcuts import render
 from django.urls import re_path as url
 
-from ..config import COL_IRI, CONT_FILE_IRI, EDIT_IRI, EM_IRI, SD_IRI, SE_IRI, STATE_IRI
-from .collection import CollectionAPI
-from .content import ContentAPI
-from .edit import EditAPI
-from .edit_media import EditMediaAPI
-from .service_document import ServiceDocumentAPI
-from .state import StateAPI
-from .sword_edit import SwordEditAPI
+from swh.deposit.api.collection import CollectionAPI
+from swh.deposit.api.content import ContentAPI
+from swh.deposit.api.edit import EditAPI
+from swh.deposit.api.edit_media import EditMediaAPI
+from swh.deposit.api.service_document import ServiceDocumentAPI
+from swh.deposit.api.state import StateAPI
+from swh.deposit.api.sword_edit import SwordEditAPI
+from swh.deposit.config import (
+    COL_IRI,
+    CONT_FILE_IRI,
+    EDIT_IRI,
+    EM_IRI,
+    SD_IRI,
+    SE_IRI,
+    STATE_IRI,
+)
 
 
 def api_view(req):
diff --git a/swh/deposit/auth.py b/swh/deposit/auth.py
index f1edd20fe6c64b1e22bf90831d008d86cd406a17..525e8677d3df0a7194be71034a9e703e84d21d43 100644
--- a/swh/deposit/auth.py
+++ b/swh/deposit/auth.py
@@ -21,10 +21,9 @@ from swh.auth.keycloak import (
     KeycloakOpenIDConnect,
     keycloak_error_message,
 )
+from swh.deposit.errors import UNAUTHORIZED, make_error_response
 from swh.deposit.models import DepositClient
 
-from .errors import UNAUTHORIZED, make_error_response
-
 logger = logging.getLogger(__name__)
 
 
diff --git a/swh/deposit/cli/__init__.py b/swh/deposit/cli/__init__.py
index 663035c53f10933f901e476f3eff183cff065d4c..94c2c7599ca1e68874d613ca03ba1f4944ad2e8b 100644
--- a/swh/deposit/cli/__init__.py
+++ b/swh/deposit/cli/__init__.py
@@ -31,10 +31,10 @@ def main():
 
 # These import statements MUST be executed after defining the 'deposit' group
 # since the subcommands in these are defined using this 'deposit' group.
-from . import client  # noqa
+from swh.deposit.cli import client  # noqa
 
 try:
-    from . import admin  # noqa
+    from swh.deposit.cli import admin  # noqa
 except ImportError:  # server part is optional
     logger.debug("admin subcommand not loaded")
 
diff --git a/swh/deposit/migrations/0001_initial.py b/swh/deposit/migrations/0001_initial.py
index fed2566d3cd354360e62f05197e734e9a994d12f..9633a56115149788a94187a9456904c396e731bd 100644
--- a/swh/deposit/migrations/0001_initial.py
+++ b/swh/deposit/migrations/0001_initial.py
@@ -14,7 +14,6 @@ from swh.deposit.models import JSONField
 
 
 class Migration(migrations.Migration):
-
     initial = True
 
     dependencies = [
diff --git a/swh/deposit/migrations/0002_depositrequest_archive.py b/swh/deposit/migrations/0002_depositrequest_archive.py
index b893166755c396691a67487f55ab6cf1e65c4b13..5ed168c6d054e2d89646f19063be0f719a4a1174 100644
--- a/swh/deposit/migrations/0002_depositrequest_archive.py
+++ b/swh/deposit/migrations/0002_depositrequest_archive.py
@@ -8,7 +8,6 @@ import swh.deposit.models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0001_initial"),
     ]
diff --git a/swh/deposit/migrations/0003_temporaryarchive.py b/swh/deposit/migrations/0003_temporaryarchive.py
index a2ac83950f5831edba02d83d74682ae5c6db9686..cb2fdcc925c87bc62d9d415b69f9751c719012e0 100644
--- a/swh/deposit/migrations/0003_temporaryarchive.py
+++ b/swh/deposit/migrations/0003_temporaryarchive.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0002_depositrequest_archive"),
     ]
diff --git a/swh/deposit/migrations/0004_delete_temporaryarchive.py b/swh/deposit/migrations/0004_delete_temporaryarchive.py
index d30b92114132131041d4a219fc54ffdf03900e80..23ecf13a3ff62d07ff8b2899862bfa41bf3386d8 100644
--- a/swh/deposit/migrations/0004_delete_temporaryarchive.py
+++ b/swh/deposit/migrations/0004_delete_temporaryarchive.py
@@ -6,7 +6,6 @@ from django.db import migrations
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0003_temporaryarchive"),
     ]
diff --git a/swh/deposit/migrations/0005_auto_20171019_1436.py b/swh/deposit/migrations/0005_auto_20171019_1436.py
index 3c6b8fe88a17fa0a4434762fcac5c8869191e446..688334dd70980bc5795c8556200fe0bbda25bc70 100644
--- a/swh/deposit/migrations/0005_auto_20171019_1436.py
+++ b/swh/deposit/migrations/0005_auto_20171019_1436.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0004_delete_temporaryarchive"),
     ]
diff --git a/swh/deposit/migrations/0006_depositclient_url.py b/swh/deposit/migrations/0006_depositclient_url.py
index b6baf8894b079916cdc87a2e9b0e30cb73a803a8..ed93bb1d6150d299665289055095a6ee956ebec8 100644
--- a/swh/deposit/migrations/0006_depositclient_url.py
+++ b/swh/deposit/migrations/0006_depositclient_url.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0005_auto_20171019_1436"),
     ]
diff --git a/swh/deposit/migrations/0007_auto_20171129_1609.py b/swh/deposit/migrations/0007_auto_20171129_1609.py
index 0db3703c841b45de9311e37e5e11435abdd382f5..cedc8dd3177033f0fa439cdb27ddc630588dd77b 100644
--- a/swh/deposit/migrations/0007_auto_20171129_1609.py
+++ b/swh/deposit/migrations/0007_auto_20171129_1609.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0006_depositclient_url"),
     ]
diff --git a/swh/deposit/migrations/0008_auto_20171130_1513.py b/swh/deposit/migrations/0008_auto_20171130_1513.py
index 0ccea16098f4b1cfc8f526966e72a35a544a2e88..05b8169926d7ae7fcf689539c09990e98b82cbd1 100644
--- a/swh/deposit/migrations/0008_auto_20171130_1513.py
+++ b/swh/deposit/migrations/0008_auto_20171130_1513.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0007_auto_20171129_1609"),
     ]
diff --git a/swh/deposit/migrations/0009_deposit_parent.py b/swh/deposit/migrations/0009_deposit_parent.py
index 6683cfc7b757c5592fcb4ec32e92b0393516ed5a..4f71498e66e1aeeee4c9ce9efa82629e8980e9b0 100644
--- a/swh/deposit/migrations/0009_deposit_parent.py
+++ b/swh/deposit/migrations/0009_deposit_parent.py
@@ -7,7 +7,6 @@ import django.db.models.deletion
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0008_auto_20171130_1513"),
     ]
diff --git a/swh/deposit/migrations/0010_auto_20180110_0953.py b/swh/deposit/migrations/0010_auto_20180110_0953.py
index 9df2b0c5acd76bf2b0ca3a5b7c9710c6a6a9f2f1..38cd6df90d5093890cf44faa959ec3bb5e21d089 100644
--- a/swh/deposit/migrations/0010_auto_20180110_0953.py
+++ b/swh/deposit/migrations/0010_auto_20180110_0953.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0009_deposit_parent"),
     ]
diff --git a/swh/deposit/migrations/0011_auto_20180115_1510.py b/swh/deposit/migrations/0011_auto_20180115_1510.py
index 4d929f0bfcba9fb7ba9a31ff8e10f0e23a3e3ff4..09c38a2741de0637381f078403c34b0edbe45022 100644
--- a/swh/deposit/migrations/0011_auto_20180115_1510.py
+++ b/swh/deposit/migrations/0011_auto_20180115_1510.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0010_auto_20180110_0953"),
     ]
diff --git a/swh/deposit/migrations/0012_deposit_status_detail.py b/swh/deposit/migrations/0012_deposit_status_detail.py
index 4db4592cc158386608414fbcb3158d911eb793d5..bb55c94b589a0f8fb94a1cf3e96e8adbde689931 100644
--- a/swh/deposit/migrations/0012_deposit_status_detail.py
+++ b/swh/deposit/migrations/0012_deposit_status_detail.py
@@ -8,7 +8,6 @@ from swh.deposit.models import JSONField
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0011_auto_20180115_1510"),
     ]
diff --git a/swh/deposit/migrations/0013_depositrequest_raw_metadata.py b/swh/deposit/migrations/0013_depositrequest_raw_metadata.py
index 58055b37ddf089a39820f6761f73c9050d0ab9d5..21a718ab8416d0ab831fa93b891567e18c585a79 100644
--- a/swh/deposit/migrations/0013_depositrequest_raw_metadata.py
+++ b/swh/deposit/migrations/0013_depositrequest_raw_metadata.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0012_deposit_status_detail"),
     ]
diff --git a/swh/deposit/migrations/0014_auto_20180720_1221.py b/swh/deposit/migrations/0014_auto_20180720_1221.py
index 50ff8ba99b61f3dff1fd79935b880959e2875692..29c05b28cef181a556c97b103bba61f718eabe74 100644
--- a/swh/deposit/migrations/0014_auto_20180720_1221.py
+++ b/swh/deposit/migrations/0014_auto_20180720_1221.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0013_depositrequest_raw_metadata"),
     ]
diff --git a/swh/deposit/migrations/0015_depositrequest_typemigration.py b/swh/deposit/migrations/0015_depositrequest_typemigration.py
index f9748109ed44bde615d2160ffbecb647a4491eb4..1e0b964928f91cd1cab9fb90f27384509c8ff056 100644
--- a/swh/deposit/migrations/0015_depositrequest_typemigration.py
+++ b/swh/deposit/migrations/0015_depositrequest_typemigration.py
@@ -16,7 +16,6 @@ def populate_deposit_type2(apps, schema_editor):
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0014_auto_20180720_1221"),
     ]
diff --git a/swh/deposit/migrations/0016_auto_20190507_1408.py b/swh/deposit/migrations/0016_auto_20190507_1408.py
index 1d4aa2571cb092a40e21fd6ec9cf2147d26d9593..dec79a20a036dfec1a639a4d5c784413b43baa39 100644
--- a/swh/deposit/migrations/0016_auto_20190507_1408.py
+++ b/swh/deposit/migrations/0016_auto_20190507_1408.py
@@ -6,7 +6,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0015_depositrequest_typemigration"),
     ]
diff --git a/swh/deposit/migrations/0017_auto_20190925_0906.py b/swh/deposit/migrations/0017_auto_20190925_0906.py
index 3aa8305cb149ec89c9de54d8b4f8456fa90b7a91..f90180950ed0fb2560ccf8275df273400a81c69b 100644
--- a/swh/deposit/migrations/0017_auto_20190925_0906.py
+++ b/swh/deposit/migrations/0017_auto_20190925_0906.py
@@ -7,7 +7,6 @@ import django.db.models.deletion
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0016_auto_20190507_1408"),
     ]
diff --git a/swh/deposit/migrations/0019_auto_20200519_1035.py b/swh/deposit/migrations/0019_auto_20200519_1035.py
index f1e0911b54112accc0a5a1ab8f17abefa84ac861..561c2836a12b443187aabdf094cb7b3fba9ac594 100644
--- a/swh/deposit/migrations/0019_auto_20200519_1035.py
+++ b/swh/deposit/migrations/0019_auto_20200519_1035.py
@@ -6,7 +6,6 @@ from django.db import migrations
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0018_migrate_swhids"),
     ]
diff --git a/swh/deposit/migrations/0020_auto_20200929_0855.py b/swh/deposit/migrations/0020_auto_20200929_0855.py
index dafeb8c8da5fd4cc07cf0bd332a7078659111624..7f7476d0cfe76057cd40c91a21399b19c376a1b0 100644
--- a/swh/deposit/migrations/0020_auto_20200929_0855.py
+++ b/swh/deposit/migrations/0020_auto_20200929_0855.py
@@ -6,7 +6,6 @@ from django.db import migrations
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0019_auto_20200519_1035"),
     ]
diff --git a/swh/deposit/migrations/0021_deposit_origin_url_20201124_1438.py b/swh/deposit/migrations/0021_deposit_origin_url_20201124_1438.py
index 326a407bdc02d2d42ac469728a641fab4b7fd10d..6227b5ae3966023b2103df44437f80db68a24782 100644
--- a/swh/deposit/migrations/0021_deposit_origin_url_20201124_1438.py
+++ b/swh/deposit/migrations/0021_deposit_origin_url_20201124_1438.py
@@ -12,7 +12,6 @@ def fill_origin_url(apps, schema_editor):
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0020_auto_20200929_0855"),
     ]
diff --git a/swh/deposit/migrations/0022_auto_20220223_1542.py b/swh/deposit/migrations/0022_auto_20220223_1542.py
index f78c42ed74ca68cbe6e099cea64914d93dc1b49a..9d584b0443eb35e88deb62382f0240b4d233af1d 100644
--- a/swh/deposit/migrations/0022_auto_20220223_1542.py
+++ b/swh/deposit/migrations/0022_auto_20220223_1542.py
@@ -37,7 +37,6 @@ def fill_deposit_type(apps, schema_editor):
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0021_deposit_origin_url_20201124_1438"),
     ]
diff --git a/swh/deposit/migrations/0023_alter_deposit_status_detail_alter_deposit_type_and_more.py b/swh/deposit/migrations/0023_alter_deposit_status_detail_alter_deposit_type_and_more.py
index db8b79bfbccc4a123e7c780a028b0128679ae04b..f6900c9fd31f15dc4824a4424e61f77fc9a3f8a9 100644
--- a/swh/deposit/migrations/0023_alter_deposit_status_detail_alter_deposit_type_and_more.py
+++ b/swh/deposit/migrations/0023_alter_deposit_status_detail_alter_deposit_type_and_more.py
@@ -7,7 +7,6 @@ from swh.deposit.models import JSONField
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
         ("deposit", "0022_auto_20220223_1542"),
     ]
diff --git a/swh/deposit/models.py b/swh/deposit/models.py
index 32478b9be287e382212e941591a4827324dfb32b..20689599186e582488dfd283bbc92c91992c192f 100644
--- a/swh/deposit/models.py
+++ b/swh/deposit/models.py
@@ -21,8 +21,7 @@ except ImportError:
     from django.contrib.postgres.fields import JSONField as OrigJSONField
 
 from swh.auth.django.models import OIDCUser
-
-from .config import (
+from swh.deposit.config import (
     ARCHIVE_TYPE,
     DEPOSIT_STATUS_DEPOSITED,
     DEPOSIT_STATUS_LOAD_FAILURE,
diff --git a/swh/deposit/settings/development.py b/swh/deposit/settings/development.py
index 08b9a4fd945a44fae85d61d9bc51daa437859022..484d39d753ff806bbfc9ff4a25e35325e3166a93 100644
--- a/swh/deposit/settings/development.py
+++ b/swh/deposit/settings/development.py
@@ -3,7 +3,7 @@
 # License: GNU General Public License version 3, or any later version
 # See top-level LICENSE file for more information
 
-from .common import *  # noqa
+from swh.deposit.settings.common import *  # noqa
 
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
diff --git a/swh/deposit/settings/production.py b/swh/deposit/settings/production.py
index 33e1d0639ac6a4289e2ad0a3f469bb2550403cfd..bf6a578285455bcc8c28e7c7a531f09f9a7a2d17 100644
--- a/swh/deposit/settings/production.py
+++ b/swh/deposit/settings/production.py
@@ -8,9 +8,8 @@ import os
 import django
 
 from swh.core import config
-
-from .common import *  # noqa
-from .common import ALLOWED_HOSTS, CACHES
+from swh.deposit.settings.common import *  # noqa
+from swh.deposit.settings.common import ALLOWED_HOSTS, CACHES
 
 ALLOWED_HOSTS += ["deposit.softwareheritage.org"]
 # Setup support for proxy headers
diff --git a/swh/deposit/settings/testing.py b/swh/deposit/settings/testing.py
index 10e683f22f455966953ff92cb0749144f624fc06..c8a971898fee272e15a6c99ec49f04248ab1c707 100644
--- a/swh/deposit/settings/testing.py
+++ b/swh/deposit/settings/testing.py
@@ -3,10 +3,10 @@
 # License: GNU General Public License version 3, or any later version
 # See top-level LICENSE file for more information
 
-from .common import *  # noqa
-from .common import ALLOWED_HOSTS
-from .development import *  # noqa
-from .development import INSTALLED_APPS
+from swh.deposit.settings.common import *  # noqa
+from swh.deposit.settings.common import ALLOWED_HOSTS
+from swh.deposit.settings.development import *  # noqa
+from swh.deposit.settings.development import INSTALLED_APPS
 
 # django setup
 ALLOWED_HOSTS += ["testserver"]
diff --git a/swh/deposit/templates/homepage.html b/swh/deposit/templates/homepage.html
index b9fab5fe3034ffe1511b69a1e86f0ea8ead7a4a8..6832007e361f1c9506957a2800a1b8928bb819fe 100644
--- a/swh/deposit/templates/homepage.html
+++ b/swh/deposit/templates/homepage.html
@@ -34,8 +34,8 @@ href="https://archive.softwareheritage.org">SWH archive</a>).
 <p>
   <ul>
     <li><a href="https://docs.softwareheritage.org/devel/swh-deposit">Index</a></li>
-    <li><a href="https://docs.softwareheritage.org/devel/swh-deposit/user-manual.html">User Manual</a></li>
-    <li><a href="https://docs.softwareheritage.org/devel/swh-deposit/spec-api.html">Specification</a></li>
+    <li><a href="https://docs.softwareheritage.org/devel/swh-deposit/api/user-manual.html#deposit-user-manual">User Manual</a></li>
+    <li><a href="https://docs.softwareheritage.org/devel/swh-deposit/api/api-documentation.html#deposit-api-specifications">Specification</a></li>
   </ul>
 </p>
 
diff --git a/swh/deposit/tests/api/test_basic_auth.py b/swh/deposit/tests/api/test_basic_auth.py
index c5ca171c1d47d1e0194f3dc69061a1730d7453a4..481458a7b316c22a377c7a525180ff2c947ccf9d 100644
--- a/swh/deposit/tests/api/test_basic_auth.py
+++ b/swh/deposit/tests/api/test_basic_auth.py
@@ -11,8 +11,7 @@ from django.urls import reverse_lazy as reverse
 import pytest
 
 from swh.deposit.config import SD_IRI
-
-from .test_service_document import check_response
+from swh.deposit.tests.api.test_service_document import check_response
 
 
 @pytest.fixture()
diff --git a/swh/deposit/tests/api/test_checks.py b/swh/deposit/tests/api/test_checks.py
index bb19a708e9d1916723719314363ffe8f5e2d00ee..6e93c1524275201ae290a1fa6263274f0667d59a 100644
--- a/swh/deposit/tests/api/test_checks.py
+++ b/swh/deposit/tests/api/test_checks.py
@@ -40,7 +40,10 @@ PROVENANCE_XML = """
 
 _parameters1 = [
     pytest.param(textwrap.dedent(metadata_ok), id=id_)
-    for (id_, metadata_ok,) in [
+    for (
+        id_,
+        metadata_ok,
+    ) in [
         (
             "atom-only-with-name",
             f"""\
@@ -1222,7 +1225,7 @@ def test_api_checks_check_metadata_ko_schema(
         "metadata"
     ]
 
-    for (detail, expected_summary) in zip(error_detail["metadata"], expected_summaries):
+    for detail, expected_summary in zip(error_detail["metadata"], expected_summaries):
         assert detail["fields"] == expected_summary["fields"]
 
         # xmlschema returns very detailed errors, we cannot reasonably test them
diff --git a/swh/deposit/tests/api/test_collection_add_to_origin.py b/swh/deposit/tests/api/test_collection_add_to_origin.py
index 91cef83f86238d1f9902c0b38c29dedb35f43b18..bb4d9b1af0a9fcdaa7dbea7d66699e60e3cd3fd6 100644
--- a/swh/deposit/tests/api/test_collection_add_to_origin.py
+++ b/swh/deposit/tests/api/test_collection_add_to_origin.py
@@ -10,10 +10,9 @@ from swh.deposit.config import COL_IRI, DEPOSIT_STATUS_LOAD_SUCCESS
 from swh.deposit.models import Deposit
 from swh.deposit.parsers import parse_xml
 from swh.deposit.tests.common import post_atom
+from swh.deposit.tests.conftest import internal_create_deposit
 from swh.deposit.utils import NAMESPACES
 
-from ..conftest import internal_create_deposit
-
 
 def test_add_deposit_with_add_to_origin(
     authenticated_client,
diff --git a/swh/deposit/tests/api/test_collection_reuse_slug.py b/swh/deposit/tests/api/test_collection_reuse_slug.py
index 6968f51bdd0c44ac30589e16a05eed0f76e4306c..0c3f2cdaf695222adc20810379254c99517e5e34 100644
--- a/swh/deposit/tests/api/test_collection_reuse_slug.py
+++ b/swh/deposit/tests/api/test_collection_reuse_slug.py
@@ -16,10 +16,9 @@ from swh.deposit.config import (
 from swh.deposit.models import Deposit
 from swh.deposit.parsers import parse_xml
 from swh.deposit.tests.common import post_atom
+from swh.deposit.tests.conftest import internal_create_deposit
 from swh.deposit.utils import NAMESPACES
 
-from ..conftest import internal_create_deposit
-
 
 def test_act_on_deposit_rejected_is_not_permitted(
     authenticated_client, deposit_collection, rejected_deposit, atom_dataset
diff --git a/swh/deposit/tests/api/test_deposit_private_check.py b/swh/deposit/tests/api/test_deposit_private_check.py
index f582153786c3577cce8db7f9651fcf923f190204..f3cb00ce64314aee977901f5f5aeed62ddcc8a23 100644
--- a/swh/deposit/tests/api/test_deposit_private_check.py
+++ b/swh/deposit/tests/api/test_deposit_private_check.py
@@ -27,13 +27,14 @@ from swh.deposit.config import (
 from swh.deposit.models import Deposit
 from swh.deposit.parsers import parse_xml
 from swh.deposit.tests.common import (
+    SUPPORTED_TARBALL_MODES,
     create_arborescence_archive,
     create_archive_with_archive,
+    post_archive,
+    post_atom,
 )
 from swh.deposit.utils import NAMESPACES
 
-from ..common import SUPPORTED_TARBALL_MODES, post_archive, post_atom
-
 PRIVATE_CHECK_DEPOSIT_NC = PRIVATE_CHECK_DEPOSIT + "-nc"
 
 
diff --git a/swh/deposit/tests/api/test_deposit_private_list.py b/swh/deposit/tests/api/test_deposit_private_list.py
index 4511a3cca0328391629da36eae35639917e6e76f..0e800627c89c9d4920beeb544142f5efa902924f 100644
--- a/swh/deposit/tests/api/test_deposit_private_list.py
+++ b/swh/deposit/tests/api/test_deposit_private_list.py
@@ -161,7 +161,6 @@ def test_deposit_list_for_username(
     deposit_user,
     deposit_another_user,
 ):
-
     # create a new deposit with a user different from deposit_user,
     # the one that created completed_deposit
     internal_create_deposit(
@@ -227,7 +226,6 @@ def test_deposit_list_datatables_pagination(
     deposits,
     authenticated_client,
 ):
-
     deposits_data = []
     for i in range(len(deposits)):
         query_params = QueryDict(mutable=True)
@@ -254,7 +252,6 @@ def test_deposit_list_datatables_ordering(
     authenticated_client,
     sort_direction,
 ):
-
     deposits_date_sorted = list(sorted(deposits, key=lambda d: d.reception_date))
 
     if sort_direction == "desc":
@@ -289,7 +286,6 @@ def test_deposit_list_datatables_search(
     deposits,
     authenticated_client,
 ):
-
     query_params = QueryDict(mutable=True)
     query_params.update(
         {
@@ -323,7 +319,6 @@ def test_deposit_list_datatables_exclude_pattern(
     deposits,
     authenticated_client,
 ):
-
     query_params = QueryDict(mutable=True)
     query_params.update(
         {
@@ -360,7 +355,6 @@ def test_deposit_list_datatables_username(
     deposit_another_collection,
     authenticated_client,
 ):
-
     # create a new deposit with a user different from deposit_user,
     # the one that created completed_deposit
     completed_deposit_another_user = internal_create_deposit(
diff --git a/swh/deposit/tests/api/test_deposit_private_read_archive.py b/swh/deposit/tests/api/test_deposit_private_read_archive.py
index f2e3e07d03c6c9c17d40a1c517e3ab5d0557feef..0b9755acc34082cae940544a98bdf8e80424ca4c 100644
--- a/swh/deposit/tests/api/test_deposit_private_read_archive.py
+++ b/swh/deposit/tests/api/test_deposit_private_read_archive.py
@@ -12,9 +12,11 @@ from rest_framework import status
 from swh.deposit.api.private.deposit_read import aggregate_tarballs
 from swh.deposit.config import EM_IRI, PRIVATE_GET_RAW_CONTENT
 from swh.deposit.models import DepositRequest
-from swh.deposit.tests.common import create_arborescence_archive
-
-from ..common import compute_info, post_archive
+from swh.deposit.tests.common import (
+    compute_info,
+    create_arborescence_archive,
+    post_archive,
+)
 
 PRIVATE_GET_RAW_CONTENT_NC = PRIVATE_GET_RAW_CONTENT + "-nc"
 
diff --git a/swh/deposit/tests/cli/test_client.py b/swh/deposit/tests/cli/test_client.py
index a169960174ad04455267388ce52eb4b9272f2032..9c4baed2d12874d69a521187e89381263ae56a3f 100644
--- a/swh/deposit/tests/cli/test_client.py
+++ b/swh/deposit/tests/cli/test_client.py
@@ -29,11 +29,10 @@ from swh.deposit.client import (
     ServiceDocumentDepositClient,
 )
 from swh.deposit.parsers import parse_xml
+from swh.deposit.tests.conftest import TEST_USER
 from swh.deposit.utils import NAMESPACES
 from swh.model.exceptions import ValidationError
 
-from ..conftest import TEST_USER
-
 
 def generate_slug() -> str:
     """Generate a slug (sample purposes)."""
@@ -327,7 +326,7 @@ def test_cli_single_minimal_deposit_with_slug(
         assert authors[0].text == "Jane Doe"
 
     count_warnings = 0
-    for (_, log_level, _) in caplog.record_tuples:
+    for _, log_level, _ in caplog.record_tuples:
         count_warnings += 1 if log_level == logging.WARNING else 0
 
     assert (
@@ -412,7 +411,7 @@ def test_cli_single_minimal_deposit_with_create_origin(
         assert authors[0].text == "Jane Doe"
 
     count_warnings = 0
-    for (_, log_level, _) in caplog.record_tuples:
+    for _, log_level, _ in caplog.record_tuples:
         count_warnings += 1 if log_level == logging.WARNING else 0
 
     assert (
@@ -916,7 +915,7 @@ def test_cli_metadata_only_deposit_full_metadata_file(
 
     count_warnings = 0
     warning_record: Optional[str] = None
-    for (_, log_level, msg) in caplog.record_tuples:
+    for _, log_level, msg in caplog.record_tuples:
         if log_level == logging.WARNING:
             count_warnings += 1
             warning_record = msg
@@ -1025,7 +1024,7 @@ def test_cli_deposit_warning_missing_origin(
     )
     # fmt: on
 
-    for (_, log_level, _) in caplog.record_tuples:
+    for _, log_level, _ in caplog.record_tuples:
         # all messages are info or below messages so everything is fine
         assert log_level < logging.WARNING
 
diff --git a/swh/deposit/tests/conftest.py b/swh/deposit/tests/conftest.py
index 24ea99f209600595baae1cff63cbee53324a901e..a752da003dd714871bd63eec464c653f3e2966f2 100644
--- a/swh/deposit/tests/conftest.py
+++ b/swh/deposit/tests/conftest.py
@@ -544,7 +544,6 @@ def partial_deposit_with_metadata(
 def partial_deposit_only_metadata(
     deposit_collection, authenticated_client, atom_dataset
 ):
-
     response = post_atom(
         authenticated_client,
         reverse(COL_IRI, args=[deposit_collection.name]),
diff --git a/tox.ini b/tox.ini
index 52e042f0b4e6ec3893d2b31aa0176ad70fc9fe53..5ce2689be3735d626f8e2a181f9f656d68706e94 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,7 +1,11 @@
 [tox]
-requires =
-  tox>4
-envlist=flake8,mypy,py3-django2,py3-django3
+minversion = 4
+envlist =
+  black
+  flake8
+  mypy
+  py3-django2
+  py3-django3
 
 [testenv]
 extras =
@@ -16,15 +20,21 @@ deps =
   django2: Django>=2,<3
   django3: Django>=3,<4
 commands =
-  pytest \
-  !dev: --cov {envsitepackagesdir}/swh/deposit --cov-branch \
-        {envsitepackagesdir}/swh/deposit \
-        {posargs}
+  pytest --doctest-modules \
+         --rootdir {envsitepackagesdir} \
+         --cov={envsitepackagesdir}/swh/deposit \
+         --cov-branch \
+         --ignore {envsitepackagesdir}/swh/deposit/settings/ \
+         {envsitepackagesdir}/swh/deposit \
+         {posargs}
+# --rootdir (with --import-mode from pytest.ini) are required to make tests
+# that depends on the test file to be a proper submodule of the swh namespace
+# after migration to PEP420 (implicit namespace).
 
 [testenv:black]
 skip_install = true
 deps =
-  black==22.10.0
+  black==23.1.0
 commands =
   {envpython} -m black --check swh
 
@@ -52,32 +62,14 @@ commands =
 # breaking doc build
 [testenv:sphinx]
 allowlist_externals = make
-usedevelop = true
 extras =
   testing
 deps =
-  # fetch and install swh-docs in develop mode
-  -e git+https://gitlab.softwareheritage.org/swh/devel/swh-docs.git\#egg=swh.docs
+  # fetch and install swh-docs
+  git+https://gitlab.softwareheritage.org/swh/devel/swh-docs.git\#egg=swh.docs
 setenv =
   SWH_PACKAGE_DOC_TOX_BUILD = 1
   # turn warnings into errors
   SPHINXOPTS = -W
 commands =
-  make -I ../.tox/sphinx/src/swh-docs/swh/ -C docs
-
-# build documentation only inside swh-environment using local state
-# of swh-docs package
-[testenv:sphinx-dev]
-allowlist_externals = make
-usedevelop = true
-extras =
-  testing
-deps =
-  # install swh-docs in develop mode
-  -e ../swh-docs
-setenv =
-  SWH_PACKAGE_DOC_TOX_BUILD = 1
-  # turn warnings into errors
-  SPHINXOPTS = -W
-commands =
-  make -I ../.tox/sphinx-dev/src/swh-docs/swh/ -C docs
+  make -I {env_dir}/share/swh-docs -C docs