diff --git a/.gitlab/issue_templates/Issue Template.md b/.gitlab/issue_templates/Issue Template.md
new file mode 100644
index 0000000000000000000000000000000000000000..e1a06675f693f618533c1a1a981e4df5b7621069
--- /dev/null
+++ b/.gitlab/issue_templates/Issue Template.md	
@@ -0,0 +1,15 @@
+# Headline
+
+### Description
+
+
+
+
+
+
+
+
+
+
+
+/label ~conduit
diff --git a/Cargo.lock b/Cargo.lock
index d9dbbf27cf375e73f6145a946a39a61f657c0651..d7538e0683967b746f0e4beecec3efa4c8ed3d4e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -15,6 +15,15 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "arrayref"
 version = "0.3.6"
@@ -113,15 +122,15 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.6.0"
+version = "3.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
+checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
 
 [[package]]
 name = "bytemuck"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a4bad0c5981acc24bc09e532f35160f952e35422603f0563cd7a73c2c2e65a0"
+checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58"
 
 [[package]]
 name = "byteorder"
@@ -137,9 +146,9 @@ checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
 
 [[package]]
 name = "cc"
-version = "1.0.66"
+version = "1.0.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
+checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
 
 [[package]]
 name = "cfg-if"
@@ -176,7 +185,9 @@ dependencies = [
  "image",
  "jsonwebtoken",
  "log",
- "rand 0.7.3",
+ "opentelemetry",
+ "opentelemetry-jaeger",
+ "rand",
  "regex",
  "reqwest",
  "ring",
@@ -190,6 +201,9 @@ dependencies = [
  "state-res",
  "thiserror",
  "tokio",
+ "tracing",
+ "tracing-opentelemetry",
+ "tracing-subscriber",
  "trust-dns-resolver",
 ]
 
@@ -242,12 +256,11 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.1"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
+checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
 dependencies = [
  "cfg-if",
- "const_fn",
  "crossbeam-utils",
  "lazy_static",
  "memoffset",
@@ -256,9 +269,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.1"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
+checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
 dependencies = [
  "autocfg",
  "cfg-if",
@@ -372,10 +385,11 @@ dependencies = [
 
 [[package]]
 name = "figment"
-version = "0.10.2"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3add2ec7727c9584a0ce75ee3c0f54f0ab692c7934450cc3a0287251e3a4f06"
+checksum = "c38799b106530aa30f774f7fca6d8f7e5f6234a79f427c4fad3c975eaf678931"
 dependencies = [
+ "atomic",
  "pear",
  "serde",
  "toml",
@@ -406,9 +420,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
 [[package]]
 name = "form_urlencoded"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
+checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
 dependencies = [
  "matches",
  "percent-encoding",
@@ -426,9 +440,9 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150"
+checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -441,9 +455,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846"
+checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -451,15 +465,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65"
+checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9"
+checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -468,15 +482,15 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500"
+checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd"
+checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -486,24 +500,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6"
+checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
 
 [[package]]
 name = "futures-task"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86"
-dependencies = [
- "once_cell",
-]
+checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
 
 [[package]]
 name = "futures-util"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b"
+checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -568,9 +579,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
 [[package]]
 name = "h2"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5"
+checksum = "d832b01df74254fe364568d6ddc294443f61cbec82816b60904303af87efae78"
 dependencies = [
  "bytes",
  "fnv",
@@ -583,7 +594,6 @@ dependencies = [
  "tokio",
  "tokio-util",
  "tracing",
- "tracing-futures",
 ]
 
 [[package]]
@@ -670,7 +680,7 @@ dependencies = [
  "httparse",
  "httpdate",
  "itoa",
- "pin-project 1.0.5",
+ "pin-project",
  "socket2",
  "tokio",
  "tower-service",
@@ -693,9 +703,9 @@ dependencies = [
 
 [[package]]
 name = "idna"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094"
+checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
 dependencies = [
  "matches",
  "unicode-bidi",
@@ -704,9 +714,9 @@ dependencies = [
 
 [[package]]
 name = "image"
-version = "0.23.13"
+version = "0.23.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "293f07a1875fa7e9c5897b51aa68b2d8ed8271b87e1a44cb64b9c3d98aabbc0d"
+checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
 dependencies = [
  "bytemuck",
  "byteorder",
@@ -744,6 +754,12 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "integer-encoding"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48dc51180a9b377fd75814d0cc02199c20f8e99433d6762f650d39cdbbd3b56f"
+
 [[package]]
 name = "ipconfig"
 version = "0.2.2"
@@ -785,9 +801,9 @@ checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
 
 [[package]]
 name = "js-sys"
-version = "0.3.47"
+version = "0.3.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
+checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -823,9 +839,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.86"
+version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
+checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213"
 
 [[package]]
 name = "linked-hash-map"
@@ -872,6 +888,15 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
 
+[[package]]
+name = "matchers"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
+dependencies = [
+ "regex-automata",
+]
+
 [[package]]
 name = "matches"
 version = "0.1.8"
@@ -910,9 +935,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.7.7"
+version = "0.7.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7"
+checksum = "a5dede4e2065b3842b8b0af444119f3aa331cc7cc2dd20388bfb0f5d5a38823a"
 dependencies = [
  "libc",
  "log",
@@ -1022,9 +1047,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.5.2"
+version = "1.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
+checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
 
 [[package]]
 name = "openssl"
@@ -1048,9 +1073,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
 
 [[package]]
 name = "openssl-src"
-version = "111.13.0+1.1.1i"
+version = "111.14.0+1.1.1j"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "045e4dc48af57aad93d665885789b43222ae26f4886494da12d1ed58d309dcb6"
+checksum = "055b569b5bd7e5462a1700f595c7c7d487691d73b5ce064176af7f9f0cbb80a9"
 dependencies = [
  "cc",
 ]
@@ -1069,6 +1094,44 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "opentelemetry"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "514d24875c140ed269eecc2d1b56d7b71b573716922a763c317fb1b1b4b58f15"
+dependencies = [
+ "async-trait",
+ "futures",
+ "js-sys",
+ "lazy_static",
+ "percent-encoding",
+ "pin-project",
+ "rand",
+ "thiserror",
+]
+
+[[package]]
+name = "opentelemetry-jaeger"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5677b3a361784aff6e2b1b30dbdb5f85f4ec57ff2ced41d9a481ad70a9d0b57"
+dependencies = [
+ "async-trait",
+ "lazy_static",
+ "opentelemetry",
+ "thiserror",
+ "thrift",
+]
+
+[[package]]
+name = "ordered-float"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "parking_lot"
 version = "0.11.1"
@@ -1082,14 +1145,14 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
+checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
 dependencies = [
  "cfg-if",
  "instant",
  "libc",
- "redox_syscall 0.1.57",
+ "redox_syscall 0.2.5",
  "smallvec",
  "winapi",
 ]
@@ -1102,9 +1165,9 @@ checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1"
 
 [[package]]
 name = "pear"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09f612cbd0f9dd03f5dd28a191c48e4148c3b027e41207b32eee130373c6c941"
+checksum = "86ab3a2b792945ed67eadbbdcbd2898f8dd2319392b2a45ac21adea5245cb113"
 dependencies = [
  "inlinable_string",
  "pear_codegen",
@@ -1113,9 +1176,9 @@ dependencies = [
 
 [[package]]
 name = "pear_codegen"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "602cf1780ee9bbca663ea75769e05643e16fe87d7c8ac9f4f385a2ed8940a75c"
+checksum = "620c9c4776ba41b59ab101360c9b1419c0c8c81cd2e6e39fae7109e7425994cb"
 dependencies = [
  "proc-macro2",
  "proc-macro2-diagnostics",
@@ -1125,9 +1188,9 @@ dependencies = [
 
 [[package]]
 name = "pem"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c220d01f863d13d96ca82359d1e81e64a7c6bf0637bcde7b2349630addf0c6"
+checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb"
 dependencies = [
  "base64 0.13.0",
  "once_cell",
@@ -1140,33 +1203,13 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
-[[package]]
-name = "pin-project"
-version = "0.4.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
-dependencies = [
- "pin-project-internal 0.4.27",
-]
-
 [[package]]
 name = "pin-project"
 version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63"
 dependencies = [
- "pin-project-internal 1.0.5",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "0.4.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "pin-project-internal",
 ]
 
 [[package]]
@@ -1182,9 +1225,9 @@ dependencies = [
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
+checksum = "0cf491442e4b033ed1c722cb9f0df5fcfcf4de682466c46469c36bc47dc5548a"
 
 [[package]]
 name = "pin-utils"
@@ -1267,26 +1310,13 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
 name = "quote"
-version = "1.0.8"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
 dependencies = [
  "proc-macro2",
 ]
 
-[[package]]
-name = "rand"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
-dependencies = [
- "getrandom 0.1.16",
- "libc",
- "rand_chacha 0.2.2",
- "rand_core 0.5.1",
- "rand_hc 0.2.0",
-]
-
 [[package]]
 name = "rand"
 version = "0.8.3"
@@ -1294,19 +1324,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
 dependencies = [
  "libc",
- "rand_chacha 0.3.0",
- "rand_core 0.6.1",
- "rand_hc 0.3.0",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.5.1",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
 ]
 
 [[package]]
@@ -1316,43 +1336,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.6.1",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-dependencies = [
- "getrandom 0.1.16",
+ "rand_core",
 ]
 
 [[package]]
 name = "rand_core"
-version = "0.6.1"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
+checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
 dependencies = [
  "getrandom 0.2.2",
 ]
 
-[[package]]
-name = "rand_hc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
-dependencies = [
- "rand_core 0.5.1",
-]
-
 [[package]]
 name = "rand_hc"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
 dependencies = [
- "rand_core 0.6.1",
+ "rand_core",
 ]
 
 [[package]]
@@ -1363,9 +1365,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
+checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
 dependencies = [
  "bitflags",
 ]
@@ -1413,6 +1415,16 @@ dependencies = [
  "thread_local",
 ]
 
+[[package]]
+name = "regex-automata"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
+dependencies = [
+ "byteorder",
+ "regex-syntax",
+]
+
 [[package]]
 name = "regex-syntax"
 version = "0.6.22"
@@ -1430,9 +1442,9 @@ dependencies = [
 
 [[package]]
 name = "reqwest"
-version = "0.11.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd281b1030aa675fb90aa994d07187645bb3c8fc756ca766e7c3070b439de9de"
+checksum = "0460542b551950620a3648c6aa23318ac6b3cd779114bd873209e6e8b5eb1c34"
 dependencies = [
  "base64 0.13.0",
  "bytes",
@@ -1490,7 +1502,7 @@ dependencies = [
 [[package]]
 name = "rocket"
 version = "0.5.0-dev"
-source = "git+https://github.com/SergioBenitez/Rocket.git?rev=c24f15c18f02319be83af4f3c1951dc220b52c5e#c24f15c18f02319be83af4f3c1951dc220b52c5e"
+source = "git+https://github.com/SergioBenitez/Rocket.git?rev=93e62c86eddf7cc9a7fc40b044182f83f0d7d92a#93e62c86eddf7cc9a7fc40b044182f83f0d7d92a"
 dependencies = [
  "async-trait",
  "atomic",
@@ -1503,7 +1515,7 @@ dependencies = [
  "memchr",
  "num_cpus",
  "parking_lot",
- "rand 0.7.3",
+ "rand",
  "ref-cast",
  "rocket_codegen",
  "rocket_http",
@@ -1519,7 +1531,7 @@ dependencies = [
 [[package]]
 name = "rocket_codegen"
 version = "0.5.0-dev"
-source = "git+https://github.com/SergioBenitez/Rocket.git?rev=c24f15c18f02319be83af4f3c1951dc220b52c5e#c24f15c18f02319be83af4f3c1951dc220b52c5e"
+source = "git+https://github.com/SergioBenitez/Rocket.git?rev=93e62c86eddf7cc9a7fc40b044182f83f0d7d92a#93e62c86eddf7cc9a7fc40b044182f83f0d7d92a"
 dependencies = [
  "devise",
  "glob",
@@ -1531,7 +1543,7 @@ dependencies = [
 [[package]]
 name = "rocket_http"
 version = "0.5.0-dev"
-source = "git+https://github.com/SergioBenitez/Rocket.git?rev=c24f15c18f02319be83af4f3c1951dc220b52c5e#c24f15c18f02319be83af4f3c1951dc220b52c5e"
+source = "git+https://github.com/SergioBenitez/Rocket.git?rev=93e62c86eddf7cc9a7fc40b044182f83f0d7d92a#93e62c86eddf7cc9a7fc40b044182f83f0d7d92a"
 dependencies = [
  "cookie",
  "either",
@@ -1693,7 +1705,7 @@ version = "0.18.0-alpha.1"
 source = "git+https://github.com/ruma/ruma?rev=0a10afe6dacc2b7a50a8002c953d10b7fb4e37bc#0a10afe6dacc2b7a50a8002c953d10b7fb4e37bc"
 dependencies = [
  "paste",
- "rand 0.8.3",
+ "rand",
  "ruma-identifiers-macros",
  "ruma-identifiers-validation",
  "ruma-serde",
@@ -1850,9 +1862,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework"
-version = "2.0.0"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
+checksum = "2dfd318104249865096c8da1dfabf09ddbb6d0330ea176812a62ec75e40c4166"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -1863,9 +1875,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.0.0"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
+checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -1908,9 +1920,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.62"
+version = "1.0.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
+checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
 dependencies = [
  "itoa",
  "ryu",
@@ -1931,9 +1943,9 @@ dependencies = [
 
 [[package]]
 name = "serde_yaml"
-version = "0.8.16"
+version = "0.8.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdd2af560da3c1fdc02cb80965289254fc35dff869810061e2d8290ee48848ae"
+checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
 dependencies = [
  "dtoa",
  "linked-hash-map",
@@ -1947,6 +1959,15 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
 
+[[package]]
+name = "sharded-slab"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3"
+dependencies = [
+ "lazy_static",
+]
+
 [[package]]
 name = "signal-hook-registry"
 version = "1.3.0"
@@ -2109,26 +2130,26 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
 dependencies = [
  "cfg-if",
  "libc",
- "rand 0.8.3",
- "redox_syscall 0.2.4",
+ "rand",
+ "redox_syscall 0.2.5",
  "remove_dir_all",
  "winapi",
 ]
 
 [[package]]
 name = "thiserror"
-version = "1.0.23"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
+checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.23"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
+checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2144,6 +2165,28 @@ dependencies = [
  "once_cell",
 ]
 
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "thrift"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6d965454947cc7266d22716ebfd07b18d84ebaf35eec558586bbb2a8cb6b5b"
+dependencies = [
+ "byteorder",
+ "integer-encoding",
+ "log",
+ "ordered-float",
+ "threadpool",
+]
+
 [[package]]
 name = "time"
 version = "0.1.43"
@@ -2289,15 +2332,27 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
 
 [[package]]
 name = "tracing"
-version = "0.1.23"
+version = "0.1.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3"
+checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
 dependencies = [
  "cfg-if",
  "pin-project-lite",
+ "tracing-attributes",
  "tracing-core",
 ]
 
+[[package]]
+name = "tracing-attributes"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8a9bd1db7706f2373a190b0d067146caa39350c486f3d455b0e33b431f94c07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "tracing-core"
 version = "0.1.17"
@@ -2308,13 +2363,59 @@ dependencies = [
 ]
 
 [[package]]
-name = "tracing-futures"
-version = "0.2.4"
+name = "tracing-log"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
+dependencies = [
+ "lazy_static",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-opentelemetry"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c"
+checksum = "cccdf13c28f1654fe806838f28c5b9cb23ca4c0eae71450daa489f50e523ceb1"
 dependencies = [
- "pin-project 0.4.27",
+ "opentelemetry",
  "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-serde"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ab8966ac3ca27126141f7999361cc97dd6fb4b71da04c02044fa9045d98bb96"
+dependencies = [
+ "ansi_term",
+ "chrono",
+ "lazy_static",
+ "matchers",
+ "regex",
+ "serde",
+ "serde_json",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-serde",
 ]
 
 [[package]]
@@ -2334,7 +2435,7 @@ dependencies = [
  "ipnet",
  "lazy_static",
  "log",
- "rand 0.8.3",
+ "rand",
  "smallvec",
  "thiserror",
  "tokio",
@@ -2378,9 +2479,9 @@ dependencies = [
 
 [[package]]
 name = "uncased"
-version = "0.9.3"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "369fa7fd7969c5373541d3c9a40dc1b76ce676fc87aba30d87c0ad3b97fad179"
+checksum = "300932469d646d39929ffe84ad5c1837beecf602519ef5695e485b472de4082b"
 dependencies = [
  "version_check",
 ]
@@ -2423,9 +2524,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
 [[package]]
 name = "url"
-version = "2.2.0"
+version = "2.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
+checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -2469,9 +2570,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.70"
+version = "0.2.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
+checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7"
 dependencies = [
  "cfg-if",
  "serde",
@@ -2481,9 +2582,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.70"
+version = "0.2.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
+checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8"
 dependencies = [
  "bumpalo",
  "lazy_static",
@@ -2496,9 +2597,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.20"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94"
+checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -2508,9 +2609,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.70"
+version = "0.2.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
+checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -2518,9 +2619,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.70"
+version = "0.2.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
+checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2531,15 +2632,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.70"
+version = "0.2.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
+checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1"
 
 [[package]]
 name = "web-sys"
-version = "0.3.47"
+version = "0.3.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
+checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
diff --git a/Cargo.toml b/Cargo.toml
index 4a901e129d4f114d563d13e0d83e13e625ccec93..2293b62df3b0c5939c87b683e33940572b68c23b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,7 @@ edition = "2018"
 [dependencies]
 # Used to handle requests
 # TODO: This can become optional as soon as proper configs are supported
-rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "c24f15c18f02319be83af4f3c1951dc220b52c5e", features = ["tls"] } # Used to handle requests
+rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "93e62c86eddf7cc9a7fc40b044182f83f0d7d92a", features = ["tls"] } # Used to handle requests
 #rocket = { git = "https://github.com/timokoesters/Rocket.git", branch = "empty_parameters", default-features = false, features = ["tls"] }
 
 # Used for matrix spec type definitions and helpers
@@ -30,42 +30,47 @@ state-res = { git = "https://github.com/ruma/state-res", branch = "main", featur
 # state-res = { path = "../../state-res", features = ["unstable-pre-spec", "gen-eventid"] }
 
 # Used for long polling and federation sender, should be the same as rocket::tokio
-tokio = { version = "1.1.0", features = ["macros", "time", "sync"] }
+tokio = "1.2.0"
 # Used for storing data permanently
 sled = { version = "0.34.6", default-features = false }
 # Used for emitting log entries
-log = "0.4.11"
+log = "0.4.14"
 # Used for rocket<->ruma conversions
 http = "0.2.3"
 # Used to find data directory for default db path
 directories = "3.0.1"
-
 # Used for ruma wrapper
-serde_json = { version = "1.0.60", features = ["raw_value"] }
+serde_json = { version = "1.0.64", features = ["raw_value"] }
 # Used for appservice registration files
-serde_yaml = "0.8.14"
+serde_yaml = "0.8.17"
 # Used for pdu definition
-serde = "1.0.117"
+serde = "1.0.123"
 # Used for secure identifiers
-rand = "0.7.3"
+rand = "0.8.3"
 # Used to hash passwords
 rust-argon2 = "0.8.3"
 # Used to send requests
-reqwest = "0.11.0"
+reqwest = "0.11.1"
 # Used for conduit::Error type
-thiserror = "1.0.22"
+thiserror = "1.0.24"
 # Used to generate thumbnails for images
-image = { version = "0.23.12", default-features = false, features = ["jpeg", "png", "gif"] }
+image = { version = "0.23.14", default-features = false, features = ["jpeg", "png", "gif"] }
 # Used to encode server public key
 base64 = "0.13.0"
 # Used when hashing the state
-ring = "0.16.19"
+ring = "0.16.20"
 # Used when querying the SRV record of other servers
 trust-dns-resolver = "0.20.0"
 # Used to find matching events for appservices
-regex = "1.4.2"
+regex = "1.4.3"
 # jwt jsonwebtokens
 jsonwebtoken = "7.2.0"
+# Performance measurements
+tracing = "0.1.25"
+opentelemetry = "0.12.0"
+tracing-subscriber = "0.2.16"
+tracing-opentelemetry = "0.11.0"
+opentelemetry-jaeger = "0.11.0"
 
 [features]
 default = ["conduit_bin"]
diff --git a/conduit-example.toml b/conduit-example.toml
index bb3ae33c1bbbe20cdecf4390c9e02fc70152db30..3aca538df039cd5a47b979c9c63532cb25fcc35f 100644
--- a/conduit-example.toml
+++ b/conduit-example.toml
@@ -30,6 +30,9 @@ max_request_size = 20_000_000 # in bytes
 #allow_encryption = false
 #allow_federation = false
 
+# Enable jaeger to support monitoring and troubleshooting through jaeger
+#allow_jaeger = false
+
 #cache_capacity = 1073741824 # in bytes, 1024 * 1024 * 1024
 #max_concurrent_requests = 4 # How many requests Conduit sends to other servers at the same time
 #workers = 4 # default: cpu core count * 2
diff --git a/src/client_server/account.rs b/src/client_server/account.rs
index 6927a53c4aba061bc7c8c60428a5f4abd6254a63..7d3067e59ac9f60dd0bb4a63552e598b7be42466 100644
--- a/src/client_server/account.rs
+++ b/src/client_server/account.rs
@@ -40,6 +40,7 @@
     feature = "conduit_bin",
     get("/_matrix/client/r0/register/available", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_register_available_route(
     db: State<'_, Database>,
     body: Ruma<get_username_availability::Request<'_>>,
@@ -82,6 +83,7 @@ pub async fn get_register_available_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/register", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn register_route(
     db: State<'_, Database>,
     body: Ruma<register::Request<'_>>,
@@ -498,6 +500,7 @@ pub async fn register_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/account/password", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn change_password_route(
     db: State<'_, Database>,
     body: Ruma<change_password::Request<'_>>,
@@ -562,6 +565,7 @@ pub async fn change_password_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/account/whoami", data = "<body>")
 )]
+#[tracing::instrument(skip(body))]
 pub async fn whoami_route(body: Ruma<whoami::Request>) -> ConduitResult<whoami::Response> {
     let sender_user = body.sender_user.as_ref().expect("user is authenticated");
     Ok(whoami::Response {
@@ -582,6 +586,7 @@ pub async fn whoami_route(body: Ruma<whoami::Request>) -> ConduitResult<whoami::
     feature = "conduit_bin",
     post("/_matrix/client/r0/account/deactivate", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn deactivate_route(
     db: State<'_, Database>,
     body: Ruma<deactivate::Request<'_>>,
diff --git a/src/client_server/alias.rs b/src/client_server/alias.rs
index 0dc40a9bec340279d543a38c93c44f3c48f3e9e0..03d49093652c0b27df95d6444d57104297433a6d 100644
--- a/src/client_server/alias.rs
+++ b/src/client_server/alias.rs
@@ -1,5 +1,6 @@
 use super::State;
 use crate::{ConduitResult, Database, Error, Ruma};
+use regex::Regex;
 use ruma::{
     api::{
         appservice,
@@ -19,6 +20,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/directory/room/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn create_alias_route(
     db: State<'_, Database>,
     body: Ruma<create_alias::Request<'_>>,
@@ -39,6 +41,7 @@ pub async fn create_alias_route(
     feature = "conduit_bin",
     delete("/_matrix/client/r0/directory/room/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_alias_route(
     db: State<'_, Database>,
     body: Ruma<delete_alias::Request<'_>>,
@@ -54,6 +57,7 @@ pub async fn delete_alias_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/directory/room/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_alias_route(
     db: State<'_, Database>,
     body: Ruma<get_alias::Request<'_>>,
@@ -83,15 +87,23 @@ pub async fn get_alias_helper(
         Some(r) => room_id = Some(r),
         None => {
             for (_id, registration) in db.appservice.iter_all().filter_map(|r| r.ok()) {
-                if db
-                    .sending
-                    .send_appservice_request(
-                        &db.globals,
-                        registration,
-                        appservice::query::query_room_alias::v1::Request { room_alias },
-                    )
-                    .await
-                    .is_ok()
+                let aliases = registration
+                    .get("namespaces")
+                    .and_then(|ns| ns.get("aliases"))
+                    .and_then(|users| users.get("regex"))
+                    .and_then(|regex| regex.as_str())
+                    .and_then(|regex| Regex::new(regex).ok());
+
+                if aliases.map_or(false, |aliases| aliases.is_match(room_alias.as_str()))
+                    && db
+                        .sending
+                        .send_appservice_request(
+                            &db.globals,
+                            registration,
+                            appservice::query::query_room_alias::v1::Request { room_alias },
+                        )
+                        .await
+                        .is_ok()
                 {
                     room_id = Some(db.rooms.id_from_alias(&room_alias)?.ok_or_else(|| {
                         Error::bad_config("Appservice lied to us. Room does not exist.")
diff --git a/src/client_server/backup.rs b/src/client_server/backup.rs
index 0f34ba780a0ac544dbb1b58d31454c261848ac90..f33d0de836a6c1e4e414089ab5ad0c6adea20a31 100644
--- a/src/client_server/backup.rs
+++ b/src/client_server/backup.rs
@@ -17,6 +17,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/unstable/room_keys/version", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn create_backup_route(
     db: State<'_, Database>,
     body: Ruma<create_backup::Request>,
@@ -35,6 +36,7 @@ pub async fn create_backup_route(
     feature = "conduit_bin",
     put("/_matrix/client/unstable/room_keys/version/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn update_backup_route(
     db: State<'_, Database>,
     body: Ruma<update_backup::Request<'_>>,
@@ -52,6 +54,7 @@ pub async fn update_backup_route(
     feature = "conduit_bin",
     get("/_matrix/client/unstable/room_keys/version", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_latest_backup_route(
     db: State<'_, Database>,
     body: Ruma<get_latest_backup::Request>,
@@ -79,6 +82,7 @@ pub async fn get_latest_backup_route(
     feature = "conduit_bin",
     get("/_matrix/client/unstable/room_keys/version/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_backup_route(
     db: State<'_, Database>,
     body: Ruma<get_backup::Request<'_>>,
@@ -105,6 +109,7 @@ pub async fn get_backup_route(
     feature = "conduit_bin",
     delete("/_matrix/client/unstable/room_keys/version/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_backup_route(
     db: State<'_, Database>,
     body: Ruma<delete_backup::Request<'_>>,
@@ -123,6 +128,7 @@ pub async fn delete_backup_route(
     feature = "conduit_bin",
     put("/_matrix/client/unstable/room_keys/keys", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn add_backup_keys_route(
     db: State<'_, Database>,
     body: Ruma<add_backup_keys::Request<'_>>,
@@ -156,6 +162,7 @@ pub async fn add_backup_keys_route(
     feature = "conduit_bin",
     put("/_matrix/client/unstable/room_keys/keys/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn add_backup_key_sessions_route(
     db: State<'_, Database>,
     body: Ruma<add_backup_key_sessions::Request<'_>>,
@@ -187,6 +194,7 @@ pub async fn add_backup_key_sessions_route(
     feature = "conduit_bin",
     put("/_matrix/client/unstable/room_keys/keys/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn add_backup_key_session_route(
     db: State<'_, Database>,
     body: Ruma<add_backup_key_session::Request<'_>>,
@@ -215,6 +223,7 @@ pub async fn add_backup_key_session_route(
     feature = "conduit_bin",
     get("/_matrix/client/unstable/room_keys/keys", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_backup_keys_route(
     db: State<'_, Database>,
     body: Ruma<get_backup_keys::Request<'_>>,
@@ -230,6 +239,7 @@ pub async fn get_backup_keys_route(
     feature = "conduit_bin",
     get("/_matrix/client/unstable/room_keys/keys/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_backup_key_sessions_route(
     db: State<'_, Database>,
     body: Ruma<get_backup_key_sessions::Request<'_>>,
@@ -247,6 +257,7 @@ pub async fn get_backup_key_sessions_route(
     feature = "conduit_bin",
     get("/_matrix/client/unstable/room_keys/keys/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_backup_key_session_route(
     db: State<'_, Database>,
     body: Ruma<get_backup_key_session::Request<'_>>,
@@ -270,6 +281,7 @@ pub async fn get_backup_key_session_route(
     feature = "conduit_bin",
     delete("/_matrix/client/unstable/room_keys/keys", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_backup_keys_route(
     db: State<'_, Database>,
     body: Ruma<delete_backup_keys::Request<'_>>,
@@ -292,6 +304,7 @@ pub async fn delete_backup_keys_route(
     feature = "conduit_bin",
     delete("/_matrix/client/unstable/room_keys/keys/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_backup_key_sessions_route(
     db: State<'_, Database>,
     body: Ruma<delete_backup_key_sessions::Request<'_>>,
@@ -314,6 +327,7 @@ pub async fn delete_backup_key_sessions_route(
     feature = "conduit_bin",
     delete("/_matrix/client/unstable/room_keys/keys/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_backup_key_session_route(
     db: State<'_, Database>,
     body: Ruma<delete_backup_key_session::Request<'_>>,
diff --git a/src/client_server/capabilities.rs b/src/client_server/capabilities.rs
index fa12a08e5b1100b284f1f72c4c3b9c15e5c0282c..b4fdf690925d638d549d10a3e8d26a296905f272 100644
--- a/src/client_server/capabilities.rs
+++ b/src/client_server/capabilities.rs
@@ -9,6 +9,7 @@
 ///
 /// Get information on this server's supported feature set and other relevent capabilities.
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/capabilities"))]
+#[tracing::instrument]
 pub async fn get_capabilities_route() -> ConduitResult<get_capabilities::Response> {
     let mut available = BTreeMap::new();
     available.insert(
diff --git a/src/client_server/config.rs b/src/client_server/config.rs
index f1d233a77f425ad49950ee8d17285d1b7cda0c82..aece96e2222494b17dd9c945b6b34162c6ab3997 100644
--- a/src/client_server/config.rs
+++ b/src/client_server/config.rs
@@ -16,6 +16,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/user/<_>/account_data/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_global_account_data_route(
     db: State<'_, Database>,
     body: Ruma<set_global_account_data::Request<'_>>,
@@ -49,6 +50,7 @@ pub async fn set_global_account_data_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/user/<_>/account_data/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_global_account_data_route(
     db: State<'_, Database>,
     body: Ruma<get_global_account_data::Request<'_>>,
diff --git a/src/client_server/context.rs b/src/client_server/context.rs
index f2a8cd43f75ac939158e3726cda337fe6f6c38e8..cb9aaf99d18f06df62d8d54e91810d8e88ede220 100644
--- a/src/client_server/context.rs
+++ b/src/client_server/context.rs
@@ -10,6 +10,7 @@
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/context/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_context_route(
     db: State<'_, Database>,
     body: Ruma<get_context::Request<'_>>,
diff --git a/src/client_server/device.rs b/src/client_server/device.rs
index 86ac511c03caf0bc00cb9826d1a82d5c140cee0f..1950c5c01d727b4dc58316faef194c60912db9ac 100644
--- a/src/client_server/device.rs
+++ b/src/client_server/device.rs
@@ -16,6 +16,7 @@
     feature = "conduit_bin",
     get("/_matrix/client/r0/devices", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_devices_route(
     db: State<'_, Database>,
     body: Ruma<get_devices::Request>,
@@ -35,6 +36,7 @@ pub async fn get_devices_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/devices/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_device_route(
     db: State<'_, Database>,
     body: Ruma<get_device::Request<'_>>,
@@ -53,6 +55,7 @@ pub async fn get_device_route(
     feature = "conduit_bin",
     put("/_matrix/client/r0/devices/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn update_device_route(
     db: State<'_, Database>,
     body: Ruma<update_device::Request<'_>>,
@@ -78,6 +81,7 @@ pub async fn update_device_route(
     feature = "conduit_bin",
     delete("/_matrix/client/r0/devices/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_device_route(
     db: State<'_, Database>,
     body: Ruma<delete_device::Request<'_>>,
@@ -126,6 +130,7 @@ pub async fn delete_device_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/delete_devices", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_devices_route(
     db: State<'_, Database>,
     body: Ruma<delete_devices::Request<'_>>,
diff --git a/src/client_server/directory.rs b/src/client_server/directory.rs
index 87d5fc8f79457d083ed696f7d38ae4697d02d73f..ae70ec576829270c573c82d47d62341558c01c24 100644
--- a/src/client_server/directory.rs
+++ b/src/client_server/directory.rs
@@ -21,7 +21,7 @@
         EventType,
     },
     serde::Raw,
-    ServerName,
+    ServerName, UInt,
 };
 
 #[cfg(feature = "conduit_bin")]
@@ -31,6 +31,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/r0/publicRooms", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_public_rooms_filtered_route(
     db: State<'_, Database>,
     body: Ruma<get_public_rooms_filtered::Request<'_>>,
@@ -50,6 +51,7 @@ pub async fn get_public_rooms_filtered_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/publicRooms", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_public_rooms_route(
     db: State<'_, Database>,
     body: Ruma<get_public_rooms::Request<'_>>,
@@ -78,6 +80,7 @@ pub async fn get_public_rooms_route(
     feature = "conduit_bin",
     put("/_matrix/client/r0/directory/list/room/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_room_visibility_route(
     db: State<'_, Database>,
     body: Ruma<set_room_visibility::Request<'_>>,
@@ -107,6 +110,7 @@ pub async fn set_room_visibility_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/directory/list/room/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_room_visibility_route(
     db: State<'_, Database>,
     body: Ruma<get_room_visibility::Request<'_>>,
@@ -124,7 +128,7 @@ pub async fn get_room_visibility_route(
 pub async fn get_public_rooms_filtered_helper(
     db: &Database,
     server: Option<&ServerName>,
-    limit: Option<ruma::UInt>,
+    limit: Option<UInt>,
     since: Option<&str>,
     filter: &IncomingFilter,
     _network: &IncomingRoomNetwork,
diff --git a/src/client_server/filter.rs b/src/client_server/filter.rs
index 4513ab42fe4d9474348b2524d1d8a01f9ffd2e29..a08eb34b33e16279196675ce6391e5c45d612b9a 100644
--- a/src/client_server/filter.rs
+++ b/src/client_server/filter.rs
@@ -5,6 +5,7 @@
 use rocket::{get, post};
 
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/user/<_>/filter/<_>"))]
+#[tracing::instrument]
 pub async fn get_filter_route() -> ConduitResult<get_filter::Response> {
     // TODO
     Ok(get_filter::Response::new(filter::IncomingFilterDefinition {
@@ -18,6 +19,7 @@ pub async fn get_filter_route() -> ConduitResult<get_filter::Response> {
 }
 
 #[cfg_attr(feature = "conduit_bin", post("/_matrix/client/r0/user/<_>/filter"))]
+#[tracing::instrument]
 pub async fn create_filter_route() -> ConduitResult<create_filter::Response> {
     // TODO
     Ok(create_filter::Response::new(utils::random_string(10)).into())
diff --git a/src/client_server/keys.rs b/src/client_server/keys.rs
index 8426518b50447f370ad4b85bbee1629e90d217e6..08bb4c63db804e341a1900d61885697ed10336f7 100644
--- a/src/client_server/keys.rs
+++ b/src/client_server/keys.rs
@@ -22,6 +22,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/r0/keys/upload", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn upload_keys_route(
     db: State<'_, Database>,
     body: Ruma<upload_keys::Request>,
@@ -70,6 +71,7 @@ pub async fn upload_keys_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/keys/query", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_keys_route(
     db: State<'_, Database>,
     body: Ruma<get_keys::Request<'_>>,
@@ -150,6 +152,7 @@ pub async fn get_keys_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/keys/claim", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn claim_keys_route(
     db: State<'_, Database>,
     body: Ruma<claim_keys::Request>,
@@ -183,6 +186,7 @@ pub async fn claim_keys_route(
     feature = "conduit_bin",
     post("/_matrix/client/unstable/keys/device_signing/upload", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn upload_signing_keys_route(
     db: State<'_, Database>,
     body: Ruma<upload_signing_keys::Request<'_>>,
@@ -240,6 +244,7 @@ pub async fn upload_signing_keys_route(
     feature = "conduit_bin",
     post("/_matrix/client/unstable/keys/signatures/upload", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn upload_signatures_route(
     db: State<'_, Database>,
     body: Ruma<upload_signatures::Request>,
@@ -300,6 +305,7 @@ pub async fn upload_signatures_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/keys/changes", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_key_changes_route(
     db: State<'_, Database>,
     body: Ruma<get_key_changes::Request<'_>>,
diff --git a/src/client_server/media.rs b/src/client_server/media.rs
index 275038acb4d53a482b1587c2696d5ac19709eab4..57fc2b080a7b26aea72010bbdd84c9a13b0f39ea 100644
--- a/src/client_server/media.rs
+++ b/src/client_server/media.rs
@@ -12,6 +12,7 @@
 const MXC_LENGTH: usize = 32;
 
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/media/r0/config"))]
+#[tracing::instrument(skip(db))]
 pub async fn get_media_config_route(
     db: State<'_, Database>,
 ) -> ConduitResult<get_media_config::Response> {
@@ -25,6 +26,7 @@ pub async fn get_media_config_route(
     feature = "conduit_bin",
     post("/_matrix/media/r0/upload", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn create_content_route(
     db: State<'_, Database>,
     body: Ruma<create_content::Request<'_>>,
@@ -54,6 +56,7 @@ pub async fn create_content_route(
     feature = "conduit_bin",
     get("/_matrix/media/r0/download/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_content_route(
     db: State<'_, Database>,
     body: Ruma<get_content::Request<'_>>,
@@ -103,6 +106,7 @@ pub async fn get_content_route(
     feature = "conduit_bin",
     get("/_matrix/media/r0/thumbnail/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_content_thumbnail_route(
     db: State<'_, Database>,
     body: Ruma<get_content_thumbnail::Request<'_>>,
diff --git a/src/client_server/membership.rs b/src/client_server/membership.rs
index e3b18275f1d2259118d68b817ed6a920954722c8..b7b2d4b00f87f51a8fc4d1e2effde18d49e9bcfd 100644
--- a/src/client_server/membership.rs
+++ b/src/client_server/membership.rs
@@ -36,6 +36,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/join", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn join_room_by_id_route(
     db: State<'_, Database>,
     body: Ruma<join_room_by_id::Request<'_>>,
@@ -54,6 +55,7 @@ pub async fn join_room_by_id_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/join/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn join_room_by_id_or_alias_route(
     db: State<'_, Database>,
     body: Ruma<join_room_by_id_or_alias::Request<'_>>,
@@ -88,6 +90,7 @@ pub async fn join_room_by_id_or_alias_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/leave", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn leave_room_route(
     db: State<'_, Database>,
     body: Ruma<leave_room::Request<'_>>,
@@ -136,6 +139,7 @@ pub async fn leave_room_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/invite", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn invite_user_route(
     db: State<'_, Database>,
     body: Ruma<invite_user::Request<'_>>,
@@ -175,6 +179,7 @@ pub async fn invite_user_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/kick", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn kick_user_route(
     db: State<'_, Database>,
     body: Ruma<kick_user::Request<'_>>,
@@ -224,6 +229,7 @@ pub async fn kick_user_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/ban", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn ban_user_route(
     db: State<'_, Database>,
     body: Ruma<ban_user::Request<'_>>,
@@ -280,6 +286,7 @@ pub async fn ban_user_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/unban", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn unban_user_route(
     db: State<'_, Database>,
     body: Ruma<unban_user::Request<'_>>,
@@ -328,6 +335,7 @@ pub async fn unban_user_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/forget", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn forget_room_route(
     db: State<'_, Database>,
     body: Ruma<forget_room::Request<'_>>,
@@ -345,6 +353,7 @@ pub async fn forget_room_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/joined_rooms", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn joined_rooms_route(
     db: State<'_, Database>,
     body: Ruma<joined_rooms::Request>,
@@ -365,6 +374,7 @@ pub async fn joined_rooms_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/members", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_member_events_route(
     db: State<'_, Database>,
     body: Ruma<get_member_events::Request<'_>>,
@@ -394,6 +404,7 @@ pub async fn get_member_events_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/joined_members", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn joined_members_route(
     db: State<'_, Database>,
     body: Ruma<joined_members::Request<'_>>,
diff --git a/src/client_server/message.rs b/src/client_server/message.rs
index c64c3900b1b5a5e76aea750e97236a82ecda9805..04f27defd84f717bcf0ef4c7c258522893decf30 100644
--- a/src/client_server/message.rs
+++ b/src/client_server/message.rs
@@ -20,6 +20,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/rooms/<_>/send/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn send_message_event_route(
     db: State<'_, Database>,
     body: Ruma<send_message_event::Request<'_>>,
@@ -87,6 +88,7 @@ pub async fn send_message_event_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/messages", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_message_events_route(
     db: State<'_, Database>,
     body: Ruma<get_message_events::Request<'_>>,
diff --git a/src/client_server/mod.rs b/src/client_server/mod.rs
index 672957b3ce741334b64ac2e82dd61071533574a2..dd8e7a635065ae0b8d5c5b3d549e369fd818f23c 100644
--- a/src/client_server/mod.rs
+++ b/src/client_server/mod.rs
@@ -75,6 +75,7 @@
 
 #[cfg(feature = "conduit_bin")]
 #[options("/<_..>")]
+#[tracing::instrument]
 pub async fn options_route() -> ConduitResult<send_event_to_device::Response> {
     Ok(send_event_to_device::Response.into())
 }
diff --git a/src/client_server/presence.rs b/src/client_server/presence.rs
index 15c746e428b1b9d3e2105adc240f76d84d68e70f..175853f5734f00a9302ecb71e5f38cb82547d353 100644
--- a/src/client_server/presence.rs
+++ b/src/client_server/presence.rs
@@ -10,6 +10,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/presence/<_>/status", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_presence_route(
     db: State<'_, Database>,
     body: Ruma<set_presence::Request<'_>>,
diff --git a/src/client_server/profile.rs b/src/client_server/profile.rs
index 21759a86e44016c4b4848a271f7ecc6b54fd556d..7e57c1ef3e1c1c7e75b40fd301ee9e6f8f0f32b7 100644
--- a/src/client_server/profile.rs
+++ b/src/client_server/profile.rs
@@ -19,6 +19,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/profile/<_>/displayname", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_displayname_route(
     db: State<'_, Database>,
     body: Ruma<set_display_name::Request<'_>>,
@@ -98,6 +99,7 @@ pub async fn set_displayname_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/profile/<_>/displayname", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_displayname_route(
     db: State<'_, Database>,
     body: Ruma<get_display_name::Request<'_>>,
@@ -112,6 +114,7 @@ pub async fn get_displayname_route(
     feature = "conduit_bin",
     put("/_matrix/client/r0/profile/<_>/avatar_url", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_avatar_url_route(
     db: State<'_, Database>,
     body: Ruma<set_avatar_url::Request<'_>>,
@@ -191,6 +194,7 @@ pub async fn set_avatar_url_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/profile/<_>/avatar_url", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_avatar_url_route(
     db: State<'_, Database>,
     body: Ruma<get_avatar_url::Request<'_>>,
@@ -205,6 +209,7 @@ pub async fn get_avatar_url_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/profile/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_profile_route(
     db: State<'_, Database>,
     body: Ruma<get_profile::Request<'_>>,
diff --git a/src/client_server/push.rs b/src/client_server/push.rs
index 7c3e9d9fcfe356e0e88e757e406c5ca33f3cb880..2c05c3c2128e6b70253963abeba379189f616939 100644
--- a/src/client_server/push.rs
+++ b/src/client_server/push.rs
@@ -22,6 +22,7 @@
     feature = "conduit_bin",
     get("/_matrix/client/r0/pushrules", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_pushrules_all_route(
     db: State<'_, Database>,
     body: Ruma<get_pushrules_all::Request>,
@@ -46,6 +47,7 @@ pub async fn get_pushrules_all_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/pushrules/<_>/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_pushrule_route(
     db: State<'_, Database>,
     body: Ruma<get_pushrule::Request<'_>>,
@@ -104,6 +106,7 @@ pub async fn get_pushrule_route(
     feature = "conduit_bin",
     put("/_matrix/client/r0/pushrules/<_>/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_pushrule_route(
     db: State<'_, Database>,
     body: Ruma<set_pushrule::Request<'_>>,
@@ -250,6 +253,7 @@ pub async fn set_pushrule_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/pushrules/<_>/<_>/<_>/actions", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_pushrule_actions_route(
     db: State<'_, Database>,
     body: Ruma<get_pushrule_actions::Request<'_>>,
@@ -313,6 +317,7 @@ pub async fn get_pushrule_actions_route(
     feature = "conduit_bin",
     put("/_matrix/client/r0/pushrules/<_>/<_>/<_>/actions", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_pushrule_actions_route(
     db: State<'_, Database>,
     body: Ruma<set_pushrule_actions::Request<'_>>,
@@ -416,6 +421,7 @@ pub async fn set_pushrule_actions_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/pushrules/<_>/<_>/<_>/enabled", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_pushrule_enabled_route(
     db: State<'_, Database>,
     body: Ruma<get_pushrule_enabled::Request<'_>>,
@@ -476,6 +482,7 @@ pub async fn get_pushrule_enabled_route(
     feature = "conduit_bin",
     put("/_matrix/client/r0/pushrules/<_>/<_>/<_>/enabled", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_pushrule_enabled_route(
     db: State<'_, Database>,
     body: Ruma<set_pushrule_enabled::Request<'_>>,
@@ -579,6 +586,7 @@ pub async fn set_pushrule_enabled_route(
     feature = "conduit_bin",
     delete("/_matrix/client/r0/pushrules/<_>/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_pushrule_route(
     db: State<'_, Database>,
     body: Ruma<delete_pushrule::Request<'_>>,
@@ -669,6 +677,7 @@ pub async fn delete_pushrule_route(
 }
 
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/pushers"))]
+#[tracing::instrument]
 pub async fn get_pushers_route() -> ConduitResult<get_pushers::Response> {
     Ok(get_pushers::Response {
         pushers: Vec::new(),
@@ -677,6 +686,7 @@ pub async fn get_pushers_route() -> ConduitResult<get_pushers::Response> {
 }
 
 #[cfg_attr(feature = "conduit_bin", post("/_matrix/client/r0/pushers/set"))]
+#[tracing::instrument(skip(db))]
 pub async fn set_pushers_route(db: State<'_, Database>) -> ConduitResult<get_pushers::Response> {
     db.flush().await?;
 
diff --git a/src/client_server/read_marker.rs b/src/client_server/read_marker.rs
index 0c4ec1a49f36f002b9426780f3ca40169aa2a000..555b7e7211cf476515309ad1a739b60b6cafb4b6 100644
--- a/src/client_server/read_marker.rs
+++ b/src/client_server/read_marker.rs
@@ -2,7 +2,8 @@
 use crate::{ConduitResult, Database, Error, Ruma};
 use ruma::{
     api::client::{
-        error::ErrorKind, r0::capabilities::get_capabilities, r0::read_marker::set_read_marker,
+        error::ErrorKind,
+        r0::{read_marker::set_read_marker, receipt::create_receipt},
     },
     events::{AnyEphemeralRoomEvent, AnyEvent, EventType},
 };
@@ -15,6 +16,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/read_markers", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn set_read_marker_route(
     db: State<'_, Database>,
     body: Ruma<set_read_marker::Request<'_>>,
@@ -83,13 +85,53 @@ pub async fn set_read_marker_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_>/receipt/<_>/<_>", data = "<body>")
 )]
-pub async fn set_receipt_route(
+#[tracing::instrument(skip(db, body))]
+pub async fn create_receipt_route(
     db: State<'_, Database>,
-    body: Ruma<get_capabilities::Request>,
-) -> ConduitResult<set_read_marker::Response> {
-    let _sender_user = body.sender_user.as_ref().expect("user is authenticated");
+    body: Ruma<create_receipt::Request<'_>>,
+) -> ConduitResult<create_receipt::Response> {
+    let sender_user = body.sender_user.as_ref().expect("user is authenticated");
+
+    db.rooms.edus.private_read_set(
+        &body.room_id,
+        &sender_user,
+        db.rooms
+            .get_pdu_count(&body.event_id)?
+            .ok_or(Error::BadRequest(
+                ErrorKind::InvalidParam,
+                "Event does not exist.",
+            ))?,
+        &db.globals,
+    )?;
+
+    let mut user_receipts = BTreeMap::new();
+    user_receipts.insert(
+        sender_user.clone(),
+        ruma::events::receipt::Receipt {
+            ts: Some(SystemTime::now()),
+        },
+    );
+    let mut receipt_content = BTreeMap::new();
+    receipt_content.insert(
+        body.event_id.to_owned(),
+        ruma::events::receipt::Receipts {
+            read: Some(user_receipts),
+        },
+    );
+
+    db.rooms.edus.readreceipt_update(
+        &sender_user,
+        &body.room_id,
+        AnyEvent::Ephemeral(AnyEphemeralRoomEvent::Receipt(
+            ruma::events::receipt::ReceiptEvent {
+                content: ruma::events::receipt::ReceiptEventContent(receipt_content),
+                room_id: body.room_id.clone(),
+            },
+        )),
+        &db.globals,
+    )?;
 
     db.flush().await?;
 
-    Ok(set_read_marker::Response.into())
+    Ok(create_receipt::Response.into())
 }
diff --git a/src/client_server/redact.rs b/src/client_server/redact.rs
index 282c35a3b1b932f17b314b434b85abeb3ab3bd11..be5d3b1179d1634986a3ac99648f296ffb706e90 100644
--- a/src/client_server/redact.rs
+++ b/src/client_server/redact.rs
@@ -12,6 +12,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/rooms/<_>/redact/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn redact_event_route(
     db: State<'_, Database>,
     body: Ruma<redact_event::Request<'_>>,
diff --git a/src/client_server/room.rs b/src/client_server/room.rs
index 4adc33540c66e82b2ef46504ddcc1cb1f24d59bd..409028c2742b92a8203fba0435d6f189075b8ec1 100644
--- a/src/client_server/room.rs
+++ b/src/client_server/room.rs
@@ -22,6 +22,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/r0/createRoom", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn create_room_route(
     db: State<'_, Database>,
     body: Ruma<create_room::Request<'_>>,
@@ -306,6 +307,7 @@ pub async fn create_room_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/event/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_room_event_route(
     db: State<'_, Database>,
     body: Ruma<get_room_event::Request<'_>>,
@@ -333,6 +335,7 @@ pub async fn get_room_event_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/rooms/<_room_id>/upgrade", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn upgrade_room_route(
     db: State<'_, Database>,
     body: Ruma<upgrade_room::Request<'_>>,
diff --git a/src/client_server/search.rs b/src/client_server/search.rs
index 5fb87f015eedf05ee68c69d119947a41fbde75a2..a668a0d0b667f990d85ebf751e51012d892ff276 100644
--- a/src/client_server/search.rs
+++ b/src/client_server/search.rs
@@ -11,6 +11,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/r0/search", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn search_events_route(
     db: State<'_, Database>,
     body: Ruma<search_events::Request<'_>>,
diff --git a/src/client_server/session.rs b/src/client_server/session.rs
index f8d64f060baedfe23440dd2f6232d87d1e645865..7b3acfcca6c5a231cbf73cd3109c039802a3abdf 100644
--- a/src/client_server/session.rs
+++ b/src/client_server/session.rs
@@ -24,6 +24,7 @@ struct Claims {
 /// Get the homeserver's supported login types. One of these should be used as the `type` field
 /// when logging in.
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/login"))]
+#[tracing::instrument]
 pub async fn get_login_types_route() -> ConduitResult<get_login_types::Response> {
     Ok(get_login_types::Response::new(vec![get_login_types::LoginType::Password]).into())
 }
@@ -42,6 +43,7 @@ pub async fn get_login_types_route() -> ConduitResult<get_login_types::Response>
     feature = "conduit_bin",
     post("/_matrix/client/r0/login", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn login_route(
     db: State<'_, Database>,
     body: Ruma<login::Request<'_>>,
@@ -155,6 +157,7 @@ pub async fn login_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/logout", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn logout_route(
     db: State<'_, Database>,
     body: Ruma<logout::Request>,
@@ -182,6 +185,7 @@ pub async fn logout_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/logout/all", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn logout_all_route(
     db: State<'_, Database>,
     body: Ruma<logout_all::Request>,
diff --git a/src/client_server/state.rs b/src/client_server/state.rs
index ae5e2519eaf55139b496a86f04a0eb9e890a939d..57bf7e56146d1a30cf29eafb1d6f10802ccdd903 100644
--- a/src/client_server/state.rs
+++ b/src/client_server/state.rs
@@ -22,6 +22,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/rooms/<_>/state/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn send_state_event_for_key_route(
     db: State<'_, Database>,
     body: Ruma<send_state_event_for_key::Request<'_>>,
@@ -55,6 +56,7 @@ pub async fn send_state_event_for_key_route(
     feature = "conduit_bin",
     put("/_matrix/client/r0/rooms/<_>/state/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn send_state_event_for_empty_key_route(
     db: State<'_, Database>,
     body: Ruma<send_state_event_for_empty_key::Request<'_>>,
@@ -96,6 +98,7 @@ pub async fn send_state_event_for_empty_key_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/state", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_state_events_route(
     db: State<'_, Database>,
     body: Ruma<get_state_events::Request<'_>>,
@@ -142,6 +145,7 @@ pub async fn get_state_events_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/state/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_state_events_for_key_route(
     db: State<'_, Database>,
     body: Ruma<get_state_events_for_key::Request<'_>>,
@@ -193,6 +197,7 @@ pub async fn get_state_events_for_key_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/rooms/<_>/state/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_state_events_for_empty_key_route(
     db: State<'_, Database>,
     body: Ruma<get_state_events_for_empty_key::Request<'_>>,
diff --git a/src/client_server/sync.rs b/src/client_server/sync.rs
index 6cd518d2ae7a138989ba8a85322428b46317ff0b..fac6b155bc84fa9ceb06b5b4aa93467a00f51e2e 100644
--- a/src/client_server/sync.rs
+++ b/src/client_server/sync.rs
@@ -30,6 +30,7 @@
     feature = "conduit_bin",
     get("/_matrix/client/r0/sync", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn sync_events_route(
     db: State<'_, Database>,
     body: Ruma<sync_events::Request<'_>>,
@@ -310,8 +311,7 @@ pub async fn sync_events_route(
             };
 
             let state_events = if joined_since_last_sync {
-                db.rooms
-                    .room_state_full(&room_id)?
+                current_state
                     .into_iter()
                     .map(|(_, pdu)| pdu.to_sync_state_event())
                     .collect()
@@ -709,6 +709,7 @@ pub async fn sync_events_route(
     Ok(response.into())
 }
 
+#[tracing::instrument(skip(db))]
 fn share_encrypted_room(
     db: &Database,
     sender_user: &UserId,
diff --git a/src/client_server/tag.rs b/src/client_server/tag.rs
index 7bbf9e8d65dd1200a54bc757fbe871c55ff4ea42..21264a1767b7f8f39fc73034aed2105b239a98d5 100644
--- a/src/client_server/tag.rs
+++ b/src/client_server/tag.rs
@@ -13,6 +13,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/user/<_>/rooms/<_>/tags/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn update_tag_route(
     db: State<'_, Database>,
     body: Ruma<create_tag::Request<'_>>,
@@ -49,6 +50,7 @@ pub async fn update_tag_route(
     feature = "conduit_bin",
     delete("/_matrix/client/r0/user/<_>/rooms/<_>/tags/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn delete_tag_route(
     db: State<'_, Database>,
     body: Ruma<delete_tag::Request<'_>>,
@@ -82,6 +84,7 @@ pub async fn delete_tag_route(
     feature = "conduit_bin",
     get("/_matrix/client/r0/user/<_>/rooms/<_>/tags", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_tags_route(
     db: State<'_, Database>,
     body: Ruma<get_tags::Request<'_>>,
diff --git a/src/client_server/thirdparty.rs b/src/client_server/thirdparty.rs
index c775e9b050de7d7acd273ceb8e60068fbd001c37..3c0769949a8494462ebce08f7b2007dce4c22b5a 100644
--- a/src/client_server/thirdparty.rs
+++ b/src/client_server/thirdparty.rs
@@ -10,6 +10,7 @@
     feature = "conduit_bin",
     get("/_matrix/client/r0/thirdparty/protocols")
 )]
+#[tracing::instrument]
 pub async fn get_protocols_route() -> ConduitResult<get_protocols::Response> {
     warn!("TODO: get_protocols_route");
     Ok(get_protocols::Response {
diff --git a/src/client_server/to_device.rs b/src/client_server/to_device.rs
index 5bc001e4b10d12be68ebc95572e751cb91452b09..460bd05720b69f1dcd3475c176a7779a7b68b209 100644
--- a/src/client_server/to_device.rs
+++ b/src/client_server/to_device.rs
@@ -12,6 +12,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/sendToDevice/<_>/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn send_event_to_device_route(
     db: State<'_, Database>,
     body: Ruma<send_event_to_device::Request<'_>>,
diff --git a/src/client_server/typing.rs b/src/client_server/typing.rs
index e90746e4f2d3b8edbef3055690d1db1a9cdd56c5..4b7feb7f01af70ed5272beb633afbee4be513d44 100644
--- a/src/client_server/typing.rs
+++ b/src/client_server/typing.rs
@@ -10,6 +10,7 @@
     feature = "conduit_bin",
     put("/_matrix/client/r0/rooms/<_>/typing/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub fn create_typing_event_route(
     db: State<'_, Database>,
     body: Ruma<create_typing_event::Request<'_>>,
diff --git a/src/client_server/unversioned.rs b/src/client_server/unversioned.rs
index e51ed56539a92aa4ee57f7a2e2b96556e030b45f..d25dce663084506fbd5b6aa382b2a56e83bceb90 100644
--- a/src/client_server/unversioned.rs
+++ b/src/client_server/unversioned.rs
@@ -15,6 +15,7 @@
 /// Note: Unstable features are used while developing new features. Clients should avoid using
 /// unstable features in their stable releases
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/versions"))]
+#[tracing::instrument]
 pub async fn get_supported_versions_route() -> ConduitResult<get_supported_versions::Response> {
     let mut resp =
         get_supported_versions::Response::new(vec!["r0.5.0".to_owned(), "r0.6.0".to_owned()]);
diff --git a/src/client_server/user_directory.rs b/src/client_server/user_directory.rs
index 5829364169f2afe6afd1fb2fc1bdde3a73b2dabf..b358274626ad26d79e02d6cd12b311b556d25b88 100644
--- a/src/client_server/user_directory.rs
+++ b/src/client_server/user_directory.rs
@@ -9,6 +9,7 @@
     feature = "conduit_bin",
     post("/_matrix/client/r0/user_directory/search", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn search_users_route(
     db: State<'_, Database>,
     body: Ruma<search_users::Request<'_>>,
diff --git a/src/client_server/voip.rs b/src/client_server/voip.rs
index 9216f1a69a0b3ea64b25461c38e6d05e3ef65583..7924a7ff83eb1e7db04f36e12763954890832605 100644
--- a/src/client_server/voip.rs
+++ b/src/client_server/voip.rs
@@ -6,6 +6,7 @@
 use rocket::get;
 
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/voip/turnServer"))]
+#[tracing::instrument]
 pub async fn turn_server_route() -> ConduitResult<get_turn_server_info::Response> {
     Ok(get_turn_server_info::Response {
         username: "".to_owned(),
diff --git a/src/database.rs b/src/database.rs
index 20cc7e126f98b057bcdba934e23f1441e8be8aef..34b74be262943794c9d5e4bd26e64c67d68905bb 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -38,6 +38,8 @@ pub struct Config {
     allow_encryption: bool,
     #[serde(default = "false_fn")]
     allow_federation: bool,
+    #[serde(default = "false_fn")]
+    pub allow_jaeger: bool,
     jwt_secret: Option<String>,
     #[serde(default = "Vec::new")]
     trusted_servers: Vec<Box<ServerName>>,
diff --git a/src/database/account_data.rs b/src/database/account_data.rs
index 855ebfeb55b547f184938c93b71189d128a55b9b..38e6c32cea688ba993aa16cfebe63f5ceb638b86 100644
--- a/src/database/account_data.rs
+++ b/src/database/account_data.rs
@@ -74,6 +74,7 @@ pub fn get<T: DeserializeOwned>(
     }
 
     /// Returns all changes to the account data that happened after `since`.
+    #[tracing::instrument(skip(self))]
     pub fn changes_since(
         &self,
         room_id: Option<&RoomId>,
diff --git a/src/database/admin.rs b/src/database/admin.rs
index 501722eec34d55de61794ef00f2f923ce5c1abc2..30143859398d816bc28dc6c4e3168d3b007312eb 100644
--- a/src/database/admin.rs
+++ b/src/database/admin.rs
@@ -7,7 +7,6 @@
     events::{room::message, EventType},
     UserId,
 };
-use tokio::select;
 
 pub enum AdminCommand {
     RegisterAppservice(serde_yaml::Value),
@@ -67,7 +66,7 @@ pub fn start_handler(
             };
 
             loop {
-                select! {
+                tokio::select! {
                     Some(event) = receiver.next() => {
                         match event {
                             AdminCommand::RegisterAppservice(yaml) => {
diff --git a/src/database/globals.rs b/src/database/globals.rs
index 3c65e74eb64a16b70803b1979715ff5a747f3846..8d7f104123f5d6bfff590fcdadc62f07b599388d 100644
--- a/src/database/globals.rs
+++ b/src/database/globals.rs
@@ -12,11 +12,11 @@
 
 pub const COUNTER: &str = "c";
 
-pub type DestinationCache = Arc<RwLock<HashMap<Box<ServerName>, (String, Option<String>)>>>;
+type WellKnownMap = HashMap<Box<ServerName>, (String, Option<String>)>;
 
 #[derive(Clone)]
 pub struct Globals {
-    pub actual_destination_cache: DestinationCache, // actual_destination, host
+    pub actual_destination_cache: Arc<RwLock<WellKnownMap>>, // actual_destination, host
     pub(super) globals: sled::Tree,
     config: Config,
     keypair: Arc<ruma::signatures::Ed25519KeyPair>,
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 992c97cf38e001c54a6849db87878987e265c3be..d48494b65f536449443cfda7b482c6225f76939a 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -83,8 +83,7 @@ pub struct Rooms {
 impl Rooms {
     /// Builds a StateMap by iterating over all keys that start
     /// with state_hash, this gives the full state for the given state_hash.
-    ///
-    /// TODO: Should this check for outliers, it does now.
+    #[tracing::instrument(skip(self))]
     pub fn state_full(
         &self,
         room_id: &RoomId,
@@ -129,6 +128,7 @@ pub fn state_full(
     }
 
     /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
+    #[tracing::instrument(skip(self))]
     pub fn state_get(
         &self,
         room_id: &RoomId,
@@ -178,11 +178,13 @@ pub fn state_get(
     }
 
     /// Returns the state hash for this pdu.
+    #[tracing::instrument(skip(self))]
     pub fn pdu_state_hash(&self, pdu_id: &[u8]) -> Result<Option<StateHashId>> {
         Ok(self.pduid_statehash.get(pdu_id)?)
     }
 
     /// Returns the last state hash key added to the db for the given room.
+    #[tracing::instrument(skip(self))]
     pub fn current_state_hash(&self, room_id: &RoomId) -> Result<Option<StateHashId>> {
         Ok(self.roomid_statehash.get(room_id.as_bytes())?)
     }
@@ -290,6 +292,7 @@ pub fn force_state(
     }
 
     /// Returns the full room state.
+    #[tracing::instrument(skip(self))]
     pub fn room_state_full(
         &self,
         room_id: &RoomId,
@@ -302,6 +305,7 @@ pub fn room_state_full(
     }
 
     /// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
+    #[tracing::instrument(skip(self))]
     pub fn room_state_get(
         &self,
         room_id: &RoomId,
@@ -316,6 +320,7 @@ pub fn room_state_get(
     }
 
     /// Returns the `count` of this pdu's id.
+    #[tracing::instrument(skip(self))]
     pub fn pdu_count(&self, pdu_id: &[u8]) -> Result<u64> {
         Ok(
             utils::u64_from_bytes(&pdu_id[pdu_id.len() - mem::size_of::<u64>()..pdu_id.len()])
@@ -515,6 +520,7 @@ pub fn append_pdu_outlier(&self, pdu: &PduEvent) -> Result<()> {
     ///
     /// By this point the incoming event should be fully authenticated, no auth happens
     /// in `append_pdu`.
+    #[allow(clippy::too_many_arguments)]
     pub fn append_pdu(
         &self,
         pdu: &PduEvent,
@@ -1113,6 +1119,7 @@ pub fn build_and_append_pdu(
 
                 let user_is_joined =
                     |bridge_user_id| self.is_joined(&bridge_user_id, room_id).unwrap_or(false);
+
                 let matching_users = |users: &Regex| {
                     users.is_match(pdu.sender.as_str())
                         || pdu.kind == EventType::RoomMember
@@ -1141,6 +1148,7 @@ pub fn build_and_append_pdu(
     }
 
     /// Returns an iterator over all PDUs in a room.
+    #[tracing::instrument(skip(self))]
     pub fn all_pdus(
         &self,
         user_id: &UserId,
@@ -1151,6 +1159,7 @@ pub fn all_pdus(
 
     /// Returns a double-ended iterator over all events in a room that happened after the event with id `since`
     /// in chronological order.
+    #[tracing::instrument(skip(self))]
     pub fn pdus_since(
         &self,
         user_id: &UserId,
@@ -1217,6 +1226,7 @@ pub fn pdus_until(
 
     /// Returns an iterator over all events and their token in a room that happened after the event
     /// with id `from` in chronological order.
+    #[tracing::instrument(skip(self))]
     pub fn pdus_after(
         &self,
         user_id: &UserId,
@@ -1566,6 +1576,7 @@ pub fn search_pdus<'a>(
         ))
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn get_shared_rooms<'a>(
         &'a self,
         users: Vec<UserId>,
@@ -1627,6 +1638,7 @@ pub fn room_servers(&self, room_id: &RoomId) -> impl Iterator<Item = Result<Box<
     }
 
     /// Returns an iterator over all joined members of a room.
+    #[tracing::instrument(skip(self))]
     pub fn room_members(&self, room_id: &RoomId) -> impl Iterator<Item = Result<UserId>> {
         let mut prefix = room_id.as_bytes().to_vec();
         prefix.push(0xff);
@@ -1675,6 +1687,7 @@ pub fn room_useroncejoined(&self, room_id: &RoomId) -> impl Iterator<Item = Resu
     }
 
     /// Returns an iterator over all invited members of a room.
+    #[tracing::instrument(skip(self))]
     pub fn room_members_invited(&self, room_id: &RoomId) -> impl Iterator<Item = Result<UserId>> {
         let mut prefix = room_id.as_bytes().to_vec();
         prefix.push(0xff);
@@ -1699,6 +1712,7 @@ pub fn room_members_invited(&self, room_id: &RoomId) -> impl Iterator<Item = Res
     }
 
     /// Returns an iterator over all rooms this user joined.
+    #[tracing::instrument(skip(self))]
     pub fn rooms_joined(&self, user_id: &UserId) -> impl Iterator<Item = Result<RoomId>> {
         self.userroomid_joined
             .scan_prefix(user_id.as_bytes())
@@ -1720,6 +1734,7 @@ pub fn rooms_joined(&self, user_id: &UserId) -> impl Iterator<Item = Result<Room
     }
 
     /// Returns an iterator over all rooms a user was invited to.
+    #[tracing::instrument(skip(self))]
     pub fn rooms_invited(&self, user_id: &UserId) -> impl Iterator<Item = Result<RoomId>> {
         let mut prefix = user_id.as_bytes().to_vec();
         prefix.push(0xff);
@@ -1744,6 +1759,7 @@ pub fn rooms_invited(&self, user_id: &UserId) -> impl Iterator<Item = Result<Roo
     }
 
     /// Returns an iterator over all rooms a user left.
+    #[tracing::instrument(skip(self))]
     pub fn rooms_left(&self, user_id: &UserId) -> impl Iterator<Item = Result<RoomId>> {
         let mut prefix = user_id.as_bytes().to_vec();
         prefix.push(0xff);
diff --git a/src/database/rooms/edus.rs b/src/database/rooms/edus.rs
index 2b1b03db2a1c7304c11238a34b72467b885af092..084e4a12816e2a0394da80070d1af3eee319c061 100644
--- a/src/database/rooms/edus.rs
+++ b/src/database/rooms/edus.rs
@@ -70,6 +70,7 @@ pub fn readreceipt_update(
     }
 
     /// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`.
+    #[tracing::instrument(skip(self))]
     pub fn readreceipts_since(
         &self,
         room_id: &RoomId,
@@ -115,6 +116,7 @@ pub fn private_read_set(
     }
 
     /// Returns the private read marker.
+    #[tracing::instrument(skip(self))]
     pub fn private_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Result<Option<u64>> {
         let mut key = room_id.to_string().as_bytes().to_vec();
         key.push(0xff);
@@ -256,6 +258,7 @@ fn typings_maintain(
     }
 
     /// Returns the count of the last typing update in this room.
+    #[tracing::instrument(skip(self, globals))]
     pub fn last_typing_update(
         &self,
         room_id: &RoomId,
@@ -339,6 +342,7 @@ pub fn update_presence(
     }
 
     /// Resets the presence timeout, so the user will stay in their current presence state.
+    #[tracing::instrument(skip(self))]
     pub fn ping_presence(&self, user_id: &UserId) -> Result<()> {
         self.userid_lastpresenceupdate.insert(
             &user_id.to_string().as_bytes(),
@@ -429,6 +433,7 @@ pub fn presence_maintain(
     }
 
     /// Returns an iterator over the most recent presence updates that happened after the event with id `since`.
+    #[tracing::instrument(skip(self, globals, rooms))]
     pub fn presence_since(
         &self,
         room_id: &RoomId,
diff --git a/src/database/sending.rs b/src/database/sending.rs
index 101daf3f56505241c641d6f2e3d231221ceb93d1..97939716996931465840334cafcbf035de2e33f0 100644
--- a/src/database/sending.rs
+++ b/src/database/sending.rs
@@ -8,7 +8,8 @@
 
 use crate::{appservice_server, server_server, utils, Error, PduEvent, Result};
 use federation::transactions::send_transaction_message;
-use log::info;
+use log::{info, warn};
+use ring::digest;
 use rocket::futures::stream::{FuturesUnordered, StreamExt};
 use ruma::{
     api::{appservice, federation, OutgoingRequest},
@@ -35,6 +36,7 @@ pub fn start_handler(
     ) {
         let servernamepduids = self.servernamepduids.clone();
         let servercurrentpdus = self.servercurrentpdus.clone();
+        let maximum_requests = self.maximum_requests.clone();
         let rooms = rooms.clone();
         let globals = globals.clone();
         let appservice = appservice.clone();
@@ -43,23 +45,43 @@ pub fn start_handler(
             let mut futures = FuturesUnordered::new();
 
             // Retry requests we could not finish yet
-            let mut current_transactions = HashMap::new();
+            let mut current_transactions = HashMap::<(Box<ServerName>, bool), Vec<IVec>>::new();
 
-            for (server, pdu, is_appservice) in servercurrentpdus
+            for (key, server, pdu, is_appservice) in servercurrentpdus
                 .iter()
                 .filter_map(|r| r.ok())
                 .filter_map(|(key, _)| Self::parse_servercurrentpdus(key).ok())
-                .filter(|(_, pdu, _)| !pdu.is_empty()) // Skip reservation key
-                .take(50)
-            // This should not contain more than 50 anyway
             {
-                current_transactions
+                if pdu.is_empty() {
+                    // Remove old reservation key
+                    servercurrentpdus.remove(key).unwrap();
+                    continue;
+                }
+
+                let entry = current_transactions
                     .entry((server, is_appservice))
-                    .or_insert_with(Vec::new)
-                    .push(pdu);
+                    .or_insert_with(Vec::new);
+
+                if entry.len() > 30 {
+                    warn!("Dropping some current pdus because too many were queued. This should not happen.");
+                    servercurrentpdus.remove(key).unwrap();
+                    continue;
+                }
+
+                entry.push(pdu);
             }
 
             for ((server, is_appservice), pdus) in current_transactions {
+                // Create new reservation
+                let mut prefix = if is_appservice {
+                    b"+".to_vec()
+                } else {
+                    Vec::new()
+                };
+                prefix.extend_from_slice(server.as_bytes());
+                prefix.push(0xff);
+                servercurrentpdus.insert(prefix, &[]).unwrap();
+
                 futures.push(Self::handle_event(
                     server,
                     is_appservice,
@@ -67,6 +89,7 @@ pub fn start_handler(
                     &globals,
                     &rooms,
                     &appservice,
+                    &maximum_requests,
                 ));
             }
 
@@ -105,7 +128,7 @@ pub fn start_handler(
                                     .map(|k| {
                                         k.subslice(prefix.len(), k.len() - prefix.len())
                                     })
-                                    .take(50)
+                                    .take(30)
                                     .collect::<Vec<_>>();
 
                                 if !new_pdus.is_empty() {
@@ -116,7 +139,7 @@ pub fn start_handler(
                                         servernamepduids.remove(&current_key).unwrap();
                                     }
 
-                                    futures.push(Self::handle_event(server, is_appservice, new_pdus, &globals, &rooms, &appservice));
+                                    futures.push(Self::handle_event(server, is_appservice, new_pdus, &globals, &rooms, &appservice, &maximum_requests));
                                 } else {
                                     servercurrentpdus.remove(&prefix).unwrap();
                                     // servercurrentpdus with the prefix should be empty now
@@ -202,7 +225,7 @@ pub fn start_handler(
                                 servercurrentpdus.insert(&key, &[]).unwrap();
                                 servernamepduids.remove(&key).unwrap();
 
-                                futures.push(Self::handle_event(server, is_appservice, vec![pdu_id.into()], &globals, &rooms, &appservice));
+                                futures.push(Self::handle_event(server, is_appservice, vec![pdu_id.into()], &globals, &rooms, &appservice, &maximum_requests));
                             }
                         }
                     }
@@ -211,6 +234,7 @@ pub fn start_handler(
         });
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn send_pdu(&self, server: &ServerName, pdu_id: &[u8]) -> Result<()> {
         let mut key = server.as_bytes().to_vec();
         key.push(0xff);
@@ -220,6 +244,7 @@ pub fn send_pdu(&self, server: &ServerName, pdu_id: &[u8]) -> Result<()> {
         Ok(())
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn send_pdu_appservice(&self, appservice_id: &str, pdu_id: &[u8]) -> Result<()> {
         let mut key = b"+".to_vec();
         key.extend_from_slice(appservice_id.as_bytes());
@@ -230,6 +255,15 @@ pub fn send_pdu_appservice(&self, appservice_id: &str, pdu_id: &[u8]) -> Result<
         Ok(())
     }
 
+    #[tracing::instrument]
+    fn calculate_hash(keys: &[IVec]) -> Vec<u8> {
+        // We only hash the pdu's event ids, not the whole pdu
+        let bytes = keys.join(&0xff);
+        let hash = digest::digest(&digest::SHA256, &bytes);
+        hash.as_ref().to_owned()
+    }
+
+    #[tracing::instrument(skip(globals, rooms, appservice))]
     async fn handle_event(
         server: Box<ServerName>,
         is_appservice: bool,
@@ -237,6 +271,7 @@ async fn handle_event(
         globals: &super::globals::Globals,
         rooms: &super::rooms::Rooms,
         appservice: &super::appservice::Appservice,
+        maximum_requests: &Semaphore,
     ) -> std::result::Result<(Box<ServerName>, bool), (Box<ServerName>, bool, Error)> {
         if is_appservice {
             let pdu_jsons = pdu_ids
@@ -259,7 +294,9 @@ async fn handle_event(
                 })
                 .filter_map(|r| r.ok())
                 .collect::<Vec<_>>();
-            appservice_server::send_request(
+
+            let permit = maximum_requests.acquire().await;
+            let response = appservice_server::send_request(
                 &globals,
                 appservice
                     .get_registration(server.as_str())
@@ -267,12 +304,19 @@ async fn handle_event(
                     .unwrap(), // TODO: handle error
                 appservice::event::push_events::v1::Request {
                     events: &pdu_jsons,
-                    txn_id: &utils::random_string(16),
+                    txn_id: &base64::encode_config(
+                        Self::calculate_hash(&pdu_ids),
+                        base64::URL_SAFE_NO_PAD,
+                    ),
                 },
             )
             .await
             .map(|_response| (server.clone(), is_appservice))
-            .map_err(|e| (server, is_appservice, e))
+            .map_err(|e| (server, is_appservice, e));
+
+            drop(permit);
+
+            response
         } else {
             let pdu_jsons = pdu_ids
                 .iter()
@@ -302,7 +346,8 @@ async fn handle_event(
                 .filter_map(|r| r.ok())
                 .collect::<Vec<_>>();
 
-            server_server::send_request(
+            let permit = maximum_requests.acquire().await;
+            let response = server_server::send_request(
                 &globals,
                 &*server,
                 send_transaction_message::v1::Request {
@@ -310,17 +355,25 @@ async fn handle_event(
                     pdus: &pdu_jsons,
                     edus: &[],
                     origin_server_ts: SystemTime::now(),
-                    transaction_id: &utils::random_string(16),
+                    transaction_id: &base64::encode_config(
+                        Self::calculate_hash(&pdu_ids),
+                        base64::URL_SAFE_NO_PAD,
+                    ),
                 },
             )
             .await
             .map(|_response| (server.clone(), is_appservice))
-            .map_err(|e| (server, is_appservice, e))
+            .map_err(|e| (server, is_appservice, e));
+
+            drop(permit);
+
+            response
         }
     }
 
-    fn parse_servercurrentpdus(key: IVec) -> Result<(Box<ServerName>, IVec, bool)> {
-        let mut parts = key.splitn(2, |&b| b == 0xff);
+    fn parse_servercurrentpdus(key: IVec) -> Result<(IVec, Box<ServerName>, IVec, bool)> {
+        let key2 = key.clone();
+        let mut parts = key2.splitn(2, |&b| b == 0xff);
         let server = parts.next().expect("splitn always returns one element");
         let pdu = parts
             .next()
@@ -338,6 +391,7 @@ fn parse_servercurrentpdus(key: IVec) -> Result<(Box<ServerName>, IVec, bool)> {
         };
 
         Ok::<_, Error>((
+            key,
             Box::<ServerName>::try_from(server).map_err(|_| {
                 Error::bad_database("Invalid server string in server_currenttransaction")
             })?,
@@ -346,6 +400,7 @@ fn parse_servercurrentpdus(key: IVec) -> Result<(Box<ServerName>, IVec, bool)> {
         ))
     }
 
+    #[tracing::instrument(skip(self, globals))]
     pub async fn send_federation_request<T: OutgoingRequest>(
         &self,
         globals: &crate::database::globals::Globals,
@@ -362,6 +417,7 @@ pub async fn send_federation_request<T: OutgoingRequest>(
         response
     }
 
+    #[tracing::instrument(skip(self, globals))]
     pub async fn send_appservice_request<T: OutgoingRequest>(
         &self,
         globals: &crate::database::globals::Globals,
diff --git a/src/database/users.rs b/src/database/users.rs
index 153dce95a5a45af3ce80ee4e6bb0d6d917d27a3f..e5bc16e7e0631c5d1658f6ca33c64b317fd678af 100644
--- a/src/database/users.rs
+++ b/src/database/users.rs
@@ -311,6 +311,7 @@ pub fn add_one_time_key(
         Ok(())
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn last_one_time_keys_update(&self, user_id: &UserId) -> Result<u64> {
         self.userid_lastonetimekeyupdate
             .get(&user_id.to_string().as_bytes())?
@@ -364,6 +365,7 @@ pub fn take_one_time_key(
             .transpose()
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn count_one_time_keys(
         &self,
         user_id: &UserId,
@@ -563,6 +565,7 @@ pub fn sign_key(
         Ok(())
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn keys_changed(
         &self,
         user_or_room_id: &str,
@@ -738,6 +741,7 @@ pub fn add_to_device_event(
         Ok(())
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn get_to_device_events(
         &self,
         user_id: &UserId,
@@ -760,6 +764,7 @@ pub fn get_to_device_events(
         Ok(events)
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn remove_to_device_events(
         &self,
         user_id: &UserId,
diff --git a/src/main.rs b/src/main.rs
index 9b64506004736bec0150eaa5542fb96d7cf97788..b469f4d36ed5ecc5dbcc365056d22261d5c97d61 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,21 +11,23 @@
 mod ruma_wrapper;
 mod utils;
 
+use database::Config;
 pub use database::Database;
-pub use error::{ConduitLogger, Error, Result};
+pub use error::{Error, Result};
 pub use pdu::PduEvent;
 pub use rocket::State;
 use ruma::api::client::error::ErrorKind;
 pub use ruma_wrapper::{ConduitResult, Ruma, RumaResponse};
 
-use log::LevelFilter;
 use rocket::figment::{
     providers::{Env, Format, Toml},
     Figment,
 };
 use rocket::{catch, catchers, fairing::AdHoc, routes, Request};
+use tracing::span;
+use tracing_subscriber::{prelude::*, Registry};
 
-fn setup_rocket() -> rocket::Rocket {
+fn setup_rocket() -> (rocket::Rocket, Config) {
     // Force log level off, so we can use our own logger
     std::env::set_var("CONDUIT_LOG_LEVEL", "off");
 
@@ -39,7 +41,12 @@ fn setup_rocket() -> rocket::Rocket {
             )
             .merge(Env::prefixed("CONDUIT_").global());
 
-    rocket::custom(config)
+    let parsed_config = config
+        .extract::<Config>()
+        .expect("It looks like your config is invalid. Please take a look at the error");
+    let parsed_config2 = parsed_config.clone();
+
+    let rocket = rocket::custom(config)
         .mount(
             "/",
             routes![
@@ -90,7 +97,7 @@ fn setup_rocket() -> rocket::Rocket {
                 client_server::get_backup_key_sessions_route,
                 client_server::get_backup_keys_route,
                 client_server::set_read_marker_route,
-                client_server::set_receipt_route,
+                client_server::create_receipt_route,
                 client_server::create_typing_event_route,
                 client_server::create_room_route,
                 client_server::redact_event_route,
@@ -163,31 +170,41 @@ fn setup_rocket() -> rocket::Rocket {
             bad_json_catcher
         ])
         .attach(AdHoc::on_attach("Config", |rocket| async {
-            let config = rocket
-                .figment()
-                .extract()
-                .expect("It looks like your config is invalid. Please take a look at the error");
-
-            let data = Database::load_or_create(config)
+            let data = Database::load_or_create(parsed_config2)
                 .await
                 .expect("config is valid");
 
             data.sending
                 .start_handler(&data.globals, &data.rooms, &data.appservice);
-            log::set_boxed_logger(Box::new(ConduitLogger {
-                db: data.clone(),
-                last_logs: Default::default(),
-            }))
-            .unwrap();
-            log::set_max_level(LevelFilter::Info);
 
             Ok(rocket.manage(data))
-        }))
+        }));
+
+    (rocket, parsed_config)
 }
 
 #[rocket::main]
 async fn main() {
-    setup_rocket().launch().await.unwrap();
+    let (rocket, config) = setup_rocket();
+
+    if config.allow_jaeger {
+        let (tracer, _uninstall) = opentelemetry_jaeger::new_pipeline()
+            .with_service_name("conduit")
+            .install()
+            .unwrap();
+        let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
+        Registry::default().with(telemetry).try_init().unwrap();
+
+        let root = span!(tracing::Level::INFO, "app_start", work_units = 2);
+        let _enter = root.enter();
+
+        rocket.launch().await.unwrap();
+    } else {
+        let root = span!(tracing::Level::INFO, "app_start", work_units = 2);
+        let _enter = root.enter();
+
+        rocket.launch().await.unwrap();
+    }
 }
 
 #[catch(404)]
diff --git a/src/pdu.rs b/src/pdu.rs
index e38410fd9b2ef59c0667d3ee481a64a9036ab6a0..6085581bb5775048b6e1c1ab4b596fd2b472cc78 100644
--- a/src/pdu.rs
+++ b/src/pdu.rs
@@ -4,7 +4,7 @@
         pdu::EventHash, room::member::MemberEventContent, AnyEvent, AnyRoomEvent, AnyStateEvent,
         AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, EventType, StateEvent,
     },
-    serde::{CanonicalJsonObject, CanonicalJsonValue, Raw},
+    serde::{to_canonical_value, CanonicalJsonObject, CanonicalJsonValue, Raw},
     EventId, RoomId, RoomVersionId, ServerName, ServerSigningKeyId, UInt, UserId,
 };
 use serde::{Deserialize, Serialize};
@@ -34,6 +34,7 @@ pub struct PduEvent {
 }
 
 impl PduEvent {
+    #[tracing::instrument(skip(self))]
     pub fn redact(&mut self, reason: &PduEvent) -> crate::Result<()> {
         self.unsigned.clear();
 
@@ -80,6 +81,7 @@ pub fn redact(&mut self, reason: &PduEvent) -> crate::Result<()> {
         Ok(())
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn to_sync_room_event(&self) -> Raw<AnySyncRoomEvent> {
         let mut json = json!({
             "content": self.content,
@@ -101,6 +103,7 @@ pub fn to_sync_room_event(&self) -> Raw<AnySyncRoomEvent> {
     }
 
     /// This only works for events that are also AnyRoomEvents.
+    #[tracing::instrument(skip(self))]
     pub fn to_any_event(&self) -> Raw<AnyEvent> {
         let mut json = json!({
             "content": self.content,
@@ -122,6 +125,7 @@ pub fn to_any_event(&self) -> Raw<AnyEvent> {
         serde_json::from_value(json).expect("Raw::from_value always works")
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn to_room_event(&self) -> Raw<AnyRoomEvent> {
         let mut json = json!({
             "content": self.content,
@@ -143,6 +147,7 @@ pub fn to_room_event(&self) -> Raw<AnyRoomEvent> {
         serde_json::from_value(json).expect("Raw::from_value always works")
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn to_state_event(&self) -> Raw<AnyStateEvent> {
         let json = json!({
             "content": self.content,
@@ -158,20 +163,27 @@ pub fn to_state_event(&self) -> Raw<AnyStateEvent> {
         serde_json::from_value(json).expect("Raw::from_value always works")
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn to_sync_state_event(&self) -> Raw<AnySyncStateEvent> {
-        let json = json!({
-            "content": self.content,
-            "type": self.kind,
-            "event_id": self.event_id,
-            "sender": self.sender,
-            "origin_server_ts": self.origin_server_ts,
-            "unsigned": self.unsigned,
-            "state_key": self.state_key,
-        });
+        let json = format!(
+            r#"{{"content":{},"type":"{}","event_id":"{}","sender":"{}","origin_server_ts":{},"unsigned":{},"state_key":"{}"}}"#,
+            self.content,
+            self.kind,
+            self.event_id,
+            self.sender,
+            self.origin_server_ts,
+            serde_json::to_string(&self.unsigned).expect("Map::to_string always works"),
+            self.state_key
+                .as_ref()
+                .expect("state events have state keys")
+        );
 
-        serde_json::from_value(json).expect("Raw::from_value always works")
+        Raw::from_json(
+            serde_json::value::RawValue::from_string(json).expect("our string is valid json"),
+        )
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn to_stripped_state_event(&self) -> Raw<AnyStrippedStateEvent> {
         let json = json!({
             "content": self.content,
@@ -183,6 +195,7 @@ pub fn to_stripped_state_event(&self) -> Raw<AnyStrippedStateEvent> {
         serde_json::from_value(json).expect("Raw::from_value always works")
     }
 
+    #[tracing::instrument(skip(self))]
     pub fn to_member_event(&self) -> Raw<StateEvent<MemberEventContent>> {
         let json = json!({
             "content": self.content,
@@ -200,6 +213,7 @@ pub fn to_member_event(&self) -> Raw<StateEvent<MemberEventContent>> {
     }
 
     /// This does not return a full `Pdu` it is only to satisfy ruma's types.
+    #[tracing::instrument]
     pub fn convert_to_outgoing_federation_event(
         mut pdu_json: CanonicalJsonObject,
     ) -> Raw<ruma::events::pdu::Pdu> {
@@ -228,7 +242,7 @@ pub fn from_id_val(
     ) -> Result<Self, serde_json::Error> {
         json.insert(
             "event_id".to_string(),
-            ruma::serde::to_canonical_value(event_id).expect("event_id is a valid Value"),
+            to_canonical_value(event_id).expect("event_id is a valid Value"),
         );
 
         serde_json::from_value(serde_json::to_value(json).expect("valid JSON"))
diff --git a/src/ruma_wrapper.rs b/src/ruma_wrapper.rs
index e2f44cd71ffdc036ec61642268047f51acf4dfaf..640771ff67426edc639451a3ffd3a781badb8e7d 100644
--- a/src/ruma_wrapper.rs
+++ b/src/ruma_wrapper.rs
@@ -79,9 +79,7 @@ fn from_data(
                         registration
                             .get("as_token")
                             .and_then(|as_token| as_token.as_str())
-                            .map_or(false, |as_token| {
-                                dbg!(token.as_deref()) == dbg!(Some(as_token))
-                            })
+                            .map_or(false, |as_token| token.as_deref() == Some(as_token))
                     }) {
                 match T::METADATA.authentication {
                     AuthScheme::AccessToken | AuthScheme::QueryOnlyAccessToken => {
diff --git a/src/server_server.rs b/src/server_server.rs
index 12b60b91f1dacbc9b7ce8762daf33eef4fed1d01..a665fe9e0f82e470196c32fad9085568b63e0e02 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -2,6 +2,7 @@
 use get_profile_information::v1::ProfileField;
 use http::header::{HeaderValue, AUTHORIZATION, HOST};
 use log::{error, info, warn};
+use regex::Regex;
 use rocket::{get, post, put, response::content::Json, State};
 use ruma::{
     api::{
@@ -18,6 +19,7 @@
         OutgoingRequest,
     },
     directory::{IncomingFilter, IncomingRoomNetwork},
+    events::EventType,
     serde::to_canonical_value,
     signatures::{CanonicalJsonObject, CanonicalJsonValue, PublicKeyMap},
     EventId, RoomId, RoomVersionId, ServerName, ServerSigningKeyId, UserId,
@@ -35,6 +37,7 @@
     time::{Duration, SystemTime},
 };
 
+#[tracing::instrument(skip(globals))]
 pub async fn send_request<T: OutgoingRequest>(
     globals: &crate::database::globals::Globals,
     destination: &ServerName,
@@ -201,6 +204,7 @@ pub async fn send_request<T: OutgoingRequest>(
     }
 }
 
+#[tracing::instrument]
 fn get_ip_with_port(destination_str: String) -> Option<String> {
     if destination_str.parse::<SocketAddr>().is_ok() {
         Some(destination_str)
@@ -211,6 +215,7 @@ fn get_ip_with_port(destination_str: String) -> Option<String> {
     }
 }
 
+#[tracing::instrument]
 fn add_port_to_hostname(destination_str: String) -> String {
     match destination_str.find(':') {
         None => destination_str.to_owned() + ":8448",
@@ -221,9 +226,10 @@ fn add_port_to_hostname(destination_str: String) -> String {
 /// Returns: actual_destination, host header
 /// Implemented according to the specification at https://matrix.org/docs/spec/server_server/r0.1.4#resolving-server-names
 /// Numbers in comments below refer to bullet points in linked section of specification
+#[tracing::instrument(skip(globals))]
 async fn find_actual_destination(
     globals: &crate::database::globals::Globals,
-    destination: &ServerName,
+    destination: &'_ ServerName,
 ) -> (String, Option<String>) {
     let mut host = None;
 
@@ -279,9 +285,10 @@ async fn find_actual_destination(
     (actual_destination, host)
 }
 
+#[tracing::instrument(skip(globals))]
 async fn query_srv_record(
     globals: &crate::database::globals::Globals,
-    hostname: &str,
+    hostname: &'_ str,
 ) -> Option<String> {
     if let Ok(Some(host_port)) = globals
         .dns_resolver()
@@ -303,6 +310,7 @@ async fn query_srv_record(
     }
 }
 
+#[tracing::instrument(skip(globals))]
 pub async fn request_well_known(
     globals: &crate::database::globals::Globals,
     destination: &str,
@@ -326,6 +334,7 @@ pub async fn request_well_known(
 }
 
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/federation/v1/version"))]
+#[tracing::instrument(skip(db))]
 pub fn get_server_version_route(
     db: State<'_, Database>,
 ) -> ConduitResult<get_server_version::Response> {
@@ -343,6 +352,7 @@ pub fn get_server_version_route(
 }
 
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/key/v2/server"))]
+#[tracing::instrument(skip(db))]
 pub fn get_server_keys_route(db: State<'_, Database>) -> Json<String> {
     if !db.globals.allow_federation() {
         // TODO: Use proper types
@@ -385,6 +395,7 @@ pub fn get_server_keys_route(db: State<'_, Database>) -> Json<String> {
 }
 
 #[cfg_attr(feature = "conduit_bin", get("/_matrix/key/v2/server/<_>"))]
+#[tracing::instrument(skip(db))]
 pub fn get_server_keys_deprecated_route(db: State<'_, Database>) -> Json<String> {
     get_server_keys_route(db)
 }
@@ -393,6 +404,7 @@ pub fn get_server_keys_deprecated_route(db: State<'_, Database>) -> Json<String>
     feature = "conduit_bin",
     post("/_matrix/federation/v1/publicRooms", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_public_rooms_filtered_route(
     db: State<'_, Database>,
     body: Ruma<get_public_rooms_filtered::v1::Request<'_>>,
@@ -440,6 +452,7 @@ pub async fn get_public_rooms_filtered_route(
     feature = "conduit_bin",
     get("/_matrix/federation/v1/publicRooms", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn get_public_rooms_route(
     db: State<'_, Database>,
     body: Ruma<get_public_rooms::v1::Request<'_>>,
@@ -487,6 +500,7 @@ pub async fn get_public_rooms_route(
     feature = "conduit_bin",
     put("/_matrix/federation/v1/send/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub async fn send_transaction_message_route<'a>(
     db: State<'a, Database>,
     body: Ruma<send_transaction_message::v1::Request<'_>>,
@@ -1428,7 +1442,67 @@ pub(crate) fn append_incoming_pdu(
     db.rooms.set_room_state(pdu.room_id(), &state_hash)?;
 
     for appservice in db.appservice.iter_all().filter_map(|r| r.ok()) {
-        db.sending.send_pdu_appservice(&appservice.0, &pdu_id)?;
+        if let Some(namespaces) = appservice.1.get("namespaces") {
+            let users = namespaces
+                .get("users")
+                .and_then(|users| users.as_sequence())
+                .map_or_else(Vec::new, |users| {
+                    users
+                        .iter()
+                        .map(|users| {
+                            users
+                                .get("regex")
+                                .and_then(|regex| regex.as_str())
+                                .and_then(|regex| Regex::new(regex).ok())
+                        })
+                        .filter_map(|o| o)
+                        .collect::<Vec<_>>()
+                });
+            let aliases = namespaces
+                .get("aliases")
+                .and_then(|users| users.get("regex"))
+                .and_then(|regex| regex.as_str())
+                .and_then(|regex| Regex::new(regex).ok());
+            let rooms = namespaces
+                .get("rooms")
+                .and_then(|rooms| rooms.as_sequence());
+
+            let room_aliases = db.rooms.room_aliases(&pdu.room_id);
+
+            let bridge_user_id = appservice
+                .1
+                .get("sender_localpart")
+                .and_then(|string| string.as_str())
+                .and_then(|string| {
+                    UserId::parse_with_server_name(string, db.globals.server_name()).ok()
+                });
+
+            #[allow(clippy::blocks_in_if_conditions)]
+            if bridge_user_id.map_or(false, |bridge_user_id| {
+                db.rooms
+                    .is_joined(&bridge_user_id, &pdu.room_id)
+                    .unwrap_or(false)
+            }) || users.iter().any(|users| {
+                users.is_match(pdu.sender.as_str())
+                    || pdu.kind == EventType::RoomMember
+                        && pdu
+                            .state_key
+                            .as_ref()
+                            .map_or(false, |state_key| users.is_match(&state_key))
+            }) || aliases.map_or(false, |aliases| {
+                room_aliases
+                    .filter_map(|r| r.ok())
+                    .any(|room_alias| aliases.is_match(room_alias.as_str()))
+            }) || rooms.map_or(false, |rooms| rooms.contains(&pdu.room_id.as_str().into()))
+                || db
+                    .rooms
+                    .room_members(&pdu.room_id)
+                    .filter_map(|r| r.ok())
+                    .any(|member| users.iter().any(|regex| regex.is_match(member.as_str())))
+            {
+                db.sending.send_pdu_appservice(&appservice.0, &pdu_id)?;
+            }
+        }
     }
 
     Ok(())
@@ -1438,6 +1512,7 @@ pub(crate) fn append_incoming_pdu(
     feature = "conduit_bin",
     post("/_matrix/federation/v1/get_missing_events/<_>", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub fn get_missing_events_route<'a>(
     db: State<'a, Database>,
     body: Ruma<get_missing_events::v1::Request<'_>>,
@@ -1483,6 +1558,7 @@ pub fn get_missing_events_route<'a>(
     feature = "conduit_bin",
     get("/_matrix/federation/v1/query/profile", data = "<body>")
 )]
+#[tracing::instrument(skip(db, body))]
 pub fn get_profile_information_route<'a>(
     db: State<'a, Database>,
     body: Ruma<get_profile_information::v1::Request<'_>>,
diff --git a/src/utils.rs b/src/utils.rs
index c82e6feb8f26ebc1924053227a1b02cf7b7a55a2..0783567e9f3c5d7decfe83881053d4bc82646ed2 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -55,6 +55,7 @@ pub fn random_string(length: usize) -> String {
     thread_rng()
         .sample_iter(&rand::distributions::Alphanumeric)
         .take(length)
+        .map(char::from)
         .collect()
 }
 
diff --git a/tests/Complement.Dockerfile b/tests/Complement.Dockerfile
index 24ee9eab15c411a10c1df00145e0ab76a6847752..370db7cdb5ae908ea84703a71e64570ec54aa3ce 100644
--- a/tests/Complement.Dockerfile
+++ b/tests/Complement.Dockerfile
@@ -9,7 +9,7 @@ ARG SCCACHE_ENDPOINT
 ARG SCCACHE_S3_USE_SSL
 
 COPY . .
-RUN test -e target/release/conduit || cargo build --release --offline
+RUN test -e cached_target/release/conduit || cargo build --release
 
 FROM valkum/docker-rust-ci:latest
 WORKDIR /workdir