diff --git a/docs/.gitlab-ci.yml b/docs/.gitlab-ci.yml
index 4e3544c02095d2cc6e5e88431f6d7fe3bac0221a..c9d2fff925f56888c96d6fbf324116bce305e285 100644
--- a/docs/.gitlab-ci.yml
+++ b/docs/.gitlab-ci.yml
@@ -34,4 +34,4 @@ lint-documentation:
     - vale sync
 
   script:
-    - vale --glob='!*/_?ipam.py|!*/services/README\.md|!*/apidocs/*|!*/migrations/*' $CI_PROJECT_DIR/docs/source $CI_PROJECT_DIR/gso
+    - vale --glob='!*/migrations/*' $CI_PROJECT_DIR/docs/source $CI_PROJECT_DIR/gso
diff --git a/docs/source/conf.py b/docs/source/conf.py
index ec4ba29e5ae345953d6af2c5228c85f2a9763585..a74a714db7d5dac99f385266eb523b7ed5555a30 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -21,6 +21,8 @@ autodoc2_packages = [
     "../../gso"
 ]
 autodoc2_render_plugin = "myst"
+autodoc2_hidden_objects = ["undoc", "inherited"]
+autodoc2_index_template = None
 
 # -- Options for HTML output -------------------------------------------------
 html_theme = 'sphinx_rtd_theme'
diff --git a/docs/source/glossary.md b/docs/source/glossary.md
index e256c6a20a27c0ccb56f05feddff5447387dd08e..861b864fd63f860874c29d6dae504094eb02f0d6 100644
--- a/docs/source/glossary.md
+++ b/docs/source/glossary.md
@@ -1,8 +1,27 @@
 # Glossary of terms
 
 {.glossary}
+CRUD
+: Create, Read, Update, Delete
+
+FQDN
+: Fully Quantified Domain Name
+
 GSO
 : GÉANT Service Orchestrator
 
+IS-IS
+: Intermediate System to Intermediate System: a routing protocol described in 
+<a href="https://datatracker.ietf.org/doc/html/rfc7142" target="_blank">RFC 7142</a>.
+
+ISO
+: International Organisation for Standardisation
+
+LSO
+: Lightweight Service Orchestrator
+
+NET
+: Network Entity Title: used for {term}`IS-IS` routing.
+
 WFO
 : <a href="https://workfloworchestrator.org/" target="_blank">Workflow Orchestrator</a>
diff --git a/docs/vale/styles/Vocab/Sphinx/accept.txt b/docs/vale/styles/Vocab/Sphinx/accept.txt
index 50dad4e68dc09599fdf6d1855ec6120ab1fe15b6..11d98e5937e44a8b73fee9a6d5bacd5c10e47d4d 100644
--- a/docs/vale/styles/Vocab/Sphinx/accept.txt
+++ b/docs/vale/styles/Vocab/Sphinx/accept.txt
@@ -2,3 +2,4 @@ toctree
 [Ss]ubpackages
 virtualenv
 [Pp]revious
+mypy
diff --git a/gso/products/product_blocks/device.py b/gso/products/product_blocks/device.py
index dd2b3f4a5460e855830b0b7ca5b281940895d4c7..897540affc531aa0169fa2140ec2ba58a0231997 100644
--- a/gso/products/product_blocks/device.py
+++ b/gso/products/product_blocks/device.py
@@ -31,7 +31,7 @@ class DeviceRole(strEnum):
 class DeviceBlockInactive(
     ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="DeviceBlock"
 ):
-    """A device that is being currently inactive. See {class}`DeviceBlock`."""
+    """A device that's being currently inactive. See {class}`DeviceBlock`."""
 
     device_fqdn: Optional[str] = None
     device_ts_address: Optional[str] = None
@@ -49,7 +49,7 @@ class DeviceBlockInactive(
 
 
 class DeviceBlockProvisioning(DeviceBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
-    """A device that is being provisioned. See {class}`DeviceBlock`."""
+    """A device that's being provisioned. See {class}`DeviceBlock`."""
 
     device_fqdn: str
     device_ts_address: str
@@ -67,7 +67,7 @@ class DeviceBlockProvisioning(DeviceBlockInactive, lifecycle=[SubscriptionLifecy
 
 
 class DeviceBlock(DeviceBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
-    """A device that is currently deployed in the network."""
+    """A device that's currently deployed in the network."""
 
     device_fqdn: str
     """{term}`FQDN` of a device."""
diff --git a/gso/services/README.md b/gso/services/README.md
index dfbf4efe7bba903b88eaa17dc529885d88cc34f5..60e5253134f5598b15ca153fd0a075fd1390b206 100644
--- a/gso/services/README.md
+++ b/gso/services/README.md
@@ -1,3 +1,4 @@
+<!-- vale off -->
 ## IPAM
 
 ### Example configuration
diff --git a/gso/services/__init__.py b/gso/services/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1c03dc699fd86ccb41275feab94b126dcc6e1be1 100644
--- a/gso/services/__init__.py
+++ b/gso/services/__init__.py
@@ -0,0 +1 @@
+"""External services that the service orchestrator can interact with."""
diff --git a/gso/services/provisioning_proxy.py b/gso/services/provisioning_proxy.py
index e0cdd419b3a3a975199e560447dc861a1cfc7182..e83f15b99f1cf7a6716c13ecca06558624bcbf50 100644
--- a/gso/services/provisioning_proxy.py
+++ b/gso/services/provisioning_proxy.py
@@ -1,6 +1,6 @@
-"""The Provisioning Proxy service, which interacts with LSO running externally.
+"""The Provisioning Proxy service, which interacts with {term}`LSO` running externally.
 
-LSO is responsible for executing Ansible playbooks, that deploy subscriptions.
+{term}`LSO` is responsible for executing Ansible playbooks, that deploy subscriptions.
 """
 import json
 import logging
@@ -28,23 +28,23 @@ DEFAULT_LABEL = "Provisioning proxy is running. Please come back later for the r
 
 
 class CUDOperation(strEnum):
-    """Enumerator for different CRUD operations that the provisioning proxy supports.
+    """Enumerator for different {term}`CRUD` operations that the provisioning proxy supports.
 
-    Read isn't applicable, hence these become CUD and not CRUD operations.
+    Read isn't applicable, hence the missing R.
     """
 
     POST = "POST"
-    """Creation is done with a POST request."""
+    """Creation is done with a `POST` request."""
     PUT = "PUT"
-    """Updating is done with a PUT request."""
+    """Updating is done with a `PUT` request."""
     DELETE = "DELETE"
-    """Removal is done with a DELETE request."""
+    """Removal is done with a `DELETE` request."""
 
 
 def _send_request(endpoint: str, parameters: dict, process_id: UUIDstr, operation: CUDOperation) -> None:
-    """Send a request to LSO. The callback address is derived using the process ID provided.
+    """Send a request to {term}`LSO`. The callback address is derived using the process ID provided.
 
-    :param endpoint: The LSO-specific endpoint to call, depending on the type of service object that's acted upon.
+    :param endpoint: The {term}`LSO`-specific endpoint to call, depending on the type of service object that's acted upon.
     :type endpoint: str
     :param parameters: JSON body for the request, which will almost always at least consist of a subscription object,
         and a boolean value to indicate a dry run.
@@ -60,7 +60,7 @@ def _send_request(endpoint: str, parameters: dict, process_id: UUIDstr, operatio
     pp_params = oss.PROVISIONING_PROXY
     assert pp_params
 
-    # Build up a callback URL for the Provisioning Proxy to return its results to.
+    # Build up a callback URL of the Provisioning Proxy to return its results to.
     callback_url = f"{settings.load_oss_params().GENERAL.public_hostname}" f"/api/processes/{process_id}/resume"
     logger.debug(f"[provisioning proxy] provisioning for process {process_id}")
     logger.debug(f"[provisioning proxy] Callback URL set to {callback_url}")
@@ -84,7 +84,7 @@ def _send_request(endpoint: str, parameters: dict, process_id: UUIDstr, operatio
 
 
 def provision_device(subscription: DeviceProvisioning, process_id: UUIDstr, dry_run: bool = True) -> None:
-    """Provision a new device using LSO.
+    """Provision a new device using {term}`LSO`.
 
     :param subscription: The subscription object that's to be provisioned.
     :type subscription: {class}`DeviceProvisioning`
@@ -102,7 +102,7 @@ def provision_device(subscription: DeviceProvisioning, process_id: UUIDstr, dry_
 def provision_ip_trunk(
     subscription: IptrunkProvisioning, process_id: UUIDstr, config_object: str, dry_run: bool = True
 ) -> None:
-    """Provision an IP trunk service using LSO.
+    """Provision an IP trunk service using {term}`LSO`.
 
     :param subscription: The subscription object that's to be provisioned.
     :type subscription: {class}`IptrunkProvisioning`
@@ -125,7 +125,7 @@ def provision_ip_trunk(
 
 
 def deprovision_ip_trunk(subscription: Iptrunk, process_id: UUIDstr, dry_run: bool = True) -> None:
-    """Deprovision an IP trunk service using LSO.
+    """Deprovision an IP trunk service using {term}`LSO`.
 
     :param subscription: The subscription object that's to be provisioned.
     :type subscription: {class}`IptrunkProvisioning`
@@ -152,10 +152,10 @@ def _await_pp_results(subscription: SubscriptionModel, label_text: str = DEFAULT
 
     :param subscription: The current subscription that the provisioning proxy is acting on.
     :type subscription: {class}`orchestrator.domain.SubscriptionModel`
-    :param label_text: A label that is displayed to the operator when the provisioning proxy has not returned its
+    :param label_text: A label that's displayed to the operator when the provisioning proxy hasn't returned its
         results yet. Defaults to `DEFAULT_LABEL`.
     :type label_text: str
-    :return: The input that is given by the provisioning proxy, that should contain run results, and a `confirm`
+    :return: The input that's given by the provisioning proxy, that should contain run results, and a `confirm`
         boolean set to `True`.
     :rtype: {class}`orchestrator.types.FormGenerator`
     """
@@ -240,11 +240,11 @@ def pp_interaction(provisioning_step: Step, attempts: int) -> StepList:
         - An input step that presents the user with the results, where they must be confirmed.
 
     All these steps are wrapped in a {class}`orchestrator.workflow.conditional`. This ensures that when provisioning was
-    already successful, these steps are skipped. This mechanism is quite a dirty hack, and it is planned to be addressed
+    already successful, these steps are skipped. This mechanism is quite a dirty hack, and it's planned to be addressed
     in a later release.
 
     The parameter `attempts` indicates how many times a provisioning may be attempted. When this amount is exceeded, and
-    it is still not successful, the workflow will be aborted.
+    it's still not successful, the workflow will be aborted.
 
     :param provisioning_step: The step that executes an interaction with the provisioning proxy.
     :type provisioning_step: {class}`orchestrator.workflow.Step`
diff --git a/test-docs.sh b/test-docs.sh
index a1608b2283c341ee45afe561b4a6200b341e5b97..7415bda463ac80b3ab548a0d457ea9d56d030d92 100755
--- a/test-docs.sh
+++ b/test-docs.sh
@@ -1,8 +1,8 @@
 #!/bin/bash
 
 if [ ! -d ./docs/vale/styles/proselint ] || [ ! -d ./docs/vale/styles/Microsoft ]; then
-  docker run -it --rm -v $(pwd)/docs:/docs jdkato/vale:latest --config="/docs/vale/.vale.ini" sync
+  docker run -it --rm -v "$(pwd)"/docs:/docs jdkato/vale:latest --config="/docs/vale/.vale.ini" sync
 fi
 
-docker run -it --rm -v $(pwd):/gso jdkato/vale:latest --glob='!*/_?ipam.py|!*/apidocs/*|!*/migrations/*' \
+docker run -it --rm -v $(pwd):/gso jdkato/vale:latest --glob='!*/migrations/*' \
 --config="/gso/docs/vale/.vale.ini" /gso/docs/source /gso/gso