diff --git a/Cargo.lock b/Cargo.lock
index 511524075..51d4ce7db 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -113,9 +113,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.97"
+version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
 
 [[package]]
 name = "arc-swap"
@@ -188,7 +188,7 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
 dependencies = [
- "nom",
+ "nom 7.1.3",
 ]
 
 [[package]]
@@ -200,7 +200,7 @@ dependencies = [
  "asn1-rs-derive",
  "asn1-rs-impl",
  "displaydoc",
- "nom",
+ "nom 7.1.3",
  "num-traits",
  "rusticata-macros",
  "thiserror 1.0.69",
@@ -536,7 +536,7 @@ dependencies = [
  "bitflags 2.9.0",
  "cexpr",
  "clang-sys",
- "itertools 0.10.5",
+ "itertools 0.13.0",
  "log",
  "prettyplease",
  "proc-macro2",
@@ -654,6 +654,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
 [[package]]
 name = "bytes"
 version = "1.10.1"
@@ -675,7 +681,7 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
 dependencies = [
- "nom",
+ "nom 7.1.3",
 ]
 
 [[package]]
@@ -690,12 +696,6 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
-[[package]]
-name = "checked_int_cast"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17cc5e6b5ab06331c33589842070416baa137e8b0eb912b008cfd4a78ada7919"
-
 [[package]]
 name = "chrono"
 version = "0.4.40"
@@ -724,9 +724,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.34"
+version = "4.5.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff"
+checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -734,14 +734,14 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.34"
+version = "4.5.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489"
+checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
 dependencies = [
  "anstream",
  "anstyle",
  "clap_lex",
- "strsim 0.11.1",
+ "strsim",
 ]
 
 [[package]]
@@ -818,7 +818,7 @@ dependencies = [
  "crossbeam-epoch",
  "crossbeam-queue",
  "crossbeam-utils",
- "lru",
+ "lru 0.13.0",
  "smallvec",
  "sptr",
  "tokio",
@@ -1051,38 +1051,14 @@ dependencies = [
  "whoami",
 ]
 
-[[package]]
-name = "darling"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
-dependencies = [
- "darling_core 0.14.4",
- "darling_macro 0.14.4",
-]
-
 [[package]]
 name = "darling"
 version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
 dependencies = [
- "darling_core 0.20.10",
- "darling_macro 0.20.10",
-]
-
-[[package]]
-name = "darling_core"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
-dependencies = [
- "fnv",
- "ident_case",
- "proc-macro2",
- "quote",
- "strsim 0.10.0",
- "syn 1.0.109",
+ "darling_core",
+ "darling_macro",
 ]
 
 [[package]]
@@ -1095,28 +1071,17 @@ dependencies = [
  "ident_case",
  "proc-macro2",
  "quote",
- "strsim 0.11.1",
+ "strsim",
  "syn 2.0.100",
 ]
 
-[[package]]
-name = "darling_macro"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
-dependencies = [
- "darling_core 0.14.4",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "darling_macro"
 version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
- "darling_core 0.20.10",
+ "darling_core",
  "quote",
  "syn 2.0.100",
 ]
@@ -1148,7 +1113,7 @@ checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553"
 dependencies = [
  "asn1-rs",
  "displaydoc",
- "nom",
+ "nom 7.1.3",
  "num-bigint",
  "num-traits",
  "rusticata-macros",
@@ -1177,33 +1142,33 @@ dependencies = [
 
 [[package]]
 name = "derive_builder"
-version = "0.12.0"
+version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
+checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
 dependencies = [
  "derive_builder_macro",
 ]
 
 [[package]]
 name = "derive_builder_core"
-version = "0.12.0"
+version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
+checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
 dependencies = [
- "darling 0.14.4",
+ "darling",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "derive_builder_macro"
-version = "0.12.0"
+version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
+checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
 dependencies = [
  "derive_builder_core",
- "syn 1.0.109",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -1213,7 +1178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9313f104b590510b46fc01c0a324fc76505c13871454d3c48490468d04c8d395"
 dependencies = [
  "libc",
- "nom",
+ "nom 7.1.3",
 ]
 
 [[package]]
@@ -1408,9 +1373,9 @@ dependencies = [
 
 [[package]]
 name = "fallible-iterator"
-version = "0.2.0"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
 
 [[package]]
 name = "fallible-streaming-iterator"
@@ -1420,12 +1385,13 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
 
 [[package]]
 name = "fancy-regex"
-version = "0.11.0"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
+checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
 dependencies = [
  "bit-set 0.5.3",
- "regex",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
 ]
 
 [[package]]
@@ -1468,6 +1434,9 @@ name = "faster-hex"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "fastrand"
@@ -1761,9 +1730,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 
 [[package]]
 name = "gix"
-version = "0.64.0"
+version = "0.71.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d78414d29fcc82329080166077e0f7689f4016551fdb334d787c3d040fe2634f"
+checksum = "a61e71ec6817fc3c9f12f812682cfe51ee6ea0d2e27e02fc3849c35524617435"
 dependencies = [
  "gix-actor",
  "gix-commitgraph",
@@ -1777,16 +1746,17 @@ dependencies = [
  "gix-hash",
  "gix-hashtable",
  "gix-lock",
- "gix-macros",
  "gix-object",
  "gix-odb",
  "gix-pack",
  "gix-path",
+ "gix-protocol",
  "gix-ref",
  "gix-refspec",
  "gix-revision",
  "gix-revwalk",
  "gix-sec",
+ "gix-shallow",
  "gix-tempfile",
  "gix-trace",
  "gix-traverse",
@@ -1795,21 +1765,21 @@ dependencies = [
  "gix-validate",
  "once_cell",
  "smallvec",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-actor"
-version = "0.31.5"
+version = "0.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0e454357e34b833cc3a00b6efbbd3dd4d18b24b9fb0c023876ec2645e8aa3f2"
+checksum = "f438c87d4028aca4b82f82ba8d8ab1569823cfb3e5bc5fa8456a71678b2a20e7"
 dependencies = [
  "bstr",
  "gix-date",
  "gix-utils",
  "itoa",
- "thiserror 1.0.69",
- "winnow 0.6.26",
+ "thiserror 2.0.12",
+ "winnow 0.7.6",
 ]
 
 [[package]]
@@ -1822,24 +1792,36 @@ dependencies = [
 ]
 
 [[package]]
-name = "gix-commitgraph"
-version = "0.24.3"
+name = "gix-command"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "133b06f67f565836ec0c473e2116a60fb74f80b6435e21d88013ac0e3c60fc78"
+checksum = "c0378995847773a697f8e157fe2963ecf3462fe64be05b7b3da000b3b472def8"
+dependencies = [
+ "bstr",
+ "gix-path",
+ "gix-quote",
+ "gix-trace",
+ "shell-words",
+]
+
+[[package]]
+name = "gix-commitgraph"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "043cbe49b7a7505150db975f3cb7c15833335ac1e26781f615454d9d640a28fe"
 dependencies = [
  "bstr",
  "gix-chunk",
- "gix-features",
  "gix-hash",
  "memmap2",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-config"
-version = "0.38.0"
+version = "0.44.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28f53fd03d1bf09ebcc2c8654f08969439c4556e644ca925f27cf033bc43e658"
+checksum = "9c6f830bf746604940261b49abf7f655d2c19cadc9f4142ae9379e3a316e8cfa"
 dependencies = [
  "bstr",
  "gix-config-value",
@@ -1851,16 +1833,16 @@ dependencies = [
  "memchr",
  "once_cell",
  "smallvec",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
  "unicode-bom",
- "winnow 0.6.26",
+ "winnow 0.7.6",
 ]
 
 [[package]]
 name = "gix-config-value"
-version = "0.14.11"
+version = "0.14.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11365144ef93082f3403471dbaa94cfe4b5e72743bdb9560719a251d439f4cee"
+checksum = "8dc2c844c4cf141884678cabef736fd91dd73068b9146e6f004ba1a0457944b6"
 dependencies = [
  "bitflags 2.9.0",
  "bstr",
@@ -1871,33 +1853,33 @@ dependencies = [
 
 [[package]]
 name = "gix-date"
-version = "0.8.7"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0"
+checksum = "daa30058ec7d3511fbc229e4f9e696a35abd07ec5b82e635eff864a2726217e4"
 dependencies = [
  "bstr",
  "itoa",
- "thiserror 1.0.69",
- "time",
+ "jiff",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-diff"
-version = "0.44.1"
+version = "0.51.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1996d5c8a305b59709467d80617c9fde48d9d75fd1f4179ea970912630886c9d"
+checksum = "a2c975dad2afc85e4e233f444d1efbe436c3cdcf3a07173984509c436d00a3f8"
 dependencies = [
  "bstr",
  "gix-hash",
  "gix-object",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-discover"
-version = "0.33.0"
+version = "0.39.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67662731cec3cb31ba3ed2463809493f76d8e5d6c6d245de8b0560438c13450e"
+checksum = "f7fb8a4349b854506a3915de18d3341e5f1daa6b489c8affc9ca0d69efe86781"
 dependencies = [
  "bstr",
  "dunce",
@@ -1906,44 +1888,46 @@ dependencies = [
  "gix-path",
  "gix-ref",
  "gix-sec",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-features"
-version = "0.38.2"
+version = "0.41.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69"
+checksum = "016d6050219458d14520fe22bdfdeb9cb71631dec9bc2724767c983f60109634"
 dependencies = [
  "crc32fast",
  "flate2",
- "gix-hash",
+ "gix-path",
  "gix-trace",
  "gix-utils",
  "libc",
  "once_cell",
  "prodash",
- "sha1_smol",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
  "walkdir",
 ]
 
 [[package]]
 name = "gix-fs"
-version = "0.11.3"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2bfe6249cfea6d0c0e0990d5226a4cb36f030444ba9e35e0639275db8f98575"
+checksum = "951e886120dc5fa8cac053e5e5c89443f12368ca36811b2e43d1539081f9c111"
 dependencies = [
+ "bstr",
  "fastrand",
  "gix-features",
+ "gix-path",
  "gix-utils",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-glob"
-version = "0.16.5"
+version = "0.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111"
+checksum = "20972499c03473e773a2099e5fd0c695b9b72465837797a51a43391a1635a030"
 dependencies = [
  "bitflags 2.9.0",
  "bstr",
@@ -1953,19 +1937,21 @@ dependencies = [
 
 [[package]]
 name = "gix-hash"
-version = "0.14.2"
+version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e"
+checksum = "834e79722063958b03342edaa1e17595cd2939bb2b3306b3225d0815566dcb49"
 dependencies = [
  "faster-hex",
- "thiserror 1.0.69",
+ "gix-features",
+ "sha1-checked",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-hashtable"
-version = "0.5.2"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ddf80e16f3c19ac06ce415a38b8591993d3f73aede049cb561becb5b3a8e242"
+checksum = "f06066d8702a9186dc1fdc1ed751ff2d7e924ceca21cb5d51b8f990c9c2e014a"
 dependencies = [
  "gix-hash",
  "hashbrown 0.14.5",
@@ -1974,70 +1960,62 @@ dependencies = [
 
 [[package]]
 name = "gix-lock"
-version = "14.0.0"
+version = "17.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d"
+checksum = "df47b8f11c34520db5541bc5fc9fbc8e4b0bdfcec3736af89ccb1a5728a0126f"
 dependencies = [
  "gix-tempfile",
  "gix-utils",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "gix-macros"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "999ce923619f88194171a67fb3e6d613653b8d4d6078b529b15a765da0edcc17"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.100",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-object"
-version = "0.42.3"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386"
+checksum = "4943fcdae6ffc135920c9ea71e0362ed539182924ab7a85dd9dac8d89b0dd69a"
 dependencies = [
  "bstr",
  "gix-actor",
  "gix-date",
  "gix-features",
  "gix-hash",
+ "gix-hashtable",
+ "gix-path",
  "gix-utils",
  "gix-validate",
  "itoa",
  "smallvec",
- "thiserror 1.0.69",
- "winnow 0.6.26",
+ "thiserror 2.0.12",
+ "winnow 0.7.6",
 ]
 
 [[package]]
 name = "gix-odb"
-version = "0.61.1"
+version = "0.68.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20d384fe541d93d8a3bb7d5d5ef210780d6df4f50c4e684ccba32665a5e3bc9b"
+checksum = "50306d40dcc982eb6b7593103f066ea6289c7b094cb9db14f3cd2be0b9f5e610"
 dependencies = [
  "arc-swap",
  "gix-date",
  "gix-features",
  "gix-fs",
  "gix-hash",
+ "gix-hashtable",
  "gix-object",
  "gix-pack",
  "gix-path",
  "gix-quote",
  "parking_lot",
  "tempfile",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-pack"
-version = "0.51.1"
+version = "0.58.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e0594491fffe55df94ba1c111a6566b7f56b3f8d2e1efc750e77d572f5f5229"
+checksum = "9b65fffb09393c26624ca408d32cfe8776fb94cd0a5cdf984905e1d2f39779cb"
 dependencies = [
  "clru",
  "gix-chunk",
@@ -2048,14 +2026,26 @@ dependencies = [
  "gix-path",
  "memmap2",
  "smallvec",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "gix-packetline"
+version = "0.18.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "123844a70cf4d5352441dc06bab0da8aef61be94ec239cb631e0ba01dc6d3a04"
+dependencies = [
+ "bstr",
+ "faster-hex",
+ "gix-trace",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-path"
-version = "0.10.14"
+version = "0.10.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c40f12bb65a8299be0cfb90fe718e3be236b7a94b434877012980863a883a99f"
+checksum = "f910668e2f6b2a55ff35a1f04df88a1a049f7b868507f4cbeeaa220eaba7be87"
 dependencies = [
  "bstr",
  "gix-trace",
@@ -2065,10 +2055,29 @@ dependencies = [
 ]
 
 [[package]]
-name = "gix-quote"
-version = "0.4.15"
+name = "gix-protocol"
+version = "0.49.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e49357fccdb0c85c0d3a3292a9f6db32d9b3535959b5471bb9624908f4a066c6"
+checksum = "5678ddae1d62880bc30e2200be1b9387af3372e0e88e21f81b4e7f8367355b5a"
+dependencies = [
+ "bstr",
+ "gix-date",
+ "gix-features",
+ "gix-hash",
+ "gix-ref",
+ "gix-shallow",
+ "gix-transport",
+ "gix-utils",
+ "maybe-async",
+ "thiserror 2.0.12",
+ "winnow 0.7.6",
+]
+
+[[package]]
+name = "gix-quote"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b005c550bf84de3b24aa5e540a23e6146a1c01c7d30470e35d75a12f827f969"
 dependencies = [
  "bstr",
  "gix-utils",
@@ -2077,9 +2086,9 @@ dependencies = [
 
 [[package]]
 name = "gix-ref"
-version = "0.45.0"
+version = "0.51.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "636e96a0a5562715153fee098c217110c33a6f8218f08f4687ff99afde159bb5"
+checksum = "b2e1f7eb6b7ce82d2d19961f74bd637bab3ea79b1bc7bfb23dbefc67b0415d8b"
 dependencies = [
  "gix-actor",
  "gix-features",
@@ -2092,43 +2101,44 @@ dependencies = [
  "gix-utils",
  "gix-validate",
  "memmap2",
- "thiserror 1.0.69",
- "winnow 0.6.26",
+ "thiserror 2.0.12",
+ "winnow 0.7.6",
 ]
 
 [[package]]
 name = "gix-refspec"
-version = "0.23.1"
+version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6868f8cd2e62555d1f7c78b784bece43ace40dd2a462daf3b588d5416e603f37"
+checksum = "1d8587b21e2264a6e8938d940c5c99662779c13a10741a5737b15fc85c252ffc"
 dependencies = [
  "bstr",
  "gix-hash",
  "gix-revision",
  "gix-validate",
  "smallvec",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-revision"
-version = "0.27.2"
+version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01b13e43c2118c4b0537ddac7d0821ae0dfa90b7b8dbf20c711e153fb749adce"
+checksum = "342caa4e158df3020cadf62f656307c3948fe4eacfdf67171d7212811860c3e9"
 dependencies = [
  "bstr",
+ "gix-commitgraph",
  "gix-date",
  "gix-hash",
  "gix-object",
  "gix-revwalk",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-revwalk"
-version = "0.13.2"
+version = "0.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b030ccaab71af141f537e0225f19b9e74f25fefdba0372246b844491cab43e0"
+checksum = "2dc7c3d7e5cdc1ab8d35130106e4af0a4f9f9eca0c81f4312b690780e92bde0d"
 dependencies = [
  "gix-commitgraph",
  "gix-date",
@@ -2136,14 +2146,14 @@ dependencies = [
  "gix-hashtable",
  "gix-object",
  "smallvec",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-sec"
-version = "0.10.11"
+version = "0.10.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d84dae13271f4313f8d60a166bf27e54c968c7c33e2ffd31c48cafe5da649875"
+checksum = "47aeb0f13de9ef2f3033f5ff218de30f44db827ac9f1286f9ef050aacddd5888"
 dependencies = [
  "bitflags 2.9.0",
  "gix-path",
@@ -2152,10 +2162,22 @@ dependencies = [
 ]
 
 [[package]]
-name = "gix-tempfile"
-version = "14.0.2"
+name = "gix-shallow"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046b4927969fa816a150a0cda2e62c80016fe11fb3c3184e4dddf4e542f108aa"
+checksum = "cc0598aacfe1d52575a21c9492fee086edbb21e228ec36c819c42ab923f434c3"
+dependencies = [
+ "bstr",
+ "gix-hash",
+ "gix-lock",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "gix-tempfile"
+version = "17.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6de439bbb9a5d3550c9c7fab0e16d2d637d120fcbe0dfbc538772a187f099b"
 dependencies = [
  "gix-fs",
  "libc",
@@ -2171,10 +2193,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c396a2036920c69695f760a65e7f2677267ccf483f25046977d87e4cb2665f7"
 
 [[package]]
-name = "gix-traverse"
-version = "0.39.2"
+name = "gix-transport"
+version = "0.46.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e499a18c511e71cf4a20413b743b9f5bcf64b3d9e81e9c3c6cd399eae55a8840"
+checksum = "b3f68c2870bfca8278389d2484a7f2215b67d0b0cc5277d3c72ad72acf41787e"
+dependencies = [
+ "bstr",
+ "gix-command",
+ "gix-features",
+ "gix-packetline",
+ "gix-quote",
+ "gix-sec",
+ "gix-url",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "gix-traverse"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36c0b049f8bdb61b20016694102f7b507f2e1727e83e9c5e6dad4f7d84ff7384"
 dependencies = [
  "bitflags 2.9.0",
  "gix-commitgraph",
@@ -2184,28 +2222,28 @@ dependencies = [
  "gix-object",
  "gix-revwalk",
  "smallvec",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
 name = "gix-url"
-version = "0.27.5"
+version = "0.30.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd280c5e84fb22e128ed2a053a0daeacb6379469be6a85e3d518a0636e160c89"
+checksum = "48dfe23f93f1ddb84977d80bb0dd7aa09d1bf5d5afc0c9b6820cccacc25ae860"
 dependencies = [
  "bstr",
  "gix-features",
  "gix-path",
- "home",
- "thiserror 1.0.69",
+ "percent-encoding",
+ "thiserror 2.0.12",
  "url",
 ]
 
 [[package]]
 name = "gix-utils"
-version = "0.1.14"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff08f24e03ac8916c478c8419d7d3c33393da9bb41fa4c24455d5406aeefd35f"
+checksum = "189f8724cf903e7fd57cfe0b7bc209db255cacdcb22c781a022f52c3a774f8d0"
 dependencies = [
  "fastrand",
  "unicode-normalization",
@@ -2213,12 +2251,12 @@ dependencies = [
 
 [[package]]
 name = "gix-validate"
-version = "0.8.5"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf"
+checksum = "34b5f1253109da6c79ed7cf6e1e38437080bb6d704c76af14c93e2f255234084"
 dependencies = [
  "bstr",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
@@ -2271,6 +2309,18 @@ version = "1.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
 
+[[package]]
+name = "haproxy-protocol"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f61fc527a2f089b57ebc09301b6371bbbff4ce7b547306c17dfa55766655bec6"
+dependencies = [
+ "hex",
+ "nom 8.0.0",
+ "tokio",
+ "tracing",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.12.3"
@@ -2282,11 +2332,6 @@ name = "hashbrown"
 version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash",
- "allocator-api2",
- "serde",
-]
 
 [[package]]
 name = "hashbrown"
@@ -2297,15 +2342,16 @@ dependencies = [
  "allocator-api2",
  "equivalent",
  "foldhash",
+ "serde",
 ]
 
 [[package]]
 name = "hashlink"
-version = "0.8.4"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
 dependencies = [
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
 ]
 
 [[package]]
@@ -2314,6 +2360,12 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
 [[package]]
 name = "hex"
 version = "0.4.3"
@@ -2712,30 +2764,28 @@ dependencies = [
 
 [[package]]
 name = "image"
-version = "0.23.14"
+version = "0.25.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
+checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
 dependencies = [
  "bytemuck",
- "byteorder",
+ "byteorder-lite",
  "color_quant",
- "num-iter",
- "num-rational 0.3.2",
+ "gif",
+ "image-webp",
  "num-traits",
+ "zune-core",
+ "zune-jpeg",
 ]
 
 [[package]]
-name = "image"
-version = "0.24.9"
+name = "image-webp"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f"
 dependencies = [
- "bytemuck",
- "byteorder",
- "color_quant",
- "gif",
- "jpeg-decoder",
- "num-traits",
+ "byteorder-lite",
+ "quick-error",
 ]
 
 [[package]]
@@ -2794,9 +2844,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
 [[package]]
 name = "itertools"
-version = "0.10.5"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
 dependencies = [
  "either",
 ]
@@ -2817,10 +2867,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
 
 [[package]]
-name = "jpeg-decoder"
-version = "0.3.1"
+name = "jiff"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
+dependencies = [
+ "jiff-static",
+ "jiff-tzdb-platform",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "jiff-tzdb"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
+
+[[package]]
+name = "jiff-tzdb-platform"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
+dependencies = [
+ "jiff-tzdb",
+]
 
 [[package]]
 name = "js-sys"
@@ -2981,7 +3066,7 @@ dependencies = [
  "md-5",
  "openssl",
  "openssl-sys",
- "rand 0.8.5",
+ "rand 0.9.1",
  "serde",
  "sha-crypt",
  "sha2",
@@ -3086,7 +3171,7 @@ dependencies = [
  "clap_complete",
  "dialoguer",
  "futures",
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
  "kanidm-hsm-crypto",
  "kanidm_build_profiles",
  "kanidm_client",
@@ -3098,7 +3183,7 @@ dependencies = [
  "kanidmd_core",
  "kanidmd_testkit",
  "libc",
- "lru",
+ "lru 0.14.0",
  "mimalloc",
  "notify-debouncer-full",
  "prctl",
@@ -3142,6 +3227,8 @@ dependencies = [
  "filetime",
  "futures",
  "futures-util",
+ "haproxy-protocol",
+ "hashbrown 0.15.2",
  "hyper 1.6.0",
  "hyper-util",
  "kanidm_build_profiles",
@@ -3192,10 +3279,10 @@ dependencies = [
  "dyn-clone",
  "fernet",
  "futures",
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
  "hex",
  "idlset",
- "image 0.24.9",
+ "image",
  "itertools 0.14.0",
  "kanidm_build_profiles",
  "kanidm_lib_crypto",
@@ -3209,7 +3296,7 @@ dependencies = [
  "num_enum",
  "openssl",
  "openssl-sys",
- "rand 0.8.5",
+ "rand 0.9.1",
  "regex",
  "rusqlite",
  "serde",
@@ -3249,6 +3336,10 @@ dependencies = [
  "escargot",
  "fantoccini",
  "futures",
+ "hex",
+ "http-body-util",
+ "hyper 1.6.0",
+ "hyper-util",
  "jsonschema",
  "kanidm_build_profiles",
  "kanidm_client",
@@ -3294,6 +3385,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "lambert_w"
+version = "1.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "913e1e36ca541d75f384593fa70bf5a5e9f001f2996bfa7926550d921f83baf6"
+dependencies = [
+ "num-complex",
+ "num-traits",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.5.0"
@@ -3313,7 +3414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2df7f9fd9f64cf8f59e1a4a0753fe7d575a5b38d3d7ac5758dcee9357d83ef0a"
 dependencies = [
  "bytes",
- "nom",
+ "nom 7.1.3",
 ]
 
 [[package]]
@@ -3345,7 +3446,7 @@ dependencies = [
  "base64 0.21.7",
  "bytes",
  "lber",
- "nom",
+ "nom 7.1.3",
  "peg",
  "serde",
  "thiserror 1.0.69",
@@ -3356,9 +3457,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.171"
+version = "0.2.172"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
 [[package]]
 name = "libloading"
@@ -3378,9 +3479,9 @@ checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
 
 [[package]]
 name = "libmimalloc-sys"
-version = "0.1.40"
+version = "0.1.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07d0e07885d6a754b9c7993f2625187ad694ee985d60f23355ff0e7077261502"
+checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4"
 dependencies = [
  "cc",
  "libc",
@@ -3409,9 +3510,9 @@ dependencies = [
 
 [[package]]
 name = "libsqlite3-sys"
-version = "0.25.2"
+version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa"
+checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa"
 dependencies = [
  "cc",
  "pkg-config",
@@ -3499,6 +3600,15 @@ dependencies = [
  "hashbrown 0.15.2",
 ]
 
+[[package]]
+name = "lru"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198"
+dependencies = [
+ "hashbrown 0.15.2",
+]
+
 [[package]]
 name = "malloced"
 version = "1.3.1"
@@ -3522,13 +3632,39 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
 
 [[package]]
 name = "mathru"
-version = "0.13.0"
+version = "0.15.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a42bf938e4c9a6ad581cf528d5606eb50c5458ac759ca23719291e2f6499bec"
+checksum = "f3df2d16c016b28cd94248072b6cd8106d8abd7184ec90ea09660c4b7fa989f1"
 dependencies = [
+ "lambert_w",
+ "matrixmultiply",
  "rand 0.8.5",
 ]
 
+[[package]]
+name = "matrixmultiply"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a"
+dependencies = [
+ "autocfg",
+ "num_cpus",
+ "once_cell",
+ "rawpointer",
+ "thread-tree",
+]
+
+[[package]]
+name = "maybe-async"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
 [[package]]
 name = "md-5"
 version = "0.10.6"
@@ -3565,9 +3701,9 @@ dependencies = [
 
 [[package]]
 name = "mimalloc"
-version = "0.1.44"
+version = "0.1.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99585191385958383e13f6b822e6b6d8d9cf928e7d286ceb092da92b43c87bc1"
+checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af"
 dependencies = [
  "libmimalloc-sys",
 ]
@@ -3678,10 +3814,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "nonempty"
-version = "0.8.1"
+name = "nom"
+version = "8.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aeaf4ad7403de93e699c191202f017118df734d3850b01e13a3a8b2e6953d3c9"
+checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "nonempty"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "549e471b99ccaf2f89101bec68f4d244457d5a95a9c3d0672e9564124397741d"
 dependencies = [
  "serde",
 ]
@@ -3755,7 +3900,7 @@ dependencies = [
  "num-complex",
  "num-integer",
  "num-iter",
- "num-rational 0.4.2",
+ "num-rational",
  "num-traits",
 ]
 
@@ -3821,17 +3966,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "num-rational"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
 [[package]]
 name = "num-rational"
 version = "0.4.2"
@@ -3853,24 +3987,34 @@ dependencies = [
 ]
 
 [[package]]
-name = "num_enum"
-version = "0.5.11"
+name = "num_cpus"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
 dependencies = [
  "num_enum_derive",
 ]
 
 [[package]]
 name = "num_enum_derive"
-version = "0.5.11"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -4093,14 +4237,14 @@ dependencies = [
  "clap",
  "crossbeam",
  "csv",
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
  "idlset",
  "kanidm_build_profiles",
  "kanidm_client",
  "mathru",
  "mimalloc",
- "rand 0.8.5",
- "rand_chacha 0.3.1",
+ "rand 0.9.1",
+ "rand_chacha 0.9.0",
  "serde",
  "serde_json",
  "tokio",
@@ -4305,6 +4449,21 @@ version = "0.3.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
 
+[[package]]
+name = "portable-atomic"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
 [[package]]
 name = "powerfmt"
 version = "0.2.0"
@@ -4347,7 +4506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
 dependencies = [
  "once_cell",
- "toml_edit",
+ "toml_edit 0.19.15",
 ]
 
 [[package]]
@@ -4376,18 +4535,22 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "prodash"
-version = "28.0.0"
+version = "29.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79"
+checksum = "f04bb108f648884c23b98a0e940ebc2c93c0c3b89f04dbaf7eb8256ce617d1bc"
+dependencies = [
+ "log",
+ "parking_lot",
+]
 
 [[package]]
 name = "prost"
@@ -4430,12 +4593,11 @@ dependencies = [
 
 [[package]]
 name = "qrcode"
-version = "0.12.0"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16d2f1455f3630c6e5107b4f2b94e74d76dea80736de0981fd27644216cff57f"
+checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec"
 dependencies = [
- "checked_int_cast",
- "image 0.23.14",
+ "image",
 ]
 
 [[package]]
@@ -4472,7 +4634,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
 dependencies = [
  "bytes",
  "getrandom 0.3.2",
- "rand 0.9.0",
+ "rand 0.9.1",
  "ring",
  "rustc-hash 2.1.1",
  "rustls",
@@ -4526,13 +4688,12 @@ dependencies = [
 
 [[package]]
 name = "rand"
-version = "0.9.0"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
 dependencies = [
  "rand_chacha 0.9.0",
  "rand_core 0.9.3",
- "zerocopy 0.8.24",
 ]
 
 [[package]]
@@ -4573,6 +4734,12 @@ dependencies = [
  "getrandom 0.3.2",
 ]
 
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
 [[package]]
 name = "redox_syscall"
 version = "0.5.10"
@@ -4613,12 +4780,6 @@ dependencies = [
  "syn 2.0.100",
 ]
 
-[[package]]
-name = "reference-counted-singleton"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5daffa8f5ca827e146485577fa9dba9bd9c6921e06e954ab8f6408c10f753086"
-
 [[package]]
 name = "referencing"
 version = "0.29.1"
@@ -4805,11 +4966,11 @@ checksum = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
 
 [[package]]
 name = "rusqlite"
-version = "0.28.0"
+version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
+checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.9.0",
  "fallible-iterator",
  "fallible-streaming-iterator",
  "hashlink",
@@ -4875,7 +5036,7 @@ version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
 dependencies = [
- "nom",
+ "nom 7.1.3",
 ]
 
 [[package]]
@@ -5059,16 +5220,16 @@ dependencies = [
 
 [[package]]
 name = "selinux"
-version = "0.4.6"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0139b2436c81305eb6bda33af151851f75bd62783817b25f44daa371119c30b5"
+checksum = "e37f432dfe840521abd9a72fefdf88ed7ad0f43bbea7d9d1d3d80383e9f4ad13"
 dependencies = [
  "bitflags 2.9.0",
  "libc",
  "once_cell",
- "reference-counted-singleton",
+ "parking_lot",
  "selinux-sys",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
@@ -5160,6 +5321,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "serde_urlencoded"
 version = "0.7.1"
@@ -5196,7 +5366,7 @@ version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
 dependencies = [
- "darling 0.20.10",
+ "darling",
  "proc-macro2",
  "quote",
  "syn 2.0.100",
@@ -5215,10 +5385,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "sha1_smol"
-version = "1.0.1"
+name = "sha1"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha1-checked"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423"
+dependencies = [
+ "digest",
+ "sha1",
+]
 
 [[package]]
 name = "sha2"
@@ -5248,9 +5433,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
 
 [[package]]
 name = "shellexpand"
-version = "2.1.2"
+version = "3.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4"
+checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb"
 dependencies = [
  "dirs",
 ]
@@ -5366,7 +5551,7 @@ checksum = "34285eaade87ba166c4f17c0ae1e35d52659507db81888beae277e962b9e5a02"
 dependencies = [
  "base64 0.21.7",
  "base64urlsafedata",
- "nom",
+ "nom 7.1.3",
  "openssl",
  "serde",
  "serde_cbor_2",
@@ -5379,8 +5564,7 @@ dependencies = [
 [[package]]
 name = "sshkeys"
 version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45287473d24bf7ad9ebad1aff097ad0424c16cd9430549170c3a67c5b05705bd"
+source = "git+https://github.com/Firstyear/rust-sshkeys.git?rev=3a081cbf7480628223bcb96fc8aaa8c19109d007#3a081cbf7480628223bcb96fc8aaa8c19109d007"
 dependencies = [
  "base64 0.22.1",
  "byteorder",
@@ -5400,12 +5584,6 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
 [[package]]
 name = "strsim"
 version = "0.11.1"
@@ -5420,9 +5598,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "svg"
-version = "0.13.1"
+version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02d815ad337e8449d2374d4248448645edfe74e699343dd5719139d93fa87112"
+checksum = "94afda9cd163c04f6bee8b4bf2501c91548deae308373c436f36aeff3cf3c4a3"
 
 [[package]]
 name = "syn"
@@ -5567,6 +5745,15 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
 
+[[package]]
+name = "thread-tree"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbd370cb847953a25954d9f63e14824a36113f8c72eecf6eccef5dc4b45d630"
+dependencies = [
+ "crossbeam-channel",
+]
+
 [[package]]
 name = "thread_local"
 version = "1.1.8"
@@ -5743,11 +5930,14 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.5.11"
+version = "0.8.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
 dependencies = [
  "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.22.24",
 ]
 
 [[package]]
@@ -5755,6 +5945,9 @@ name = "toml_datetime"
 version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "toml_edit"
@@ -5767,6 +5960,19 @@ dependencies = [
  "winnow 0.5.40",
 ]
 
+[[package]]
+name = "toml_edit"
+version = "0.22.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
+dependencies = [
+ "indexmap 2.8.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow 0.7.6",
+]
+
 [[package]]
 name = "tonic"
 version = "0.12.3"
@@ -6343,7 +6549,7 @@ dependencies = [
  "bitflags 1.3.2",
  "futures",
  "hex",
- "nom",
+ "nom 7.1.3",
  "num-derive",
  "num-traits",
  "openssl",
@@ -6388,7 +6594,7 @@ dependencies = [
  "compact_jwt",
  "der-parser",
  "hex",
- "nom",
+ "nom 7.1.3",
  "openssl",
  "rand 0.8.5",
  "rand_chacha 0.3.1",
@@ -6837,6 +7043,15 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "winnow"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "winreg"
 version = "0.50.0"
@@ -6890,7 +7105,7 @@ dependencies = [
  "data-encoding",
  "der-parser",
  "lazy_static",
- "nom",
+ "nom 7.1.3",
  "oid-registry",
  "rusticata-macros",
  "thiserror 1.0.69",
@@ -7037,17 +7252,33 @@ dependencies = [
 ]
 
 [[package]]
-name = "zxcvbn"
-version = "2.2.2"
+name = "zune-core"
+version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "103fa851fff70ea29af380e87c25c48ff7faac5c530c70bd0e65366d4e0c94e4"
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
+[[package]]
+name = "zune-jpeg"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
 dependencies = [
+ "zune-core",
+]
+
+[[package]]
+name = "zxcvbn"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad76e35b00ad53688d6b90c431cabe3cbf51f7a4a154739e04b63004ab1c736c"
+dependencies = [
+ "chrono",
  "derive_builder",
- "fancy-regex 0.11.0",
- "itertools 0.10.5",
- "js-sys",
+ "fancy-regex 0.13.0",
+ "itertools 0.13.0",
  "lazy_static",
- "quick-error",
  "regex",
  "time",
+ "wasm-bindgen",
+ "web-sys",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 2503267d6..6d614450e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -118,9 +118,11 @@ codegen-units = 256
 # webauthn-rs-proto = { path = "../webauthn-rs/webauthn-rs-proto" }
 # sshkey-attest = { path = "../webauthn-rs/sshkey-attest" }
 
-# kanidm-hsm-crypto = { path = "../hsm-crypto" }
-
+# For BSD nss support
 libnss = { git = "https://github.com/Firstyear/libnss-rs.git", branch = "20250207-freebsd" }
+# Allow ssh keys to have comments with spaces.
+sshkeys = { git = "https://github.com/Firstyear/rust-sshkeys.git", rev = "3a081cbf7480628223bcb96fc8aaa8c19109d007" }
+
 
 [workspace.dependencies]
 kanidmd_core = { path = "./server/core", version = "=1.6.0-dev" }
@@ -138,7 +140,7 @@ kanidm_utils_users = { path = "./libs/users", version = "=1.6.0-dev" }
 scim_proto = { path = "./libs/scim_proto", version = "=1.6.0-dev" }
 sketching = { path = "./libs/sketching", version = "=1.6.0-dev" }
 
-anyhow = { version = "1.0.95" }
+anyhow = { version = "1.0.98" }
 argon2 = { version = "0.5.3", features = ["alloc"] }
 askama = { version = "0.12.1", features = ["serde", "with-axum"] }
 askama_axum = { version = "0.4.0" }
@@ -159,7 +161,7 @@ base64 = "^0.22.1"
 base64urlsafedata = "0.5.1"
 bitflags = "^2.8.0"
 bytes = "^1.9.0"
-clap = { version = "^4.5.34", features = ["derive", "env"] }
+clap = { version = "4.5.37", features = ["derive", "env"] }
 clap_complete = "^4.5.42"
 # Forced by saffron/cron
 chrono = "^0.4.39"
@@ -176,16 +178,18 @@ filetime = "^0.2.24"
 fs4 = "^0.13.0"
 futures = "^0.3.31"
 futures-util = { version = "^0.3.30", features = ["sink"] }
-gix = { version = "0.64.0", default-features = false }
-hashbrown = { version = "0.14.3", features = ["serde", "inline-more", "ahash"] }
+gix = { version = "0.71.0", default-features = false }
+haproxy-protocol = { version = "0.0.1" }
+hashbrown = { version = "0.15.2", features = ["serde", "inline-more"] }
 hex = "^0.4.3"
 http = "1.2.0"
+http-body-util = "0.1"
 hyper = { version = "1.5.1", features = [
     "full",
 ] } # hyper full includes client/server/http2
 hyper-util = { version = "0.1.10", features = ["server", "tokio"] }
 idlset = "^0.2.5"
-image = { version = "0.24.9", default-features = false, features = [
+image = { version = "0.25.6", default-features = false, features = [
     "gif",
     "jpeg",
     "webp",
@@ -198,16 +202,16 @@ lazy_static = "^1.5.0"
 ldap3_client = "^0.5.2"
 ldap3_proto = { version = "^0.5.2", features = ["serde"] }
 
-libc = "^0.2.168"
+libc = "0.2.172"
 libnss = "^0.8.0"
 libsqlite3-sys = "^0.25.2"
 lodepng = "3.11.0"
-lru = "^0.13.0"
-mathru = "^0.13.0"
+lru = "0.14.0"
+mathru = "0.15.5"
 md-5 = "0.10.6"
-mimalloc = "0.1.43"
+mimalloc = "0.1.46"
 notify-debouncer-full = { version = "0.5" }
-num_enum = "^0.5.11"
+num_enum = "0.7.3"
 oauth2_ext = { version = "^4.4.2", package = "oauth2", default-features = false }
 openssl-sys = "^0.9"
 openssl = "^0.10.72"
@@ -229,11 +233,11 @@ tracing-core = "0.1.33"
 peg = "0.8"
 pkg-config = "^0.3.31"
 prctl = "1.0.0"
-proc-macro2 = "1.0.93"
-qrcode = "^0.12.0"
+proc-macro2 = "1.0.95"
+qrcode = "0.14.1"
 quote = "1"
-rand = "^0.8.5"
-rand_chacha = "0.3.1"
+rand = "0.9.1"
+rand_chacha = "0.9.0"
 regex = "1.11.0"
 reqwest = { version = "0.12.12", default-features = false, features = [
     "cookies",
@@ -243,13 +247,13 @@ reqwest = { version = "0.12.12", default-features = false, features = [
     "rustls-tls-native-roots",
     "rustls-tls-native-roots-no-provider",
 ] }
-rusqlite = { version = "^0.28.0", features = ["array", "bundled"] }
-rustls = { version = "0.23.21", default-features = false, features = [
+rusqlite = { version = "0.35.0", features = ["array", "bundled"] }
+rustls = { version = "0.23.26", default-features = false, features = [
     "aws_lc_rs",
 ] }
 
 sd-notify = "^0.4.5"
-selinux = "^0.4.6"
+selinux = "^0.5.1"
 serde = "^1.0.217"
 serde_cbor = { version = "0.12.0-dev", package = "serde_cbor_2" }
 serde_json = "^1.0.137"
@@ -257,13 +261,13 @@ serde_urlencoded = "^0.7.1"
 serde_with = "3.12.0"
 sha-crypt = "0.5.0"
 sha2 = "0.10.8"
-shellexpand = "^2.1.2"
+shellexpand = "3.1.1"
 smartstring = "^1.0.1"
 smolset = "^1.3.1"
 sshkey-attest = "^0.5.0"
 sshkeys = "0.3.3"
-svg = "0.13.1"
-syn = { version = "2.0.96", features = ["full"] }
+svg = "0.18.0"
+syn = { version = "2.0.100", features = ["full"] }
 tempfile = "3.15.0"
 testkit-macros = { path = "./server/testkit-macros" }
 time = { version = "^0.3.36", features = ["formatting", "local-offset"] }
@@ -272,7 +276,7 @@ tokio = "^1.44.2"
 tokio-openssl = "^0.6.5"
 tokio-util = "^0.7.13"
 
-toml = "^0.5.11"
+toml = "^0.8.20"
 tracing = { version = "^0.1.41", features = [
     "max_level_trace",
     "release_max_level_debug",
@@ -299,6 +303,6 @@ walkdir = "2"
 
 x509-cert = "0.2.5"
 
-zxcvbn = "^2.2.2"
+zxcvbn = "3.1.0"
 
-nonempty = "0.8.1"
+nonempty = "0.11.0"
diff --git a/examples/server.toml b/examples/server.toml
index 9a41738c5..5a69fbbcc 100644
--- a/examples/server.toml
+++ b/examples/server.toml
@@ -13,16 +13,6 @@ bindaddress = "[::]:443"
 #   Defaults to "" (disabled)
 # ldapbindaddress = "[::]:636"
 #
-#   HTTPS requests can be reverse proxied by a loadbalancer.
-#   To preserve the original IP of the caller, these systems
-#   will often add a header such as "Forwarded" or
-#   "X-Forwarded-For". If set to true, then this header is
-#   respected as the "authoritative" source of the IP of the
-#   connected client. If you are not using a load balancer
-#   then you should leave this value as default.
-#   Defaults to false
-# trust_x_forward_for = false
-#
 #   The path to the kanidm database.
 db_path = "/var/lib/private/kanidm/kanidm.db"
 #
@@ -86,6 +76,32 @@ domain = "idm.example.com"
 #   origin = "https://idm.example.com"
 origin = "https://idm.example.com:8443"
 #
+
+#   HTTPS requests can be reverse proxied by a loadbalancer.
+#   To preserve the original IP of the caller, these systems
+#   will often add a header such as "Forwarded" or
+#   "X-Forwarded-For". Some other proxies can use the PROXY
+#   protocol v2 header.
+#   This setting allows configuration of the range of trusted
+#   IPs which can supply this header information, and which
+#   format the information is provided in.
+#   Defaults to "none" (no trusted sources)
+#   Only one option can be used at a time.
+# [http_client_address_info]
+# proxy-v2 = ["127.0.0.1"]
+#   # OR
+# x-forward-for = ["127.0.0.1"]
+
+#   LDAPS requests can be reverse proxied by a loadbalancer.
+#   To preserve the original IP of the caller, these systems
+#   can add a header such as the PROXY protocol v2 header.
+#   This setting allows configuration of the range of trusted
+#   IPs which can supply this header information, and which
+#   format the information is provided in.
+#   Defaults to "none" (no trusted sources)
+# [ldap_client_address_info]
+# proxy-v2 = ["127.0.0.1"]
+
 [online_backup]
 #   The path to the output folder for online backups
 path = "/var/lib/private/kanidm/backups/"
diff --git a/examples/server_container.toml b/examples/server_container.toml
index f57923a40..2d706b77d 100644
--- a/examples/server_container.toml
+++ b/examples/server_container.toml
@@ -13,16 +13,6 @@ bindaddress = "[::]:8443"
 #   Defaults to "" (disabled)
 # ldapbindaddress = "[::]:3636"
 #
-#   HTTPS requests can be reverse proxied by a loadbalancer.
-#   To preserve the original IP of the caller, these systems
-#   will often add a header such as "Forwarded" or
-#   "X-Forwarded-For". If set to true, then this header is
-#   respected as the "authoritative" source of the IP of the
-#   connected client. If you are not using a load balancer
-#   then you should leave this value as default.
-#   Defaults to false
-# trust_x_forward_for = false
-#
 #   The path to the kanidm database.
 db_path = "/data/kanidm.db"
 #
@@ -85,7 +75,32 @@ domain = "idm.example.com"
 #   not consistent, the server WILL refuse to start!
 #   origin = "https://idm.example.com"
 origin = "https://idm.example.com:8443"
-#
+
+#   HTTPS requests can be reverse proxied by a loadbalancer.
+#   To preserve the original IP of the caller, these systems
+#   will often add a header such as "Forwarded" or
+#   "X-Forwarded-For". Some other proxies can use the PROXY
+#   protocol v2 header.
+#   This setting allows configuration of the range of trusted
+#   IPs which can supply this header information, and which
+#   format the information is provided in.
+#   Defaults to "none" (no trusted sources)
+#   Only one option can be used at a time.
+# [http_client_address_info]
+# proxy-v2 = ["127.0.0.1"]
+#   # OR
+# x-forward-for = ["127.0.0.1"]
+
+#   LDAPS requests can be reverse proxied by a loadbalancer.
+#   To preserve the original IP of the caller, these systems
+#   can add a header such as the PROXY protocol v2 header.
+#   This setting allows configuration of the range of trusted
+#   IPs which can supply this header information, and which
+#   format the information is provided in.
+#   Defaults to "none" (no trusted sources)
+# [ldap_client_address_info]
+# proxy-v2 = ["127.0.0.1"]
+
 [online_backup]
 #   The path to the output folder for online backups
 path = "/data/kanidm/backups/"
diff --git a/libs/crypto/src/lib.rs b/libs/crypto/src/lib.rs
index f95b2ae73..c1417d35b 100644
--- a/libs/crypto/src/lib.rs
+++ b/libs/crypto/src/lib.rs
@@ -834,9 +834,9 @@ impl TryFrom<&str> for Password {
 
 impl Password {
     fn bench_pbkdf2(pbkdf2_cost: usize) -> Option<Duration> {
-        let mut rng = rand::thread_rng();
-        let salt: Vec<u8> = (0..PBKDF2_SALT_LEN).map(|_| rng.gen()).collect();
-        let input: Vec<u8> = (0..PBKDF2_SALT_LEN).map(|_| rng.gen()).collect();
+        let mut rng = rand::rng();
+        let salt: Vec<u8> = (0..PBKDF2_SALT_LEN).map(|_| rng.random()).collect();
+        let input: Vec<u8> = (0..PBKDF2_SALT_LEN).map(|_| rng.random()).collect();
         // This is 512 bits of output
         let mut key: Vec<u8> = (0..PBKDF2_KEY_LEN).map(|_| 0).collect();
 
@@ -855,9 +855,9 @@ impl Password {
     }
 
     fn bench_argon2id(params: Params) -> Option<Duration> {
-        let mut rng = rand::thread_rng();
-        let salt: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.gen()).collect();
-        let input: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.gen()).collect();
+        let mut rng = rand::rng();
+        let salt: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.random()).collect();
+        let input: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.random()).collect();
         let mut key: Vec<u8> = (0..ARGON2_KEY_LEN).map(|_| 0).collect();
 
         let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
@@ -873,8 +873,8 @@ impl Password {
 
     pub fn new_pbkdf2(policy: &CryptoPolicy, cleartext: &str) -> Result<Self, CryptoError> {
         let pbkdf2_cost = policy.pbkdf2_cost;
-        let mut rng = rand::thread_rng();
-        let salt: Vec<u8> = (0..PBKDF2_SALT_LEN).map(|_| rng.gen()).collect();
+        let mut rng = rand::rng();
+        let salt: Vec<u8> = (0..PBKDF2_SALT_LEN).map(|_| rng.random()).collect();
         let mut key: Vec<u8> = (0..PBKDF2_KEY_LEN).map(|_| 0).collect();
 
         pbkdf2_hmac(
@@ -897,8 +897,8 @@ impl Password {
 
         let argon = Argon2::new(Algorithm::Argon2id, version, policy.argon2id_params.clone());
 
-        let mut rng = rand::thread_rng();
-        let salt: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.gen()).collect();
+        let mut rng = rand::rng();
+        let salt: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.random()).collect();
         let mut key: Vec<u8> = (0..ARGON2_KEY_LEN).map(|_| 0).collect();
 
         argon
@@ -925,8 +925,8 @@ impl Password {
 
         let argon = Argon2::new(Algorithm::Argon2id, version, policy.argon2id_params.clone());
 
-        let mut rng = rand::thread_rng();
-        let salt: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.gen()).collect();
+        let mut rng = rand::rng();
+        let salt: Vec<u8> = (0..ARGON2_SALT_LEN).map(|_| rng.random()).collect();
         let mut check_key: Vec<u8> = (0..ARGON2_KEY_LEN).map(|_| 0).collect();
 
         argon
diff --git a/libs/profiles/src/lib.rs b/libs/profiles/src/lib.rs
index a0714eff3..29b35582a 100644
--- a/libs/profiles/src/lib.rs
+++ b/libs/profiles/src/lib.rs
@@ -77,7 +77,10 @@ pub fn apply_profile() {
         .decode(contents)
         .unwrap_or_else(|_| panic!("Failed to parse profile - {} - {}", profile, contents));
 
-    let profile_cfg: ProfileConfig = toml::from_slice(&data)
+    let data_str = String::from_utf8(data)
+        .unwrap_or_else(|_| panic!("Failed to read profile data to UTF-8 string - {}", profile));
+
+    let profile_cfg: ProfileConfig = toml::from_str(&data_str)
         .unwrap_or_else(|_| panic!("Failed to parse profile - {} - {}", profile, contents));
 
     // We have to setup for our pkg version to be passed into things correctly
diff --git a/server/core/Cargo.toml b/server/core/Cargo.toml
index 1c753cb44..89cf3c59a 100644
--- a/server/core/Cargo.toml
+++ b/server/core/Cargo.toml
@@ -34,6 +34,8 @@ cron = { workspace = true }
 filetime = { workspace = true }
 futures = { workspace = true }
 futures-util = { workspace = true }
+haproxy-protocol = { workspace = true, features = ["tokio"] }
+hashbrown = { workspace = true }
 hyper = { workspace = true }
 hyper-util = { workspace = true }
 kanidm_proto = { workspace = true }
diff --git a/server/core/src/config.rs b/server/core/src/config.rs
index ad3d3bd9c..01bf005fd 100644
--- a/server/core/src/config.rs
+++ b/server/core/src/config.rs
@@ -4,18 +4,18 @@
 //! These components should be "per server". Any "per domain" config should be in the system
 //! or domain entries that are able to be replicated.
 
-use std::fmt::{self, Display};
-use std::fs::File;
-use std::io::Read;
-use std::path::{Path, PathBuf};
-use std::str::FromStr;
-
+use hashbrown::HashSet;
 use kanidm_proto::constants::DEFAULT_SERVER_ADDRESS;
 use kanidm_proto::internal::FsType;
 use kanidm_proto::messages::ConsoleOutputMode;
-
 use serde::Deserialize;
 use sketching::LogLevel;
+use std::fmt::{self, Display};
+use std::fs::File;
+use std::io::Read;
+use std::net::IpAddr;
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
 use url::Url;
 
 use crate::repl::config::ReplicationConfiguration;
@@ -100,6 +100,111 @@ pub struct TlsConfiguration {
     pub client_ca: Option<PathBuf>,
 }
 
+#[derive(Deserialize, Debug, Clone, Default)]
+pub enum LdapAddressInfo {
+    #[default]
+    None,
+    #[serde(rename = "proxy-v2")]
+    ProxyV2(HashSet<IpAddr>),
+}
+
+impl LdapAddressInfo {
+    pub fn trusted_proxy_v2(&self) -> Option<HashSet<IpAddr>> {
+        if let Self::ProxyV2(trusted) = self {
+            Some(trusted.clone())
+        } else {
+            None
+        }
+    }
+}
+
+impl Display for LdapAddressInfo {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::None => f.write_str("none"),
+            Self::ProxyV2(trusted) => {
+                f.write_str("proxy-v2 [ ")?;
+                for ip in trusted {
+                    write!(f, "{} ", ip)?;
+                }
+                f.write_str("]")
+            }
+        }
+    }
+}
+
+pub(crate) enum AddressSet {
+    NonContiguousIpSet(HashSet<IpAddr>),
+    All,
+}
+
+impl AddressSet {
+    pub(crate) fn contains(&self, ip_addr: &IpAddr) -> bool {
+        match self {
+            Self::All => true,
+            Self::NonContiguousIpSet(range) => range.contains(ip_addr),
+        }
+    }
+}
+
+#[derive(Deserialize, Debug, Clone, Default)]
+pub enum HttpAddressInfo {
+    #[default]
+    None,
+    #[serde(rename = "x-forward-for")]
+    XForwardFor(HashSet<IpAddr>),
+    // IMPORTANT: This is undocumented, and only exists for backwards compat
+    // with config v1 which has a boolean toggle for this option.
+    #[serde(rename = "x-forward-for-all-source-trusted")]
+    XForwardForAllSourcesTrusted,
+    #[serde(rename = "proxy-v2")]
+    ProxyV2(HashSet<IpAddr>),
+}
+
+impl HttpAddressInfo {
+    pub(crate) fn trusted_x_forward_for(&self) -> Option<AddressSet> {
+        match self {
+            Self::XForwardForAllSourcesTrusted => Some(AddressSet::All),
+            Self::XForwardFor(trusted) => Some(AddressSet::NonContiguousIpSet(trusted.clone())),
+            _ => None,
+        }
+    }
+
+    pub(crate) fn trusted_proxy_v2(&self) -> Option<HashSet<IpAddr>> {
+        if let Self::ProxyV2(trusted) = self {
+            Some(trusted.clone())
+        } else {
+            None
+        }
+    }
+}
+
+impl Display for HttpAddressInfo {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::None => f.write_str("none"),
+
+            Self::XForwardFor(trusted) => {
+                f.write_str("x-forward-for [ ")?;
+                for ip in trusted {
+                    write!(f, "{} ", ip)?;
+                }
+                f.write_str("]")
+            }
+            Self::XForwardForAllSourcesTrusted => {
+                f.write_str("x-forward-for [ ALL SOURCES TRUSTED ]")
+            }
+            Self::ProxyV2(trusted) => {
+                f.write_str("proxy-v2 [ ")?;
+                for ip in trusted {
+                    write!(f, "{} ", ip)?;
+                }
+                f.write_str("]")
+            }
+        }
+    }
+}
+
 /// This is the Server Configuration as read from `server.toml` or environment variables.
 ///
 /// Fields noted as "REQUIRED" are required for the server to start, even if they show as optional due to how file parsing works.
@@ -217,7 +322,10 @@ pub struct ServerConfigV2 {
     role: Option<ServerRole>,
     log_level: Option<LogLevel>,
     online_backup: Option<OnlineBackup>,
-    trust_x_forward_for: Option<bool>,
+
+    http_client_address_info: Option<HttpAddressInfo>,
+    ldap_client_address_info: Option<LdapAddressInfo>,
+
     adminbindpath: Option<String>,
     thread_count: Option<usize>,
     maximum_request_size_bytes: Option<usize>,
@@ -490,7 +598,10 @@ pub struct Configuration {
     pub db_fs_type: Option<FsType>,
     pub db_arc_size: Option<usize>,
     pub maximum_request: usize,
-    pub trust_x_forward_for: bool,
+
+    pub http_client_address_info: HttpAddressInfo,
+    pub ldap_client_address_info: LdapAddressInfo,
+
     pub tls_config: Option<TlsConfiguration>,
     pub integration_test_config: Option<Box<IntegrationTestConfig>>,
     pub online_backup: Option<OnlineBackup>,
@@ -522,7 +633,8 @@ impl Configuration {
             db_fs_type: None,
             db_arc_size: None,
             maximum_request: 256 * 1024, // 256k
-            trust_x_forward_for: None,
+            http_client_address_info: HttpAddressInfo::default(),
+            ldap_client_address_info: LdapAddressInfo::default(),
             tls_key: None,
             tls_chain: None,
             tls_client_ca: None,
@@ -547,7 +659,8 @@ impl Configuration {
             db_fs_type: None,
             db_arc_size: None,
             maximum_request: 256 * 1024, // 256k
-            trust_x_forward_for: false,
+            http_client_address_info: HttpAddressInfo::default(),
+            ldap_client_address_info: LdapAddressInfo::default(),
             tls_config: None,
             integration_test_config: None,
             online_backup: None,
@@ -587,7 +700,17 @@ impl fmt::Display for Configuration {
             None => write!(f, "arcsize: AUTO, "),
         }?;
         write!(f, "max request size: {}b, ", self.maximum_request)?;
-        write!(f, "trust X-Forwarded-For: {}, ", self.trust_x_forward_for)?;
+        write!(
+            f,
+            "http client address info: {}, ",
+            self.http_client_address_info
+        )?;
+        write!(
+            f,
+            "ldap client address info: {}, ",
+            self.ldap_client_address_info
+        )?;
+
         write!(f, "with TLS: {}, ", self.tls_config.is_some())?;
         match &self.online_backup {
             Some(bck) => write!(
@@ -642,7 +765,8 @@ pub struct ConfigurationBuilder {
     db_fs_type: Option<FsType>,
     db_arc_size: Option<usize>,
     maximum_request: usize,
-    trust_x_forward_for: Option<bool>,
+    http_client_address_info: HttpAddressInfo,
+    ldap_client_address_info: LdapAddressInfo,
     tls_key: Option<PathBuf>,
     tls_chain: Option<PathBuf>,
     tls_client_ca: Option<PathBuf>,
@@ -691,8 +815,8 @@ impl ConfigurationBuilder {
             self.db_arc_size = env_config.db_arc_size;
         }
 
-        if env_config.trust_x_forward_for.is_some() {
-            self.trust_x_forward_for = env_config.trust_x_forward_for;
+        if env_config.trust_x_forward_for == Some(true) {
+            self.http_client_address_info = HttpAddressInfo::XForwardForAllSourcesTrusted;
         }
 
         if env_config.tls_key.is_some() {
@@ -813,8 +937,8 @@ impl ConfigurationBuilder {
             self.db_arc_size = config.db_arc_size;
         }
 
-        if config.trust_x_forward_for.is_some() {
-            self.trust_x_forward_for = config.trust_x_forward_for;
+        if config.trust_x_forward_for == Some(true) {
+            self.http_client_address_info = HttpAddressInfo::XForwardForAllSourcesTrusted;
         }
 
         if config.online_backup.is_some() {
@@ -893,8 +1017,12 @@ impl ConfigurationBuilder {
             self.db_arc_size = config.db_arc_size;
         }
 
-        if config.trust_x_forward_for.is_some() {
-            self.trust_x_forward_for = config.trust_x_forward_for;
+        if let Some(http_client_address_info) = config.http_client_address_info {
+            self.http_client_address_info = http_client_address_info
+        }
+
+        if let Some(ldap_client_address_info) = config.ldap_client_address_info {
+            self.ldap_client_address_info = ldap_client_address_info
         }
 
         if config.online_backup.is_some() {
@@ -930,7 +1058,8 @@ impl ConfigurationBuilder {
             db_fs_type,
             db_arc_size,
             maximum_request,
-            trust_x_forward_for,
+            http_client_address_info,
+            ldap_client_address_info,
             tls_key,
             tls_chain,
             tls_client_ca,
@@ -986,7 +1115,6 @@ impl ConfigurationBuilder {
         let adminbindpath =
             adminbindpath.unwrap_or(env!("KANIDM_SERVER_ADMIN_BIND_PATH").to_string());
         let address = bindaddress.unwrap_or(DEFAULT_SERVER_ADDRESS.to_string());
-        let trust_x_forward_for = trust_x_forward_for.unwrap_or_default();
         let output_mode = output_mode.unwrap_or_default();
         let role = role.unwrap_or(ServerRole::WriteReplica);
         let log_level = log_level.unwrap_or_default();
@@ -1000,7 +1128,8 @@ impl ConfigurationBuilder {
             db_fs_type,
             db_arc_size,
             maximum_request,
-            trust_x_forward_for,
+            http_client_address_info,
+            ldap_client_address_info,
             tls_config,
             online_backup,
             domain,
diff --git a/server/core/src/https/extractors/mod.rs b/server/core/src/https/extractors/mod.rs
index 4d3fd686f..105b4c680 100644
--- a/server/core/src/https/extractors/mod.rs
+++ b/server/core/src/https/extractors/mod.rs
@@ -5,7 +5,6 @@ use axum::{
     http::{
         header::HeaderName, header::AUTHORIZATION as AUTHORISATION, request::Parts, StatusCode,
     },
-    serve::IncomingStream,
     RequestPartsExt,
 };
 
@@ -40,7 +39,8 @@ impl FromRequestParts<ServerState> for TrustedClientIp {
         state: &ServerState,
     ) -> Result<Self, Self::Rejection> {
         let ConnectInfo(ClientConnInfo {
-            addr,
+            connection_addr,
+            client_addr,
             client_cert: _,
         }) = parts
             .extract::<ConnectInfo<ClientConnInfo>>()
@@ -53,7 +53,13 @@ impl FromRequestParts<ServerState> for TrustedClientIp {
                 )
             })?;
 
-        let ip_addr = if state.trust_x_forward_for {
+        let trust_x_forward_for = state
+            .trust_x_forward_for_ips
+            .as_ref()
+            .map(|range| range.contains(&connection_addr.ip()))
+            .unwrap_or_default();
+
+        let ip_addr = if trust_x_forward_for {
             if let Some(x_forward_for) = parts.headers.get(X_FORWARDED_FOR_HEADER) {
                 // X forward for may be comma separated.
                 let first = x_forward_for
@@ -75,10 +81,14 @@ impl FromRequestParts<ServerState> for TrustedClientIp {
                     )
                 })?
             } else {
-                addr.ip()
+                client_addr.ip()
             }
         } else {
-            addr.ip()
+            // This can either be the client_addr == connection_addr if there are
+            // no ip address trust sources, or this is the value as reported by
+            // proxy protocol header. If the proxy protocol header is used, then
+            // trust_x_forward_for can never have been true so we catch here.
+            client_addr.ip()
         };
 
         Ok(TrustedClientIp(ip_addr))
@@ -97,7 +107,11 @@ impl FromRequestParts<ServerState> for VerifiedClientInformation {
         parts: &mut Parts,
         state: &ServerState,
     ) -> Result<Self, Self::Rejection> {
-        let ConnectInfo(ClientConnInfo { addr, client_cert }) = parts
+        let ConnectInfo(ClientConnInfo {
+            connection_addr,
+            client_addr,
+            client_cert,
+        }) = parts
             .extract::<ConnectInfo<ClientConnInfo>>()
             .await
             .map_err(|_| {
@@ -108,7 +122,13 @@ impl FromRequestParts<ServerState> for VerifiedClientInformation {
                 )
             })?;
 
-        let ip_addr = if state.trust_x_forward_for {
+        let trust_x_forward_for = state
+            .trust_x_forward_for_ips
+            .as_ref()
+            .map(|range| range.contains(&connection_addr.ip()))
+            .unwrap_or_default();
+
+        let ip_addr = if trust_x_forward_for {
             if let Some(x_forward_for) = parts.headers.get(X_FORWARDED_FOR_HEADER) {
                 // X forward for may be comma separated.
                 let first = x_forward_for
@@ -130,10 +150,10 @@ impl FromRequestParts<ServerState> for VerifiedClientInformation {
                     )
                 })?
             } else {
-                addr.ip()
+                client_addr.ip()
             }
         } else {
-            addr.ip()
+            client_addr.ip()
         };
 
         let (basic_authz, bearer_token) = if let Some(header) = parts.headers.get(AUTHORISATION) {
@@ -201,30 +221,30 @@ impl FromRequestParts<ServerState> for DomainInfo {
 
 #[derive(Debug, Clone)]
 pub struct ClientConnInfo {
-    pub addr: SocketAddr,
+    /// This is the address that is *connected* to Kanidm right now
+    /// for this operation.
+    #[allow(dead_code)]
+    pub connection_addr: SocketAddr,
+    /// This is the client address as reported by a remote IP source
+    /// such as x-forward-for or the PROXY protocol header
+    pub client_addr: SocketAddr,
     // Only set if the certificate is VALID
     pub client_cert: Option<ClientCertInfo>,
 }
 
+// This is the normal way that our extractors get the ip info
 impl Connected<ClientConnInfo> for ClientConnInfo {
     fn connect_info(target: ClientConnInfo) -> Self {
         target
     }
 }
 
+// This is only used for plaintext http - in other words, integration tests only.
 impl Connected<SocketAddr> for ClientConnInfo {
-    fn connect_info(addr: SocketAddr) -> Self {
+    fn connect_info(connection_addr: SocketAddr) -> Self {
         ClientConnInfo {
-            addr,
-            client_cert: None,
-        }
-    }
-}
-
-impl Connected<IncomingStream<'_>> for ClientConnInfo {
-    fn connect_info(target: IncomingStream<'_>) -> Self {
-        ClientConnInfo {
-            addr: target.remote_addr(),
+            client_addr: connection_addr,
+            connection_addr,
             client_cert: None,
         }
     }
diff --git a/server/core/src/https/mod.rs b/server/core/src/https/mod.rs
index 645f35202..1af317b03 100644
--- a/server/core/src/https/mod.rs
+++ b/server/core/src/https/mod.rs
@@ -17,9 +17,8 @@ mod views;
 use self::extractors::ClientConnInfo;
 use self::javascript::*;
 use crate::actors::{QueryServerReadV1, QueryServerWriteV1};
-use crate::config::{Configuration, ServerRole};
+use crate::config::{AddressSet, Configuration, ServerRole};
 use crate::CoreAction;
-
 use axum::{
     body::Body,
     extract::connect_info::IntoMakeServiceWithConnectInfo,
@@ -29,22 +28,28 @@ use axum::{
     routing::*,
     Router,
 };
-
 use axum_extra::extract::cookie::CookieJar;
 use compact_jwt::{error::JwtError, JwsCompact, JwsHs256Signer, JwsVerifier};
 use futures::pin_mut;
+use haproxy_protocol::{ProxyHdrV2, RemoteAddress};
+use hashbrown::HashSet;
 use hyper::body::Incoming;
 use hyper_util::rt::{TokioExecutor, TokioIo};
+use kanidm_lib_crypto::x509_cert::{der::Decode, x509_public_key_s256, Certificate};
 use kanidm_proto::{constants::KSESSIONID, internal::COOKIE_AUTH_SESSION_ID};
 use kanidmd_lib::{idm::ClientCertInfo, status::StatusActor};
 use openssl::ssl::{Ssl, SslAcceptor};
-
-use kanidm_lib_crypto::x509_cert::{der::Decode, x509_public_key_s256, Certificate};
-
 use serde::de::DeserializeOwned;
 use sketching::*;
 use std::fmt::Write;
+use std::io::ErrorKind;
+use std::net::IpAddr;
+use std::path::PathBuf;
+use std::pin::Pin;
+use std::sync::Arc;
+use std::{net::SocketAddr, str::FromStr};
 use tokio::{
+    io::{AsyncRead, AsyncWrite},
     net::{TcpListener, TcpStream},
     sync::broadcast,
     sync::mpsc,
@@ -56,11 +61,6 @@ use tower_http::{services::ServeDir, trace::TraceLayer};
 use url::Url;
 use uuid::Uuid;
 
-use std::io::ErrorKind;
-use std::path::PathBuf;
-use std::pin::Pin;
-use std::{net::SocketAddr, str::FromStr};
-
 #[derive(Clone)]
 pub struct ServerState {
     pub(crate) status_ref: &'static StatusActor,
@@ -68,7 +68,7 @@ pub struct ServerState {
     pub(crate) qe_r_ref: &'static QueryServerReadV1,
     // Store the token management parts.
     pub(crate) jws_signer: JwsHs256Signer,
-    pub(crate) trust_x_forward_for: bool,
+    pub(crate) trust_x_forward_for_ips: Option<Arc<AddressSet>>,
     pub(crate) csp_header: HeaderValue,
     pub(crate) origin: Url,
     pub(crate) domain: String,
@@ -211,7 +211,15 @@ pub async fn create_https_server(
         error!(?err, "Unable to generate content security policy");
     })?;
 
-    let trust_x_forward_for = config.trust_x_forward_for;
+    let trust_x_forward_for_ips = config
+        .http_client_address_info
+        .trusted_x_forward_for()
+        .map(Arc::new);
+
+    let trusted_proxy_v2_ips = config
+        .http_client_address_info
+        .trusted_proxy_v2()
+        .map(Arc::new);
 
     let origin = Url::parse(&config.origin)
         // Should be impossible!
@@ -224,7 +232,7 @@ pub async fn create_https_server(
         qe_w_ref,
         qe_r_ref,
         jws_signer,
-        trust_x_forward_for,
+        trust_x_forward_for_ips,
         csp_header,
         origin,
         domain: config.domain.clone(),
@@ -321,35 +329,41 @@ pub async fn create_https_server(
 
     info!("Starting the web server...");
 
-    match maybe_tls_acceptor {
-        Some(tls_acceptor) => {
-            let listener = match TcpListener::bind(addr).await {
-                Ok(l) => l,
-                Err(err) => {
-                    error!(?err, "Failed to bind tcp listener");
-                    return Err(());
-                }
-            };
-            Ok(task::spawn(server_loop(
-                tls_acceptor,
-                listener,
-                app,
-                rx,
-                server_message_tx,
-                tls_acceptor_reload_rx,
-            )))
+    let listener = match TcpListener::bind(addr).await {
+        Ok(l) => l,
+        Err(err) => {
+            error!(?err, "Failed to bind tcp listener");
+            return Err(());
         }
-        None => Ok(task::spawn(server_loop_plaintext(addr, app, rx))),
+    };
+
+    match maybe_tls_acceptor {
+        Some(tls_acceptor) => Ok(task::spawn(server_tls_loop(
+            tls_acceptor,
+            listener,
+            app,
+            rx,
+            server_message_tx,
+            tls_acceptor_reload_rx,
+            trusted_proxy_v2_ips,
+        ))),
+        None => Ok(task::spawn(server_plaintext_loop(
+            listener,
+            app,
+            rx,
+            trusted_proxy_v2_ips,
+        ))),
     }
 }
 
-async fn server_loop(
+async fn server_tls_loop(
     mut tls_acceptor: SslAcceptor,
     listener: TcpListener,
     app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
     mut rx: broadcast::Receiver<CoreAction>,
     server_message_tx: broadcast::Sender<CoreAction>,
     mut tls_acceptor_reload_rx: mpsc::Receiver<SslAcceptor>,
+    trusted_proxy_v2_ips: Option<Arc<HashSet<IpAddr>>>,
 ) {
     pin_mut!(listener);
 
@@ -365,7 +379,7 @@ async fn server_loop(
                     Ok((stream, addr)) => {
                         let tls_acceptor = tls_acceptor.clone();
                         let app = app.clone();
-                        task::spawn(handle_conn(tls_acceptor, stream, app, addr));
+                        task::spawn(handle_tls_conn(tls_acceptor, stream, app, addr, trusted_proxy_v2_ips.clone()));
                     }
                     Err(err) => {
                         error!("Web server exited with {:?}", err);
@@ -386,24 +400,33 @@ async fn server_loop(
     info!("Stopped {}", super::TaskName::HttpsServer);
 }
 
-async fn server_loop_plaintext(
-    addr: SocketAddr,
+async fn server_plaintext_loop(
+    listener: TcpListener,
     app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
     mut rx: broadcast::Receiver<CoreAction>,
+    trusted_proxy_v2_ips: Option<Arc<HashSet<IpAddr>>>,
 ) {
-    let listener = axum_server::bind(addr).serve(app);
-
     pin_mut!(listener);
 
     loop {
         tokio::select! {
             Ok(action) = rx.recv() => {
                 match action {
-                    CoreAction::Shutdown =>
-                        break,
+                    CoreAction::Shutdown => break,
+                }
+            }
+            accept = listener.accept() => {
+                match accept {
+                    Ok((stream, addr)) => {
+                        let app = app.clone();
+                        task::spawn(handle_conn(stream, app, addr, trusted_proxy_v2_ips.clone()));
+                    }
+                    Err(err) => {
+                        error!("Web server exited with {:?}", err);
+                        break;
+                    }
                 }
             }
-            _ = &mut listener => {}
         }
     }
 
@@ -412,11 +435,38 @@ async fn server_loop_plaintext(
 
 /// This handles an individual connection.
 pub(crate) async fn handle_conn(
+    stream: TcpStream,
+    app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
+    connection_addr: SocketAddr,
+    trusted_proxy_v2_ips: Option<Arc<HashSet<IpAddr>>>,
+) -> Result<(), std::io::Error> {
+    let (stream, client_addr) =
+        process_client_addr(stream, connection_addr, trusted_proxy_v2_ips).await?;
+
+    let client_conn_info = ClientConnInfo {
+        connection_addr,
+        client_addr,
+        client_cert: None,
+    };
+
+    // Hyper has its own `AsyncRead` and `AsyncWrite` traits and doesn't use tokio.
+    // `TokioIo` converts between them.
+    let stream = TokioIo::new(stream);
+
+    process_client_hyper(stream, app, client_conn_info).await
+}
+
+/// This handles an individual connection.
+pub(crate) async fn handle_tls_conn(
     acceptor: SslAcceptor,
     stream: TcpStream,
-    mut app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
-    addr: SocketAddr,
+    app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
+    connection_addr: SocketAddr,
+    trusted_proxy_v2_ips: Option<Arc<HashSet<IpAddr>>>,
 ) -> Result<(), std::io::Error> {
+    let (stream, client_addr) =
+        process_client_addr(stream, connection_addr, trusted_proxy_v2_ips).await?;
+
     let ssl = Ssl::new(acceptor.context()).map_err(|e| {
         error!("Failed to create TLS context: {:?}", e);
         std::io::Error::from(ErrorKind::ConnectionAborted)
@@ -459,42 +509,17 @@ pub(crate) async fn handle_conn(
                 None
             };
 
-            let client_conn_info = ClientConnInfo { addr, client_cert };
-
-            debug!(?client_conn_info);
-
-            let svc = axum_server::service::MakeService::<ClientConnInfo, hyper::Request<Body>>::make_service(
-                &mut app,
-                client_conn_info,
-            );
-
-            let svc = svc.await.map_err(|e| {
-                error!("Failed to build HTTP response: {:?}", e);
-                std::io::Error::from(ErrorKind::Other)
-            })?;
+            let client_conn_info = ClientConnInfo {
+                connection_addr,
+                client_addr,
+                client_cert,
+            };
 
             // Hyper has its own `AsyncRead` and `AsyncWrite` traits and doesn't use tokio.
             // `TokioIo` converts between them.
             let stream = TokioIo::new(tls_stream);
 
-            // Hyper also has its own `Service` trait and doesn't use tower. We can use
-            // `hyper::service::service_fn` to create a hyper `Service` that calls our app through
-            // `tower::Service::call`.
-            let hyper_service = hyper::service::service_fn(move |request: Request<Incoming>| {
-                // We have to clone `tower_service` because hyper's `Service` uses `&self` whereas
-                // tower's `Service` requires `&mut self`.
-                //
-                // We don't need to call `poll_ready` since `Router` is always ready.
-                svc.clone().call(request)
-            });
-
-            hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
-                .serve_connection_with_upgrades(stream, hyper_service)
-                .await
-                .map_err(|e| {
-                    debug!("Failed to complete connection: {:?}", e);
-                    std::io::Error::from(ErrorKind::ConnectionAborted)
-                })
+            process_client_hyper(stream, app, client_conn_info).await
         }
         Err(error) => {
             trace!("Failed to handle connection: {:?}", error);
@@ -502,3 +527,83 @@ pub(crate) async fn handle_conn(
         }
     }
 }
+
+async fn process_client_addr(
+    stream: TcpStream,
+    connection_addr: SocketAddr,
+    trusted_proxy_v2_ips: Option<Arc<HashSet<IpAddr>>>,
+) -> Result<(TcpStream, SocketAddr), std::io::Error> {
+    let enable_proxy_v2_hdr = trusted_proxy_v2_ips
+        .map(|trusted| trusted.contains(&connection_addr.ip()))
+        .unwrap_or_default();
+
+    let (stream, client_addr) = if enable_proxy_v2_hdr {
+        match ProxyHdrV2::parse_from_read(stream).await {
+            Ok((stream, hdr)) => {
+                let remote_socket_addr = match hdr.to_remote_addr() {
+                    RemoteAddress::Local => {
+                        debug!("PROXY protocol liveness check - will not contain client data");
+                        return Err(std::io::Error::from(ErrorKind::ConnectionAborted));
+                    }
+                    RemoteAddress::TcpV4 { src, dst: _ } => SocketAddr::from(src),
+                    RemoteAddress::TcpV6 { src, dst: _ } => SocketAddr::from(src),
+                    remote_addr => {
+                        error!(?remote_addr, "remote address in proxy header is invalid");
+                        return Err(std::io::Error::from(ErrorKind::ConnectionAborted));
+                    }
+                };
+
+                (stream, remote_socket_addr)
+            }
+            Err(err) => {
+                error!(?connection_addr, ?err, "Unable to process proxy v2 header");
+                return Err(std::io::Error::from(ErrorKind::ConnectionAborted));
+            }
+        }
+    } else {
+        (stream, connection_addr)
+    };
+
+    Ok((stream, client_addr))
+}
+
+async fn process_client_hyper<T>(
+    stream: TokioIo<T>,
+    mut app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
+    client_conn_info: ClientConnInfo,
+) -> Result<(), std::io::Error>
+where
+    T: AsyncRead + AsyncWrite + std::marker::Unpin + std::marker::Send + 'static,
+{
+    debug!(?client_conn_info);
+
+    let svc =
+        axum_server::service::MakeService::<ClientConnInfo, hyper::Request<Body>>::make_service(
+            &mut app,
+            client_conn_info,
+        );
+
+    let svc = svc.await.map_err(|e| {
+        error!("Failed to build HTTP response: {:?}", e);
+        std::io::Error::from(ErrorKind::Other)
+    })?;
+
+    // Hyper also has its own `Service` trait and doesn't use tower. We can use
+    // `hyper::service::service_fn` to create a hyper `Service` that calls our app through
+    // `tower::Service::call`.
+    let hyper_service = hyper::service::service_fn(move |request: Request<Incoming>| {
+        // We have to clone `tower_service` because hyper's `Service` uses `&self` whereas
+        // tower's `Service` requires `&mut self`.
+        //
+        // We don't need to call `poll_ready` since `Router` is always ready.
+        svc.clone().call(request)
+    });
+
+    hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
+        .serve_connection_with_upgrades(stream, hyper_service)
+        .await
+        .map_err(|e| {
+            debug!("Failed to complete connection: {:?}", e);
+            std::io::Error::from(ErrorKind::ConnectionAborted)
+        })
+}
diff --git a/server/core/src/ldaps.rs b/server/core/src/ldaps.rs
index ca57a7e1b..9ce9f01b7 100644
--- a/server/core/src/ldaps.rs
+++ b/server/core/src/ldaps.rs
@@ -2,14 +2,17 @@ use crate::actors::QueryServerReadV1;
 use crate::CoreAction;
 use futures_util::sink::SinkExt;
 use futures_util::stream::StreamExt;
+use haproxy_protocol::{ProxyHdrV2, RemoteAddress};
+use hashbrown::HashSet;
 use kanidmd_lib::idm::ldap::{LdapBoundToken, LdapResponseState};
 use kanidmd_lib::prelude::*;
 use ldap3_proto::proto::LdapMsg;
 use ldap3_proto::LdapCodec;
 use openssl::ssl::{Ssl, SslAcceptor};
-use std::net;
+use std::net::{IpAddr, SocketAddr};
 use std::pin::Pin;
 use std::str::FromStr;
+use std::sync::Arc;
 use tokio::io::{AsyncRead, AsyncWrite};
 use tokio::net::{TcpListener, TcpStream};
 use tokio::sync::broadcast;
@@ -33,7 +36,7 @@ impl LdapSession {
 #[instrument(name = "ldap-request", skip(client_address, qe_r_ref))]
 async fn client_process_msg(
     uat: Option<LdapBoundToken>,
-    client_address: net::SocketAddr,
+    client_address: SocketAddr,
     protomsg: LdapMsg,
     qe_r_ref: &'static QueryServerReadV1,
 ) -> Option<LdapResponseState> {
@@ -50,7 +53,8 @@ async fn client_process_msg(
 
 async fn client_process<STREAM>(
     stream: STREAM,
-    client_address: net::SocketAddr,
+    client_address: SocketAddr,
+    connection_address: SocketAddr,
     qe_r_ref: &'static QueryServerReadV1,
 ) where
     STREAM: AsyncRead + AsyncWrite,
@@ -67,6 +71,8 @@ async fn client_process<STREAM>(
         let uat = session.uat.clone();
         let caddr = client_address;
 
+        debug!(?client_address, ?connection_address);
+
         match client_process_msg(uat, caddr, protomsg, qe_r_ref).await {
             // I'd really have liked to have put this near the [LdapResponseState::Bind] but due
             // to the handing of `audit` it isn't possible due to borrows, etc.
@@ -112,28 +118,65 @@ async fn client_process<STREAM>(
 }
 
 async fn client_tls_accept(
-    tcpstream: TcpStream,
+    stream: TcpStream,
     tls_acceptor: SslAcceptor,
-    client_socket_addr: net::SocketAddr,
+    connection_addr: SocketAddr,
     qe_r_ref: &'static QueryServerReadV1,
+    trusted_proxy_v2_ips: Option<Arc<HashSet<IpAddr>>>,
 ) {
+    let enable_proxy_v2_hdr = trusted_proxy_v2_ips
+        .map(|trusted| trusted.contains(&connection_addr.ip()))
+        .unwrap_or_default();
+
+    let (stream, client_addr) = if enable_proxy_v2_hdr {
+        match ProxyHdrV2::parse_from_read(stream).await {
+            Ok((stream, hdr)) => {
+                let remote_socket_addr = match hdr.to_remote_addr() {
+                    RemoteAddress::Local => {
+                        debug!("PROXY protocol liveness check - will not contain client data");
+                        return;
+                    }
+                    RemoteAddress::TcpV4 { src, dst: _ } => SocketAddr::from(src),
+                    RemoteAddress::TcpV6 { src, dst: _ } => SocketAddr::from(src),
+                    remote_addr => {
+                        error!(?remote_addr, "remote address in proxy header is invalid");
+                        return;
+                    }
+                };
+
+                (stream, remote_socket_addr)
+            }
+            Err(err) => {
+                error!(?connection_addr, ?err, "Unable to process proxy v2 header");
+                return;
+            }
+        }
+    } else {
+        (stream, connection_addr)
+    };
+
     // Start the event
     // From the parameters we need to create an SslContext.
     let mut tlsstream = match Ssl::new(tls_acceptor.context())
-        .and_then(|tls_obj| SslStream::new(tls_obj, tcpstream))
+        .and_then(|tls_obj| SslStream::new(tls_obj, stream))
     {
         Ok(ta) => ta,
         Err(err) => {
-            error!(?err, %client_socket_addr, "LDAP TLS setup error");
+            error!(?err, %client_addr, %connection_addr, "LDAP TLS setup error");
             return;
         }
     };
     if let Err(err) = SslStream::accept(Pin::new(&mut tlsstream)).await {
-        error!(?err, %client_socket_addr, "LDAP TLS accept error");
+        error!(?err, %client_addr, %connection_addr, "LDAP TLS accept error");
         return;
     };
 
-    tokio::spawn(client_process(tlsstream, client_socket_addr, qe_r_ref));
+    tokio::spawn(client_process(
+        tlsstream,
+        client_addr,
+        connection_addr,
+        qe_r_ref,
+    ));
 }
 
 /// TLS LDAP Listener, hands off to [client_tls_accept]
@@ -143,6 +186,7 @@ async fn ldap_tls_acceptor(
     qe_r_ref: &'static QueryServerReadV1,
     mut rx: broadcast::Receiver<CoreAction>,
     mut tls_acceptor_reload_rx: mpsc::Receiver<SslAcceptor>,
+    trusted_proxy_v2_ips: Option<Arc<HashSet<IpAddr>>>,
 ) {
     loop {
         tokio::select! {
@@ -155,7 +199,7 @@ async fn ldap_tls_acceptor(
                 match accept_result {
                     Ok((tcpstream, client_socket_addr)) => {
                         let clone_tls_acceptor = tls_acceptor.clone();
-                        tokio::spawn(client_tls_accept(tcpstream, clone_tls_acceptor, client_socket_addr, qe_r_ref));
+                        tokio::spawn(client_tls_accept(tcpstream, clone_tls_acceptor, client_socket_addr, qe_r_ref, trusted_proxy_v2_ips.clone()));
                     }
                     Err(err) => {
                         warn!(?err, "LDAP acceptor error, continuing");
@@ -187,7 +231,7 @@ async fn ldap_plaintext_acceptor(
             accept_result = listener.accept() => {
                 match accept_result {
                     Ok((tcpstream, client_socket_addr)) => {
-                        tokio::spawn(client_process(tcpstream, client_socket_addr, qe_r_ref));
+                        tokio::spawn(client_process(tcpstream, client_socket_addr, client_socket_addr, qe_r_ref));
                     }
                     Err(e) => {
                         error!("LDAP acceptor error, continuing -> {:?}", e);
@@ -205,6 +249,7 @@ pub(crate) async fn create_ldap_server(
     qe_r_ref: &'static QueryServerReadV1,
     rx: broadcast::Receiver<CoreAction>,
     tls_acceptor_reload_rx: mpsc::Receiver<SslAcceptor>,
+    trusted_proxy_v2_ips: Option<HashSet<IpAddr>>,
 ) -> Result<tokio::task::JoinHandle<()>, ()> {
     if address.starts_with(":::") {
         // takes :::xxxx to xxxx
@@ -212,7 +257,7 @@ pub(crate) async fn create_ldap_server(
         error!("Address '{}' looks like an attempt to wildcard bind with IPv6 on port {} - please try using ldapbindaddress = '[::]:{}'", address, port, port);
     };
 
-    let addr = net::SocketAddr::from_str(address).map_err(|e| {
+    let addr = SocketAddr::from_str(address).map_err(|e| {
         error!("Could not parse LDAP server address {} -> {:?}", address, e);
     })?;
 
@@ -223,6 +268,8 @@ pub(crate) async fn create_ldap_server(
         );
     })?;
 
+    let trusted_proxy_v2_ips = trusted_proxy_v2_ips.map(Arc::new);
+
     let ldap_acceptor_handle = match opt_ssl_acceptor {
         Some(ssl_acceptor) => {
             info!("Starting LDAPS interface ldaps://{} ...", address);
@@ -233,6 +280,7 @@ pub(crate) async fn create_ldap_server(
                 qe_r_ref,
                 rx,
                 tls_acceptor_reload_rx,
+                trusted_proxy_v2_ips,
             ))
         }
         None => tokio::spawn(ldap_plaintext_acceptor(listener, qe_r_ref, rx)),
diff --git a/server/core/src/lib.rs b/server/core/src/lib.rs
index 1117f446a..392668ba8 100644
--- a/server/core/src/lib.rs
+++ b/server/core/src/lib.rs
@@ -1087,6 +1087,7 @@ pub async fn create_server_core(
                 server_read_ref,
                 broadcast_tx.subscribe(),
                 ldap_tls_acceptor_reload_rx,
+                config.ldap_client_address_info.trusted_proxy_v2(),
             )
             .await?;
             Some(h)
diff --git a/server/lib/src/be/mod.rs b/server/lib/src/be/mod.rs
index e2ce78acc..e6b68ea18 100644
--- a/server/lib/src/be/mod.rs
+++ b/server/lib/src/be/mod.rs
@@ -2417,7 +2417,7 @@ mod tests {
             let lims = Limits::unlimited();
 
             let r = be.search(&lims, &filt);
-            assert!(r.expect("Search failed!").len() == 0);
+            assert!(r.expect("Search failed!").is_empty());
         });
     }
 
diff --git a/server/lib/src/credential/totp.rs b/server/lib/src/credential/totp.rs
index f6ae0fd1e..e887f90e9 100644
--- a/server/lib/src/credential/totp.rs
+++ b/server/lib/src/credential/totp.rs
@@ -145,8 +145,8 @@ impl Totp {
 
     // Create a new token with secure key and algo.
     pub fn generate_secure(step: u64) -> Self {
-        let mut rng = rand::thread_rng();
-        let secret: Vec<u8> = (0..SECRET_SIZE_BYTES).map(|_| rng.gen()).collect();
+        let mut rng = rand::rng();
+        let secret: Vec<u8> = (0..SECRET_SIZE_BYTES).map(|_| rng.random()).collect();
         let algo = TotpAlgo::Sha256;
         let digits = TotpDigits::Six;
         Totp {
diff --git a/server/lib/src/idm/credupdatesession.rs b/server/lib/src/idm/credupdatesession.rs
index 0453fdbdc..c30518d50 100644
--- a/server/lib/src/idm/credupdatesession.rs
+++ b/server/lib/src/idm/credupdatesession.rs
@@ -17,6 +17,7 @@ use webauthn_rs::prelude::{
     AttestedPasskey as AttestedPasskeyV4, AttestedPasskeyRegistration, CreationChallengeResponse,
     Passkey as PasskeyV4, PasskeyRegistration, RegisterPublicKeyCredential, WebauthnError,
 };
+use zxcvbn::{zxcvbn, Score};
 
 use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
 use crate::credential::{BackupCodes, Credential};
@@ -1663,23 +1664,14 @@ impl IdmServerCredUpdateTransaction<'_> {
         }
 
         // does the password pass zxcvbn?
-        let entropy = zxcvbn::zxcvbn(cleartext, related_inputs).map_err(|e| {
-            admin_error!("zxcvbn check failure (password empty?) {:?}", e);
-            // Return some generic feedback when the password is this bad.
-            PasswordQuality::Feedback(vec![
-                PasswordFeedback::UseAFewWordsAvoidCommonPhrases,
-                PasswordFeedback::AddAnotherWordOrTwo,
-                PasswordFeedback::NoNeedForSymbolsDigitsOrUppercaseLetters,
-            ])
-        })?;
+        let entropy = zxcvbn(cleartext, related_inputs);
 
         // PW's should always be enforced as strong as possible.
-        if entropy.score() < 4 {
+        if entropy.score() < Score::Four {
             // The password is too week as per:
             // https://docs.rs/zxcvbn/2.0.0/zxcvbn/struct.Entropy.html
             let feedback: zxcvbn::feedback::Feedback = entropy
                 .feedback()
-                .as_ref()
                 .ok_or(OperationError::InvalidState)
                 .cloned()
                 .map_err(|e| {
@@ -3405,7 +3397,7 @@ mod tests {
         assert!(
             matches!(
                 c_status.mfaregstate,
-                MfaRegStateStatus::TotpNameTryAgain(ref val) if val == ""
+                MfaRegStateStatus::TotpNameTryAgain(ref val) if val.is_empty()
             ),
             "{:?}",
             c_status.mfaregstate
diff --git a/server/lib/src/idm/ldap.rs b/server/lib/src/idm/ldap.rs
index 4b7a4e37e..6275c987b 100644
--- a/server/lib/src/idm/ldap.rs
+++ b/server/lib/src/idm/ldap.rs
@@ -1209,7 +1209,7 @@ mod tests {
         // Searching a malformed spn shouldn't cause the query to fail
         let sr = SearchRequest {
             msgid: 1,
-            base: format!("dc=example,dc=com"),
+            base: "dc=example,dc=com".to_string(),
             scope: LdapSearchScope::Subtree,
             filter: LdapFilter::Or(vec![
                 LdapFilter::Equality(Attribute::Name.to_string(), usr_name.to_string()),
@@ -1232,7 +1232,7 @@ mod tests {
 
         let sr = SearchRequest {
             msgid: 1,
-            base: format!("dc=example,dc=com"),
+            base: "dc=example,dc=com".to_string(),
             scope: LdapSearchScope::Subtree,
             filter: LdapFilter::And(vec![
                 LdapFilter::Equality(Attribute::Name.to_string(), usr_name.to_string()),
diff --git a/server/lib/src/idm/oauth2.rs b/server/lib/src/idm/oauth2.rs
index 9628e1ea4..2905abbd2 100644
--- a/server/lib/src/idm/oauth2.rs
+++ b/server/lib/src/idm/oauth2.rs
@@ -2992,11 +2992,12 @@ fn validate_scopes(req_scopes: &BTreeSet<String>) -> Result<(), Oauth2Error> {
 #[cfg(any(feature = "dev-oauth2-device-flow", test))]
 #[allow(dead_code)]
 fn gen_device_code() -> Result<[u8; 16], Oauth2Error> {
-    let mut rng = rand::thread_rng();
+    use rand::TryRngCore;
+
+    let mut rng = rand::rng();
     let mut result = [0u8; 16];
     // doing it here because of feature-shenanigans.
-    use rand::Rng;
-    if let Err(err) = rng.try_fill(&mut result) {
+    if let Err(err) = rng.try_fill_bytes(&mut result) {
         error!("Failed to generate device code! {:?}", err);
         return Err(Oauth2Error::ServerError(OperationError::Backend));
     }
@@ -3009,8 +3010,8 @@ fn gen_device_code() -> Result<[u8; 16], Oauth2Error> {
 /// Returns (xxx-yyy-zzz, digits) where one's the human-facing code, the other is what we store in the DB.
 fn gen_user_code() -> (String, u32) {
     use rand::Rng;
-    let mut rng = rand::thread_rng();
-    let num: u32 = rng.gen_range(0..=999999999);
+    let mut rng = rand::rng();
+    let num: u32 = rng.random_range(0..=999999999);
     let result = format!("{:09}", num);
     (
         format!("{}-{}-{}", &result[0..3], &result[3..6], &result[6..9]),
@@ -3100,6 +3101,7 @@ mod tests {
             $code_challenge:expr,
             $scope:expr
         ) => {{
+            #[allow(clippy::unnecessary_to_owned)]
             let scope: BTreeSet<String> = $scope.split(" ").map(|s| s.to_string()).collect();
 
             let auth_req = AuthorisationRequest {
@@ -7312,10 +7314,7 @@ mod tests {
             &Url::parse(example_is_not_local)
                 .expect("Failed to parse example.com as a host?")
                 .host()
-                .expect(&format!(
-                    "Couldn't get a host from {}",
-                    example_is_not_local
-                ))
+                .unwrap_or_else(|| panic!("Couldn't get a host from {}", example_is_not_local))
         ));
 
         let test_urls = [
diff --git a/server/lib/src/idm/server.rs b/server/lib/src/idm/server.rs
index ed55cf69c..d047bb319 100644
--- a/server/lib/src/idm/server.rs
+++ b/server/lib/src/idm/server.rs
@@ -21,6 +21,7 @@ use tokio::sync::{Mutex, Semaphore};
 use tracing::trace;
 use url::Url;
 use webauthn_rs::prelude::{Webauthn, WebauthnBuilder};
+use zxcvbn::{zxcvbn, Score};
 
 use super::event::ReadBackupCodeEvent;
 use super::ldap::{LdapBoundToken, LdapSession};
@@ -235,7 +236,7 @@ impl IdmServer {
         let qs_read = self.qs.read().await?;
 
         let mut sid = [0; 4];
-        let mut rng = StdRng::from_entropy();
+        let mut rng = StdRng::from_os_rng();
         rng.fill(&mut sid);
 
         Ok(IdmServerAuthTransaction {
@@ -278,7 +279,7 @@ impl IdmServer {
         let qs_write = self.qs.write(ts).await?;
 
         let mut sid = [0; 4];
-        let mut rng = StdRng::from_entropy();
+        let mut rng = StdRng::from_os_rng();
         rng.fill(&mut sid);
 
         Ok(IdmServerProxyWriteTransaction {
@@ -1657,18 +1658,14 @@ impl IdmServerProxyWriteTransaction<'_> {
 
         // does the password pass zxcvbn?
 
-        let entropy = zxcvbn::zxcvbn(cleartext, related_inputs).map_err(|e| {
-            admin_error!("zxcvbn check failure (password empty?) {:?}", e);
-            OperationError::PasswordQuality(vec![PasswordFeedback::TooShort(PW_MIN_LENGTH)])
-        })?;
+        let entropy = zxcvbn(cleartext, related_inputs);
 
         // Unix PW's are a single factor, so we enforce good pws
-        if entropy.score() < 4 {
+        if entropy.score() < Score::Four {
             // The password is too week as per:
             // https://docs.rs/zxcvbn/2.0.0/zxcvbn/struct.Entropy.html
             let feedback: zxcvbn::feedback::Feedback = entropy
                 .feedback()
-                .as_ref()
                 .ok_or(OperationError::InvalidState)
                 .cloned()
                 .inspect_err(|err| {
diff --git a/server/lib/src/repl/entry.rs b/server/lib/src/repl/entry.rs
index 4f7c26dfb..75fed5f50 100644
--- a/server/lib/src/repl/entry.rs
+++ b/server/lib/src/repl/entry.rs
@@ -217,9 +217,9 @@ impl EntryChangeState {
     }
 
     #[cfg(test)]
-    pub(crate) fn get_attr_cid(&self, attr: Attribute) -> Option<&Cid> {
+    pub(crate) fn get_attr_cid(&self, attr: &Attribute) -> Option<&Cid> {
         match &self.st {
-            State::Live { at: _, changes } => changes.get(&attr),
+            State::Live { at: _, changes } => changes.get(attr),
             State::Tombstone { at: _ } => None,
         }
     }
diff --git a/server/lib/src/repl/tests.rs b/server/lib/src/repl/tests.rs
index 5da89fd53..951df6254 100644
--- a/server/lib/src/repl/tests.rs
+++ b/server/lib/src/repl/tests.rs
@@ -705,7 +705,7 @@ async fn test_repl_increment_basic_deleted_attr(server_a: &QueryServer, server_b
     let e1_cs = e1.get_changestate();
     let e2_cs = e2.get_changestate();
     assert_eq!(e1_cs, e2_cs);
-    assert!(e1_cs.get_attr_cid(Attribute::Description).is_some());
+    assert!(e1_cs.get_attr_cid(&Attribute::Description).is_some());
 
     server_b_txn.commit().expect("Failed to commit");
     drop(server_a_txn);
diff --git a/server/lib/src/server/scim.rs b/server/lib/src/server/scim.rs
index be529e8be..ef375f523 100644
--- a/server/lib/src/server/scim.rs
+++ b/server/lib/src/server/scim.rs
@@ -186,7 +186,7 @@ mod tests {
 
         match desc {
             ScimValueKanidm::String(gdesc) if gdesc == "Group Description" => {}
-            _ => assert!(false),
+            _ => unreachable!("Expected a string"),
         };
 
         // null removes attr
@@ -201,7 +201,7 @@ mod tests {
                 .expect("Failed to resolve data type");
 
         let updated_entry = server_txn.scim_put(put_event).expect("Failed to put");
-        assert!(updated_entry.attrs.get(&Attribute::Description).is_none());
+        assert!(!updated_entry.attrs.contains_key(&Attribute::Description));
 
         // set one
         let put = ScimEntryPutKanidm {
@@ -234,7 +234,7 @@ mod tests {
                     value: "extra_1@example.com".to_string(),
                 }));
             }
-            _ => assert!(false),
+            _ => unreachable!("Expected 1 member"),
         };
 
         // set many
@@ -285,7 +285,7 @@ mod tests {
                     value: "extra_3@example.com".to_string(),
                 }));
             }
-            _ => assert!(false),
+            _ => unreachable!("Expected 3 members"),
         };
 
         // set many with a removal
@@ -333,7 +333,7 @@ mod tests {
                     value: "extra_2@example.com".to_string(),
                 }));
             }
-            _ => assert!(false),
+            _ => unreachable!("Expected 2 members"),
         };
 
         // empty set removes attr
@@ -348,6 +348,6 @@ mod tests {
                 .expect("Failed to resolve data type");
 
         let updated_entry = server_txn.scim_put(put_event).expect("Failed to put");
-        assert!(updated_entry.attrs.get(&Attribute::Member).is_none());
+        assert!(!updated_entry.attrs.contains_key(&Attribute::Member));
     }
 }
diff --git a/server/lib/src/testkit.rs b/server/lib/src/testkit.rs
index 98342020b..2eb24abf8 100644
--- a/server/lib/src/testkit.rs
+++ b/server/lib/src/testkit.rs
@@ -4,12 +4,19 @@ use crate::schema::Schema;
 
 pub struct TestConfiguration {
     pub domain_level: DomainVersion,
+    // This is literally here to make clippy happy, just leave it alone!
+    // if you don't believe me then remove it and run 'cargo clippy --all-targets' it'll complain
+    // about "struct update has no effect, all the fields in the struct have already been specified"
+    // because the domain_level was set, then we ..Default::default() the "rest"
+    #[allow(dead_code)]
+    pub ignore_this_field: bool,
 }
 
 impl Default for TestConfiguration {
     fn default() -> Self {
         TestConfiguration {
             domain_level: DOMAIN_TGT_LEVEL,
+            ignore_this_field: false,
         }
     }
 }
diff --git a/server/lib/src/utils.rs b/server/lib/src/utils.rs
index 1c762b027..bd033fa42 100644
--- a/server/lib/src/utils.rs
+++ b/server/lib/src/utils.rs
@@ -2,8 +2,8 @@
 
 use crate::prelude::*;
 use hashbrown::HashSet;
-use rand::distributions::{Distribution, Uniform};
-use rand::{thread_rng, Rng};
+use rand::distr::{Distribution, Uniform};
+use rand::{rng, Rng};
 use std::ops::Range;
 
 #[derive(Debug)]
@@ -35,7 +35,7 @@ pub fn uuid_from_duration(d: Duration, sid: Sid) -> Uuid {
 }
 
 pub(crate) fn password_from_random_len(len: u32) -> String {
-    thread_rng()
+    rng()
         .sample_iter(&DistinctAlpha)
         .take(len as usize)
         .collect::<String>()
@@ -52,7 +52,7 @@ pub fn backup_code_from_random() -> HashSet<String> {
 pub fn readable_password_from_random() -> String {
     // 2^112 bits, means we need at least 55^20 to have as many bits of entropy.
     // this leads us to 4 groups of 5 to create 55^20
-    let mut trng = thread_rng();
+    let mut trng = rng();
     format!(
         "{}-{}-{}-{}",
         (&mut trng)
@@ -80,8 +80,9 @@ impl Distribution<char> for DistinctAlpha {
         const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ\
                 abcdefghjkpqrstuvwxyz\
                 0123456789";
-
-        let range = Uniform::new(0, RANGE);
+        // TODO: this needs to handle the error, maybe?
+        #[allow(clippy::expect_used)]
+        let range = Uniform::new(0, RANGE).expect("Failed to get a uniform range");
 
         let n = range.sample(rng);
         GEN_ASCII_STR_CHARSET[n as usize] as char
diff --git a/server/lib/src/valueset/address.rs b/server/lib/src/valueset/address.rs
index f50d1b58c..03c50ed22 100644
--- a/server/lib/src/valueset/address.rs
+++ b/server/lib/src/valueset/address.rs
@@ -683,10 +683,10 @@ mod tests {
             "value": "claire@example.com"
           }
         ]"#;
-        crate::valueset::scim_json_reflexive(vs.clone(), data);
+        crate::valueset::scim_json_reflexive(&vs, data);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetEmailAddress>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetEmailAddress>(&vs, &[])
     }
 
     #[test]
@@ -711,9 +711,9 @@ mod tests {
           }
         ]"#;
 
-        crate::valueset::scim_json_reflexive(vs.clone(), data);
+        crate::valueset::scim_json_reflexive(&vs, data);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetAddress>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetAddress>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/apppwd.rs b/server/lib/src/valueset/apppwd.rs
index 1885b12d8..499ae3f70 100644
--- a/server/lib/src/valueset/apppwd.rs
+++ b/server/lib/src/valueset/apppwd.rs
@@ -381,6 +381,6 @@ mod tests {
   }
 ]
 "#;
-        crate::valueset::scim_json_reflexive(vs, data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
 }
diff --git a/server/lib/src/valueset/auditlogstring.rs b/server/lib/src/valueset/auditlogstring.rs
index 5795a23ee..839ccc10e 100644
--- a/server/lib/src/valueset/auditlogstring.rs
+++ b/server/lib/src/valueset/auditlogstring.rs
@@ -400,6 +400,6 @@ mod tests {
   }
 ]
 "#;
-        crate::valueset::scim_json_reflexive(vs, data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
 }
diff --git a/server/lib/src/valueset/bool.rs b/server/lib/src/valueset/bool.rs
index 6f8f05fac..998365d85 100644
--- a/server/lib/src/valueset/bool.rs
+++ b/server/lib/src/valueset/bool.rs
@@ -185,9 +185,9 @@ mod tests {
     #[test]
     fn test_scim_boolean() {
         let vs: ValueSet = ValueSetBool::new(true);
-        crate::valueset::scim_json_reflexive(vs.clone(), "true");
+        crate::valueset::scim_json_reflexive(&vs, "true");
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetBool>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetBool>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/certificate.rs b/server/lib/src/valueset/certificate.rs
index 596a349d7..c558bd2b1 100644
--- a/server/lib/src/valueset/certificate.rs
+++ b/server/lib/src/valueset/certificate.rs
@@ -352,6 +352,6 @@ raBy6edj7W0EIH+yQxkDEwIhAI0nVKaI6duHLAvtKW6CfEQFG6jKg7dyk37YYiRD
         assert_eq!(cert.s256, expect_s256);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetCertificate>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetCertificate>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/cid.rs b/server/lib/src/valueset/cid.rs
index 105dccabe..589d75b73 100644
--- a/server/lib/src/valueset/cid.rs
+++ b/server/lib/src/valueset/cid.rs
@@ -193,6 +193,6 @@ mod tests {
         let vs: ValueSet = ValueSetCid::new(Cid::new_zero());
 
         let data = r#""00000000000000000000000000000000-00000000-0000-0000-0000-000000000000""#;
-        crate::valueset::scim_json_reflexive(vs, data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
 }
diff --git a/server/lib/src/valueset/cred.rs b/server/lib/src/valueset/cred.rs
index 09205fb16..ceb914fb7 100644
--- a/server/lib/src/valueset/cred.rs
+++ b/server/lib/src/valueset/cred.rs
@@ -1183,15 +1183,15 @@ mod tests {
   }
 ]
         "#;
-        crate::valueset::scim_json_reflexive(vs, data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
 
     #[test]
     fn test_scim_credential_type() {
         let vs: ValueSet = ValueSetCredentialType::new(CredentialType::Mfa);
-        crate::valueset::scim_json_reflexive(vs.clone(), r#""mfa""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""mfa""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetCredentialType>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetCredentialType>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/datetime.rs b/server/lib/src/valueset/datetime.rs
index b5e5c9d0e..8a3d31ebe 100644
--- a/server/lib/src/valueset/datetime.rs
+++ b/server/lib/src/valueset/datetime.rs
@@ -210,9 +210,9 @@ mod tests {
         let odt = OffsetDateTime::UNIX_EPOCH + Duration::from_secs(69_420);
         let vs: ValueSet = ValueSetDateTime::new(odt);
 
-        crate::valueset::scim_json_reflexive(vs.clone(), r#""1970-01-01T19:17:00Z""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""1970-01-01T19:17:00Z""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetDateTime>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetDateTime>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/hexstring.rs b/server/lib/src/valueset/hexstring.rs
index f1ea14224..059514c23 100644
--- a/server/lib/src/valueset/hexstring.rs
+++ b/server/lib/src/valueset/hexstring.rs
@@ -183,9 +183,6 @@ mod tests {
     fn test_scim_hexstring() {
         let vs: ValueSet =
             ValueSetHexString::new("D68475C760A7A0F6A924C28F095573A967F600D6".to_string());
-        crate::valueset::scim_json_reflexive(
-            vs.clone(),
-            r#""D68475C760A7A0F6A924C28F095573A967F600D6""#,
-        );
+        crate::valueset::scim_json_reflexive(&vs, r#""D68475C760A7A0F6A924C28F095573A967F600D6""#);
     }
 }
diff --git a/server/lib/src/valueset/image/jpg.rs b/server/lib/src/valueset/image/jpg.rs
index 47f579aec..55a52293c 100644
--- a/server/lib/src/valueset/image/jpg.rs
+++ b/server/lib/src/valueset/image/jpg.rs
@@ -1,3 +1,5 @@
+use std::io::Cursor;
+
 use image::codecs::jpeg::JpegDecoder;
 use image::ImageDecoder;
 use sketching::*;
@@ -79,9 +81,9 @@ pub fn has_trailer(contents: &Vec<u8>) -> Result<bool, ImageValidationError> {
 pub fn validate_decoding(
     filename: &str,
     contents: &[u8],
-    limits: image::io::Limits,
+    limits: image::Limits,
 ) -> Result<(), ImageValidationError> {
-    let mut decoder = match JpegDecoder::new(contents) {
+    let mut decoder = match JpegDecoder::new(Cursor::new(contents)) {
         Ok(val) => val,
         Err(err) => {
             return Err(ImageValidationError::InvalidImage(format!(
diff --git a/server/lib/src/valueset/image/mod.rs b/server/lib/src/valueset/image/mod.rs
index 634d8ae55..66e554253 100644
--- a/server/lib/src/valueset/image/mod.rs
+++ b/server/lib/src/valueset/image/mod.rs
@@ -1,6 +1,7 @@
 #![allow(dead_code)]
 use crate::valueset::ScimResolveStatus;
 use std::fmt::Display;
+use std::io::Cursor;
 
 use crate::be::dbvalue::DbValueImage;
 use crate::prelude::*;
@@ -37,8 +38,8 @@ pub trait ImageValueThings {
     /// A sha256 of the filename/type/contents
     fn hash_imagevalue(&self) -> String;
 
-    fn get_limits(&self) -> image::io::Limits {
-        let mut limits = image::io::Limits::default();
+    fn get_limits(&self) -> image::Limits {
+        let mut limits = image::Limits::default();
         limits.max_image_height = Some(MAX_IMAGE_HEIGHT);
         limits.max_image_width = Some(MAX_IMAGE_WIDTH);
         limits
@@ -148,7 +149,7 @@ impl ImageValueThings for ImageValue {
 
     /// validate the GIF file contents, and that it's actually a GIF
     fn validate_is_gif(&self) -> Result<(), ImageValidationError> {
-        let Ok(mut decoder) = GifDecoder::new(&self.contents[..]) else {
+        let Ok(mut decoder) = GifDecoder::new(Cursor::new(&self.contents[..])) else {
             return Err(ImageValidationError::InvalidImage(
                 "Failed to parse GIF".to_string(),
             ));
@@ -189,7 +190,7 @@ impl ImageValueThings for ImageValue {
             ));
         }
 
-        let Ok(mut decoder) = WebPDecoder::new(&self.contents[..]) else {
+        let Ok(mut decoder) = WebPDecoder::new(Cursor::new(&self.contents[..])) else {
             return Err(ImageValidationError::InvalidImage(
                 "Failed to parse WebP file".to_string(),
             ));
@@ -532,7 +533,7 @@ mod tests {
             "142dc7984dd548dd5dacfe2ad30f8473e3217e39b3b6c8d17a0cf6e4e24b02e0"
         ]"#;
 
-        crate::valueset::scim_json_reflexive(vs.clone(), data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
     */
 }
diff --git a/server/lib/src/valueset/iname.rs b/server/lib/src/valueset/iname.rs
index 52b6d9a44..1b80fa17b 100644
--- a/server/lib/src/valueset/iname.rs
+++ b/server/lib/src/valueset/iname.rs
@@ -226,9 +226,9 @@ mod tests {
     #[test]
     fn test_scim_iname() {
         let vs: ValueSet = ValueSetIname::new("stevo");
-        crate::valueset::scim_json_reflexive(vs.clone(), r#""stevo""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""stevo""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetIname>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetIname>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/index.rs b/server/lib/src/valueset/index.rs
index fc367a4a4..44438d9fd 100644
--- a/server/lib/src/valueset/index.rs
+++ b/server/lib/src/valueset/index.rs
@@ -183,9 +183,9 @@ mod tests {
     #[test]
     fn test_scim_index() {
         let vs: ValueSet = ValueSetIndex::new(IndexType::Equality);
-        crate::valueset::scim_json_reflexive(vs.clone(), r#"["EQUALITY"]"#);
+        crate::valueset::scim_json_reflexive(&vs, r#"["EQUALITY"]"#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetIndex>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetIndex>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/iutf8.rs b/server/lib/src/valueset/iutf8.rs
index 6bc82490e..577019fc9 100644
--- a/server/lib/src/valueset/iutf8.rs
+++ b/server/lib/src/valueset/iutf8.rs
@@ -227,9 +227,9 @@ mod tests {
     #[test]
     fn test_scim_iutf8() {
         let vs: ValueSet = ValueSetIutf8::new("lowercase string");
-        crate::valueset::scim_json_reflexive(vs.clone(), r#""lowercase string""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""lowercase string""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetIutf8>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetIutf8>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/json.rs b/server/lib/src/valueset/json.rs
index fc069cd54..a72d7ca16 100644
--- a/server/lib/src/valueset/json.rs
+++ b/server/lib/src/valueset/json.rs
@@ -205,9 +205,9 @@ mod tests {
   "{\"pres\":\"class\"}"
 ]
         "#;
-        crate::valueset::scim_json_reflexive(vs.clone(), data);
+        crate::valueset::scim_json_reflexive(&vs, data);
 
         // Test that we can parse json values into a valueset.
-        // crate::valueset::scim_json_put_reflexive::<ValueSetJsonFilter>(vs, &[])
+        // crate::valueset::scim_json_put_reflexive::<ValueSetJsonFilter>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/key_internal.rs b/server/lib/src/valueset/key_internal.rs
index 7e08c0cc1..fb5034595 100644
--- a/server/lib/src/valueset/key_internal.rs
+++ b/server/lib/src/valueset/key_internal.rs
@@ -649,6 +649,6 @@ mod tests {
   }
 ]
         "#;
-        crate::valueset::scim_json_reflexive(vs, data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
 }
diff --git a/server/lib/src/valueset/mod.rs b/server/lib/src/valueset/mod.rs
index 0cb86a506..3463e0156 100644
--- a/server/lib/src/valueset/mod.rs
+++ b/server/lib/src/valueset/mod.rs
@@ -995,7 +995,7 @@ pub fn from_db_valueset_v2(dbvs: DbValueSetV2) -> Result<ValueSet, OperationErro
 }
 
 #[cfg(test)]
-pub(crate) fn scim_json_reflexive(vs: ValueSet, data: &str) {
+pub(crate) fn scim_json_reflexive(vs: &ValueSet, data: &str) {
     let scim_value = vs.to_scim_value().unwrap().assume_resolved();
 
     let strout = serde_json::to_string_pretty(&scim_value).unwrap();
@@ -1012,25 +1012,27 @@ pub(crate) fn scim_json_reflexive(vs: ValueSet, data: &str) {
 #[cfg(test)]
 pub(crate) fn scim_json_reflexive_unresolved(
     write_txn: &mut QueryServerWriteTransaction,
-    vs: ValueSet,
+    vs: &ValueSet,
     data: &str,
 ) {
     let scim_int_value = vs.to_scim_value().unwrap().assume_unresolved();
     let scim_value = write_txn.resolve_scim_interim(scim_int_value).unwrap();
 
-    let strout = serde_json::to_string_pretty(&scim_value).unwrap();
+    let strout = serde_json::to_string_pretty(&scim_value).expect("Failed to serialize");
     eprintln!("{}", strout);
 
-    let json_value: serde_json::Value = serde_json::to_value(&scim_value).unwrap();
+    let json_value: serde_json::Value =
+        serde_json::to_value(&scim_value).expect("Failed to convert to JSON");
 
-    let expect: serde_json::Value = serde_json::from_str(data).unwrap();
+    let expect: serde_json::Value =
+        serde_json::from_str(data).expect("Failed to parse expected JSON");
 
     assert_eq!(json_value, expect);
 }
 
 #[cfg(test)]
 pub(crate) fn scim_json_put_reflexive<T: ValueSetScimPut>(
-    expect_vs: ValueSet,
+    expect_vs: &ValueSet,
     additional_tests: &[(JsonValue, ValueSet)],
 ) {
     let scim_value = expect_vs.to_scim_value().unwrap().assume_resolved();
@@ -1041,7 +1043,7 @@ pub(crate) fn scim_json_put_reflexive<T: ValueSetScimPut>(
     let generic = serde_json::to_value(scim_value).unwrap();
     // Check that we can turn back into a vs from the generic version.
     let vs = T::from_scim_json_put(generic).unwrap().assume_resolved();
-    assert_eq!(&vs, &expect_vs);
+    assert_eq!(&vs, expect_vs);
 
     // For each additional check, assert they work as expected.
     for (jv, expect_vs) in additional_tests {
@@ -1053,7 +1055,7 @@ pub(crate) fn scim_json_put_reflexive<T: ValueSetScimPut>(
 #[cfg(test)]
 pub(crate) fn scim_json_put_reflexive_unresolved<T: ValueSetScimPut>(
     write_txn: &mut QueryServerWriteTransaction,
-    expect_vs: ValueSet,
+    expect_vs: &ValueSet,
     additional_tests: &[(JsonValue, ValueSet)],
 ) {
     let scim_int_value = expect_vs.to_scim_value().unwrap().assume_unresolved();
@@ -1063,7 +1065,7 @@ pub(crate) fn scim_json_put_reflexive_unresolved<T: ValueSetScimPut>(
     // Check that we can turn back into a vs from the generic version.
     let vs_inter = T::from_scim_json_put(generic).unwrap().assume_unresolved();
     let vs = write_txn.resolve_valueset_intermediate(vs_inter).unwrap();
-    assert_eq!(&vs, &expect_vs);
+    assert_eq!(&vs, expect_vs);
 
     // For each additional check, assert they work as expected.
     for (jv, expect_vs) in additional_tests {
diff --git a/server/lib/src/valueset/nsuniqueid.rs b/server/lib/src/valueset/nsuniqueid.rs
index ab0746266..85db71062 100644
--- a/server/lib/src/valueset/nsuniqueid.rs
+++ b/server/lib/src/valueset/nsuniqueid.rs
@@ -189,12 +189,9 @@ mod tests {
     fn test_scim_nsuniqueid() {
         let vs: ValueSet =
             ValueSetNsUniqueId::new("3a163ca0-47624620-a18806b7-50c84c86".to_string());
-        crate::valueset::scim_json_reflexive(
-            vs.clone(),
-            r#""3a163ca0-47624620-a18806b7-50c84c86""#,
-        );
+        crate::valueset::scim_json_reflexive(&vs, r#""3a163ca0-47624620-a18806b7-50c84c86""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetNsUniqueId>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetNsUniqueId>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/oauth.rs b/server/lib/src/valueset/oauth.rs
index 0b6ec0f2d..ec0c0050b 100644
--- a/server/lib/src/valueset/oauth.rs
+++ b/server/lib/src/valueset/oauth.rs
@@ -898,10 +898,10 @@ mod tests {
     fn test_scim_oauth2_scope() {
         let vs: ValueSet = ValueSetOauthScope::new("fully_sick_scope_m8".to_string());
         let data = r#"["fully_sick_scope_m8"]"#;
-        crate::valueset::scim_json_reflexive(vs.clone(), data);
+        crate::valueset::scim_json_reflexive(&vs, data);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetOauthScope>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetOauthScope>(&vs, &[])
     }
 
     #[qs_test]
@@ -930,12 +930,12 @@ mod tests {
   }
 ]
         "#;
-        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, vs.clone(), data);
+        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, &vs, data);
 
         // Test that we can parse json values into a valueset.
         crate::valueset::scim_json_put_reflexive_unresolved::<ValueSetOauthScopeMap>(
             &mut write_txn,
-            vs,
+            &vs,
             &[],
         );
 
@@ -970,12 +970,12 @@ mod tests {
   }
 ]
         "#;
-        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, vs.clone(), data);
+        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, &vs, data);
 
         // Test that we can parse json values into a valueset.
         crate::valueset::scim_json_put_reflexive_unresolved::<ValueSetOauthClaimMap>(
             &mut write_txn,
-            vs,
+            &vs,
             &[],
         );
 
diff --git a/server/lib/src/valueset/restricted.rs b/server/lib/src/valueset/restricted.rs
index 29ddb9747..d099a3415 100644
--- a/server/lib/src/valueset/restricted.rs
+++ b/server/lib/src/valueset/restricted.rs
@@ -205,6 +205,6 @@ mod tests {
     #[test]
     fn test_scim_restricted() {
         let vs: ValueSet = ValueSetRestricted::new("Test".to_string());
-        crate::valueset::scim_json_reflexive(vs, r#""Test""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""Test""#);
     }
 }
diff --git a/server/lib/src/valueset/session.rs b/server/lib/src/valueset/session.rs
index 84d350dd2..d10e7ebd7 100644
--- a/server/lib/src/valueset/session.rs
+++ b/server/lib/src/valueset/session.rs
@@ -1920,7 +1920,7 @@ mod tests {
   }
 ]
         "#;
-        crate::valueset::scim_json_reflexive(vs, data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
 
     #[test]
@@ -1948,6 +1948,6 @@ mod tests {
 ]
         "#;
 
-        crate::valueset::scim_json_reflexive(vs, data);
+        crate::valueset::scim_json_reflexive(&vs, data);
     }
 }
diff --git a/server/lib/src/valueset/spn.rs b/server/lib/src/valueset/spn.rs
index 2bdea78a5..774771f50 100644
--- a/server/lib/src/valueset/spn.rs
+++ b/server/lib/src/valueset/spn.rs
@@ -188,6 +188,6 @@ mod tests {
     #[test]
     fn test_scim_spn() {
         let vs: ValueSet = ValueSetSpn::new(("claire".to_string(), "example.com".to_string()));
-        crate::valueset::scim_json_reflexive(vs, r#""claire@example.com""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""claire@example.com""#);
     }
 }
diff --git a/server/lib/src/valueset/ssh.rs b/server/lib/src/valueset/ssh.rs
index aa9bd6f7e..b9c114f9c 100644
--- a/server/lib/src/valueset/ssh.rs
+++ b/server/lib/src/valueset/ssh.rs
@@ -247,9 +247,9 @@ mod tests {
   }
 ]
         "#;
-        crate::valueset::scim_json_reflexive(vs.clone(), data);
+        crate::valueset::scim_json_reflexive(&vs, data);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetSshKey>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetSshKey>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/syntax.rs b/server/lib/src/valueset/syntax.rs
index 53df1c77b..c8fb640a2 100644
--- a/server/lib/src/valueset/syntax.rs
+++ b/server/lib/src/valueset/syntax.rs
@@ -188,9 +188,9 @@ mod tests {
     #[test]
     fn test_scim_syntax() {
         let vs: ValueSet = ValueSetSyntax::new(SyntaxType::Uuid);
-        crate::valueset::scim_json_reflexive(vs.clone(), r#""UUID""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""UUID""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetSyntax>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetSyntax>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/uihint.rs b/server/lib/src/valueset/uihint.rs
index e00a8d588..2c2ea8ce7 100644
--- a/server/lib/src/valueset/uihint.rs
+++ b/server/lib/src/valueset/uihint.rs
@@ -163,9 +163,9 @@ mod tests {
     #[test]
     fn test_scim_uihint() {
         let vs: ValueSet = ValueSetUiHint::new(UiHint::PosixAccount);
-        crate::valueset::scim_json_reflexive(vs.clone(), r#"["posixaccount"]"#);
+        crate::valueset::scim_json_reflexive(&vs, r#"["posixaccount"]"#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetUiHint>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetUiHint>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/uint32.rs b/server/lib/src/valueset/uint32.rs
index 410db12e9..9ae32e9e5 100644
--- a/server/lib/src/valueset/uint32.rs
+++ b/server/lib/src/valueset/uint32.rs
@@ -196,9 +196,9 @@ mod tests {
     #[test]
     fn test_scim_uint32() {
         let vs: ValueSet = ValueSetUint32::new(69);
-        crate::valueset::scim_json_reflexive(vs.clone(), "69");
+        crate::valueset::scim_json_reflexive(&vs, "69");
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetUint32>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetUint32>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/url.rs b/server/lib/src/valueset/url.rs
index eece0dacb..db60e16c5 100644
--- a/server/lib/src/valueset/url.rs
+++ b/server/lib/src/valueset/url.rs
@@ -183,9 +183,9 @@ mod tests {
     fn test_scim_url() {
         let u = Url::parse("https://idm.example.com").unwrap();
         let vs: ValueSet = ValueSetUrl::new(u);
-        crate::valueset::scim_json_reflexive(vs.clone(), r#""https://idm.example.com/""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""https://idm.example.com/""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetUrl>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetUrl>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/utf8.rs b/server/lib/src/valueset/utf8.rs
index 89a704aa7..43dd49570 100644
--- a/server/lib/src/valueset/utf8.rs
+++ b/server/lib/src/valueset/utf8.rs
@@ -246,9 +246,9 @@ mod tests {
     fn test_scim_utf8() {
         let vs: ValueSet = ValueSetUtf8::new("Test".to_string());
         // Test that the output json matches some known str
-        crate::valueset::scim_json_reflexive(vs.clone(), r#""Test""#);
+        crate::valueset::scim_json_reflexive(&vs, r#""Test""#);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetUtf8>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetUtf8>(&vs, &[])
     }
 }
diff --git a/server/lib/src/valueset/uuid.rs b/server/lib/src/valueset/uuid.rs
index 1c3e109e6..0c9f97588 100644
--- a/server/lib/src/valueset/uuid.rs
+++ b/server/lib/src/valueset/uuid.rs
@@ -422,10 +422,10 @@ mod tests {
 
         let data = r#""4d21d04a-dc0e-42eb-b850-34dd180b107f""#;
 
-        crate::valueset::scim_json_reflexive(vs.clone(), data);
+        crate::valueset::scim_json_reflexive(&vs, data);
 
         // Test that we can parse json values into a valueset.
-        crate::valueset::scim_json_put_reflexive::<ValueSetUuid>(vs, &[])
+        crate::valueset::scim_json_put_reflexive::<ValueSetUuid>(&vs, &[])
     }
 
     #[qs_test]
@@ -449,12 +449,12 @@ mod tests {
 
         let data = r#"[{"uuid": "4d21d04a-dc0e-42eb-b850-34dd180b107f", "value": "testperson1@example.com"}]"#;
 
-        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, vs.clone(), data);
+        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, &vs, data);
 
         // Test that we can parse json values into a valueset.
         crate::valueset::scim_json_put_reflexive_unresolved::<ValueSetRefer>(
             &mut write_txn,
-            vs,
+            &vs,
             &[],
         );
 
diff --git a/server/testkit-macros/src/entry.rs b/server/testkit-macros/src/entry.rs
index 81e1ef701..6fa1b9a48 100644
--- a/server/testkit-macros/src/entry.rs
+++ b/server/testkit-macros/src/entry.rs
@@ -10,16 +10,17 @@ const ALLOWED_ATTRIBUTES: &[&str] = &[
     "threads",
     "db_path",
     "maximum_request",
-    "trust_x_forward_for",
+    "http_client_address_info",
     "role",
     "output_mode",
     "log_level",
     "ldap",
+    "with_test_env",
 ];
 
 #[derive(Default)]
 struct Flags {
-    ldap: bool,
+    target_wants_test_env: bool,
 }
 
 fn parse_attributes(
@@ -60,8 +61,11 @@ fn parse_attributes(
             .unwrap_or_default()
             .as_str()
         {
+            "with_test_env" => {
+                flags.target_wants_test_env = true;
+            }
             "ldap" => {
-                flags.ldap = true;
+                flags.target_wants_test_env = true;
                 field_modifications.extend(quote! {
                 ldapbindaddress: Some("on".to_string()),})
             }
@@ -134,7 +138,7 @@ pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
         #[::core::prelude::v1::test]
     };
 
-    let test_fn_args = if flags.ldap {
+    let test_fn_args = if flags.target_wants_test_env {
         quote! {
             &test_env
         }
diff --git a/server/testkit-macros/src/lib.rs b/server/testkit-macros/src/lib.rs
index 6ca72af02..fb1d94da2 100644
--- a/server/testkit-macros/src/lib.rs
+++ b/server/testkit-macros/src/lib.rs
@@ -44,7 +44,7 @@ pub fn cli_kanidm(_input: TokenStream) -> TokenStream {
             .run()
             .unwrap();
         let mut kanidm = kanidm.command();
-        kanidm.env("KANIDM_URL", &rsclient.get_url().to_string());
+        kanidm.env("KANIDM_URL", rsclient.get_url().to_string());
         kanidm.env("KANIDM_TOKEN_CACHE_PATH", &token_cache_path);
         kanidm
         }
diff --git a/server/testkit/Cargo.toml b/server/testkit/Cargo.toml
index 6689649a2..83f87bf50 100644
--- a/server/testkit/Cargo.toml
+++ b/server/testkit/Cargo.toml
@@ -53,6 +53,10 @@ escargot = "0.5.13"
 # used for webdriver testing
 fantoccini = { version = "0.21.5" }
 futures = { workspace = true }
+hex = { workspace = true }
+hyper = { workspace = true }
+http-body-util = { workspace = true }
+hyper-util = { workspace = true }
 ldap3_client = { workspace = true }
 oauth2_ext = { workspace = true, default-features = false, features = [
     "reqwest",
diff --git a/server/testkit/src/lib.rs b/server/testkit/src/lib.rs
index 7eef97a25..ec35a2199 100644
--- a/server/testkit/src/lib.rs
+++ b/server/testkit/src/lib.rs
@@ -15,7 +15,7 @@ use kanidm_proto::internal::{Filter, Modify, ModifyList};
 use kanidmd_core::config::{Configuration, IntegrationTestConfig};
 use kanidmd_core::{create_server_core, CoreHandle};
 use kanidmd_lib::prelude::{Attribute, NAME_SYSTEM_ADMINS};
-use std::net::TcpStream;
+use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
 use std::sync::atomic::{AtomicU16, Ordering};
 use tokio::task;
 use tracing::error;
@@ -64,6 +64,7 @@ fn port_loop() -> u16 {
 
 pub struct AsyncTestEnvironment {
     pub rsclient: KanidmClient,
+    pub http_sock_addr: SocketAddr,
     pub core_handle: CoreHandle,
     pub ldap_url: Option<Url>,
 }
@@ -86,8 +87,9 @@ pub async fn setup_async_test(mut config: Configuration) -> AsyncTestEnvironment
 
     let ldap_url = if config.ldapbindaddress.is_some() {
         let ldapport = port_loop();
-        config.ldapbindaddress = Some(format!("127.0.0.1:{}", ldapport));
-        Url::parse(&format!("ldap://127.0.0.1:{}", ldapport))
+        let ldap_sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), ldapport);
+        config.ldapbindaddress = Some(ldap_sock_addr.to_string());
+        Url::parse(&format!("ldap://{}", ldap_sock_addr))
             .inspect_err(|err| error!(?err, "ldap address setup"))
             .ok()
     } else {
@@ -95,7 +97,9 @@ pub async fn setup_async_test(mut config: Configuration) -> AsyncTestEnvironment
     };
 
     // Setup the address and origin..
-    config.address = format!("127.0.0.1:{}", port);
+    let http_sock_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
+
+    config.address = http_sock_addr.to_string();
     config.integration_test_config = Some(int_config);
     config.domain = "localhost".to_string();
     config.origin.clone_from(&addr);
@@ -123,6 +127,7 @@ pub async fn setup_async_test(mut config: Configuration) -> AsyncTestEnvironment
 
     AsyncTestEnvironment {
         rsclient,
+        http_sock_addr,
         core_handle,
         ldap_url,
     }
diff --git a/server/testkit/tests/testkit/group.rs b/server/testkit/tests/testkit/group.rs
index 4eaeb30a0..a4e7cb8da 100644
--- a/server/testkit/tests/testkit/group.rs
+++ b/server/testkit/tests/testkit/group.rs
@@ -10,7 +10,7 @@ async fn test_v1_group_id_patch(rsclient: &KanidmClient) {
         .await;
     assert!(res.is_ok());
 
-    create_user(&rsclient, "foo", "foogroup").await;
+    create_user(rsclient, "foo", "foogroup").await;
 
     let post_body = serde_json::json!({"attrs": { ATTR_DESCRIPTION : ["Fancy group change"]}});
 
@@ -31,7 +31,7 @@ async fn test_v1_group_id_attr_post(rsclient: &KanidmClient) {
         .await;
     assert!(res.is_ok());
 
-    create_user(&rsclient, "foo", "foogroup").await;
+    create_user(rsclient, "foo", "foogroup").await;
 
     let post_body = serde_json::json!(["foo"]);
 
diff --git a/server/testkit/tests/testkit/https_extractors.rs b/server/testkit/tests/testkit/https_extractors.rs
deleted file mode 100644
index b664517cb..000000000
--- a/server/testkit/tests/testkit/https_extractors.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-use std::{
-    net::{IpAddr, Ipv4Addr},
-    str::FromStr,
-};
-
-use kanidm_client::KanidmClient;
-use kanidm_proto::constants::X_FORWARDED_FOR;
-
-const DEFAULT_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
-
-// *test where we don't trust the x-forwarded-for header
-
-#[kanidmd_testkit::test(trust_x_forward_for = false)]
-async fn dont_trust_xff_send_header(rsclient: &KanidmClient) {
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(
-            X_FORWARDED_FOR,
-            "An invalid header that will get through!!!",
-        )
-        .send()
-        .await
-        .unwrap();
-    let ip_res: IpAddr = res
-        .json()
-        .await
-        .expect("Failed to parse response as IpAddr");
-
-    assert_eq!(ip_res, DEFAULT_IP_ADDRESS);
-}
-
-#[kanidmd_testkit::test(trust_x_forward_for = false)]
-async fn dont_trust_xff_dont_send_header(rsclient: &KanidmClient) {
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(
-            X_FORWARDED_FOR,
-            "An invalid header that will get through!!!",
-        )
-        .send()
-        .await
-        .unwrap();
-    let body = res.bytes().await.unwrap();
-    let ip_res: IpAddr = serde_json::from_slice(&body).unwrap_or_else(|op| {
-        panic!(
-            "Failed to parse response as IpAddr: {:?} body: {:?}",
-            op, body,
-        )
-    });
-    eprintln!("Body: {:?}", body);
-    assert_eq!(ip_res, DEFAULT_IP_ADDRESS);
-}
-
-// *test where we trust the x-forwarded-for header
-
-#[kanidmd_testkit::test(trust_x_forward_for = true)]
-async fn trust_xff_send_invalid_header_single_value(rsclient: &KanidmClient) {
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(
-            X_FORWARDED_FOR,
-            "An invalid header that will get through!!!",
-        )
-        .send()
-        .await
-        .unwrap();
-
-    assert_eq!(res.status(), 400);
-}
-
-// TODO: Right now we reject the request only if the leftmost address is invalid. In the future that could change so we could also have a test
-// with a valid leftmost address and an invalid address later in the list. Right now it wouldn't work.
-//
-#[kanidmd_testkit::test(trust_x_forward_for = true)]
-async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient) {
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(
-            X_FORWARDED_FOR,
-            "203.0.113.195_noooo_my_ip_address, 2001:db8:85a3:8d3:1319:8a2e:370:7348",
-        )
-        .send()
-        .await
-        .unwrap();
-
-    assert_eq!(res.status(), 400);
-}
-
-#[kanidmd_testkit::test(trust_x_forward_for = true)]
-async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: &KanidmClient) {
-    let ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
-
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(X_FORWARDED_FOR, ip_addr)
-        .send()
-        .await
-        .unwrap();
-    let ip_res: IpAddr = res
-        .json()
-        .await
-        .expect("Failed to parse response as Vec<IpAddr>");
-
-    assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap());
-}
-
-#[kanidmd_testkit::test(trust_x_forward_for = true)]
-async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: &KanidmClient) {
-    let ip_addr = "203.0.113.195";
-
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(X_FORWARDED_FOR, ip_addr)
-        .send()
-        .await
-        .unwrap();
-    let ip_res: IpAddr = res
-        .json()
-        .await
-        .expect("Failed to parse response as Vec<IpAddr>");
-
-    assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap());
-}
-
-#[kanidmd_testkit::test(trust_x_forward_for = true)]
-async fn trust_xff_send_valid_header_multiple_address(rsclient: &KanidmClient) {
-    let first_ip_addr = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348";
-
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(X_FORWARDED_FOR, first_ip_addr)
-        .send()
-        .await
-        .unwrap();
-    let ip_res: IpAddr = res
-        .json()
-        .await
-        .expect("Failed to parse response as Vec<IpAddr>");
-
-    assert_eq!(
-        ip_res,
-        IpAddr::from_str(first_ip_addr.split(",").collect::<Vec<&str>>()[0]).unwrap()
-    );
-
-    let second_ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348, 198.51.100.178, 203.0.113.195";
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .header(X_FORWARDED_FOR, second_ip_addr)
-        .send()
-        .await
-        .unwrap();
-    let ip_res: IpAddr = res
-        .json()
-        .await
-        .expect("Failed to parse response as Vec<IpAddr>");
-
-    assert_eq!(
-        ip_res,
-        IpAddr::from_str(second_ip_addr.split(",").collect::<Vec<&str>>()[0]).unwrap()
-    );
-}
-
-#[kanidmd_testkit::test(trust_x_forward_for = true)]
-async fn trust_xff_dont_send_header(rsclient: &KanidmClient) {
-    let client = rsclient.client();
-
-    let res = client
-        .get(rsclient.make_url("/v1/debug/ipinfo"))
-        .send()
-        .await
-        .unwrap();
-    let ip_res: IpAddr = res
-        .json()
-        .await
-        .expect("Failed to parse response as Vec<IpAddr>");
-
-    assert_eq!(ip_res, DEFAULT_IP_ADDRESS);
-}
diff --git a/server/testkit/tests/testkit/identity_verification_tests.rs b/server/testkit/tests/testkit/identity_verification_tests.rs
index 2ed48a816..9f702042c 100644
--- a/server/testkit/tests/testkit/identity_verification_tests.rs
+++ b/server/testkit/tests/testkit/identity_verification_tests.rs
@@ -17,8 +17,8 @@ static USER_B_NAME: &str = "valid_user_b";
 #[kanidmd_testkit::test]
 async fn test_not_authenticated(rsclient: &KanidmClient) {
     // basically here we try a bit of all the possible combinations while unauthenticated to check it's not working
-    setup_server(&rsclient).await;
-    create_user(&rsclient, USER_A_NAME).await;
+    setup_server(rsclient).await;
+    create_user(rsclient, USER_A_NAME).await;
     let _ = rsclient.logout().await;
     let res = rsclient
         .idm_person_identify_user(USER_A_NAME, IdentifyUserRequest::Start)
@@ -47,11 +47,11 @@ async fn test_not_authenticated(rsclient: &KanidmClient) {
 
 #[kanidmd_testkit::test]
 async fn test_non_existing_user_id(rsclient: &KanidmClient) {
-    setup_server(&rsclient).await;
-    create_user(&rsclient, USER_A_NAME).await;
-    create_user(&rsclient, USER_B_NAME).await;
+    setup_server(rsclient).await;
+    create_user(rsclient, USER_A_NAME).await;
+    create_user(rsclient, USER_B_NAME).await;
     let non_existing_user = "non_existing_user";
-    login_with_user(&rsclient, USER_A_NAME).await;
+    login_with_user(rsclient, USER_A_NAME).await;
     let res: Result<IdentifyUserResponse, kanidm_client::ClientError> = rsclient
         .idm_person_identify_user(non_existing_user, IdentifyUserRequest::Start)
         .await;
@@ -87,9 +87,9 @@ async fn test_non_existing_user_id(rsclient: &KanidmClient) {
 // Each tests is named like `test_{api input}_response_{expected api output}_or_{expected api output}`
 #[kanidmd_testkit::test]
 async fn test_start_response_identity_verification_available(rsclient: &KanidmClient) {
-    setup_server(&rsclient).await;
-    create_user(&rsclient, USER_A_NAME).await;
-    login_with_user(&rsclient, USER_A_NAME).await;
+    setup_server(rsclient).await;
+    create_user(rsclient, USER_A_NAME).await;
+    login_with_user(rsclient, USER_A_NAME).await;
 
     let response = rsclient
         .idm_person_identify_user(USER_A_NAME, IdentifyUserRequest::Start)
@@ -106,10 +106,10 @@ async fn test_start_response_identity_verification_available(rsclient: &KanidmCl
 // `Start`, that is WaitForCode or ProvideCode
 #[kanidmd_testkit::test]
 async fn test_start_response_wait_for_code_or_provide_code(rsclient: &KanidmClient) {
-    setup_server(&rsclient).await;
-    let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
-    let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
-    login_with_user(&rsclient, USER_A_NAME).await;
+    setup_server(rsclient).await;
+    let user_a_uuid = create_user(rsclient, USER_A_NAME).await;
+    let user_b_uuid = create_user(rsclient, USER_B_NAME).await;
+    login_with_user(rsclient, USER_A_NAME).await;
     let response = rsclient
         .idm_person_identify_user(USER_B_NAME, IdentifyUserRequest::Start)
         .await;
@@ -130,10 +130,10 @@ async fn test_start_response_wait_for_code_or_provide_code(rsclient: &KanidmClie
 
 #[kanidmd_testkit::test]
 async fn test_provide_code_response_code_failure_or_provide_code(rsclient: &KanidmClient) {
-    setup_server(&rsclient).await;
-    let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
-    let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
-    login_with_user(&rsclient, USER_A_NAME).await;
+    setup_server(rsclient).await;
+    let user_a_uuid = create_user(rsclient, USER_A_NAME).await;
+    let user_b_uuid = create_user(rsclient, USER_B_NAME).await;
+    login_with_user(rsclient, USER_A_NAME).await;
     let response = rsclient
         .idm_person_identify_user(
             USER_B_NAME,
@@ -158,12 +158,12 @@ async fn test_provide_code_response_code_failure_or_provide_code(rsclient: &Kani
 // here we actually test the full idm flow by duplicating the server
 #[kanidmd_testkit::test]
 async fn test_full_identification_flow(rsclient: &KanidmClient) {
-    setup_server(&rsclient).await;
-    let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
-    let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
+    setup_server(rsclient).await;
+    let user_a_uuid = create_user(rsclient, USER_A_NAME).await;
+    let user_b_uuid = create_user(rsclient, USER_B_NAME).await;
     //user A session
     let valid_user_a_client = rsclient;
-    login_with_user(&valid_user_a_client, USER_A_NAME).await;
+    login_with_user(valid_user_a_client, USER_A_NAME).await;
     //user B session
     let valid_user_b_client = valid_user_a_client.new_session().unwrap();
     login_with_user(&valid_user_b_client, USER_B_NAME).await;
diff --git a/server/testkit/tests/testkit/integration.rs b/server/testkit/tests/testkit/integration.rs
index 4b2b2eac7..b9fd3e099 100644
--- a/server/testkit/tests/testkit/integration.rs
+++ b/server/testkit/tests/testkit/integration.rs
@@ -76,10 +76,10 @@ async fn test_webdriver_user_login(rsclient: &KanidmClient) {
     use fantoccini::Locator;
     use kanidmd_testkit::*;
     use std::time::Duration;
-    login_put_admin_idm_admins(&rsclient).await;
+    login_put_admin_idm_admins(rsclient).await;
 
     create_user_with_all_attrs(
-        &rsclient,
+        rsclient,
         NOT_ADMIN_TEST_USERNAME,
         Some(NOT_ADMIN_TEST_PASSWORD),
     )
@@ -89,7 +89,7 @@ async fn test_webdriver_user_login(rsclient: &KanidmClient) {
 
     handle_error!(
         c,
-        c.goto(&rsclient.get_url().to_string()).await,
+        c.goto(rsclient.get_url().to_string()).await,
         "Couldn't get URL"
     );
 
@@ -207,7 +207,7 @@ async fn test_webdriver_user_login(rsclient: &KanidmClient) {
 
 #[kanidmd_testkit::test]
 async fn test_domain_reset_token_key(rsclient: &KanidmClient) {
-    login_put_admin_idm_admins(&rsclient).await;
+    login_put_admin_idm_admins(rsclient).await;
 
     let token = rsclient.get_token().await.expect("No bearer token present");
 
@@ -220,7 +220,7 @@ async fn test_domain_reset_token_key(rsclient: &KanidmClient) {
 
 #[kanidmd_testkit::test]
 async fn test_idm_domain_set_ldap_basedn(rsclient: &KanidmClient) {
-    login_put_admin_idm_admins(&rsclient).await;
+    login_put_admin_idm_admins(rsclient).await;
     assert!(rsclient
         .idm_domain_set_ldap_basedn("dc=krabsarekool,dc=example,dc=com")
         .await
@@ -233,7 +233,7 @@ async fn test_idm_domain_set_ldap_basedn(rsclient: &KanidmClient) {
 
 #[kanidmd_testkit::test]
 async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: &KanidmClient) {
-    login_put_admin_idm_admins(&rsclient).await;
+    login_put_admin_idm_admins(rsclient).await;
     assert!(rsclient
         .idm_domain_set_ldap_max_queryable_attrs(20)
         .await
@@ -247,7 +247,7 @@ async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: &KanidmClient) {
 #[kanidmd_testkit::test]
 /// Checks that a built-in group idm_all_persons has the "builtin" class as expected.
 async fn test_all_persons_has_builtin_class(rsclient: &KanidmClient) {
-    login_put_admin_idm_admins(&rsclient).await;
+    login_put_admin_idm_admins(rsclient).await;
     let res = rsclient
         .idm_group_get("idm_all_persons")
         .await
@@ -306,7 +306,7 @@ async fn test_all_persons_has_builtin_class(rsclient: &KanidmClient) {
 /// Testing the CLI doing things.
 async fn test_integration_with_assert_cmd(rsclient: KanidmClient) {
     // setup the admin things
-    login_put_admin_idm_admins(&rsclient).await;
+    login_put_admin_idm_admins(rsclient).await;
 
     rsclient
         .idm_person_account_primary_credential_set_password(
@@ -334,13 +334,13 @@ async fn test_integration_with_assert_cmd(rsclient: KanidmClient) {
         assert!(anon_whoami.status.success());
         println!("Output: {:?}", anon_whoami);
 
-        test_cmd_admin(&token_cache_path, &rsclient, "login -D admin");
+        test_cmd_admin(&token_cache_path, rsclient, "login -D admin");
 
         // login as idm_admin
-        test_cmd_idm_admin(&token_cache_path, &rsclient, "login -D idm_admin");
+        test_cmd_idm_admin(&token_cache_path, rsclient, "login -D idm_admin");
         test_cmd_admin_split(
             &token_cache_path,
-            &rsclient,
+            rsclient,
             &[
                 "service-account",
                 "create",
@@ -355,13 +355,13 @@ async fn test_integration_with_assert_cmd(rsclient: KanidmClient) {
 
         test_cmd_admin(
             &token_cache_path,
-            &rsclient,
+            rsclient,
             &format!("service-account get -D admin {}", NOT_ADMIN_TEST_USERNAME),
         );
         // updating the display name
         test_cmd_admin(
             &token_cache_path,
-            &rsclient,
+            rsclient,
             &format!(
                 "service-account update -D admin {} --displayname cheeseballs",
                 NOT_ADMIN_TEST_USERNAME
@@ -370,7 +370,7 @@ async fn test_integration_with_assert_cmd(rsclient: KanidmClient) {
         // updating the email
         test_cmd_admin(
             &token_cache_path,
-            &rsclient,
+            rsclient,
             &format!(
                 "service-account update -D admin {} --mail foo@bar.com",
                 NOT_ADMIN_TEST_USERNAME
@@ -380,7 +380,7 @@ async fn test_integration_with_assert_cmd(rsclient: KanidmClient) {
         // checking the email was changed
         let sad = test_cmd_admin(
             &token_cache_path,
-            &rsclient,
+            rsclient,
             &format!(
                 "service-account get -D admin -o json {}",
                 NOT_ADMIN_TEST_USERNAME
diff --git a/server/testkit/tests/testkit/ip_addr_extractors.rs b/server/testkit/tests/testkit/ip_addr_extractors.rs
new file mode 100644
index 000000000..0d7642a43
--- /dev/null
+++ b/server/testkit/tests/testkit/ip_addr_extractors.rs
@@ -0,0 +1,324 @@
+use kanidm_client::KanidmClient;
+use kanidm_proto::constants::X_FORWARDED_FOR;
+use kanidmd_core::config::HttpAddressInfo;
+use kanidmd_testkit::AsyncTestEnvironment;
+use std::{
+    net::{IpAddr, Ipv4Addr, SocketAddr},
+    str::FromStr,
+};
+use tracing::error;
+
+const DEFAULT_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
+
+// =====================================================
+// *test where we don't trust the x-forwarded-for header
+
+#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::None)]
+async fn dont_trust_xff_send_header(rsclient: &KanidmClient) {
+    let client = rsclient.client();
+
+    // Send an invalid header to x forwdr for
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(X_FORWARDED_FOR, "a.b.c.d")
+        .send()
+        .await
+        .unwrap();
+
+    let ip_res: IpAddr = res
+        .json()
+        .await
+        .expect("Failed to parse response as IpAddr");
+
+    assert_eq!(ip_res, DEFAULT_IP_ADDRESS);
+
+    // Send a valid header for xforward for, but we don't trust it.
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(X_FORWARDED_FOR, "203.0.113.195")
+        .send()
+        .await
+        .unwrap();
+
+    let ip_res: IpAddr = res
+        .json()
+        .await
+        .expect("Failed to parse response as IpAddr");
+
+    assert_eq!(ip_res, DEFAULT_IP_ADDRESS);
+}
+
+// =====================================================
+// *test where we do trust the x-forwarded-for header
+
+#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor ( [DEFAULT_IP_ADDRESS].into() ))]
+async fn trust_xff_address_set(rsclient: &KanidmClient) {
+    inner_test_trust_xff(rsclient).await;
+}
+
+#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardForAllSourcesTrusted)]
+async fn trust_xff_all_addresses_trusted(rsclient: &KanidmClient) {
+    inner_test_trust_xff(rsclient).await;
+}
+
+async fn inner_test_trust_xff(rsclient: &KanidmClient) {
+    let client = rsclient.client();
+
+    // An invalid address.
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(X_FORWARDED_FOR, "a.b.c.d")
+        .send()
+        .await
+        .unwrap();
+
+    // Header was invalid
+    assert_eq!(res.status(), 400);
+
+    // An invalid address - what follows doesn't matter, even if it was valid. We only
+    // care about the left most address anyway.
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(
+            X_FORWARDED_FOR,
+            "203.0.113.195_noooo_my_ip_address, 2001:db8:85a3:8d3:1319:8a2e:370:7348",
+        )
+        .send()
+        .await
+        .unwrap();
+
+    assert_eq!(res.status(), 400);
+
+    // A valid ipv6 address was provided.
+    let ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
+
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(X_FORWARDED_FOR, ip_addr)
+        .send()
+        .await
+        .unwrap();
+    let ip_res: IpAddr = res
+        .json()
+        .await
+        .expect("Failed to parse response as Vec<IpAddr>");
+
+    assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap());
+
+    // A valid ipv4 address was provided.
+    let ip_addr = "203.0.113.195";
+
+    let client = rsclient.client();
+
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(X_FORWARDED_FOR, ip_addr)
+        .send()
+        .await
+        .unwrap();
+    let ip_res: IpAddr = res
+        .json()
+        .await
+        .expect("Failed to parse response as Vec<IpAddr>");
+
+    assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap());
+
+    // A valid ipv4 address in the leftmost field.
+    let first_ip_addr = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348";
+
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(X_FORWARDED_FOR, first_ip_addr)
+        .send()
+        .await
+        .unwrap();
+    let ip_res: IpAddr = res
+        .json()
+        .await
+        .expect("Failed to parse response as Vec<IpAddr>");
+
+    assert_eq!(
+        ip_res,
+        IpAddr::from_str(first_ip_addr.split(",").collect::<Vec<&str>>()[0]).unwrap()
+    );
+
+    // A valid ipv6 address in the left most field.
+    let second_ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348, 198.51.100.178, 203.0.113.195";
+
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .header(X_FORWARDED_FOR, second_ip_addr)
+        .send()
+        .await
+        .unwrap();
+    let ip_res: IpAddr = res
+        .json()
+        .await
+        .expect("Failed to parse response as Vec<IpAddr>");
+
+    assert_eq!(
+        ip_res,
+        IpAddr::from_str(second_ip_addr.split(",").collect::<Vec<&str>>()[0]).unwrap()
+    );
+
+    // If no header is sent, then the connection IP is used.
+    let res = client
+        .get(rsclient.make_url("/v1/debug/ipinfo"))
+        .send()
+        .await
+        .unwrap();
+    let ip_res: IpAddr = res
+        .json()
+        .await
+        .expect("Failed to parse response as Vec<IpAddr>");
+
+    assert_eq!(ip_res, DEFAULT_IP_ADDRESS);
+}
+
+// =====================================================
+// *test where we do trust the PROXY protocol header
+//
+// NOTE: This is MUCH HARDER TO TEST because we can't just stuff this address
+// in front of a reqwest call. We have to open raw connections and write the
+// requests to them.
+//
+// As a result, we are pretty much forced to manually dump binary headers and then
+// manually craft get reqs, followed by parsing them.
+
+#[derive(Debug, PartialEq)]
+enum ProxyV2Error {
+    TcpStream,
+    TcpWrite,
+    TornWrite,
+    HttpHandshake,
+    HttpRequestBuild,
+    HttpRequest,
+    HttpBadRequest,
+}
+
+async fn proxy_v2_make_request(
+    http_sock_addr: SocketAddr,
+    hdr: &[u8],
+) -> Result<IpAddr, ProxyV2Error> {
+    use http_body_util::BodyExt;
+    use http_body_util::Empty;
+    use hyper::body::Bytes;
+    use hyper::Request;
+    use hyper_util::rt::TokioIo;
+    use tokio::io::AsyncWriteExt as _;
+    use tokio::net::TcpStream;
+
+    let url = format!("http://{}/v1/debug/ipinfo", http_sock_addr)
+        .as_str()
+        .parse::<hyper::Uri>()
+        .unwrap();
+
+    let mut stream = TcpStream::connect(http_sock_addr).await.map_err(|err| {
+        error!(?err);
+        ProxyV2Error::TcpStream
+    })?;
+
+    // Write the proxyv2 header
+    let nbytes = stream.write(hdr).await.map_err(|err| {
+        error!(?err);
+        ProxyV2Error::TcpWrite
+    })?;
+
+    if nbytes != hdr.len() {
+        return Err(ProxyV2Error::TornWrite);
+    }
+
+    let io = TokioIo::new(stream);
+
+    let (mut sender, conn) = hyper::client::conn::http1::handshake(io)
+        .await
+        .map_err(|err| {
+            error!(?err);
+            ProxyV2Error::HttpHandshake
+        })?;
+
+    // Spawn a task to poll the connection, driving the HTTP state
+    tokio::task::spawn(async move {
+        if let Err(err) = conn.await {
+            println!("Connection failed: {:?}", err);
+        }
+    });
+
+    let authority = url.authority().unwrap().clone();
+
+    // Create an HTTP request with an empty body and a HOST header
+    let req = Request::builder()
+        .uri(url)
+        .header(hyper::header::HOST, authority.as_str())
+        .body(Empty::<Bytes>::new())
+        .map_err(|err| {
+            error!(?err);
+            ProxyV2Error::HttpRequestBuild
+        })?;
+
+    // Await the response...
+    let mut res = sender.send_request(req).await.map_err(|err| {
+        error!(?err);
+        ProxyV2Error::HttpRequest
+    })?;
+
+    println!("Response status: {}", res.status());
+
+    if res.status() != 200 {
+        return Err(ProxyV2Error::HttpBadRequest);
+    }
+
+    let mut data: Vec<u8> = Vec::new();
+
+    while let Some(next) = res.frame().await {
+        let frame = next.unwrap();
+        if let Some(chunk) = frame.data_ref() {
+            data.write_all(chunk).await.unwrap();
+        }
+    }
+
+    tracing::info!(?data);
+    let ip_res: IpAddr = serde_json::from_slice(&data).unwrap();
+    tracing::info!(?ip_res);
+
+    Ok(ip_res)
+}
+
+#[kanidmd_testkit::test(with_test_env = true, http_client_address_info = HttpAddressInfo::ProxyV2 ( [DEFAULT_IP_ADDRESS].into() ))]
+async fn trust_proxy_v2_address_set(test_env: &AsyncTestEnvironment) {
+    // Send with no header - with proxy v2, a header is ALWAYS required
+    let proxy_hdr: [u8; 0] = [];
+
+    let res = proxy_v2_make_request(test_env.http_sock_addr, &proxy_hdr)
+        .await
+        .unwrap_err();
+
+    // Can't send http request because proxy wasn't sent.
+    assert_eq!(res, ProxyV2Error::HttpRequest);
+
+    // Send with a valid header
+    let proxy_hdr =
+        hex::decode("0d0a0d0a000d0a515549540a2111000cac180c76ac180b8fcdcb027d").unwrap();
+
+    let res = proxy_v2_make_request(test_env.http_sock_addr, &proxy_hdr)
+        .await
+        .unwrap();
+
+    // The header was valid
+    assert_eq!(res, IpAddr::V4(Ipv4Addr::new(172, 24, 12, 118)));
+}
+
+#[kanidmd_testkit::test(with_test_env = true, http_client_address_info = HttpAddressInfo::ProxyV2 ( [ IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)) ].into() ))]
+async fn trust_proxy_v2_untrusted(test_env: &AsyncTestEnvironment) {
+    // Send with a valid header, but we aren't a trusted source.
+    let proxy_hdr =
+        hex::decode("0d0a0d0a000d0a515549540a2111000cac180c76ac180b8fcdcb027d").unwrap();
+
+    let res = proxy_v2_make_request(test_env.http_sock_addr, &proxy_hdr)
+        .await
+        .unwrap_err();
+
+    // Can't send http request because we aren't trusted to send it, so this
+    // ends up falling into a http request that is REJECTED.
+    assert_eq!(res, ProxyV2Error::HttpBadRequest);
+}
diff --git a/server/testkit/tests/testkit/mod.rs b/server/testkit/tests/testkit/mod.rs
index 2784a85ed..766a5fdb5 100644
--- a/server/testkit/tests/testkit/mod.rs
+++ b/server/testkit/tests/testkit/mod.rs
@@ -2,10 +2,10 @@ mod apidocs;
 mod domain;
 mod group;
 mod http_manifest;
-mod https_extractors;
 mod https_middleware;
 mod identity_verification_tests;
 mod integration;
+mod ip_addr_extractors;
 mod ldap_basic;
 mod mtls_test;
 mod oauth2_test;
diff --git a/server/testkit/tests/testkit/person.rs b/server/testkit/tests/testkit/person.rs
index 621cec3d4..e45a74992 100644
--- a/server/testkit/tests/testkit/person.rs
+++ b/server/testkit/tests/testkit/person.rs
@@ -10,7 +10,7 @@ async fn test_v1_person_id_patch(rsclient: &KanidmClient) {
         .await;
     assert!(res.is_ok());
 
-    create_user(&rsclient, "foo", "foogroup").await;
+    create_user(rsclient, "foo", "foogroup").await;
 
     let post_body = serde_json::json!({"attrs": { ATTR_MAIL : ["crab@example.com"]}});
 
@@ -31,7 +31,7 @@ async fn test_v1_person_id_ssh_pubkeys_post(rsclient: &KanidmClient) {
         .await;
     assert!(res.is_ok());
 
-    create_user(&rsclient, "foo", "foogroup").await;
+    create_user(rsclient, "foo", "foogroup").await;
 
     let post_body = serde_json::json!([
         "ssh-key-tag-goes-here",
diff --git a/server/testkit/tests/testkit/proto_v1_test.rs b/server/testkit/tests/testkit/proto_v1_test.rs
index 590b9910d..b55dfcd30 100644
--- a/server/testkit/tests/testkit/proto_v1_test.rs
+++ b/server/testkit/tests/testkit/proto_v1_test.rs
@@ -1366,7 +1366,7 @@ async fn setup_demo_account_password(
 
 #[kanidmd_testkit::test]
 async fn test_server_credential_update_session_passkey(rsclient: &KanidmClient) {
-    let mut wa = setup_demo_account_passkey(&rsclient).await;
+    let mut wa = setup_demo_account_passkey(rsclient).await;
 
     let res = rsclient
         .auth_passkey_begin("demo_account")
@@ -1690,7 +1690,7 @@ async fn test_server_user_auth_token_lifecycle(rsclient: &KanidmClient) {
 
 #[kanidmd_testkit::test]
 async fn test_server_user_auth_reauthentication(rsclient: &KanidmClient) {
-    let mut wa = setup_demo_account_passkey(&rsclient).await;
+    let mut wa = setup_demo_account_passkey(rsclient).await;
 
     let res = rsclient
         .auth_passkey_begin("demo_account")
@@ -1869,12 +1869,12 @@ async fn start_password_session(
 
 #[kanidmd_testkit::test]
 async fn test_server_user_auth_unprivileged(rsclient: &KanidmClient) {
-    let (account_name, account_pass) = setup_demo_account_password(&rsclient)
+    let (account_name, account_pass) = setup_demo_account_password(rsclient)
         .await
         .expect("Failed to setup demo_account");
 
     let uat = start_password_session(
-        &rsclient,
+        rsclient,
         account_name.as_str(),
         account_pass.as_str(),
         false,
@@ -1892,18 +1892,13 @@ async fn test_server_user_auth_unprivileged(rsclient: &KanidmClient) {
 
 #[kanidmd_testkit::test]
 async fn test_server_user_auth_privileged_shortcut(rsclient: &KanidmClient) {
-    let (account_name, account_pass) = setup_demo_account_password(&rsclient)
+    let (account_name, account_pass) = setup_demo_account_password(rsclient)
         .await
         .expect("Failed to setup demo_account");
 
-    let uat = start_password_session(
-        &rsclient,
-        account_name.as_str(),
-        account_pass.as_str(),
-        true,
-    )
-    .await
-    .expect("Failed to start session");
+    let uat = start_password_session(rsclient, account_name.as_str(), account_pass.as_str(), true)
+        .await
+        .expect("Failed to start session");
 
     match uat.purpose {
         UatPurpose::ReadOnly => panic!("Unexpected uat purpose"),
diff --git a/server/testkit/tests/testkit/unix.rs b/server/testkit/tests/testkit/unix.rs
index bc0d86300..9b3b06d59 100644
--- a/server/testkit/tests/testkit/unix.rs
+++ b/server/testkit/tests/testkit/unix.rs
@@ -6,10 +6,10 @@ use kanidmd_testkit::*;
 async fn account_id_unix_token(rsclient: &KanidmClient) {
     login_put_admin_idm_admins(rsclient).await;
 
-    create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
+    create_user(rsclient, "group_manager", "idm_group_manage_priv").await;
     // create test user without creating new groups
-    create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, NAME_IDM_ADMINS).await;
-    login_account(&rsclient, "group_manager").await;
+    create_user(rsclient, NOT_ADMIN_TEST_USERNAME, NAME_IDM_ADMINS).await;
+    login_account(rsclient, "group_manager").await;
 
     let response = rsclient
         .idm_account_unix_token_get(NOT_ADMIN_TEST_USERNAME)
@@ -32,7 +32,7 @@ async fn account_id_unix_token(rsclient: &KanidmClient) {
         assert!(format!("{:?}", val).contains("400"));
     }
 
-    login_put_admin_idm_admins(&rsclient).await;
+    login_put_admin_idm_admins(rsclient).await;
 
     rsclient
         .idm_person_account_unix_extend(NOT_ADMIN_TEST_USERNAME, None, None)
diff --git a/tools/cli/src/cli/system_config/badlist.rs b/tools/cli/src/cli/system_config/badlist.rs
index 9669be47c..f2c2f926d 100644
--- a/tools/cli/src/cli/system_config/badlist.rs
+++ b/tools/cli/src/cli/system_config/badlist.rs
@@ -5,6 +5,7 @@ use crate::{handle_client_error, PwBadlistOpt};
 use std::fs::File;
 use std::io::Read;
 use tokio::task;
+use zxcvbn::Score;
 
 const CHUNK_SIZE: usize = 1000;
 
@@ -67,10 +68,6 @@ impl PwBadlistOpt {
                 info!("Have {} unique passwords to process", pwset.len());
 
                 // Break the list into chunks per thread availability
-                // let par_count = thread::available_parallelism()
-                //     .expect("Failed to determine available parallelism")
-                //     .get();
-
                 let task_handles: Vec<_> = pwset
                     .chunks(CHUNK_SIZE)
                     .map(|chunk| chunk.to_vec())
@@ -82,18 +79,7 @@ impl PwBadlistOpt {
                                     if v.len() < 10 {
                                         return false;
                                     }
-                                    match zxcvbn::zxcvbn(v.as_str(), &[]) {
-                                        Ok(r) => r.score() >= 4,
-                                        Err(e) => {
-                                            error!(
-                                                "zxcvbn unable to process '{}' - {:?}",
-                                                v.as_str(),
-                                                e
-                                            );
-                                            error!("adding to badlist anyway ...");
-                                            true
-                                        }
-                                    }
+                                    zxcvbn::zxcvbn(v.as_str(), &[]).score() >= Score::Four
                                 })
                                 .map(|s| s.to_string())
                                 .collect::<Vec<_>>();
diff --git a/tools/orca/src/error.rs b/tools/orca/src/error.rs
index a2cc9697d..f73d3913b 100644
--- a/tools/orca/src/error.rs
+++ b/tools/orca/src/error.rs
@@ -8,4 +8,6 @@ pub enum Error {
     Interrupt,
     Crossbeam,
     InvalidState,
+    #[allow(dead_code)]
+    RandomNumber(String),
 }
diff --git a/tools/orca/src/generate.rs b/tools/orca/src/generate.rs
index afd779ff2..b34bf5fd8 100644
--- a/tools/orca/src/generate.rs
+++ b/tools/orca/src/generate.rs
@@ -4,8 +4,9 @@ use crate::model::ActorRole;
 use crate::profile::Profile;
 use crate::state::{Credential, Flag, Group, GroupName, Person, PreflightState, State};
 use hashbrown::HashMap;
-use rand::distributions::{Alphanumeric, DistString, Uniform};
-use rand::seq::{index, SliceRandom};
+use rand::distr::{Alphanumeric, SampleString, Uniform};
+use rand::seq::{index, IndexedRandom};
+
 use rand::{Rng, SeedableRng};
 use rand_chacha::ChaCha8Rng;
 
@@ -171,7 +172,8 @@ pub async fn populate(_client: &KanidmOrcaClient, profile: Profile) -> Result<St
                 let baseline = persons.len() / 3;
                 let inverse = persons.len() - baseline;
                 // Randomly add extra from the inverse
-                let extra = Uniform::new(0, inverse);
+                let extra =
+                    Uniform::new(0, inverse).map_err(|err| Error::RandomNumber(err.to_string()))?;
                 baseline + seeded_rng.sample(extra)
             }
         };
diff --git a/tools/orca/src/models/basic.rs b/tools/orca/src/models/basic.rs
index c362906a4..23f4c3142 100644
--- a/tools/orca/src/models/basic.rs
+++ b/tools/orca/src/models/basic.rs
@@ -27,7 +27,7 @@ impl ActorBasic {
     pub fn new(mut cha_rng: ChaCha8Rng, warmup_time_ms: u64) -> Self {
         let max_backoff_time_in_ms = 2 * warmup_time_ms / 3;
         let randomised_backoff_time =
-            Duration::from_millis(cha_rng.gen_range(0..max_backoff_time_in_ms));
+            Duration::from_millis(cha_rng.random_range(0..max_backoff_time_in_ms));
         ActorBasic {
             state: State::Unauthenticated,
             randomised_backoff_time,
diff --git a/tools/orca/src/models/latency_measurer.rs b/tools/orca/src/models/latency_measurer.rs
index 6601fac09..d681186a3 100644
--- a/tools/orca/src/models/latency_measurer.rs
+++ b/tools/orca/src/models/latency_measurer.rs
@@ -76,7 +76,7 @@ impl ActorLatencyMeasurer {
 
         let max_backoff_time_in_ms = 2 * warmup_time_ms / 3;
         let randomised_backoff_time =
-            Duration::from_millis(cha_rng.gen_range(0..max_backoff_time_in_ms));
+            Duration::from_millis(cha_rng.random_range(0..max_backoff_time_in_ms));
         Ok(ActorLatencyMeasurer {
             state: State::Unauthenticated,
             randomised_backoff_time,
diff --git a/tools/orca/src/models/read.rs b/tools/orca/src/models/read.rs
index f11eb9984..b280f0b5d 100644
--- a/tools/orca/src/models/read.rs
+++ b/tools/orca/src/models/read.rs
@@ -25,7 +25,7 @@ impl ActorReader {
     pub fn new(mut cha_rng: ChaCha8Rng, warmup_time_ms: u64) -> Self {
         let max_backoff_time_in_ms = warmup_time_ms - 1000;
         let randomised_backoff_time =
-            Duration::from_millis(cha_rng.gen_range(0..max_backoff_time_in_ms));
+            Duration::from_millis(cha_rng.random_range(0..max_backoff_time_in_ms));
         ActorReader {
             state: State::Unauthenticated,
             randomised_backoff_time,
diff --git a/tools/orca/src/models/write.rs b/tools/orca/src/models/write.rs
index 969f153ff..09da27c1a 100644
--- a/tools/orca/src/models/write.rs
+++ b/tools/orca/src/models/write.rs
@@ -26,7 +26,7 @@ impl ActorWriter {
     pub fn new(mut cha_rng: ChaCha8Rng, warmup_time_ms: u64) -> Self {
         let max_backoff_time_in_ms = 2 * warmup_time_ms / 3;
         let randomised_backoff_time =
-            Duration::from_millis(cha_rng.gen_range(0..max_backoff_time_in_ms));
+            Duration::from_millis(cha_rng.random_range(0..max_backoff_time_in_ms));
         ActorWriter {
             state: State::Unauthenticated,
             randomised_backoff_time,
diff --git a/tools/orca/src/profile.rs b/tools/orca/src/profile.rs
index 9b080d81d..d39a4c77b 100644
--- a/tools/orca/src/profile.rs
+++ b/tools/orca/src/profile.rs
@@ -1,6 +1,6 @@
 use crate::error::Error;
 use crate::state::{GroupName, Model};
-use rand::{thread_rng, Rng};
+use rand::{rng, Rng};
 use serde::de::{value, IntoDeserializer};
 use serde::{Deserialize, Serialize};
 use std::collections::BTreeMap;
@@ -200,8 +200,8 @@ impl ProfileBuilder {
         } = self;
 
         let seed: u64 = seed.unwrap_or_else(|| {
-            let mut rng = thread_rng();
-            rng.gen()
+            let mut rng = rng();
+            rng.random()
         });
 
         //TODO: Allow to specify group properties from the CLI
diff --git a/tools/orca/src/run.rs b/tools/orca/src/run.rs
index 393e6981f..c95187828 100644
--- a/tools/orca/src/run.rs
+++ b/tools/orca/src/run.rs
@@ -187,7 +187,7 @@ pub async fn execute(state: State, control_rx: broadcast::Receiver<Signal>) -> R
                 })
             })
             .collect::<Result<Vec<_>, _>>()?;
-        let main_client_index = seeded_rng.gen_range(0..cloned_clients.len());
+        let main_client_index = seeded_rng.random_range(0..cloned_clients.len());
         let main_client = cloned_clients.remove(main_client_index);
         //note that cloned_clients now contains all other clients except the first one
 
diff --git a/unix_integration/common/src/unix_config.rs b/unix_integration/common/src/unix_config.rs
index 09dd7d069..3e89fbab5 100644
--- a/unix_integration/common/src/unix_config.rs
+++ b/unix_integration/common/src/unix_config.rs
@@ -694,7 +694,7 @@ mod tests {
 
         for file in PathBuf::from(&examples_dir)
             .canonicalize()
-            .expect(&format!("Can't find examples dir at {}", examples_dir))
+            .unwrap_or_else(|_| panic!("Can't find examples dir at {}", examples_dir))
             .read_dir()
             .expect("Can't read examples dir!")
         {
diff --git a/unix_integration/nss_kanidm/src/tests.rs b/unix_integration/nss_kanidm/src/tests.rs
index 59629026e..e3448e7c0 100644
--- a/unix_integration/nss_kanidm/src/tests.rs
+++ b/unix_integration/nss_kanidm/src/tests.rs
@@ -71,32 +71,32 @@ impl RequestOptions {
 fn nss_fallback_unavail() {
     let req_opt = RequestOptions::fallback_unavail();
     let Response::Unavail = core::get_all_user_entries(req_opt) else {
-        unreachable!();
+        panic!("unrecoverable");
     };
 
     let req_opt = RequestOptions::fallback_unavail();
     let Response::Unavail = core::get_user_entry_by_uid(0, req_opt) else {
-        unreachable!();
+        panic!("unrecoverable");
     };
 
     let req_opt = RequestOptions::fallback_unavail();
     let Response::Unavail = core::get_user_entry_by_name("root".to_string(), req_opt) else {
-        unreachable!();
+        panic!("unrecoverable");
     };
 
     let req_opt = RequestOptions::fallback_unavail();
     let Response::Unavail = core::get_all_group_entries(req_opt) else {
-        unreachable!();
+        panic!("unrecoverable");
     };
 
     let req_opt = RequestOptions::fallback_unavail();
     let Response::Unavail = core::get_group_entry_by_gid(0, req_opt) else {
-        unreachable!();
+        panic!("unrecoverable");
     };
 
     let req_opt = RequestOptions::fallback_unavail();
     let Response::Unavail = core::get_group_entry_by_name("root".to_string(), req_opt) else {
-        unreachable!();
+        panic!("unrecoverable");
     };
 }
 
@@ -105,7 +105,7 @@ fn nss_fallback_all_user_entries() {
     let req_opt = RequestOptions::fallback_fixture();
 
     let Response::Success(users) = core::get_all_user_entries(req_opt) else {
-        unreachable!();
+        panic!("Failed to get all user entries");
     };
 
     assert_eq!(users.len(), 3);
@@ -129,7 +129,7 @@ fn nss_fallback_all_user_entries() {
 fn nss_fallback_user_entry_by_uid() {
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(user) = core::get_user_entry_by_uid(0, req_opt) else {
-        unreachable!();
+        panic!("Failed to get user entry by uid");
     };
 
     assert_eq!(user.name, "root");
@@ -139,7 +139,7 @@ fn nss_fallback_user_entry_by_uid() {
 
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(user) = core::get_user_entry_by_uid(1000, req_opt) else {
-        unreachable!();
+        panic!("Failed to get user entry by uid");
     };
 
     assert_eq!(user.name, "tobias");
@@ -149,7 +149,7 @@ fn nss_fallback_user_entry_by_uid() {
 
     let req_opt = RequestOptions::fallback_fixture();
     let Response::NotFound = core::get_user_entry_by_uid(10, req_opt) else {
-        unreachable!();
+        panic!("Wrong result");
     };
 }
 
@@ -157,7 +157,7 @@ fn nss_fallback_user_entry_by_uid() {
 fn nss_fallback_user_entry_by_name() {
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(user) = core::get_user_entry_by_name("root".to_string(), req_opt) else {
-        unreachable!();
+        panic!("Failed to get user entry by name");
     };
 
     assert_eq!(user.name, "root");
@@ -167,7 +167,7 @@ fn nss_fallback_user_entry_by_name() {
 
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(user) = core::get_user_entry_by_name("ellie".to_string(), req_opt) else {
-        unreachable!();
+        panic!("Failed to get user entry by name");
     };
 
     assert_eq!(user.name, "ellie");
@@ -177,7 +177,7 @@ fn nss_fallback_user_entry_by_name() {
 
     let req_opt = RequestOptions::fallback_fixture();
     let Response::NotFound = core::get_user_entry_by_name("william".to_string(), req_opt) else {
-        unreachable!();
+        panic!("Wrong result");
     };
 }
 
@@ -186,7 +186,7 @@ fn nss_fallback_all_group_entries() {
     let req_opt = RequestOptions::fallback_fixture();
 
     let Response::Success(groups) = core::get_all_group_entries(req_opt) else {
-        unreachable!();
+        panic!("Failed to get all group entries");
     };
 
     assert_eq!(groups.len(), 3);
@@ -207,7 +207,7 @@ fn nss_fallback_all_group_entries() {
 fn nss_fallback_group_entry_by_uid() {
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(group) = core::get_group_entry_by_gid(0, req_opt) else {
-        unreachable!();
+        panic!("Failed to get group entry by gid");
     };
 
     assert_eq!(group.name, "root");
@@ -216,7 +216,7 @@ fn nss_fallback_group_entry_by_uid() {
 
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(group) = core::get_group_entry_by_gid(1000, req_opt) else {
-        unreachable!();
+        panic!("Failed to get group entry by gid");
     };
 
     assert_eq!(group.name, "tobias");
@@ -225,7 +225,7 @@ fn nss_fallback_group_entry_by_uid() {
 
     let req_opt = RequestOptions::fallback_fixture();
     let Response::NotFound = core::get_group_entry_by_gid(10, req_opt) else {
-        unreachable!();
+        panic!("Wrong result");
     };
 }
 
@@ -234,7 +234,7 @@ fn nss_fallback_group_entry_by_name() {
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(group) = core::get_group_entry_by_name("root".to_string(), req_opt)
     else {
-        unreachable!();
+        panic!("Failed to get group entry by name");
     };
 
     assert_eq!(group.name, "root");
@@ -244,7 +244,7 @@ fn nss_fallback_group_entry_by_name() {
     let req_opt = RequestOptions::fallback_fixture();
     let Response::Success(group) = core::get_group_entry_by_name("ellie".to_string(), req_opt)
     else {
-        unreachable!();
+        panic!("Failed to get group entry by name");
     };
 
     assert_eq!(group.name, "ellie");
@@ -253,6 +253,6 @@ fn nss_fallback_group_entry_by_name() {
 
     let req_opt = RequestOptions::fallback_fixture();
     let Response::NotFound = core::get_group_entry_by_name("william".to_string(), req_opt) else {
-        unreachable!();
+        panic!("Wrong result");
     };
 }
diff --git a/unix_integration/pam_kanidm/src/tests.rs b/unix_integration/pam_kanidm/src/tests.rs
index 6ecf7a08c..31291997f 100644
--- a/unix_integration/pam_kanidm/src/tests.rs
+++ b/unix_integration/pam_kanidm/src/tests.rs
@@ -121,23 +121,17 @@ impl PamHandler for TestHandler {
     /// Display a message to the user.
     fn message(&self, _prompt: &str) -> PamResult<()> {
         let mut q = self.response_queue.lock().unwrap();
-        match q.pop_front() {
-            e => {
-                eprintln!("{:?}", e);
-                panic!("Invalid event transition");
-            }
-        }
+        let e = q.pop_front();
+        eprintln!("{:?}", e);
+        panic!("Invalid event transition message");
     }
 
     /// Display a device grant request to the user.
     fn message_device_grant(&self, _data: &DeviceAuthorizationResponse) -> PamResult<()> {
         let mut q = self.response_queue.lock().unwrap();
-        match q.pop_front() {
-            e => {
-                eprintln!("{:?}", e);
-                panic!("Invalid event transition");
-            }
-        }
+        let e = q.pop_front();
+        eprintln!("{:?}", e);
+        panic!("Invalid event transition message_device_grant");
     }
 
     /// Request a password from the user.
@@ -154,22 +148,16 @@ impl PamHandler for TestHandler {
 
     fn prompt_for_pin(&self, _msg: Option<&str>) -> PamResult<Option<String>> {
         let mut q = self.response_queue.lock().unwrap();
-        match q.pop_front() {
-            e => {
-                eprintln!("{:?}", e);
-                panic!("Invalid event transition");
-            }
-        }
+        let e = q.pop_front();
+        eprintln!("{:?}", e);
+        panic!("Invalid event transition prompt_for_pin");
     }
 
     fn prompt_for_mfacode(&self) -> PamResult<Option<String>> {
-        let mut q = self.response_queue.lock().unwrap();
-        match q.pop_front() {
-            e => {
-                eprintln!("{:?}", e);
-                panic!("Invalid event transition");
-            }
-        }
+        let mut q = self.response_queue.lock().expect("Failed to lock mutex");
+        let e = q.pop_front();
+        eprintln!("{:?}", e);
+        panic!("Invalid event transition prompt_for_mfacode");
     }
 }
 
diff --git a/unix_integration/resolver/tests/cache_layer_test.rs b/unix_integration/resolver/tests/cache_layer_test.rs
index ad85f48ec..30b659e07 100644
--- a/unix_integration/resolver/tests/cache_layer_test.rs
+++ b/unix_integration/resolver/tests/cache_layer_test.rs
@@ -1189,7 +1189,7 @@ async fn test_cache_extend_group_members() {
     assert!(groups.iter().any(|group| {
         group.name == "extensible_group"
             && group.members.as_slice()
-                == &[
+                == [
                     "local_account".to_string(),
                     "testaccount1@idm.example.com".to_string(),
                 ]