diff --git a/Cargo.lock b/Cargo.lock
index 5ac696fe688f043f1df410f7fe7520441246e75f..d456ebed0c0c8f7bdf31cd64ed3b59d756734615 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -72,7 +72,7 @@ dependencies = [
  "http",
  "httparse",
  "httpdate",
- "itoa 1.0.5",
+ "itoa",
  "language-tags",
  "local-channel",
  "mime",
@@ -112,9 +112,9 @@ dependencies = [
 
 [[package]]
 name = "actix-rt"
-version = "2.7.0"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ea16c295198e958ef31930a6ef37d0fb64e9ca3b6116e6b93a8bdae96ee1000"
+checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e"
 dependencies = [
  "futures-core",
  "tokio",
@@ -122,9 +122,9 @@ dependencies = [
 
 [[package]]
 name = "actix-server"
-version = "2.1.1"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0da34f8e659ea1b077bb4637948b815cd3768ad5a188fdcd74ff4d84240cd824"
+checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327"
 dependencies = [
  "actix-rt",
  "actix-service",
@@ -184,7 +184,7 @@ dependencies = [
  "futures-core",
  "futures-util",
  "http",
- "itoa 1.0.5",
+ "itoa",
  "language-tags",
  "log",
  "mime",
@@ -196,7 +196,7 @@ dependencies = [
  "serde_urlencoded",
  "smallvec",
  "socket2",
- "time 0.3.17",
+ "time 0.3.19",
  "url",
 ]
 
@@ -225,7 +225,7 @@ dependencies = [
  "actix-utils",
  "actix-web",
  "actix-web-lab-derive",
- "ahash 0.8.2",
+ "ahash 0.8.3",
  "arc-swap",
  "async-trait",
  "bytes",
@@ -304,9 +304,9 @@ dependencies = [
 
 [[package]]
 name = "ahash"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
+checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
 dependencies = [
  "cfg-if",
  "getrandom",
@@ -355,9 +355,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.68"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
+checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
 dependencies = [
  "backtrace",
 ]
@@ -408,9 +408,9 @@ checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
 
 [[package]]
 name = "async-trait"
-version = "0.1.61"
+version = "0.1.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282"
+checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -451,8 +451,8 @@ dependencies = [
  "actix-web-lab",
  "anyhow",
  "argon2",
- "bincode",
  "clap",
+ "dashmap",
  "dotenv",
  "glue",
  "jwt-simple",
@@ -462,7 +462,6 @@ dependencies = [
  "reqwest",
  "serde",
  "serde_json",
- "sled",
  "sqlx",
  "thiserror",
  "tokio",
@@ -588,9 +587,9 @@ dependencies = [
 
 [[package]]
 name = "brotli-decompressor"
-version = "2.3.2"
+version = "2.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
+checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -605,14 +604,13 @@ dependencies = [
  "lazy_static",
  "memchr",
  "regex-automata",
- "serde",
 ]
 
 [[package]]
 name = "bumpalo"
-version = "3.11.1"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
 
 [[package]]
 name = "byteorder"
@@ -622,9 +620,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
 
 [[package]]
 name = "bytestring"
@@ -712,9 +710,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.78"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
 dependencies = [
  "jobserver",
 ]
@@ -742,14 +740,14 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.0.32"
+version = "4.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
+checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
 dependencies = [
  "bitflags",
  "clap_derive",
  "clap_lex",
- "is-terminal 0.4.2",
+ "is-terminal 0.4.4",
  "once_cell",
  "strsim",
  "termcolor",
@@ -757,9 +755,9 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.0.21"
+version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
+checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
 dependencies = [
  "heck",
  "proc-macro-error",
@@ -770,9 +768,9 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
+checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
 dependencies = [
  "os_str_bytes",
 ]
@@ -829,7 +827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
 dependencies = [
  "percent-encoding",
- "time 0.3.17",
+ "time 0.3.19",
  "version_check",
 ]
 
@@ -967,18 +965,18 @@ dependencies = [
 
 [[package]]
 name = "crc"
-version = "3.0.0"
+version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
+checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
 dependencies = [
  "crc-catalog",
 ]
 
 [[package]]
 name = "crc-catalog"
-version = "2.1.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff"
+checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
 
 [[package]]
 name = "crc32fast"
@@ -1072,13 +1070,12 @@ dependencies = [
 
 [[package]]
 name = "csv"
-version = "1.1.6"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
+checksum = "af91f40b7355f82b0a891f50e70399475945bb0b0da4f1700ce60761c9d3e359"
 dependencies = [
- "bstr",
  "csv-core",
- "itoa 0.4.8",
+ "itoa",
  "ryu",
  "serde",
 ]
@@ -1100,9 +1097,9 @@ checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"
 
 [[package]]
 name = "cxx"
-version = "1.0.86"
+version = "1.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579"
+checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
 dependencies = [
  "cc",
  "cxxbridge-flags",
@@ -1112,9 +1109,9 @@ dependencies = [
 
 [[package]]
 name = "cxx-build"
-version = "1.0.86"
+version = "1.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70"
+checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
 dependencies = [
  "cc",
  "codespan-reporting",
@@ -1127,21 +1124,34 @@ dependencies = [
 
 [[package]]
 name = "cxxbridge-flags"
-version = "1.0.86"
+version = "1.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c"
+checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "1.0.86"
+version = "1.0.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5"
+checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn",
 ]
 
+[[package]]
+name = "dashmap"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
+dependencies = [
+ "cfg-if",
+ "hashbrown",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core 0.9.7",
+]
+
 [[package]]
 name = "der"
 version = "0.6.1"
@@ -1258,14 +1268,14 @@ checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0"
 
 [[package]]
 name = "ecdsa"
-version = "0.14.8"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
+checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e"
 dependencies = [
  "der",
  "elliptic-curve",
  "rfc6979",
- "signature",
+ "signature 2.0.0",
 ]
 
 [[package]]
@@ -1280,9 +1290,9 @@ dependencies = [
 
 [[package]]
 name = "either"
-version = "1.8.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
 [[package]]
 name = "elliptic-curve"
@@ -1317,9 +1327,9 @@ dependencies = [
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.31"
+version = "0.8.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
+checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
 dependencies = [
  "cfg-if",
 ]
@@ -1350,7 +1360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
 dependencies = [
  "humantime",
- "is-terminal 0.4.2",
+ "is-terminal 0.4.4",
  "log",
  "regex",
  "termcolor",
@@ -1407,22 +1417,22 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 
 [[package]]
 name = "fastrand"
-version = "1.8.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
 dependencies = [
  "instant",
 ]
 
 [[package]]
 name = "fd-lock"
-version = "3.0.8"
+version = "3.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb21c69b9fea5e15dbc1049e4b77145dd0ba1c84019c488102de0dc4ea4b0a27"
+checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9"
 dependencies = [
  "cfg-if",
- "rustix 0.36.6",
- "windows-sys 0.42.0",
+ "rustix 0.36.8",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1502,21 +1512,11 @@ dependencies = [
  "windows-sys 0.36.1",
 ]
 
-[[package]]
-name = "fs2"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
-dependencies = [
- "libc",
- "winapi",
-]
-
 [[package]]
 name = "futures-channel"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
+checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1524,9 +1524,9 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
+checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
 
 [[package]]
 name = "futures-intrusive"
@@ -1541,21 +1541,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
+checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
 
 [[package]]
 name = "futures-task"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
+checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
 
 [[package]]
 name = "futures-util"
-version = "0.3.25"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
+checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1683,9 +1683,9 @@ dependencies = [
 
 [[package]]
 name = "heck"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 dependencies = [
  "unicode-segmentation",
 ]
@@ -1708,6 +1708,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
 [[package]]
 name = "hex"
 version = "0.4.3"
@@ -1764,13 +1770,13 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
 dependencies = [
  "bytes",
  "fnv",
- "itoa 1.0.5",
+ "itoa",
 ]
 
 [[package]]
@@ -1810,9 +1816,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "hyper"
-version = "0.14.23"
+version = "0.14.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
+checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -1823,7 +1829,7 @@ dependencies = [
  "http-body",
  "httparse",
  "httpdate",
- "itoa 1.0.5",
+ "itoa",
  "pin-project-lite",
  "socket2",
  "tokio",
@@ -1936,12 +1942,12 @@ dependencies = [
 
 [[package]]
 name = "io-lifetimes"
-version = "1.0.3"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
+checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
 dependencies = [
  "libc",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1952,9 +1958,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
 
 [[package]]
 name = "is-macro"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c068d4c6b922cd6284c609cfa6dec0e41615c9c5a1a4ba729a970d8daba05fb"
+checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20"
 dependencies = [
  "Inflector",
  "pmutil",
@@ -1977,14 +1983,14 @@ dependencies = [
 
 [[package]]
 name = "is-terminal"
-version = "0.4.2"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
+checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
 dependencies = [
- "hermit-abi 0.2.6",
- "io-lifetimes 1.0.3",
- "rustix 0.36.6",
- "windows-sys 0.42.0",
+ "hermit-abi 0.3.1",
+ "io-lifetimes 1.0.5",
+ "rustix 0.36.8",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1996,12 +2002,6 @@ dependencies = [
  "either",
 ]
 
-[[package]]
-name = "itoa"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
-
 [[package]]
 name = "itoa"
 version = "1.0.5"
@@ -2010,9 +2010,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
 
 [[package]]
 name = "ittapi"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8c4f6ff06169ce7048dac5150b1501c7e3716a929721aeb06b87e51a43e42f4"
+checksum = "2e648c437172ce7d3ac35ca11a068755072054826fa455a916b43524fa4a62a7"
 dependencies = [
  "anyhow",
  "ittapi-sys",
@@ -2021,9 +2021,9 @@ dependencies = [
 
 [[package]]
 name = "ittapi-sys"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87e078cce01485f418bae3beb34dd604aaedf2065502853c7da17fbce8e64eda"
+checksum = "a9b32a4d23f72548178dde54f3c12c6b6a08598e25575c0d0fa5bd861e0dc1a5"
 dependencies = [
  "cc",
 ]
@@ -2039,18 +2039,18 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.60"
+version = "0.3.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
 dependencies = [
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "jwt-simple"
-version = "0.11.2"
+version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "529a00f2d42d7dc349c994e65917c81bf53225831a65361f6c0454124c550f63"
+checksum = "21a4c8e544a27e20e2fe4b82a93a9e823f01ebcc1e4e135e839db66df0e7dc54"
 dependencies = [
  "anyhow",
  "binstring",
@@ -2074,14 +2074,16 @@ dependencies = [
 
 [[package]]
 name = "k256"
-version = "0.11.6"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b"
+checksum = "92a55e0ff3b72c262bcf041d9e97f1b84492b68f1c1a384de2323d3dc9403397"
 dependencies = [
  "cfg-if",
  "ecdsa",
  "elliptic-curve",
+ "once_cell",
  "sha2 0.10.6",
+ "signature 2.0.0",
 ]
 
 [[package]]
@@ -2263,9 +2265,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 
 [[package]]
 name = "matches"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
 
 [[package]]
 name = "maybe-owned"
@@ -2284,9 +2286,9 @@ dependencies = [
 
 [[package]]
 name = "mediatype"
-version = "0.19.11"
+version = "0.19.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a280afd79e5a4c3e7f298c6139321aad0365690f24a57539e18590b5a606966"
+checksum = "fea6e62614ab2fc0faa58bb15102a0382d368f896a9fa4776592589ab55c4de7"
 
 [[package]]
 name = "memchr"
@@ -2300,7 +2302,7 @@ version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb"
 dependencies = [
- "rustix 0.36.6",
+ "rustix 0.36.8",
 ]
 
 [[package]]
@@ -2354,14 +2356,14 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.5"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
 dependencies = [
  "libc",
  "log",
  "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -2409,16 +2411,37 @@ dependencies = [
  "memoffset 0.6.5",
 ]
 
+[[package]]
+name = "nix"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
+dependencies = [
+ "autocfg",
+ "bitflags",
+ "cfg-if",
+ "libc",
+]
+
 [[package]]
 name = "nom"
-version = "7.1.2"
+version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
  "memchr",
  "minimal-lexical",
 ]
 
+[[package]]
+name = "nom8"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "nu-ansi-term"
 version = "0.46.0"
@@ -2460,9 +2483,9 @@ dependencies = [
 
 [[package]]
 name = "num-complex"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
+checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
 dependencies = [
  "num-traits",
  "serde",
@@ -2523,18 +2546,18 @@ dependencies = [
 
 [[package]]
 name = "num_enum"
-version = "0.5.7"
+version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
+checksum = "3e0072973714303aa6e3631c7e8e777970cf4bdd25dc4932e41031027b8bcc4e"
 dependencies = [
  "num_enum_derive",
 ]
 
 [[package]]
 name = "num_enum_derive"
-version = "0.5.7"
+version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
+checksum = "0629cbd6b897944899b1f10496d9c4a7ac5878d45fd61bc22e9e79bfbbc29597"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -2565,9 +2588,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.17.0"
+version = "1.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
 [[package]]
 name = "opaque-debug"
@@ -2640,23 +2663,25 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
 
 [[package]]
 name = "p256"
-version = "0.11.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594"
+checksum = "49c124b3cbce43bcbac68c58ec181d98ed6cc7e6d0aa7c3ba97b2563410b0e55"
 dependencies = [
  "ecdsa",
  "elliptic-curve",
+ "primeorder",
  "sha2 0.10.6",
 ]
 
 [[package]]
 name = "p384"
-version = "0.11.2"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa"
+checksum = "630a4a9b2618348ececfae61a4905f564b817063bf2d66cdfc2ced523fe1d2d4"
 dependencies = [
  "ecdsa",
  "elliptic-curve",
+ "primeorder",
  "sha2 0.10.6",
 ]
 
@@ -2678,7 +2703,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 dependencies = [
  "lock_api",
- "parking_lot_core 0.9.5",
+ "parking_lot_core 0.9.7",
 ]
 
 [[package]]
@@ -2697,15 +2722,15 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.5"
+version = "0.9.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
 dependencies = [
  "cfg-if",
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -2742,9 +2767,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
 
 [[package]]
 name = "petgraph"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
+checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
 dependencies = [
  "fixedbitset",
  "indexmap",
@@ -2857,15 +2882,23 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
+[[package]]
+name = "primeorder"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b54f7131b3dba65a2f414cf5bd25b66d4682e4608610668eae785750ba4c5b2"
+dependencies = [
+ "elliptic-curve",
+]
+
 [[package]]
 name = "proc-macro-crate"
-version = "1.2.1"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
+checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
 dependencies = [
  "once_cell",
- "thiserror",
- "toml",
+ "toml_edit",
 ]
 
 [[package]]
@@ -2894,9 +2927,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.49"
+version = "1.0.51"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
 dependencies = [
  "unicode-ident",
 ]
@@ -3016,9 +3049,9 @@ dependencies = [
 
 [[package]]
 name = "rayon-core"
-version = "1.10.1"
+version = "1.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
+checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
@@ -3092,11 +3125,11 @@ dependencies = [
 
 [[package]]
 name = "reqwest"
-version = "0.11.13"
+version = "0.11.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
+checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
 dependencies = [
- "base64 0.13.1",
+ "base64 0.21.0",
  "bytes",
  "encoding_rs",
  "futures-core",
@@ -3129,18 +3162,18 @@ dependencies = [
 
 [[package]]
 name = "result-like"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b80fe0296795a96913be20558326b797a187bb3986ce84ed82dee0fb7414428"
+checksum = "ccc7ce6435c33898517a30e85578cd204cbb696875efb93dec19a2d31294f810"
 dependencies = [
  "result-like-derive",
 ]
 
 [[package]]
 name = "result-like-derive"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a29c8a4ac7839f1dcb8b899263b501e0d6932f210300c8a0d271323727b35c1"
+checksum = "1fabf0a2e54f711c68c50d49f648a1a8a37adcb57353f518ac4df374f0788f42"
 dependencies = [
  "pmutil",
  "proc-macro2",
@@ -3175,7 +3208,7 @@ dependencies = [
  "pkcs1",
  "pkcs8",
  "rand_core",
- "signature",
+ "signature 1.6.4",
  "smallvec",
  "subtle",
  "zeroize",
@@ -3262,7 +3295,7 @@ dependencies = [
  "bitflags",
  "errno",
  "io-lifetimes 0.7.5",
- "itoa 1.0.5",
+ "itoa",
  "libc",
  "linux-raw-sys 0.0.46",
  "once_cell",
@@ -3271,16 +3304,16 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.36.6"
+version = "0.36.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
+checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
 dependencies = [
  "bitflags",
  "errno",
- "io-lifetimes 1.0.3",
+ "io-lifetimes 1.0.5",
  "libc",
  "linux-raw-sys 0.1.4",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -3444,7 +3477,7 @@ dependencies = [
  "log",
  "memchr",
  "memoffset 0.6.5",
- "nix",
+ "nix 0.24.3",
  "num-bigint",
  "num-complex",
  "num-integer",
@@ -3498,9 +3531,9 @@ checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
 
 [[package]]
 name = "rustyline"
-version = "10.0.0"
+version = "10.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d1cd5ae51d3f7bf65d7969d579d502168ef578f289452bd8ccc91de28fda20e"
+checksum = "c1e83c32c3f3c33b08496e0d1df9ea8c64d39adb8eb36a1ebb1440c690697aef"
 dependencies = [
  "bitflags",
  "cfg-if",
@@ -3510,7 +3543,7 @@ dependencies = [
  "libc",
  "log",
  "memchr",
- "nix",
+ "nix 0.25.1",
  "radix_trie",
  "scopeguard",
  "unicode-segmentation",
@@ -3571,9 +3604,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework"
-version = "2.7.0"
+version = "2.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
+checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -3584,9 +3617,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.6.1"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
+checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -3637,18 +3670,18 @@ checksum = "53192e38d5c88564b924dbe9b60865ecbb71b81d38c4e61c817cffd3e36ef696"
 dependencies = [
  "form_urlencoded",
  "indexmap",
- "itoa 1.0.5",
+ "itoa",
  "ryu",
  "serde",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.91"
+version = "1.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
+checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
 dependencies = [
- "itoa 1.0.5",
+ "itoa",
  "ryu",
  "serde",
 ]
@@ -3660,7 +3693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
 dependencies = [
  "form_urlencoded",
- "itoa 1.0.5",
+ "itoa",
  "ryu",
  "serde",
 ]
@@ -3720,9 +3753,9 @@ dependencies = [
 
 [[package]]
 name = "signal-hook-registry"
-version = "1.4.0"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
 dependencies = [
  "libc",
 ]
@@ -3737,6 +3770,16 @@ dependencies = [
  "rand_core",
 ]
 
+[[package]]
+name = "signature"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d"
+dependencies = [
+ "digest 0.10.6",
+ "rand_core",
+]
+
 [[package]]
 name = "siphasher"
 version = "0.3.10"
@@ -3745,29 +3788,13 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
 
 [[package]]
 name = "slab"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
 dependencies = [
  "autocfg",
 ]
 
-[[package]]
-name = "sled"
-version = "0.34.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
-dependencies = [
- "crc32fast",
- "crossbeam-epoch",
- "crossbeam-utils",
- "fs2",
- "fxhash",
- "libc",
- "log",
- "parking_lot 0.11.2",
-]
-
 [[package]]
 name = "slice-group-by"
 version = "0.3.0"
@@ -3808,9 +3835,9 @@ dependencies = [
 
 [[package]]
 name = "sqlformat"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a"
+checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e"
 dependencies = [
  "itertools",
  "nom",
@@ -3854,7 +3881,7 @@ dependencies = [
  "hkdf",
  "hmac",
  "indexmap",
- "itoa 1.0.5",
+ "itoa",
  "libc",
  "log",
  "md-5",
@@ -3994,9 +4021,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
 
 [[package]]
 name = "syn"
-version = "1.0.107"
+version = "1.0.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4030,9 +4057,9 @@ dependencies = [
 
 [[package]]
 name = "target-lexicon"
-version = "0.12.5"
+version = "0.12.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d"
+checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5"
 
 [[package]]
 name = "tempfile"
@@ -4061,9 +4088,9 @@ dependencies = [
 
 [[package]]
 name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
 dependencies = [
  "winapi-util",
 ]
@@ -4096,10 +4123,11 @@ dependencies = [
 
 [[package]]
 name = "thread_local"
-version = "1.1.4"
+version = "1.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
 dependencies = [
+ "cfg-if",
  "once_cell",
 ]
 
@@ -4116,11 +4144,11 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
+checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2"
 dependencies = [
- "itoa 1.0.5",
+ "itoa",
  "serde",
  "time-core",
  "time-macros",
@@ -4134,9 +4162,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
 
 [[package]]
 name = "time-macros"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
+checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c"
 dependencies = [
  "time-core",
 ]
@@ -4167,15 +4195,15 @@ dependencies = [
 
 [[package]]
 name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.24.1"
+version = "1.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae"
+checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
 dependencies = [
  "autocfg",
  "bytes",
@@ -4204,9 +4232,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-native-tls"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
 dependencies = [
  "native-tls",
  "tokio",
@@ -4225,9 +4253,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.4"
+version = "0.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
+checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
 dependencies = [
  "bytes",
  "futures-core",
@@ -4239,13 +4267,30 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.5.10"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
 dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "toml_datetime"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
+
+[[package]]
+name = "toml_edit"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
+dependencies = [
+ "indexmap",
+ "nom8",
+ "toml_datetime",
+]
+
 [[package]]
 name = "tower-service"
 version = "0.3.2"
@@ -4428,9 +4473,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.8"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
 
 [[package]]
 name = "unicode-casing"
@@ -4455,9 +4500,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.10.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
 
 [[package]]
 name = "unicode-width"
@@ -4502,9 +4547,9 @@ checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
 
 [[package]]
 name = "utoipa"
-version = "3.0.2"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bdd08005ac3ff3aa380060bc30b2ff4a840c4405ea072deba2bad4891e404c0"
+checksum = "a15f6da6a2b471134ca44b7d18e8a76d73035cf8b3ed24c4dd5ca6a63aa439c5"
 dependencies = [
  "indexmap",
  "serde",
@@ -4514,9 +4559,9 @@ dependencies = [
 
 [[package]]
 name = "utoipa-gen"
-version = "3.0.2"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c6e78c81140a4b93470645091775e4048a57a29b6cc78add875ae60ffced17c"
+checksum = "6f2e33027986a4707b3f5c37ed01b33d0e5a53da30204b52ff18f80600f1d0ec"
 dependencies = [
  "lazy_static",
  "proc-macro-error",
@@ -4545,9 +4590,9 @@ dependencies = [
 
 [[package]]
 name = "uuid"
-version = "1.2.2"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
+checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
 dependencies = [
  "getrandom",
  "serde",
@@ -4654,9 +4699,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -4664,9 +4709,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
 dependencies = [
  "bumpalo",
  "log",
@@ -4679,9 +4724,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.33"
+version = "0.4.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -4691,9 +4736,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -4701,9 +4746,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4714,9 +4759,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.83"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
 
 [[package]]
 name = "wasm-encoder"
@@ -4729,9 +4774,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.20.0"
+version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614"
+checksum = "68f7d56227d910901ce12dfd19acc40c12687994dfb3f57c90690f80be946ec5"
 dependencies = [
  "leb128",
 ]
@@ -4966,30 +5011,30 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "50.0.0"
+version = "54.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34"
+checksum = "3d48d9d731d835f4f8dacbb8de7d47be068812cb9877f5c60d408858778d8d2a"
 dependencies = [
  "leb128",
  "memchr",
  "unicode-width",
- "wasm-encoder 0.20.0",
+ "wasm-encoder 0.24.1",
 ]
 
 [[package]]
 name = "wat"
-version = "1.0.52"
+version = "1.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15"
+checksum = "d1db2e3ed05ea31243761439194bec3af6efbbaf87c4c8667fb879e4f23791a0"
 dependencies = [
- "wast 50.0.0",
+ "wast 54.0.1",
 ]
 
 [[package]]
 name = "web-sys"
-version = "0.3.60"
+version = "0.3.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -4997,9 +5042,9 @@ dependencies = [
 
 [[package]]
 name = "which"
-version = "4.3.0"
+version = "4.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
 dependencies = [
  "either",
  "libc",
@@ -5128,19 +5173,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
 dependencies = [
  "windows_aarch64_gnullvm",
- "windows_aarch64_msvc 0.42.0",
- "windows_i686_gnu 0.42.0",
- "windows_i686_msvc 0.42.0",
- "windows_x86_64_gnu 0.42.0",
+ "windows_aarch64_msvc 0.42.1",
+ "windows_i686_gnu 0.42.1",
+ "windows_i686_msvc 0.42.1",
+ "windows_x86_64_gnu 0.42.1",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.1",
+ "windows_i686_gnu 0.42.1",
+ "windows_i686_msvc 0.42.1",
+ "windows_x86_64_gnu 0.42.1",
  "windows_x86_64_gnullvm",
- "windows_x86_64_msvc 0.42.0",
+ "windows_x86_64_msvc 0.42.1",
 ]
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
 
 [[package]]
 name = "windows_aarch64_msvc"
@@ -5156,9 +5225,9 @@ checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
 
 [[package]]
 name = "windows_i686_gnu"
@@ -5174,9 +5243,9 @@ checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
 
 [[package]]
 name = "windows_i686_msvc"
@@ -5192,9 +5261,9 @@ checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
 
 [[package]]
 name = "windows_x86_64_gnu"
@@ -5210,15 +5279,15 @@ checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
 
 [[package]]
 name = "windows_x86_64_msvc"
@@ -5234,9 +5303,9 @@ checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.42.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
 
 [[package]]
 name = "winreg"
@@ -5389,9 +5458,9 @@ checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
 
 [[package]]
 name = "zip"
-version = "0.6.3"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080"
+checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef"
 dependencies = [
  "byteorder",
  "crc32fast",
diff --git a/Cargo.toml b/Cargo.toml
index d813e3e43df6ff846f76b9520346acf000b5acb8..55a8dc7c9d76fc4fc85b9940155eab828598a3b8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,19 +17,19 @@ authors = []
 #authors = [ "Overpeek <overpeek.fin@gmail.com>" ]
 
 [workspace.dependencies]
-anyhow = { version = "1.0.66", features = ["backtrace"] }
+anyhow = { version = "1.0.69", features = ["backtrace"] }
 thiserror = "1.0.38"
 actix-web = "4.3.0"
 tracing = "0.1.37"
 tracing-subscriber = "0.3.16"
 dotenv = "0.15.0"
-serde = { version = "1.0.147", features = ["derive"] }
-reqwest = { version = "0.11.13", features = ["json"] }
+serde = { version = "1.0.152", features = ["derive"] }
+reqwest = { version = "0.11.14", features = ["json"] }
 #utoipa = "2.4.2"
-utoipa = { version = "3.0.2", features = ["actix_extras", "uuid"] }
+utoipa = { version = "3.0.3", features = ["actix_extras", "uuid"] }
 utoipa-swagger-ui = { version = "3.0.2", features = ["actix-web"] }
-serde_json = "1.0.89"
-clap = { version = "4.0.29", features = ["derive", "env"] }
+serde_json = "1.0.93"
+clap = { version = "4.1.6", features = ["derive", "env"] }
 # tokio-stream = "0.1.12"
 
 #[workspace.dependencies.paperclip]
@@ -38,7 +38,7 @@ clap = { version = "4.0.29", features = ["derive", "env"] }
 #features = ["actix4", "v3", "uuid"]
 
 [workspace.dependencies.tokio]
-version = "1.21.2"
+version = "1.25.0"
 features = ["rt-multi-thread", "macros", "time", "signal"]
 
 [profile.release-wasm]
diff --git a/aava.sql b/aava.sql
index eacdffe8af907c4a5bd4f195ebe4c5f4b5187dd7..db33931f5550d76893b74756c5e7014339e0af94 100644
--- a/aava.sql
+++ b/aava.sql
@@ -14,7 +14,7 @@ CREATE TABLE users (
 CREATE TABLE courses (
     id UUID NOT NULL DEFAULT uuid_generate_v4() PRIMARY KEY,
     name VARCHAR(256) NOT NULL,
-    description VARCHAR(2048),
+    description TEXT,
     -- TODO: image
 
     points_max int NOT NULL DEFAULT 0,
@@ -31,7 +31,7 @@ CREATE TABLE course_admins (
 CREATE TABLE lessons (
     id UUID NOT NULL DEFAULT uuid_generate_v4() PRIMARY KEY,
     name VARCHAR(256) NOT NULL,
-    description VARCHAR(2048),
+    description TEXT,
     -- TODO: image
 
     points_max int NOT NULL DEFAULT 0,
@@ -43,7 +43,7 @@ CREATE TABLE lessons (
 CREATE TABLE exercises (
     id UUID NOT NULL DEFAULT uuid_generate_v4() PRIMARY KEY,
     name VARCHAR(256) NOT NULL,
-    description VARCHAR(2048),
+    description TEXT,
     -- TODO: image
 
     points_max int NOT NULL DEFAULT 0,
@@ -51,10 +51,9 @@ CREATE TABLE exercises (
 
     lesson UUID NOT NULL REFERENCES lessons(id),
 
-    code VARCHAR(65536) NOT NULL,
-    example_code VARCHAR(65536),
-    stdin VARCHAR(65536) NOT NULL,
-    stdout VARCHAR(65536) NOT NULL
+    code TEXT NOT NULL,
+    stdin TEXT NOT NULL,
+    stdout TEXT NOT NULL
 );
 
 -- user specific(private) tables
@@ -86,7 +85,7 @@ CREATE TABLE exercise_progress (
     points int NOT NULL DEFAULT 0,
     attempts int NOT NULL DEFAULT 0,
 
-    code VARCHAR(65536) NOT NULL
+    code TEXT NOT NULL
 );
 
 -- test data
diff --git a/crates/back/Cargo.toml b/crates/back/Cargo.toml
index af96a7069c8eecc975e532f250f95527e79c9e4a..823e74ce9c37e6c270b231d9513910491edfc428 100644
--- a/crates/back/Cargo.toml
+++ b/crates/back/Cargo.toml
@@ -8,7 +8,7 @@ authors.workspace = true
 
 [features]
 default = ["embedded_db", "postgres_db"]
-embedded_db = ["bincode", "sled"]
+embedded_db = ["dashmap"]
 postgres_db = ["sqlx/postgres"]
 
 [dependencies]
@@ -45,7 +45,7 @@ serde_json.workspace = true # required by utoipa
 
 # data serialization
 serde.workspace = true
-bincode = { version = "1.3.3", optional = true }
+# bincode = { version = "1.3.3", optional = true }
 
 # runner interaction
 reqwest.workspace = true
@@ -53,12 +53,13 @@ glue = { path = "../glue" }
 
 # database interaction
 sqlx = { version = "0.6.2", features = ["runtime-tokio-native-tls", "uuid"] }
-sled = { version = "0.34.7", optional = true }
-uuid = { version = "1.2.2", features = ["serde", "v4"] }
+# sled = { version = "0.34.7", optional = true }
+dashmap = { version = "5.4.0", optional = true }
+uuid = { version = "1.3.0", features = ["serde", "v4"] }
 
 # auth
 argon2 = { version = "0.4.1", features = ["std"] }
-jwt-simple = "0.11.2"
+jwt-simple = "0.11.3"
 
 regex = "1.7.1"
-once_cell = "1.17.0"
+once_cell = "1.17.1"
diff --git a/crates/back/src/api/auth.rs b/crates/back/src/api/auth.rs
index 999433e045e446d85a7d2f1a74f0e832c419afce..a568e20c31e8c767d95fc5d7ca78bbc387768b8d 100644
--- a/crates/back/src/api/auth.rs
+++ b/crates/back/src/api/auth.rs
@@ -1,29 +1,27 @@
-use crate::db::{Database, DbCreateUser, DbVerifyUser};
+use crate::{
+    create_jwt,
+    db::{Database, DbCreateUser, DbVerifyUser},
+};
 use actix_web::{
     dev::HttpServiceFactory,
     web::{self, Data, Json},
-    HttpResponse, Responder,
+    HttpResponse,
 };
-use jwt_simple::prelude::*;
-use once_cell::sync::Lazy;
 use regex::Regex;
-
 use serde::{Deserialize, Serialize};
-
 use utoipa::ToSchema;
-use uuid::Uuid;
 
 //
 
 pub fn api() -> impl HttpServiceFactory {
-    let _username_regex = UsernameRegex(
+    let username_regex = UsernameRegex(
         Regex::new(r#"^[a-zA-Z0-9\-_]{4,32}$"#).expect("The username regex is invalid"),
     );
 
     web::scope("/auth")
+        .app_data(Data::new(username_regex))
         .route("/login", web::post().to(login))
-        .route("/register", web::post().to(create))
-        .app_data(UsernameRegex)
+        .route("/register", web::post().to(register))
 }
 
 //
@@ -42,11 +40,11 @@ struct UsernameRegex(Regex);
         (status = CONFLICT, description = "Username is already taken"),
     ),
 )]
-async fn create(
+async fn register(
     Json(info): Json<UserCreate>,
     db: Data<Database>,
     username_regex: Data<UsernameRegex>,
-) -> impl Responder {
+) -> HttpResponse {
     let UserCreate { username, password } = info;
 
     if password.len() < 8 {
@@ -68,15 +66,23 @@ async fn create(
         }
     };
 
+    let jwt = match create_jwt(id) {
+        Ok(v) => v,
+        Err(err) => {
+            tracing::error!("JWT gen error: {err}");
+            return HttpResponse::InternalServerError().finish(); /* .json("Internal Server Error"); */
+        }
+    };
+
     HttpResponse::Created().json(UserInfo {
         username,
         privileged: false,
-        jwt: create_jwt(id),
+        jwt,
     })
 }
 
 #[utoipa::path(
-    get,
+    post,
     path = "/api/auth/login",
     request_body = UserCreate,
     responses(
@@ -84,7 +90,7 @@ async fn create(
         (status = UNAUTHORIZED, description = "Invalid username/password"),
     ),
 )]
-async fn login(Json(info): Json<UserLogin>, db: Data<Database>) -> impl Responder {
+async fn login(Json(info): Json<UserLogin>, db: Data<Database>) -> HttpResponse {
     let UserLogin { username, password } = info;
 
     let (id, privileged) = match db.verify_user(&username, &password).await {
@@ -98,22 +104,21 @@ async fn login(Json(info): Json<UserLogin>, db: Data<Database>) -> impl Responde
         }
     };
 
+    let jwt = match create_jwt(id) {
+        Ok(v) => v,
+        Err(err) => {
+            tracing::error!("JWT gen error: {err}");
+            return HttpResponse::InternalServerError().finish(); /* .json("Internal Server Error"); */
+        }
+    };
+
     HttpResponse::Ok().json(UserInfo {
         username,
         privileged,
-        jwt: create_jwt(id),
+        jwt,
     })
 }
 
-fn create_jwt(id: Uuid) -> String {
-    // lazy initialized JWT crypt key
-    // => JWT:s become invalid after a server restart
-    static JWT_KEYS: Lazy<HS512Key> = Lazy::new(HS512Key::generate);
-
-    let claims = Claims::with_custom_claims(id, Duration::from_hours(2));
-    JWT_KEYS.authenticate(claims).unwrap()
-}
-
 /// User creation information
 #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 pub(super) struct UserCreate {
diff --git a/crates/back/src/api/course.rs b/crates/back/src/api/course.rs
index e1ab0322fa558804e4666f332de48e2dc0ced435..cf07e39f89433a9f66395623afd0268e0c8cb0d7 100644
--- a/crates/back/src/api/course.rs
+++ b/crates/back/src/api/course.rs
@@ -1,12 +1,10 @@
-use super::lesson::Lesson;
-use crate::{db::DbCourse, ierr, HttpResult};
+use crate::db::Database;
 use actix_web::{
     dev::HttpServiceFactory,
-    web::{self, Data, Json, Path},
-    Responder,
+    web::{self, Data, Path},
+    HttpResponse,
 };
 use serde::{Deserialize, Serialize};
-use sqlx::{FromRow, PgPool};
 use utoipa::ToSchema;
 use uuid::Uuid;
 
@@ -23,49 +21,16 @@ pub fn api() -> impl HttpServiceFactory {
     responses((status = 200, description = "List lessons in course", body = [Exercise])),
     params(("uuid", description = "UUID for the course"))
 )]
-async fn lessons(course: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Responder> {
+async fn lessons(course: Path<Uuid>, db: Data<Database>) -> HttpResponse {
     // TODO: auth
 
-    #[derive(FromRow)]
-    struct Lessons {
-        id: Uuid,
-        name: String,
-        description: Option<String>,
-        points_max: i32,
+    match db.get_lessons(*course).await {
+        Ok(v) => HttpResponse::Ok().json(v),
+        Err(err) => {
+            tracing::error!("Database error: {err}");
+            HttpResponse::InternalServerError().finish()
+        }
     }
-
-    let query = sqlx::query_as(
-        r#"SELECT id, name, description, points_max
-           FROM lessons
-           WHERE course = $1"#,
-    )
-    .bind(*course)
-    .fetch_all(&**db)
-    .await
-    .map_err(|err| {
-        tracing::error!("Database interaction error: {err}");
-        ierr()
-    })?;
-
-    let lessons = query
-        .into_iter()
-        .map(
-            |Lessons {
-                 id,
-                 name,
-                 description,
-                 points_max,
-             }| Lesson {
-                id,
-                name,
-                description,
-                points: 0, // TODO:
-                points_max: points_max as _,
-            },
-        )
-        .collect::<Vec<_>>();
-
-    Ok(Json(lessons))
 }
 
 //
@@ -93,25 +58,6 @@ pub struct Course {
     pub points_max: u32,
 }
 
-impl From<DbCourse> for Course {
-    fn from(
-        DbCourse {
-            id,
-            name,
-            description,
-            points_max,
-        }: DbCourse,
-    ) -> Self {
-        Course {
-            id,
-            name,
-            description,
-            points: 0, // TODO:
-            points_max: points_max as _,
-        }
-    }
-}
-
 //
 
 #[cfg(test)]
diff --git a/crates/back/src/api/courses.rs b/crates/back/src/api/courses.rs
index 718d52deaa74ba05cd70a104fe4281b54fc5b678..b79b820a2fcb5d8f76f210bcd7c9145834e4c75c 100644
--- a/crates/back/src/api/courses.rs
+++ b/crates/back/src/api/courses.rs
@@ -1,9 +1,8 @@
-use super::course::Course;
 use crate::db::Database;
 use actix_web::{
     dev::HttpServiceFactory,
     web::{self, Data},
-    HttpResponse, Responder,
+    HttpResponse,
 };
 
 //
@@ -18,18 +17,16 @@ pub fn api() -> impl HttpServiceFactory {
     path = "/api/courses",
     responses((status = OK, description = "List of courses available", body = [Course])),
 )]
-async fn get(db: Data<Database>) -> impl Responder {
+async fn get(db: Data<Database>) -> HttpResponse {
     // TODO: auth
 
-    let courses = match db.get_courses().await {
-        Ok(v) => v,
+    match db.get_courses().await {
+        Ok(v) => HttpResponse::Ok().json(v),
         Err(err) => {
             tracing::error!("Database error: {err}");
-            return HttpResponse::InternalServerError().finish(); /* .json("Internal Server Error"); */
+            HttpResponse::InternalServerError().finish()
         }
-    };
-
-    HttpResponse::Ok().json(courses.into_iter().map(Course::from).collect::<Vec<_>>())
+    }
 }
 
 //
diff --git a/crates/back/src/api/exercise.rs b/crates/back/src/api/exercise.rs
index bcbcaa99bcbd0e36ec6782e70eef8e55f21a3e0b..398e63e29382deae8354ca5c016100cf88581bef 100644
--- a/crates/back/src/api/exercise.rs
+++ b/crates/back/src/api/exercise.rs
@@ -1,13 +1,11 @@
-use crate::{ierr, HttpResult, Runner};
+use crate::{db::Database, Runner};
 use actix_web::{
     dev::HttpServiceFactory,
-    error::ErrorNotFound,
     web::{self, Data, Json, Path},
-    Responder,
+    HttpResponse,
 };
 use glue::{CodeRunArgs, CodeRunResult};
 use serde::{Deserialize, Serialize};
-use sqlx::{FromRow, PgPool};
 use utoipa::ToSchema;
 use uuid::Uuid;
 
@@ -23,26 +21,24 @@ pub fn api() -> impl HttpServiceFactory {
 #[utoipa::path(
     get,
     path = "/api/exercise/{uuid}/code",
-    responses((status = 200, description = "Default code and stdin for an exercise", body = DefaultCode)),
+    responses((status = 200, description = "Default code and stdin for an exercise", body = ExerciseDetail)),
+    responses((status = 404, description = "Exercise not found")),
     params(("uuid", description = "UUID for the exercise"))
 )]
-async fn code(exercise: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Responder> {
+async fn code(exercise: Path<Uuid>, db: Data<Database>) -> HttpResponse {
     // TODO: auth
 
-    let query: DefaultCode = sqlx::query_as(
-        r#"SELECT code, stdin, stdout
-           FROM exercises
-           WHERE id = $1"#,
-    )
-    .bind(*exercise)
-    .fetch_one(&**db)
-    .await
-    .map_err(|err| {
+    let Ok(details) = db.get_exercise_details(*exercise).await.map_err(|err| {
         tracing::error!("Database interaction error: {err}");
-        ierr()
-    })?;
+    }) else {
+        return HttpResponse::InternalServerError().finish();
+    };
+
+    let Some(details) = details else {
+        return HttpResponse::NotFound().finish();
+    };
 
-    Ok(Json(query))
+    HttpResponse::Ok().json(details)
 }
 
 /// Submit an answer for an exercise and get the result
@@ -50,64 +46,68 @@ async fn code(exercise: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Respond
     post,
     path = "/api/exercise/{uuid}/submit",
     request_body = CodeSubmit,
-    responses((status = 200, description = "Submit exercise", body = CodeSubmitResult)),
-    params(("uuid", description = "UUID for the exercise"))
+    responses(
+        (status = 200, description = "Submit exercise", body = CodeSubmitResult),
+        (status = 404, description = "Exercise not found"),
+    ),
+    params(
+        ("uuid" = Uuid, Path, description = "UUID for the exercise"),
+        ("code" = CodeSubmit, description = "Submitted code"),
+    )
 )]
 pub async fn submit(
     exercise: Path<Uuid>,
     code: Json<CodeSubmit>,
     runner: Data<Runner>,
-    db: Data<PgPool>,
-) -> HttpResult<impl Responder> {
+    db: Data<Database>,
+) -> HttpResponse {
     // TODO: auth
     // TODO: rate limit
     // TODO: max attempts
 
-    // load the stdin for the exercise
-    let Some((stdin, expected_stdout, points_max)): Option<(String, String, i32)> = sqlx::query_as(
-        r#"SELECT stdin, stdout, points_max
-           FROM exercises
-           WHERE id = $1"#,
-    )
-    .bind(*exercise)
-    .fetch_optional(&**db)
-    .await
-    .map_err(|err| {
-        tracing::error!("Database interaction error: {err}");
-        ierr()
-    })? else {
-        return Err(ErrorNotFound("Exercise not found"));
+    let ExerciseVerifyData {
+        stdin,
+        stdout: expected_stdout,
+        max_points: points_max,
+    } = match db.get_exercise_verify(*exercise).await {
+        Ok(Some(v)) => v,
+        Ok(None) => {
+            return HttpResponse::NotFound().finish();
+        }
+        Err(err) => {
+            tracing::error!("Database interaction error: {err}");
+            return HttpResponse::InternalServerError().finish();
+        }
     };
-    let points_max = points_max.max(0) as u32;
 
-    // get the code run results from the Runner
-    let CodeRunResult {
-        stdout,
-        stderr,
-        exit_code,
-    } = reqwest::Client::builder()
-        .build()
-        .map_err(|err| {
-            tracing::error!("Failed to create a reqwest Client: {err}");
-            ierr()
-        })?
-        .get(runner.0.clone())
+    let runner_resp = reqwest::Client::new()
+        .get(runner.0.as_str())
         .json(&CodeRunArgs {
             code: code.code.to_string(),
             stdin,
         })
         .send()
-        .await
-        .map_err(|err| {
-            tracing::error!("Runner server responded with an unexpected error: {err}");
-            ierr()
-        })?
-        .json::<CodeRunResult>()
-        .await
-        .map_err(|err| {
-            tracing::error!("Runner server responded with an invalid json: {err}");
-            ierr()
-        })?;
+        .await;
+
+    let runner_result = match runner_resp {
+        Ok(v) => v.json().await,
+        Err(err) => {
+            tracing::error!("Runner responded with an unknown error: {err}");
+            return HttpResponse::InternalServerError().finish();
+        }
+    };
+
+    let CodeRunResult {
+        stdout,
+        stderr,
+        exit_code,
+    } = match runner_result {
+        Ok(v) => v,
+        Err(err) => {
+            tracing::error!("Runner responded with an invalid json: {err}");
+            return HttpResponse::InternalServerError().finish();
+        }
+    };
 
     // TODO: points
     let points = if !stderr.is_empty() {
@@ -120,13 +120,13 @@ pub async fn submit(
 
     // TODO: update points
 
-    Ok(Json(CodeSubmitResult {
+    HttpResponse::Ok().json(CodeSubmitResult {
         stdout,
         stderr,
         exit_code,
         points,
         points_max,
-    }))
+    })
 }
 
 //
@@ -164,8 +164,8 @@ pub struct Exercise {
     pub attempts_max: u32,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, FromRow)]
-pub struct DefaultCode {
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ExerciseDetail {
     /// Initial code for the exercise
     #[schema(example = "print('x')")]
     pub code: String,
@@ -210,6 +210,13 @@ pub struct CodeSubmitResult {
     pub points_max: u32,
 }
 
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct ExerciseVerifyData {
+    pub stdin: String,
+    pub stdout: String,
+    pub max_points: u32,
+}
+
 //
 
 #[cfg(test)]
diff --git a/crates/back/src/api/lesson.rs b/crates/back/src/api/lesson.rs
index c3f3e230b7f433fc0249563ba8dc816f4b7f44f7..4e0647cab8fe2bdfac166b2a922367f7af608e89 100644
--- a/crates/back/src/api/lesson.rs
+++ b/crates/back/src/api/lesson.rs
@@ -1,12 +1,10 @@
-use super::exercise::Exercise;
-use crate::{ierr, HttpResult};
+use crate::db::Database;
 use actix_web::{
     dev::HttpServiceFactory,
-    web::{self, Data, Json, Path},
-    Responder,
+    web::{self, Data, Path},
+    HttpResponse,
 };
 use serde::{Deserialize, Serialize};
-use sqlx::{FromRow, PgPool};
 use utoipa::ToSchema;
 use uuid::Uuid;
 
@@ -23,53 +21,16 @@ pub fn api() -> impl HttpServiceFactory {
     responses((status = 200, description = "List exercises in lesson", body = [Exercise])),
     params(("uuid", description = "UUID for the lesson"))
 )]
-async fn exercises(lesson: Path<Uuid>, db: Data<PgPool>) -> HttpResult<impl Responder> {
+async fn exercises(lesson: Path<Uuid>, db: Data<Database>) -> HttpResponse {
     // TODO: auth
 
-    #[derive(FromRow)]
-    struct Exercises {
-        id: Uuid,
-        name: String,
-        description: Option<String>,
-        points_max: i32,
-        attempts_max: i32,
+    match db.get_exercises(*lesson).await {
+        Ok(exercises) => HttpResponse::Ok().json(exercises),
+        Err(err) => {
+            tracing::error!("Database error: {err}");
+            HttpResponse::InternalServerError().finish()
+        }
     }
-
-    let query = sqlx::query_as(
-        r#"SELECT id, name, description, points_max, attempts_max
-           FROM exercises
-           WHERE lesson = $1"#,
-    )
-    .bind(*lesson)
-    .fetch_all(&**db)
-    .await
-    .map_err(|err| {
-        tracing::error!("Database interaction error: {err}");
-        ierr()
-    })?;
-
-    let courses: Vec<_> = query
-        .into_iter()
-        .map(
-            |Exercises {
-                 id,
-                 name,
-                 description,
-                 points_max,
-                 attempts_max,
-             }| Exercise {
-                id,
-                name,
-                description,
-                points: 0, // TODO:
-                points_max: points_max as _,
-                attempts: 0, // TODO:
-                attempts_max: attempts_max as _,
-            },
-        )
-        .collect();
-
-    Ok(Json(courses))
 }
 
 //
diff --git a/crates/back/src/api/mod.rs b/crates/back/src/api/mod.rs
index a30c04779e4a9aa2774ad28b605fbdc7d5a4ba05..a5270044df025bf206fcfec3757507dcc8e4910e 100644
--- a/crates/back/src/api/mod.rs
+++ b/crates/back/src/api/mod.rs
@@ -3,18 +3,18 @@ use utoipa::OpenApi;
 
 //
 
-mod auth;
-mod course;
-mod courses;
-mod exercise;
-mod lesson;
+pub mod auth;
+pub mod course;
+pub mod courses;
+pub mod exercise;
+pub mod lesson;
 
 //
 
 #[derive(OpenApi)]
 #[openapi(
     paths(
-        auth::create,
+        auth::register,
         auth::login,
         courses::get,
         course::lessons,
@@ -28,7 +28,7 @@ mod lesson;
         auth::UserInfo,
         course::Course,
         exercise::Exercise,
-        exercise::DefaultCode,
+        exercise::ExerciseDetail,
         exercise::CodeSubmit,
         exercise::CodeSubmitResult,
         lesson::Lesson,
diff --git a/crates/back/src/cli.rs b/crates/back/src/cli.rs
index 8c86371cddc578fed93fb9306c28d49a461e6b84..2996dd291288189a8f9859352094d7857ba20aeb 100644
--- a/crates/back/src/cli.rs
+++ b/crates/back/src/cli.rs
@@ -9,7 +9,7 @@ use std::{
 
 //
 
-#[derive(Debug, Clone, Parser)]
+#[derive(Clone, Parser)]
 #[clap(name = "Aava Main Backend")]
 #[clap(author, version)]
 pub struct Args {
@@ -28,9 +28,13 @@ pub struct Args {
     /// address for the Aava database
     ///
     /// examples:
-    ///  - postgres=username:password@localhost
-    ///  - embedded=./db/
-    ///  - none=
+    ///
+    #[cfg_attr(
+        feature = "postgres_db",
+        doc = " - postgres://username:password@localhost\n"
+    )]
+    #[cfg_attr(feature = "embedded_db", doc = " - embedded=./db/\n")]
+    ///  - none
     #[clap(alias = "db", short, long, env, default_value_t)]
     pub database: DatabaseConfig,
 
@@ -39,7 +43,7 @@ pub struct Args {
     pub test_data: bool,
 }
 
-#[derive(Debug, Clone, Default)]
+#[derive(Clone, Default)]
 pub enum DatabaseConfig {
     #[cfg(feature = "postgres_db")]
     Postgres(Url),
@@ -51,27 +55,30 @@ pub enum DatabaseConfig {
     None,
 }
 
+//
+
 impl FromStr for DatabaseConfig {
     type Err = String;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
+        // nodb
         if s == "none" {
             return Ok(Self::None);
         }
 
-        let (left, right) = s.split_once('=').ok_or_else(|| "missing '='".to_owned())?;
+        // embedded db
+        #[cfg(feature = "embedded_db")]
+        if let Some(("embedded", path)) = s.split_once('=') {
+            let path = PathBuf::from_str(path).map_err(|err| err.to_string())?;
+            return Ok(Self::Embedded(path));
+        }
 
-        match left {
-            #[cfg(feature = "postgres_db")]
-            "postgres" => Ok(Self::Postgres(
-                Url::from_str(&format!("postgres://{right}")).map_err(|err| err.to_string())?,
-            )),
-
-            #[cfg(feature = "embedded_db")]
-            "embedded" => Ok(Self::Embedded(
-                PathBuf::from_str(right).map_err(|err| err.to_string())?,
-            )),
+        // url db
+        let url = Url::from_str(s).map_err(|err| err.to_string())?;
 
+        match url.scheme() {
+            #[cfg(feature = "postgres_db")]
+            "postgres" => Ok(Self::Postgres(url)),
             other => Err(format!("unknown database type '{other}'")),
         }
     }
@@ -93,41 +100,24 @@ impl Display for DatabaseConfig {
     }
 }
 
-// impl ValueParserFactory for DatabaseConfig {
-//     type Parser = DatabaseConfigParser;
-//
-//     fn value_parser() -> Self::Parser {
-//         DatabaseConfigParser
-//     }
-// }
-//
-// #[derive(Debug, Clone, Copy)]
-// pub struct DatabaseConfigParser;
-//
-// impl TypedValueParser for DatabaseConfigParser {
-//     type Value = DatabaseConfig;
-//
-//     fn parse_ref(
-//         &self,
-//         cmd: &clap::Command,
-//         arg: Option<&clap::Arg>,
-//         value: &std::ffi::OsStr,
-//     ) -> Result<Self::Value, clap::Error> {
-//         match value {}
-//         value;
-//         todo!()
-//     }
-// }
-
-//
+impl Debug for Args {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("Args")
+            .field("bind", &self.bind)
+            .field("frontend", &self.frontend)
+            .field("runner", &self.runner.as_str())
+            .field("database", &self.database)
+            .field("test_data", &self.test_data)
+            .finish()
+    }
+}
 
-// impl Debug for Args {
-//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-//         f.debug_struct("Args")
-//             .field("bind", &self.bind)
-//             .field("frontend", &self.frontend)
-//             .field("runner", &self.runner.as_str())
-//             .field("database", &self.database.as_str())
-//             .finish()
-//     }
-// }
+impl Debug for DatabaseConfig {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::Postgres(arg0) => f.debug_tuple("Postgres").field(&arg0.as_str()).finish(),
+            Self::Embedded(arg0) => f.debug_tuple("Embedded").field(&arg0.as_os_str()).finish(),
+            Self::None => write!(f, "None"),
+        }
+    }
+}
diff --git a/crates/back/src/db/any.rs b/crates/back/src/db/any.rs
index e328b01c8176baa9505603012a16364d0c07b492..92897825474a19ee479df0b88b88c2e32c052087 100644
--- a/crates/back/src/db/any.rs
+++ b/crates/back/src/db/any.rs
@@ -1,25 +1,33 @@
-use super::{postgres, sled, DbCourse, DbCreateUser, DbError, DbUser};
-use crate::{cli::DatabaseConfig, LazyAwait};
+use std::mem::transmute;
+
+use super::{embedded, postgres, DbCreateUser, DbError, DbResult, DbUser};
+use crate::{
+    api::{
+        course::Course,
+        exercise::{Exercise, ExerciseDetail, ExerciseVerifyData},
+        lesson::Lesson,
+    },
+    cli::DatabaseConfig,
+    LazyAwait,
+};
+use actix_web_lab::__reexports::futures_util::future::join_all;
 use argon2::{
     password_hash::{rand_core::OsRng, SaltString},
     Argon2, PasswordHash, PasswordHasher,
 };
-use std::{future::Future, pin::Pin};
+use tokio::task::JoinHandle;
 use uuid::Uuid;
 
 //
 
+// TODO:
+// mysql/mariadb  - SQL not compatible with postgreSQL
+// surrealdb      - pure rust db
+// sled           - in-memory db
 pub(super) enum AnyDatabase {
-    // TODO:
-    // mysql/mariadb  - SQL not compatible with postgreSQL
-    // surrealdb      - pure rust db
-    // sled           - in-memory db
-
-    // embedded db
     #[cfg(feature = "postgres_db")]
-    Sled(sled::Database),
+    Embedded(embedded::Database),
 
-    // postgre sql
     #[cfg(feature = "embedded_db")]
     Postgres(postgres::Database),
 
@@ -29,24 +37,16 @@ pub(super) enum AnyDatabase {
 
 //
 
-pub(super) type DbFut<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
-pub(super) type GetCoursesFut<'a> = DbFut<'a, Result<Vec<DbCourse>, DbError>>;
-pub(super) type CreateUserFut<'a> = DbFut<'a, Result<DbCreateUser, DbError>>;
-pub(super) type GetUserFut<'a> = DbFut<'a, Result<Option<DbUser>, DbError>>;
-
-pub(super) trait DatabaseImpl {
-    // TODO: https://github.com/rust-lang/rust/issues/91611
-
-    fn get_courses(&self) -> GetCoursesFut;
-
-    fn create_user<'a>(
-        &'a self,
-        uuid: Uuid,
-        name: &'a str,
-        passwd: PasswordHash<'a>,
-    ) -> CreateUserFut<'a>;
-
-    fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a>;
+macro_rules! gen_match {
+    ($self:ident, $($f:tt)*) => {
+        match $self {
+            #[cfg(feature = "postgres_db")]
+            AnyDatabase::Postgres(v) => v.$($f)*.await,
+            #[cfg(feature = "embedded_db")]
+            AnyDatabase::Embedded(v) => v.$($f)*.await,
+            AnyDatabase::None => Err(DbError::DatabaseNotConnected),
+        }
+    };
 }
 
 //
@@ -72,7 +72,7 @@ impl AnyDatabase {
             .unwrap_or(AnyDatabase::None)
     }
 
-    pub async fn new(db: &DatabaseConfig, test_data: bool) -> Result<AnyDatabase, DbError> {
+    pub async fn new(db: &DatabaseConfig, test_data: bool) -> DbResult<AnyDatabase> {
         let result = match db {
             #[cfg(feature = "postgres_db")]
             DatabaseConfig::Postgres(url) => {
@@ -82,7 +82,7 @@ impl AnyDatabase {
             #[cfg(feature = "embedded_db")]
             DatabaseConfig::Embedded(path) => {
                 tracing::info!("Creating an embedded db");
-                AnyDatabase::Sled(sled::Database::new(path)?)
+                AnyDatabase::Embedded(embedded::Database::new(path)?)
             }
             DatabaseConfig::None => {
                 tracing::warn!("No database configured");
@@ -91,87 +91,90 @@ impl AnyDatabase {
         };
 
         if test_data {
-            for _ in 0..10 {
+            result.insert_test_data().await?;
+        }
+
+        Ok(result)
+    }
+
+    pub async fn insert_test_data(&self) -> DbResult<()> {
+        let mut futs: Vec<JoinHandle<DbResult<()>>> = vec![];
+
+        // TODO: tokio::scoped whenever it is added
+        // SAFETY: tokio::spawn needs to take a static ref
+        // because these JoinHandles could be ignored, but
+        // every single one of them is joined before returning
+        let sta: &'static Self = unsafe { transmute(self) };
+
+        // insert random users
+        for _ in 0..10 {
+            futs.push(tokio::spawn(async move {
                 let name = Uuid::new_v4().to_string();
                 let passwd = Uuid::new_v4().to_string();
                 let salt = SaltString::generate(&mut OsRng);
+                // this is the slow part:
                 let hash = Argon2::default().hash_password(passwd.as_bytes(), &salt)?;
 
-                let res = result.create_user(Uuid::new_v4(), &name, hash).await;
+                let res = sta.create_user(&name, hash).await;
                 tracing::info!("Created a test user {name} with password {passwd} ({res:?})");
-            }
+
+                Ok(())
+            }));
         }
 
-        Ok(result)
-    }
-}
+        // insert random courses
+        // TODO:
 
-/* impl Deref for AnyDatabase {
-    type Target = dyn DatabaseImpl;
+        // insert random lessons
+        // TODO:
 
-    fn deref(&self) -> &Self::Target {
-        match self {
-            #[cfg(feature = "postgres_db")]
-            AnyDatabase::Postgres(v) => v,
-            #[cfg(feature = "embedded_db")]
-            AnyDatabase::Sled(v) => v,
-            AnyDatabase::None => &(),
+        // insert random exercises
+        // TODO:
+
+        // join handles
+        let results = join_all(futs).await;
+        for result in results {
+            result.unwrap_or_else(|err| {
+                // returning from `insert_test_data` is now UB
+                panic!("Failed to join a handle ({err})");
+            })?;
         }
+
+        Ok(())
     }
-} */
 
-impl DatabaseImpl for AnyDatabase {
-    fn get_courses(&self) -> GetCoursesFut {
-        match self {
-            #[cfg(feature = "postgres_db")]
-            AnyDatabase::Postgres(v) => v.get_courses(),
-            #[cfg(feature = "embedded_db")]
-            AnyDatabase::Sled(v) => v.get_courses(),
-            AnyDatabase::None => ().get_courses(),
-        }
+    pub async fn get_courses(&self) -> DbResult<Vec<Course>> {
+        gen_match!(self, get_courses())
     }
 
-    fn create_user<'a>(
-        &'a self,
-        uuid: Uuid,
-        name: &'a str,
-        passwd: PasswordHash<'a>,
-    ) -> CreateUserFut<'a> {
-        match self {
-            #[cfg(feature = "postgres_db")]
-            AnyDatabase::Postgres(v) => v.create_user(uuid, name, passwd),
-            #[cfg(feature = "embedded_db")]
-            AnyDatabase::Sled(v) => v.create_user(uuid, name, passwd),
-            AnyDatabase::None => ().create_user(uuid, name, passwd),
-        }
+    pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> {
+        gen_match!(self, get_lessons(course))
     }
 
-    fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a> {
-        match self {
-            #[cfg(feature = "postgres_db")]
-            AnyDatabase::Postgres(v) => v.get_user(name),
-            #[cfg(feature = "embedded_db")]
-            AnyDatabase::Sled(v) => v.get_user(name),
-            AnyDatabase::None => ().get_user(name),
-        }
+    pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> {
+        gen_match!(self, get_exercises(lesson))
+    }
+
+    pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> {
+        gen_match!(self, get_exercise_details(exercise))
     }
-}
 
-impl DatabaseImpl for () {
-    fn get_courses(&self) -> GetCoursesFut {
-        Box::pin(async { Err(DbError::DatabaseNotConnected) })
+    pub async fn get_exercise_verify(
+        &self,
+        exercise: Uuid,
+    ) -> DbResult<Option<ExerciseVerifyData>> {
+        gen_match!(self, get_exercise_verify(exercise))
     }
 
-    fn create_user<'a>(
+    pub async fn create_user<'a>(
         &'a self,
-        _uuid: Uuid,
-        _name: &'a str,
-        _passwd: PasswordHash<'a>,
-    ) -> CreateUserFut<'a> {
-        Box::pin(async { Err(DbError::DatabaseNotConnected) })
+        name: &'a str,
+        passwd: PasswordHash<'a>,
+    ) -> DbResult<DbCreateUser> {
+        gen_match!(self, create_user(name, passwd))
     }
 
-    fn get_user<'a>(&'a self, _name: &'a str) -> GetUserFut<'a> {
-        Box::pin(async { Err(DbError::DatabaseNotConnected) })
+    pub async fn get_user<'a>(&'a self, name: &'a str) -> DbResult<Option<DbUser>> {
+        gen_match!(self, get_user(name))
     }
 }
diff --git a/crates/back/src/db/embedded.rs b/crates/back/src/db/embedded.rs
new file mode 100644
index 0000000000000000000000000000000000000000..af5fd91ab34b38512e0e69fcfda138c3dc48725b
--- /dev/null
+++ b/crates/back/src/db/embedded.rs
@@ -0,0 +1,204 @@
+use super::{DbCreateUser, DbError, DbResult, DbUser};
+use crate::api::{
+    course::Course,
+    exercise::{Exercise, ExerciseDetail, ExerciseVerifyData},
+    lesson::Lesson,
+};
+use argon2::PasswordHash;
+use dashmap::{mapref::entry::Entry, DashMap};
+use std::{
+    collections::hash_map::DefaultHasher,
+    hash::{Hash, Hasher},
+    path::Path,
+};
+use uuid::Uuid;
+
+//
+
+#[derive(Debug, Default)]
+pub struct Database {
+    // hash   : String |---> u64
+    // lookup :   u64  |---> Uuid
+    usernames: DashMap<u64, Uuid>,
+
+    users: DashMap<Uuid, UsersRow>,
+    courses: DashMap<Uuid, CoursesRow>,
+    lessons: DashMap<Uuid, LessonsRow>,
+    exercises: DashMap<Uuid, ExercisesRow>,
+}
+
+#[derive(Debug, Default)]
+struct UsersRow {
+    username: String,
+    password: String, // argon2id
+    privileged: bool,
+}
+
+#[derive(Debug, Default)]
+struct CoursesRow {
+    name: String,
+    description: Option<String>,
+
+    points_max: u32,
+
+    _owner: Uuid,
+}
+
+#[derive(Debug, Default)]
+struct LessonsRow {
+    name: String,
+    description: Option<String>,
+
+    points_max: u32,
+
+    course: Uuid,
+}
+
+#[derive(Debug, Default)]
+struct ExercisesRow {
+    name: String,
+    description: Option<String>,
+
+    points_max: u32,
+    attempts_max: u32,
+
+    lesson: Uuid,
+
+    code: String,
+    stdin: String,
+    stdout: String,
+}
+
+//
+
+impl Database {
+    pub fn new(_path: &Path) -> Result<Self, DbError> {
+        // TODO: load
+
+        Ok(Self::default())
+    }
+}
+
+impl Drop for Database {
+    fn drop(&mut self) {
+        // TODO: save
+    }
+}
+
+impl Database {
+    pub async fn get_courses(&self) -> DbResult<Vec<Course>> {
+        Ok(self
+            .courses
+            .iter()
+            .map(|row| Course {
+                id: *row.key(),
+                name: row.name.clone(), // TODO: copy-on-write
+                description: row.description.clone(),
+                points: 0, // TODO:
+                points_max: row.points_max,
+            })
+            .collect())
+    }
+
+    pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> {
+        Ok(self
+            .lessons
+            .iter()
+            .filter(|row| row.course == course)
+            .map(|row| Lesson {
+                id: *row.key(),
+                name: row.name.clone(), // TODO: copy-on-write
+                description: row.description.clone(),
+                points: 0, // TODO:
+                points_max: row.points_max,
+            })
+            .collect())
+    }
+
+    pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> {
+        Ok(self
+            .exercises
+            .iter()
+            .filter(|row| row.lesson == lesson)
+            .map(|row| Exercise {
+                id: *row.key(),
+                name: row.name.clone(), // TODO: copy-on-write
+                description: row.description.clone(),
+                points: 0, // TODO:
+                points_max: row.points_max,
+                attempts: 0, // TODO:
+                attempts_max: row.attempts_max,
+            })
+            .collect())
+    }
+
+    pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> {
+        Ok(self.exercises.get(&exercise).map(|row| ExerciseDetail {
+            code: row.code.clone(), // TODO: copy-on-write
+            stdin: row.stdin.clone(),
+            stdout: row.stdout.clone(),
+        }))
+    }
+
+    pub async fn get_exercise_verify(
+        &self,
+        exercise: Uuid,
+    ) -> DbResult<Option<ExerciseVerifyData>> {
+        Ok(self.exercises.get(&exercise).map(|row| ExerciseVerifyData {
+            max_points: row.points_max,
+            stdin: row.stdin.clone(),
+            stdout: row.stdout.clone(),
+        }))
+    }
+
+    pub async fn create_user<'a>(
+        &'a self,
+        name: &'a str,
+        passwd: PasswordHash<'a>,
+    ) -> DbResult<DbCreateUser> {
+        let res @ DbCreateUser::Created(uuid) = self.take_username(name).await else {
+            return Ok(DbCreateUser::Existed);
+        };
+
+        self.users.insert(
+            uuid,
+            UsersRow {
+                username: name.to_string(),
+                password: passwd.to_string(),
+                privileged: false,
+            },
+        );
+
+        Ok(res)
+    }
+
+    pub async fn get_user<'a>(&'a self, name: &'a str) -> DbResult<Option<DbUser>> {
+        let Some(uuid) = self.usernames.get(&Self::hash_username(name)).map(|row| *row.value()) else {
+            return Ok(None);
+        };
+
+        Ok(self.users.get(&uuid).map(|row| DbUser {
+            id: *row.key(),
+            name: row.username.clone(),
+            passwd: row.password.clone(),
+            privileged: row.privileged,
+        }))
+    }
+
+    async fn take_username(&self, name: &str) -> DbCreateUser {
+        match self.usernames.entry(Self::hash_username(name)) {
+            Entry::Occupied(_) => DbCreateUser::Existed,
+            Entry::Vacant(entry) => {
+                let uuid = Uuid::new_v4();
+                entry.insert(uuid);
+                DbCreateUser::Created(uuid)
+            }
+        }
+    }
+
+    fn hash_username(name: &str) -> u64 {
+        let mut hasher = DefaultHasher::new();
+        name.hash(&mut hasher);
+        hasher.finish()
+    }
+}
diff --git a/crates/back/src/db/mod.rs b/crates/back/src/db/mod.rs
index b197880af4c707b9f4ff6900aa6cef1bb8ca1a77..1d9bb92670a59723255a1da8f3455460d91a24e0 100644
--- a/crates/back/src/db/mod.rs
+++ b/crates/back/src/db/mod.rs
@@ -1,5 +1,13 @@
-use self::any::{AnyDatabase, DatabaseImpl};
-use crate::{cli::DatabaseConfig, LazyAwait};
+use self::any::AnyDatabase;
+use crate::{
+    api::{
+        course::Course,
+        exercise::{Exercise, ExerciseDetail, ExerciseVerifyData},
+        lesson::Lesson,
+    },
+    cli::DatabaseConfig,
+    LazyAwait,
+};
 use argon2::{
     password_hash::{rand_core::OsRng, SaltString},
     Argon2, PasswordHash, PasswordHasher, PasswordVerifier,
@@ -12,10 +20,10 @@ use uuid::Uuid;
 //
 
 mod any;
+#[cfg(feature = "embedded_db")]
+mod embedded;
 #[cfg(feature = "postgres_db")]
 mod postgres;
-#[cfg(feature = "embedded_db")]
-mod sled;
 
 //
 
@@ -23,15 +31,8 @@ pub struct Database {
     pool: LazyAwait<AnyDatabase>,
 }
 
-#[derive(Debug, FromRow, Serialize, Deserialize)]
-pub struct DbCourse {
-    pub id: Uuid,
-    pub name: String,
-    pub description: Option<String>,
-    pub points_max: i32,
-}
-
-#[derive(Debug, FromRow, Serialize, Deserialize)]
+#[derive(Debug, FromRow)]
+#[cfg_attr(feature = "embedded_db", derive(Serialize, Deserialize))]
 pub struct DbUser {
     pub id: Uuid,
     pub name: String,
@@ -39,13 +40,13 @@ pub struct DbUser {
     pub privileged: bool, // can create courses
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
 pub enum DbCreateUser {
     Created(Uuid),
     Existed,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
 pub enum DbVerifyUser {
     Valid((Uuid, bool)),
     Invalid,
@@ -59,22 +60,15 @@ pub enum DbError {
     #[error("Database interaction error ({0})")]
     Database(#[from] sqlx::Error),
 
-    #[cfg(feature = "embedded_db")]
+    /* #[cfg(feature = "embedded_db")]
     #[error("Embedded database error ({0})")]
-    EDatabase(#[from] ::sled::Error),
-
-    #[cfg(feature = "embedded_db")]
-    #[error("Embedded database error ({0})")]
-    EDatabaseTransaction(#[from] ::sled::transaction::TransactionError),
-
-    #[cfg(feature = "embedded_db")]
-    #[error("Database data format error ({0})")]
-    DataFormat(#[from] bincode::Error),
-
+    EDatabase(#[from] ::sled::Error), */
     #[error("No connection to the database")]
     DatabaseNotConnected,
 }
 
+pub type DbResult<T> = Result<T, DbError>;
+
 //
 
 impl Database {
@@ -84,29 +78,49 @@ impl Database {
         Self { pool }
     }
 
-    pub async fn get_courses(&self) -> Result<Vec<DbCourse>, DbError> {
+    pub async fn get_courses(&self) -> DbResult<Vec<Course>> {
         self.db().await.get_courses().await
     }
 
-    pub async fn create_user(&self, name: &str, passwd: &str) -> Result<DbCreateUser, DbError> {
-        let uuid = Uuid::new_v4();
+    pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> {
+        self.db().await.get_lessons(course).await
+    }
+
+    pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> {
+        self.db().await.get_exercises(lesson).await
+    }
+
+    pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> {
+        self.db().await.get_exercise_details(exercise).await
+    }
+
+    pub async fn get_exercise_verify(
+        &self,
+        exercise: Uuid,
+    ) -> DbResult<Option<ExerciseVerifyData>> {
+        self.db().await.get_exercise_verify(exercise).await
+    }
+
+    pub async fn create_user(&self, name: &str, passwd: &str) -> DbResult<DbCreateUser> {
         let salt = SaltString::generate(&mut OsRng);
         let passwd = Argon2::default().hash_password(passwd.as_bytes(), &salt)?;
 
-        self.db().await.create_user(uuid, name, passwd).await
+        self.db().await.create_user(name, passwd).await
     }
 
-    pub async fn verify_user(&self, name: &str, _passwd: &str) -> Result<DbVerifyUser, DbError> {
+    pub async fn verify_user(&self, name: &str, passwd: &str) -> DbResult<DbVerifyUser> {
         let Some(user) = self.db().await.get_user(name).await? else {
+            tracing::trace!("Username not found");
             // username not found
             return Ok(DbVerifyUser::Invalid);
         };
 
         let hash = PasswordHash::new(&user.passwd)?;
         if Argon2::default()
-            .verify_password(user.passwd.as_bytes(), &hash)
+            .verify_password(passwd.as_bytes(), &hash)
             .is_err()
         {
+            tracing::trace!("Invalid password");
             // invalid password
             return Ok(DbVerifyUser::Invalid);
         }
diff --git a/crates/back/src/db/postgres.rs b/crates/back/src/db/postgres.rs
index 393c1bcf6bca451d819f6ca6abac62bc14d88721..a00d642ac091b1312d53007b5ec5b0e0fbc11fc2 100644
--- a/crates/back/src/db/postgres.rs
+++ b/crates/back/src/db/postgres.rs
@@ -1,10 +1,11 @@
-use super::{
-    any::{CreateUserFut, DatabaseImpl, GetCoursesFut, GetUserFut},
-    DbCreateUser, DbError,
+use super::{DbCreateUser, DbError, DbResult, DbUser};
+use crate::api::{
+    course::Course,
+    exercise::{Exercise, ExerciseDetail, ExerciseVerifyData},
+    lesson::Lesson,
 };
 use argon2::PasswordHash;
 use sqlx::{postgres::PgPoolOptions, PgPool};
-use std::{future::Future, pin::Pin};
 use uuid::Uuid;
 
 //
@@ -29,60 +30,166 @@ impl Database {
     }
 }
 
-impl DatabaseImpl for Database {
-    fn get_courses(&self) -> GetCoursesFut {
-        Box::pin(async move {
-            Ok(sqlx::query_as(
-                r#"SELECT id, name, description, points_max
-           FROM courses"#,
-            )
-            .fetch_all(&self.pool)
-            .await?)
-        })
+impl Database {
+    pub async fn get_courses(&self) -> DbResult<Vec<Course>> {
+        Ok(sqlx::query_as(
+            r#"SELECT
+                 (id, name, description, points_max)
+               FROM
+                 courses"#,
+        )
+        .fetch_all(&self.pool)
+        .await?
+        .into_iter()
+        .map(
+            |(id, name, description, points_max): (Uuid, String, Option<String>, i32)| Course {
+                id,
+                name,
+                description,
+                points: 0, // TODO:
+                points_max: points_max.max(0) as u32,
+            },
+        )
+        .collect())
+    }
+
+    pub async fn get_lessons(&self, course: Uuid) -> DbResult<Vec<Lesson>> {
+        Ok(sqlx::query_as(
+            r#"SELECT
+                 (id, name, description, points_max)
+               FROM
+                 lessons
+               WHERE
+                 course = $1"#,
+        )
+        .bind(course)
+        .fetch_all(&self.pool)
+        .await?
+        .into_iter()
+        .map(
+            |(id, name, description, points_max): (Uuid, String, Option<String>, i32)| Lesson {
+                id,
+                name,
+                description,
+                points: 0, // TODO:
+                points_max: points_max.max(0) as u32,
+            },
+        )
+        .collect())
+    }
+
+    pub async fn get_exercises(&self, lesson: Uuid) -> DbResult<Vec<Exercise>> {
+        Ok(sqlx::query_as(
+            r#"SELECT
+                 (id, name, description, points_max, attempts_max)
+               FROM
+                 exercises
+               WHERE
+                 lesson = $1"#,
+        )
+        .bind(lesson)
+        .fetch_all(&self.pool)
+        .await?
+        .into_iter()
+        .map(
+            |(id, name, description, points_max, attempts_max): (
+                Uuid,
+                String,
+                Option<String>,
+                i32,
+                i32,
+            )| Exercise {
+                id,
+                name,
+                description,
+                points: 0, // TODO:
+                points_max: points_max.max(0) as u32,
+                attempts: 0, // TODO:
+                attempts_max: attempts_max.max(0) as u32,
+            },
+        )
+        .collect())
+    }
+
+    pub async fn get_exercise_details(&self, exercise: Uuid) -> DbResult<Option<ExerciseDetail>> {
+        Ok(sqlx::query_as(
+            r#"SELECT
+                 (code, stdin, stdout)
+               FROM
+                 exercises
+               WHERE
+                 id = $1"#,
+        )
+        .bind(exercise)
+        .fetch_optional(&self.pool)
+        .await?
+        .map(
+            |(code, stdin, stdout): (String, String, String)| ExerciseDetail {
+                code,
+                stdin,
+                stdout,
+            },
+        ))
     }
 
-    fn create_user<'a>(
+    pub async fn get_exercise_verify(
+        &self,
+        exercise: Uuid,
+    ) -> DbResult<Option<ExerciseVerifyData>> {
+        Ok(sqlx::query_as(
+            r#"SELECT
+                 (stdin, stdout, points_max)
+               FROM
+                 exercises
+               WHERE
+                 id = $1"#,
+        )
+        .bind(exercise)
+        .fetch_optional(&self.pool)
+        .await?
+        .map(
+            |(stdin, stdout, max_points): (String, String, i32)| ExerciseVerifyData {
+                stdin,
+                stdout,
+                max_points: max_points.max(0) as _,
+            },
+        ))
+    }
+
+    pub async fn create_user<'a>(
         &'a self,
-        uuid: Uuid,
         name: &'a str,
         passwd: PasswordHash<'a>,
-    ) -> CreateUserFut<'a> {
-        Box::pin(async move {
-            let result: Option<(Uuid,)> = sqlx::query_as(
-                r#"
-            INSERT INTO
-                users (username, password)
-            VALUES
-                ($2, $3);
-            ON CONFLICT DO NOTHING
-            RETURNING id"#,
-            )
-            .bind(uuid)
-            .bind(name)
-            .bind(passwd.to_string())
-            .fetch_optional(&self.pool)
-            .await?;
-
-            if let Some((uuid,)) = result {
-                Ok(DbCreateUser::Created(uuid))
-            } else {
-                Ok(DbCreateUser::Existed)
-            }
-        }) as Pin<Box<dyn Future<Output = Result<DbCreateUser, DbError>> + Send>>
+    ) -> DbResult<DbCreateUser> {
+        Ok(sqlx::query_as(
+            r#"INSERT INTO
+                 users (username, password)
+               VALUES
+                 ($2, $3);
+               ON CONFLICT
+                 DO NOTHING
+               RETURNING
+                 id"#,
+        )
+        .bind(name)
+        .bind(passwd.to_string())
+        .fetch_optional(&self.pool)
+        .await?
+        .map(|(uuid,): (Uuid,)| DbCreateUser::Created(uuid))
+        .unwrap_or(DbCreateUser::Existed))
     }
 
-    fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a> {
-        Box::pin(async move {
-            let result = sqlx::query_as(
-                r#"SELECT (id, name, password, privileged)
-              FROM users
-              WHERE username = $1"#,
-            )
-            .bind(name)
-            .fetch_optional(&self.pool)
-            .await?;
-
-            Ok(result)
-        })
+    pub async fn get_user<'a>(&'a self, name: &'a str) -> DbResult<Option<DbUser>> {
+        Ok(sqlx::query_as(
+            r#"SELECT
+                 (id, name, password, privileged)
+               FROM
+                 users
+               WHERE
+                 username = $1"#,
+        )
+        .bind(name)
+        .fetch_optional(&self.pool)
+        .await?)
     }
 }
diff --git a/crates/back/src/db/sled.rs b/crates/back/src/db/sled.rs
deleted file mode 100644
index 3d8c208a5e78c5ef3b9edbd22df4658da315dbec..0000000000000000000000000000000000000000
--- a/crates/back/src/db/sled.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-use super::{
-    any::{CreateUserFut, DatabaseImpl, GetCoursesFut, GetUserFut},
-    DbCreateUser, DbError, DbUser,
-};
-use argon2::PasswordHash;
-use sled::{IVec, Tree};
-use std::path::Path;
-use uuid::Uuid;
-
-//
-
-pub struct Database {
-    /* db: Db, */
-    users: Tree,
-    courses: Tree,
-    /* course_admins: Tree,
-    lessons: Tree,
-    exercises: Tree,
-
-    course_progress: Tree,
-    lesson_progress: Tree,
-    exercise_progress: Tree, */
-}
-
-//
-
-impl Database {
-    pub fn new(path: &Path) -> Result<Self, DbError> {
-        let db = sled::open(path)?;
-
-        let users = db.open_tree("users")?;
-        let courses = db.open_tree("courses")?;
-        /* let course_admins = db.open_tree("course_admins")?;
-        let lessons = db.open_tree("lessons")?;
-        let exercises = db.open_tree("exercises")?;
-        let course_progress = db.open_tree("course_progress")?;
-        let lesson_progress = db.open_tree("lesson_progress")?;
-        let exercise_progress = db.open_tree("exercise_progress")?; */
-
-        Ok(Self {
-            /* db, */
-            users,
-            courses,
-            /* course_admins,
-            lessons,
-            exercises,
-
-            course_progress,
-            lesson_progress,
-            exercise_progress, */
-        })
-    }
-}
-
-impl DatabaseImpl for Database {
-    fn get_courses(&self) -> GetCoursesFut {
-        Box::pin(async move {
-            self.courses
-                .iter()
-                .map(|row: Result<(IVec, IVec), sled::Error>| {
-                    row.map_err(DbError::from)
-                        .and_then(|(_, val)| Ok(bincode::deserialize(&val[..])?))
-                })
-                .collect::<Result<Vec<_>, _>>()
-        })
-    }
-
-    fn create_user<'a>(
-        &'a self,
-        uuid: Uuid,
-        name: &'a str,
-        passwd: PasswordHash<'a>,
-    ) -> CreateUserFut<'a> {
-        Box::pin(async move {
-            let userdata = bincode::serialize(&DbUser {
-                id: uuid,
-                name: name.to_owned(),
-                passwd: passwd.to_string(),
-                privileged: false,
-            })?;
-
-            Ok(self.users.transaction(move |t| {
-                if t.get(uuid.as_bytes()).is_ok() {
-                    return Ok(DbCreateUser::Existed);
-                }
-
-                t.insert(uuid.as_bytes(), &userdata[..])?;
-                Ok(DbCreateUser::Created(uuid))
-            })?)
-        })
-    }
-
-    fn get_user<'a>(&'a self, name: &'a str) -> GetUserFut<'a> {
-        Box::pin(async move {
-            // TODO: name -> uuid table
-            Ok(self
-                .users
-                .iter()
-                .flat_map(|row| row.ok())
-                .flat_map(|(_, row)| bincode::deserialize(&row[..]).ok())
-                .find(|i: &DbUser| i.name == name))
-        })
-    }
-}
diff --git a/crates/back/src/util/jwt.rs b/crates/back/src/util/jwt.rs
new file mode 100644
index 0000000000000000000000000000000000000000..41be9c6de74db2b805a4d789f11e679c93dff22e
--- /dev/null
+++ b/crates/back/src/util/jwt.rs
@@ -0,0 +1,29 @@
+use jwt_simple::prelude::*;
+use once_cell::sync::Lazy;
+use serde::{Deserialize, Serialize};
+use uuid::Uuid;
+
+//
+
+pub fn create_jwt(id: Uuid) -> anyhow::Result<String> {
+    let claims = Claims::with_custom_claims(JwtData { id }, Duration::from_hours(2));
+    JWT_KEYS.authenticate(claims)
+}
+
+pub fn verify_jwt(jwt: &str) -> Option<Uuid> {
+    JWT_KEYS
+        .verify_token::<JwtData>(jwt, None)
+        .ok()
+        .map(|claims| claims.custom.id)
+}
+
+//
+
+// lazy initialized JWT crypt key
+// => JWT:s become invalid after a server restart
+static JWT_KEYS: Lazy<HS512Key> = Lazy::new(HS512Key::generate);
+
+#[derive(Serialize, Deserialize)]
+struct JwtData {
+    id: Uuid,
+}
diff --git a/crates/back/src/util/mod.rs b/crates/back/src/util/mod.rs
index 27c524ec3ad03d998264c4cf5dab81fb0fd9316b..2e79d6e40cc1f090be184d2b94193ff1ffcd0199 100644
--- a/crates/back/src/util/mod.rs
+++ b/crates/back/src/util/mod.rs
@@ -1,5 +1,6 @@
 pub use actix_web::Result as HttpResult;
 pub use iter_list::*;
+pub use jwt::*;
 pub use lazy_await::*;
 
 //
@@ -10,6 +11,7 @@ use reqwest::Url;
 //
 
 mod iter_list;
+mod jwt;
 mod lazy_await;
 
 //