diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3c08f52b8801b7390129fa8b705d94d067c96b0a..53001d3030e62280222c943406fec0aa20a755ca 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,6 @@
 stages:
-  - test
+  - tests
+  - quality
   - package
   - integration
   - prerelease
@@ -41,6 +42,14 @@ workflow:
     paths:
       - node_modules/
 
+.rust_env:
+  image: registry.duniter.org/docker/rust/rust-x64-stable-ci:latest
+  tags:
+    - redshift
+  before_script:
+    - export PATH="$HOME/.cargo/bin:$PATH"
+    - rustup show
+    - rustc --version && cargo --version
 
 #pages:
 #  <<: *nvm_env
@@ -66,7 +75,26 @@ workflow:
 #    - loki
 #    - dev
 
-test:
+tests:rs:
+  extends: .nvm_env
+  rules:
+    - if: $CI_COMMIT_TAG
+      when: never
+    - if: $CI_MERGE_REQUEST_ID
+      when: manual
+    - when: on_success
+  stage: tests
+  script: 
+    - cargo fmt -- --version
+    - cargo fmt -- --check
+    - RUSTFLAGS="-D warnings" cargo build
+    - cargo test --all
+    # Coverage (doesn't make sense yet)
+    #- cargo test --doc
+    #- cargo tarpaulin --ignore-tests -iv --out Xml
+    #- bash <(curl -s https://codecov.io/bash)
+
+tests:ts:
   <<: *nvm_env
   rules:
     - if: $CI_COMMIT_TAG
@@ -74,7 +102,7 @@ test:
     - if: $CI_MERGE_REQUEST_ID
       when: manual
     - when: on_success
-  stage: test
+  stage: tests
   script:
     - yarn
     - yarn test
@@ -88,6 +116,26 @@ test:
       - coverage.tar.gz
     expire_in: 4h
 
+clippy:
+  extends: .nvm_env
+  rules:
+    - if: $CI_COMMIT_TAG
+      when: never
+    - if: $CI_MERGE_REQUEST_ID
+    - when: manual
+  stage: quality
+  script:
+    - cargo clippy -- -V
+    - cargo clippy --all --tests -- -D warnings --verbose
+    
+audit_dependencies:
+  extends: .rust_env
+  before_script:
+    - cargo deny -V
+  stage: quality
+  script:
+    - cargo deny check
+
 .integration_rules: &integration_rules
   rules:
     - if: $CI_COMMIT_TAG
diff --git a/deny.toml b/deny.toml
new file mode 100644
index 0000000000000000000000000000000000000000..7ea098af15d4d4f92ffe4ba45343304b57e57717
--- /dev/null
+++ b/deny.toml
@@ -0,0 +1,50 @@
+[bans]
+multiple-versions = "warn"
+deny = [
+    # color-backtrace is nice but brings in too many dependencies and that are often outdated, so not worth it for us.
+    { name = "color-backtrace" },
+
+    # deprecated
+    { name = "quickersort" },
+
+    # term is not fully maintained, and termcolor is replacing it
+    { name = "term" },
+]
+skip-tree = [ 
+    { name = "winapi", version = "<= 0.3" },
+]
+
+[licenses]
+unlicensed = "deny"
+# We want really high confidence when inferring licenses from text
+confidence-threshold = 0.92
+allow = [
+    "AGPL-3.0",
+    "Apache-2.0",
+    "BSD-2-Clause",
+    "BSD-3-Clause",
+    "CC0-1.0",
+    "ISC",
+    "MIT",
+    "MPL-2.0",
+    "OpenSSL",
+    "Zlib"
+]
+
+[[licenses.clarify]]
+name = "ring"
+# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses
+# https://spdx.org/licenses/OpenSSL.html
+# ISC - Both BoringSSL and ring use this for their new files
+# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT
+# license, for third_party/fiat, which, unlike other third_party directories, is
+# compiled into non-test libraries, is included below."
+# OpenSSL - Obviously
+expression = "ISC AND MIT AND OpenSSL"
+license-files = [
+    { path = "LICENSE", hash = 0xbd0eed23 },
+]
+
+[sources]
+unknown-registry = "deny"
+unknown-git = "deny"