diff --git a/docker-compose.yml b/docker-compose.yml
index da905bdfdc13a6af67a7c4bc91f605cfdb441e08..c32a3b016120cb37f7fda9f1f911009c73a7e9f1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -33,15 +33,15 @@ services:
       - "5432:5432"
     labels:
       tech.jetsam.environment: 'staging'
-  graphql-engine:
-    image: hasura/graphql-engine:v2.4.0-beta.1
-    restart: on-failure
-    ports:
-      - "15432:8080"
-    depends_on:
-      - "postgres"
-    env_file:
-      - hasura/.env
+#  graphql-engine:
+#    image: hasura/graphql-engine:v2.4.0-beta.1
+#    restart: on-failure
+#    ports:
+#      - "15432:8080"
+#    depends_on:
+#      - "postgres"
+#    env_file:
+#      - hasura/.env
 #  hsaura:
 #    image: hasura/graphql-engine:v1.3.3
 #    ports:
diff --git a/package-lock.json b/package-lock.json
index 45c5bad01f0569b5dfa8fd4a69397609a704be96..df92de0248ab19072cb5998f27bea3c99e0dae5c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "jetsam-api",
-  "version": "2.3.0",
+  "version": "2.5.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "jetsam-api",
-      "version": "2.3.0",
+      "version": "2.5.0",
       "license": "GPL-3.0+",
       "dependencies": {
         "@commander-lol/vault-client": "^0.1.1",
@@ -28,7 +28,7 @@
         "handlebars": "^4.7.6",
         "ioredis": "^4.17.3",
         "joi": "^17.3.0",
-        "jose": "^3.6.1",
+        "jose": "^4.6.1",
         "koa": "^2.13.0",
         "koa-bodyparser": "^4.3.0",
         "koa-compose": "^4.1.0",
@@ -47,7 +47,7 @@
         "node-fetch": "^2.6.1",
         "nodemailer": "^6.4.17",
         "oauth2-server": "^3.1.1",
-        "oidc-provider": "^7.10.1",
+        "oidc-provider": "git+ssh://git@github.com:Commander-lol/node-oidc-provider.git#de6429dd4aaab02c73a6d22faaee33efd78d4584",
         "pg": "^8.3.0",
         "pg-hstore": "^2.3.3",
         "pluralize": "^8.0.0",
@@ -65,7 +65,8 @@
         "jest": "^26.6.3",
         "nodemon": "^2.0.4",
         "prettier": "^2.2.1",
-        "supertest": "^6.1.3"
+        "supertest": "^6.1.3",
+        "umzug": "^3.1.1"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -1356,6 +1357,18 @@
       "resolved": "https://npm.lcr.gr/@root%2frequest/-/request-1.7.0.tgz",
       "integrity": "sha512-lre7XVeEwszgyrayWWb/kRn5fuJfa+n0Nh+rflM9E+EpC28yIYA+FPm/OL1uhzp3TxhQM0HFN4FE2RDIPGlnmg=="
     },
+    "node_modules/@rushstack/ts-command-line": {
+      "version": "4.10.9",
+      "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.9.tgz",
+      "integrity": "sha512-TE3eZgHNVHOY3p8lp38FoNEJUr0+swPb24sCcYuwlC+MHgMGXyJNM+p7l3TKSBRiY01XShoL2k601oGwL00KlA==",
+      "dev": true,
+      "dependencies": {
+        "@types/argparse": "1.0.38",
+        "argparse": "~1.0.9",
+        "colors": "~1.2.1",
+        "string-argv": "~0.3.1"
+      }
+    },
     "node_modules/@sendgrid/client": {
       "version": "7.4.2",
       "resolved": "https://npm.lcr.gr/@sendgrid%2fclient/-/client-7.4.2.tgz",
@@ -1551,6 +1564,12 @@
         "node": ">= 6"
       }
     },
+    "node_modules/@types/argparse": {
+      "version": "1.0.38",
+      "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz",
+      "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==",
+      "dev": true
+    },
     "node_modules/@types/babel__core": {
       "version": "7.1.12",
       "resolved": "https://npm.lcr.gr/@types%2fbabel__core/-/babel__core-7.1.12.tgz",
@@ -2986,6 +3005,15 @@
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
       "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
     },
+    "node_modules/colors": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz",
+      "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
     "node_modules/combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -7686,9 +7714,9 @@
       }
     },
     "node_modules/jose": {
-      "version": "3.6.1",
-      "resolved": "https://npm.lcr.gr/jose/-/jose-3.6.1.tgz",
-      "integrity": "sha512-AZ+dcXaYbX79uvqedXl7QMDkhpQBVXFezLRP734phyVw8EEcnwRIsOMLw4JAMJ+7Iyhv5Eb7isQUEZvqCCk6vA==",
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/jose/-/jose-4.6.1.tgz",
+      "integrity": "sha512-EFnufEivlIB6j7+JwaenYQzdUDs/McajDr9WnhT6EI0WxbexnfuZimpWX1GnobF6OnQsUFmWFXUXdWyZHWdQow==",
       "funding": {
         "url": "https://github.com/sponsors/panva"
       }
@@ -8974,9 +9002,10 @@
       "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
     },
     "node_modules/oidc-provider": {
-      "version": "7.10.1",
-      "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-7.10.1.tgz",
-      "integrity": "sha512-SRFVePq/ki76SbKB/vx7ach6mgc6iof2dtYOKMA88f3i1gZ6XKbqqxqyEVAj4n9L13nssUWJE9O4AyN8M0bNKA==",
+      "version": "7.10.6",
+      "resolved": "git+ssh://git@github.com/Commander-lol/node-oidc-provider.git#de6429dd4aaab02c73a6d22faaee33efd78d4584",
+      "integrity": "sha512-qXHx7oEjm0UgTHC+KLLCRzCyup5J/kNKGUxQOxmIG0xeA2IRSkYkBHZgElTulqrJNZfGiJYxMTO/Wk3KdgEUsA==",
+      "license": "MIT",
       "dependencies": {
         "@koa/cors": "^3.1.0",
         "cacheable-lookup": "^6.0.1",
@@ -8990,7 +9019,7 @@
         "nanoid": "^3.1.28",
         "object-hash": "^2.2.0",
         "oidc-token-hash": "^5.0.1",
-        "paseto2": "npm:paseto@^2.1.3",
+        "paseto": "^2.1.3",
         "quick-lru": "^5.1.1",
         "raw-body": "^2.4.1"
       },
@@ -9001,7 +9030,7 @@
         "url": "https://github.com/sponsors/panva"
       },
       "optionalDependencies": {
-        "paseto3": "npm:paseto@^3.0.0"
+        "paseto3": "npm:paseto@^3.1.0"
       }
     },
     "node_modules/oidc-provider/node_modules/@sindresorhus/is": {
@@ -9111,14 +9140,6 @@
         "node": ">=10.6.0"
       }
     },
-    "node_modules/oidc-provider/node_modules/jose": {
-      "version": "4.3.6",
-      "resolved": "https://registry.npmjs.org/jose/-/jose-4.3.6.tgz",
-      "integrity": "sha512-A/JgZGUerqG2IMuxkUDBtZ4aTxg/l1Y+pt/QAAYiRAR3EFlxIE0Su0xdpB8tQcPZK5eudB7g1PHCZ5uHatbY+g==",
-      "funding": {
-        "url": "https://github.com/sponsors/panva"
-      }
-    },
     "node_modules/oidc-provider/node_modules/jsesc": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
@@ -9436,8 +9457,7 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/paseto2": {
-      "name": "paseto",
+    "node_modules/paseto": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/paseto/-/paseto-2.1.3.tgz",
       "integrity": "sha512-BNkbvr0ZFDbh3oV13QzT5jXIu8xpFc9r0o5mvWBhDU1GBkVt1IzHK1N6dcYmN7XImrUmPQ0HCUXmoe2WPo8xsg==",
@@ -9704,6 +9724,15 @@
         "node": ">=4"
       }
     },
+    "node_modules/pony-cause": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz",
+      "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://npm.lcr.gr/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -10871,6 +10900,17 @@
         "node": ">=10.0.0"
       }
     },
+    "node_modules/sequelize-cli/node_modules/umzug": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz",
+      "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==",
+      "dependencies": {
+        "bluebird": "^3.7.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/sequelize-pool": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz",
@@ -11423,6 +11463,15 @@
         }
       ]
     },
+    "node_modules/string-argv": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
+      "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.6.19"
+      }
+    },
     "node_modules/string-length": {
       "version": "4.0.1",
       "resolved": "https://npm.lcr.gr/string-length/-/string-length-4.0.1.tgz",
@@ -12023,14 +12072,54 @@
       }
     },
     "node_modules/umzug": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz",
-      "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.1.1.tgz",
+      "integrity": "sha512-sgMDzUK6ZKS3pjzRJpAHqSkvAQ+64Dourq6JfQv11i0nMu0/QqE3V3AUpj2pWYxFBaSvnUxKrzZQmPr6NZhvdQ==",
+      "dev": true,
       "dependencies": {
-        "bluebird": "^3.7.2"
+        "@rushstack/ts-command-line": "^4.7.7",
+        "emittery": "^0.10.2",
+        "fs-jetpack": "^4.1.0",
+        "glob": "^7.1.6",
+        "pony-cause": "^1.1.1",
+        "type-fest": "^2.0.0"
       },
       "engines": {
-        "node": ">=6.0.0"
+        "node": ">=12"
+      }
+    },
+    "node_modules/umzug/node_modules/emittery": {
+      "version": "0.10.2",
+      "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz",
+      "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+      }
+    },
+    "node_modules/umzug/node_modules/fs-jetpack": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz",
+      "integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==",
+      "dev": true,
+      "dependencies": {
+        "minimatch": "^3.0.2",
+        "rimraf": "^2.6.3"
+      }
+    },
+    "node_modules/umzug/node_modules/type-fest": {
+      "version": "2.12.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz",
+      "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/undefsafe": {
@@ -13723,6 +13812,18 @@
       "resolved": "https://npm.lcr.gr/@root%2frequest/-/request-1.7.0.tgz",
       "integrity": "sha512-lre7XVeEwszgyrayWWb/kRn5fuJfa+n0Nh+rflM9E+EpC28yIYA+FPm/OL1uhzp3TxhQM0HFN4FE2RDIPGlnmg=="
     },
+    "@rushstack/ts-command-line": {
+      "version": "4.10.9",
+      "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.9.tgz",
+      "integrity": "sha512-TE3eZgHNVHOY3p8lp38FoNEJUr0+swPb24sCcYuwlC+MHgMGXyJNM+p7l3TKSBRiY01XShoL2k601oGwL00KlA==",
+      "dev": true,
+      "requires": {
+        "@types/argparse": "1.0.38",
+        "argparse": "~1.0.9",
+        "colors": "~1.2.1",
+        "string-argv": "~0.3.1"
+      }
+    },
     "@sendgrid/client": {
       "version": "7.4.2",
       "resolved": "https://npm.lcr.gr/@sendgrid%2fclient/-/client-7.4.2.tgz",
@@ -13879,6 +13980,12 @@
       "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
       "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
     },
+    "@types/argparse": {
+      "version": "1.0.38",
+      "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz",
+      "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==",
+      "dev": true
+    },
     "@types/babel__core": {
       "version": "7.1.12",
       "resolved": "https://npm.lcr.gr/@types%2fbabel__core/-/babel__core-7.1.12.tgz",
@@ -15071,6 +15178,12 @@
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
       "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
     },
+    "colors": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz",
+      "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==",
+      "dev": true
+    },
     "combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -18734,9 +18847,9 @@
       }
     },
     "jose": {
-      "version": "3.6.1",
-      "resolved": "https://npm.lcr.gr/jose/-/jose-3.6.1.tgz",
-      "integrity": "sha512-AZ+dcXaYbX79uvqedXl7QMDkhpQBVXFezLRP734phyVw8EEcnwRIsOMLw4JAMJ+7Iyhv5Eb7isQUEZvqCCk6vA=="
+      "version": "4.6.1",
+      "resolved": "https://registry.npmjs.org/jose/-/jose-4.6.1.tgz",
+      "integrity": "sha512-EFnufEivlIB6j7+JwaenYQzdUDs/McajDr9WnhT6EI0WxbexnfuZimpWX1GnobF6OnQsUFmWFXUXdWyZHWdQow=="
     },
     "js-beautify": {
       "version": "1.11.0",
@@ -19771,9 +19884,9 @@
       "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
     },
     "oidc-provider": {
-      "version": "7.10.1",
-      "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-7.10.1.tgz",
-      "integrity": "sha512-SRFVePq/ki76SbKB/vx7ach6mgc6iof2dtYOKMA88f3i1gZ6XKbqqxqyEVAj4n9L13nssUWJE9O4AyN8M0bNKA==",
+      "version": "git+ssh://git@github.com/Commander-lol/node-oidc-provider.git#de6429dd4aaab02c73a6d22faaee33efd78d4584",
+      "integrity": "sha512-qXHx7oEjm0UgTHC+KLLCRzCyup5J/kNKGUxQOxmIG0xeA2IRSkYkBHZgElTulqrJNZfGiJYxMTO/Wk3KdgEUsA==",
+      "from": "oidc-provider@git+ssh://git@github.com:Commander-lol/node-oidc-provider.git#de6429dd4aaab02c73a6d22faaee33efd78d4584",
       "requires": {
         "@koa/cors": "^3.1.0",
         "cacheable-lookup": "^6.0.1",
@@ -19787,8 +19900,8 @@
         "nanoid": "^3.1.28",
         "object-hash": "^2.2.0",
         "oidc-token-hash": "^5.0.1",
-        "paseto2": "npm:paseto@^2.1.3",
-        "paseto3": "npm:paseto@^3.0.0",
+        "paseto": "^2.1.3",
+        "paseto3": "npm:paseto@^3.1.0",
         "quick-lru": "^5.1.1",
         "raw-body": "^2.4.1"
       },
@@ -19866,11 +19979,6 @@
             }
           }
         },
-        "jose": {
-          "version": "4.3.6",
-          "resolved": "https://registry.npmjs.org/jose/-/jose-4.3.6.tgz",
-          "integrity": "sha512-A/JgZGUerqG2IMuxkUDBtZ4aTxg/l1Y+pt/QAAYiRAR3EFlxIE0Su0xdpB8tQcPZK5eudB7g1PHCZ5uHatbY+g=="
-        },
         "jsesc": {
           "version": "3.0.2",
           "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
@@ -20102,8 +20210,8 @@
       "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
       "dev": true
     },
-    "paseto2": {
-      "version": "npm:paseto@2.1.3",
+    "paseto": {
+      "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/paseto/-/paseto-2.1.3.tgz",
       "integrity": "sha512-BNkbvr0ZFDbh3oV13QzT5jXIu8xpFc9r0o5mvWBhDU1GBkVt1IzHK1N6dcYmN7XImrUmPQ0HCUXmoe2WPo8xsg=="
     },
@@ -20305,6 +20413,12 @@
       "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
       "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="
     },
+    "pony-cause": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-1.1.1.tgz",
+      "integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==",
+      "dev": true
+    },
     "posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://npm.lcr.gr/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -21224,6 +21338,16 @@
         "resolve": "^1.5.0",
         "umzug": "^2.3.0",
         "yargs": "^13.1.0"
+      },
+      "dependencies": {
+        "umzug": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz",
+          "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==",
+          "requires": {
+            "bluebird": "^3.7.2"
+          }
+        }
       }
     },
     "sequelize-pool": {
@@ -21675,6 +21799,12 @@
         }
       }
     },
+    "string-argv": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
+      "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==",
+      "dev": true
+    },
     "string-length": {
       "version": "4.0.1",
       "resolved": "https://npm.lcr.gr/string-length/-/string-length-4.0.1.tgz",
@@ -22137,11 +22267,41 @@
       }
     },
     "umzug": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz",
-      "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.1.1.tgz",
+      "integrity": "sha512-sgMDzUK6ZKS3pjzRJpAHqSkvAQ+64Dourq6JfQv11i0nMu0/QqE3V3AUpj2pWYxFBaSvnUxKrzZQmPr6NZhvdQ==",
+      "dev": true,
       "requires": {
-        "bluebird": "^3.7.2"
+        "@rushstack/ts-command-line": "^4.7.7",
+        "emittery": "^0.10.2",
+        "fs-jetpack": "^4.1.0",
+        "glob": "^7.1.6",
+        "pony-cause": "^1.1.1",
+        "type-fest": "^2.0.0"
+      },
+      "dependencies": {
+        "emittery": {
+          "version": "0.10.2",
+          "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz",
+          "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==",
+          "dev": true
+        },
+        "fs-jetpack": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-4.3.1.tgz",
+          "integrity": "sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==",
+          "dev": true,
+          "requires": {
+            "minimatch": "^3.0.2",
+            "rimraf": "^2.6.3"
+          }
+        },
+        "type-fest": {
+          "version": "2.12.2",
+          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz",
+          "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==",
+          "dev": true
+        }
       }
     },
     "undefsafe": {
diff --git a/package.json b/package.json
index 5276081ffd28a3e7599b63629b955bf9d1512c90..ea10a5565ae2e730231a242aba6c0a9869429d8e 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
     "handlebars": "^4.7.6",
     "ioredis": "^4.17.3",
     "joi": "^17.3.0",
-    "jose": "^3.6.1",
+    "jose": "^4.6.1",
     "koa": "^2.13.0",
     "koa-bodyparser": "^4.3.0",
     "koa-compose": "^4.1.0",
@@ -58,7 +58,7 @@
     "node-fetch": "^2.6.1",
     "nodemailer": "^6.4.17",
     "oauth2-server": "^3.1.1",
-    "oidc-provider": "^7.10.1",
+    "oidc-provider": "git+ssh://git@github.com:Commander-lol/node-oidc-provider.git#de6429dd4aaab02c73a6d22faaee33efd78d4584",
     "pg": "^8.3.0",
     "pg-hstore": "^2.3.3",
     "pluralize": "^8.0.0",
@@ -76,6 +76,7 @@
     "jest": "^26.6.3",
     "nodemon": "^2.0.4",
     "prettier": "^2.2.1",
-    "supertest": "^6.1.3"
+    "supertest": "^6.1.3",
+    "umzug": "^3.1.1"
   }
 }
diff --git a/scripts/jest.js b/scripts/jest.js
index 36eb8faa049f070db0d53a7bb7c29338a76aafde..5bd90421f82df55f8268f4dc2f16973920fb07cc 100644
--- a/scripts/jest.js
+++ b/scripts/jest.js
@@ -2,6 +2,8 @@ const { execSync } = require('child_process')
 const bootstrap = require('../src/bootstrap')
 const { v4: uuid } = require('uuid')
 const { exec } = require('core/utils/process')
+const Sequelize = require("sequelize");
+const {config} = require("bootstrap");
 
 async function run() {
 	process.env.NODE_ENV = 'testing'
@@ -10,25 +12,77 @@ async function run() {
 	const id = uuid().replace(/-/g, '')
 	const dbname = `${ bootstrap.config('database.database') }_${ id }`
 
-	await bootstrap.invoke('db:fresh', [id, '--and-migrate'], false)
+	const pg = require('pg')
+	const conf = bootstrap.config('database')
+	const dbconf = {
+		user: conf.username,
+		password: conf.password,
+		host: conf.host,
+		database: conf.database,
+		port: conf.port,
+		application_name: `${ bootstrap.config('app.name') } Test Harness`
+	}
+
+	console.log("[BOOT] Setting up database ", dbname)
+	let client = new pg.Client(dbconf)
+	await client.connect()
+	await client.query(`CREATE DATABASE ${ dbname }`)
+
+	const { Sequelize } = require('sequelize')
+	const sqlconf = bootstrap.config('sequelize')['testing']
+	const sql = new Sequelize(
+		dbname,
+		conf.username,
+		conf.password,
+		sqlconf,
+	)
+
+	const { Umzug, SequelizeStorage } = require('umzug')
+	const umzug = new Umzug({
+		storage: new SequelizeStorage({ sequelize: sql }),
+		context: [sql.getQueryInterface(), Sequelize],
+		migrations: {
+			glob: 'database/migrations/*.js',
+			resolve: ({ name, path, context }) => {
+				// console.log(path, context, name)
+				return {
+					name,
+					up: () => require(path).up(...context),
+					down: () => require(path).down(...context),
+				}
+			}
+		}
+	})
+
+	console.log("[BOOT] Migrating database ", dbname)
+	await umzug.up()
+	await sql.close()
 
 	try {
-		const runner = await exec('npx jest --forceExit --runInBand', {
+		console.log("[BOOT] Running tests ", dbname)
+		const runner = await exec('npx jest --env=node --forceExit --runInBand', {
 			env: {
 				...process.env,
 				DATABASE_NAME: dbname,
+				QUEUE_DRIVER: 'async',
+				CACHE_DRIVER: 'memory',
 				SENTRY_ENABLED: false,
 			},
 			stdio: 'inherit',
 		}, true)
+		if (runner > 0) {
+			throw new Error(`Tests failed with code ${ runner }`)
+		}
 	} finally {
-		await bootstrap.invoke('db:prune')
-		process.exit()
+		console.log("[BOOT] Dropping database ", dbname)
+		await client.query(`DROP DATABASE ${ dbname }`)
+		await client.end()
 	}
 }
 
 
 run()
+	.then(() => process.exit())
 	.catch(e => {
 		console.error(e)
 		process.exit(1)
diff --git a/src/app.js b/src/app.js
index 91bf6668fdfa0f6813e5630c7a8a6dd29425374a..1fcec2c77849ef80ccb4f3bde943d69a023dd576 100644
--- a/src/app.js
+++ b/src/app.js
@@ -4,7 +4,7 @@ const logger = require('koa-logger')
 const bodyparser = require('koa-bodyparser')
 const etag = require('koa-etag')
 const session = require('koa-session')
-const static = require('koa-static')
+const files = require('koa-static')
 const mount = require('koa-mount')
 
 const createOIDCServer = require('domain/auth/oidc/OIDCServer')
@@ -22,7 +22,12 @@ const serviceProvider = require('core/injection/ServiceProvider')
 const notFound = require("http/middleware/NotFoundHandler");
 
 module.exports = async function createApp(app = new Koa()) {
-	const { fs } = require('bootstrap')
+	const { fs, boot } = require('bootstrap')
+
+	if (process.env.NODE_ENV === 'testing') {
+		await boot()
+	}
+
 	const routers = require('http/routes')
 
 	app.keys = [config('app.key')]
@@ -35,7 +40,7 @@ module.exports = async function createApp(app = new Koa()) {
 	app.use(etag({ weak: true }))
 	app.use(bodyparser())
 	app.use(logger(s => requestLog(s)))
-	app.use(static(pathutil.resolve(__dirname + '/../public')))
+	app.use(files(pathutil.resolve(__dirname + '/../public')))
 	app.use(notFound)
 
 	app.use(async (ctx, next) => {
diff --git a/src/core/utils/jwt.js b/src/core/utils/jwt.js
index 53bc5074a203c5a86c0633a687ec0935a7f695d4..fe2208a7e332729ef11926762fe27de531d7c31e 100644
--- a/src/core/utils/jwt.js
+++ b/src/core/utils/jwt.js
@@ -39,10 +39,10 @@ exports.getKeys = () => {
 exports.getJWKS = async (type = 'pub') => {
 	const { config } = require('bootstrap')
 	const keys = exports.getKeys()
-	const { default: fromKeyLike } = require('jose/jwk/from_key_like')
+	const jose = require('jose')
 
 	const key = keys[type]
-	const jwk = await fromKeyLike(key)
+	const jwk = await jose.exportJWK(key)
 	const kid = config('app.security.key_id')
 
 	return {
@@ -97,11 +97,11 @@ exports.loadKeys = async () => {
 exports.sign = async payload => {
 	const threadContext = require('core/injection/ThreadContext')
 	const { config } = require('bootstrap')
-	const { default: SignJWT } = require('jose/jwt/sign')
+	const jose = require('jose')
 	const { priv } = exports.getKeys()
 
 	return await threadContext.profile('jwt.sign', JSON.stringify(payload), () =>
-		new SignJWT(payload)
+		new jose.SignJWT(payload)
 			.setIssuer(exports.jwtOptions.issuer)
 			.setIssuedAt()
 			.setProtectedHeader({ alg: 'RS256', kid: exports.jwtOptions.keyid_prefix + config('app.security.key_id') })
@@ -111,12 +111,12 @@ exports.sign = async payload => {
 
 exports.verify = async token => {
 	const threadContext = require('core/injection/ThreadContext')
-	const { default: jwtVerify } = require('jose/jwt/verify')
+	const jose = require('jose')
 	const { getKeys, jwtOptions } = exports
 	const { pub } = getKeys()
 
 	return await threadContext.profile('jwt.verify', undefined, async () => {
-		const { payload } = await jwtVerify(token, pub, jwtOptions)
+		const { payload } = await jose.jwtVerify(token, pub, jwtOptions)
 		return payload
 	})
 }
diff --git a/src/http/middleware/ErrorHandler.js b/src/http/middleware/ErrorHandler.js
index 29985984bc80c85b4a333e9c38f1e3c69e8d3cea..5e6a0d55f8fb24429669fd51caced5b7cbf8ab29 100644
--- a/src/http/middleware/ErrorHandler.js
+++ b/src/http/middleware/ErrorHandler.js
@@ -8,9 +8,7 @@ module.exports = async (ctx, next) => {
 	try {
 		await next(ctx)
 	} catch (e) {
-		if (e instanceof SafeModeError) {
-			console.error(e)
-		} else {
+		if (!(e instanceof SafeModeError)) {
 			await SentryReporter.report(e, ctx)
 		}
 		hasHandledError = true
diff --git a/src/http/middleware/NotFoundHandler.js b/src/http/middleware/NotFoundHandler.js
index 4c2f0eff0841872856813bd2e7b7ded2699f2a66..573e9ef2d80a6dfee154dc73bf10e5ff866d9fab 100644
--- a/src/http/middleware/NotFoundHandler.js
+++ b/src/http/middleware/NotFoundHandler.js
@@ -5,6 +5,7 @@ module.exports = async (ctx, next) => {
 		await next()
 		await applyResponse(ctx)
 	} catch(e) {
+		console.error(e)
 		ctx.status = 500
 		await applyResponse(ctx, e)
 	}
diff --git a/tests/integration/authentication/Login.test.js b/tests/integration/authentication/Login.test.js
index 16b61fcc556b323deb676243ff9b2844a6f4afcb..f989762bbe2655342fd40f68d28f798b4f903ecf 100644
--- a/tests/integration/authentication/Login.test.js
+++ b/tests/integration/authentication/Login.test.js
@@ -5,6 +5,7 @@ test('Logging in returns a token', async () => {
 	const app = await createApp()
 	const response = await request(app.callback())
 		.post('/api/register')
+		.set('Accept', 'application/json')
 		.send({
 			email: 'login-returns-token@example.com',
 			password: 'password',
@@ -15,6 +16,7 @@ test('Logging in returns a token', async () => {
 
 	const response2 = await request(app.callback())
 		.post('/api/login')
+		.set('Accept', 'application/json')
 		.send({
 			email: 'login-returns-token@example.com',
 			password: 'password',
diff --git a/tests/integration/authentication/Registration.test.js b/tests/integration/authentication/Registration.test.js
index 09bcf458fde13a95543b799b46567ee769bbeabc..9b4396194f05ebcb3f79e83f9ae8f8c5bc268749 100644
--- a/tests/integration/authentication/Registration.test.js
+++ b/tests/integration/authentication/Registration.test.js
@@ -13,6 +13,7 @@ test('Can register a new account', async () => {
 	const app = await createApp()
 	const response = await request(app.callback())
 		.post('/api/register')
+		.set('Accept', 'application/json')
 		.send({
 			email: 'johnson@example.com',
 			password: 'foo',
@@ -30,6 +31,7 @@ test('Invalid registration request returns 400', async () => {
 	const app = await createApp()
 	const response = await request(app.callback())
 		.post('/api/register')
+		.set('Accept', 'application/json')
 		.send({
 			incorrect: 'property'
 		})
@@ -41,6 +43,7 @@ test('Two accounts cannot share an email address', async () => {
 	const app = await createApp()
 	const value1 = await request(app.callback())
 		.post('/api/register')
+		.set('Accept', 'application/json')
 		.send({
 			email: 'example@example.com',
 			password: 'foo',
@@ -49,6 +52,7 @@ test('Two accounts cannot share an email address', async () => {
 
 	const value2 = await request(app.callback())
 		.post('/api/register')
+		.set('Accept', 'application/json')
 		.send({
 			email: 'example@example.com',
 			password: 'foo',
diff --git a/tests/integration/system/SafeMode.test.js b/tests/integration/system/SafeMode.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..9b5ddb210640159be5e1eb3f7cdef41f6b90b106
--- /dev/null
+++ b/tests/integration/system/SafeMode.test.js
@@ -0,0 +1,12 @@
+const request = require('supertest')
+const createApp = require('app')
+
+test('Safe Mode Blocks Mutations', async () => {
+	process.env.DISABLE_MUTATION = 'true'
+	const app = await createApp()
+	const response = await request(app.callback())
+		.post('/api/v2/metrics')
+		.set('Accept', 'application/json')
+
+	expect(response.status).toBe(5400)
+})
\ No newline at end of file