diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 106f89a6c..155d6ee06 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -8,11 +8,12 @@ jobs:
     steps:
       - uses: actions/checkout@v2
 
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          components: rustfmt
+
       - name: Formatting
-        run: |
-          # Install extra rustup components
-          rustup component add rustfmt
-          cargo fmt --all -- --check
+        run: cargo fmt --all -- --check
 
       - name: Test
         run: |
@@ -20,6 +21,7 @@ jobs:
           IFS=$'\n\t'
           # Check if the code is good
           cargo build --all --locked
+          cargo clippy -- --deny warnings
           cargo test --all --locked
 
       - name: Build the Docker image
diff --git a/Cargo.lock b/Cargo.lock
index eee0c5a23..d1de248d5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -79,9 +79,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.0.2"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
 
 [[package]]
 name = "block-buffer"
@@ -124,9 +124,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cookie"
-version = "0.17.0"
+version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
+checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8"
 dependencies = [
  "percent-encoding",
  "time",
@@ -213,7 +213,7 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
 dependencies = [
- "bitflags 2.0.2",
+ "bitflags 2.4.1",
  "proc-macro2",
  "proc-macro2-diagnostics",
  "quote",
@@ -257,24 +257,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "errno"
-version = "0.2.8"
+name = "equivalent"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
-dependencies = [
- "errno-dragonfly",
- "libc",
- "winapi",
-]
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
+name = "errno"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
 dependencies = [
- "cc",
  "libc",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -302,14 +297,14 @@ dependencies = [
 
 [[package]]
 name = "filetime"
-version = "0.2.20"
+version = "0.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
+checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
- "windows-sys 0.45.0",
+ "redox_syscall 0.3.5",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -560,7 +555,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http",
- "indexmap",
+ "indexmap 1.9.1",
  "slab",
  "tokio",
  "tokio-util",
@@ -603,6 +598,12 @@ version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
+[[package]]
+name = "hashbrown"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+
 [[package]]
 name = "hermit-abi"
 version = "0.1.19"
@@ -706,7 +707,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
 dependencies = [
  "autocfg",
- "hashbrown",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.2",
  "serde",
 ]
 
@@ -784,17 +795,6 @@ dependencies = [
  "unic-langid 0.9.1",
 ]
 
-[[package]]
-name = "io-lifetimes"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
-dependencies = [
- "hermit-abi 0.3.1",
- "libc",
- "windows-sys 0.45.0",
-]
-
 [[package]]
 name = "ipnet"
 version = "2.5.0"
@@ -803,14 +803,13 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
 
 [[package]]
 name = "is-terminal"
-version = "0.4.5"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
 dependencies = [
  "hermit-abi 0.3.1",
- "io-lifetimes",
  "rustix",
- "windows-sys 0.45.0",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -856,9 +855,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.135"
+version = "0.2.150"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
 
 [[package]]
 name = "linked-hash-map"
@@ -868,9 +867,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.1.4"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
 
 [[package]]
 name = "lock_api"
@@ -988,20 +987,21 @@ dependencies = [
 
 [[package]]
 name = "notify"
-version = "5.1.0"
+version = "6.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9"
+checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.4.1",
  "crossbeam-channel",
  "filetime",
  "fsevent-sys",
  "inotify",
  "kqueue",
  "libc",
+ "log",
  "mio",
  "walkdir",
- "windows-sys 0.42.0",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1098,7 +1098,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.2.16",
  "smallvec",
  "windows-sys 0.45.0",
 ]
@@ -1111,7 +1111,7 @@ checksum = "0ec95680a7087503575284e5063e14b694b7a9c0b065e5dceec661e0497127e8"
 dependencies = [
  "inlinable_string",
  "pear_codegen",
- "yansi",
+ "yansi 0.5.1",
 ]
 
 [[package]]
@@ -1225,7 +1225,7 @@ dependencies = [
  "quote",
  "syn 2.0.10",
  "version_check",
- "yansi",
+ "yansi 0.5.1",
 ]
 
 [[package]]
@@ -1276,6 +1276,15 @@ dependencies = [
  "bitflags 1.3.2",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
 [[package]]
 name = "ref-cast"
 version = "1.0.16"
@@ -1391,9 +1400,9 @@ dependencies = [
 
 [[package]]
 name = "rocket"
-version = "0.5.0-rc.3"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58734f7401ae5cfd129685b48f61182331745b357b96f2367f01aebaf1cc9cc9"
+checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150"
 dependencies = [
  "async-stream",
  "async-trait",
@@ -1403,8 +1412,7 @@ dependencies = [
  "either",
  "figment",
  "futures",
- "indexmap",
- "is-terminal",
+ "indexmap 2.1.0",
  "log",
  "memchr",
  "multer",
@@ -1424,50 +1432,51 @@ dependencies = [
  "tokio-util",
  "ubyte",
  "version_check",
- "yansi",
+ "yansi 1.0.0-rc.1",
 ]
 
 [[package]]
 name = "rocket_codegen"
-version = "0.5.0-rc.3"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7093353f14228c744982e409259fb54878ba9563d08214f2d880d59ff2fc508b"
+checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c"
 dependencies = [
  "devise",
  "glob",
- "indexmap",
+ "indexmap 2.1.0",
  "proc-macro2",
  "quote",
  "rocket_http",
  "syn 2.0.10",
  "unicode-xid",
+ "version_check",
 ]
 
 [[package]]
 name = "rocket_dyn_templates"
-version = "0.1.0-rc.3"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "276cac97fcddca93d741a4a530f58969f45a5bdb587f8c6b04c75cf849ca7f4c"
+checksum = "04bfc006e547e4f72b760ab861f5943b688aed8a82c4977b5500c98f5d17dbfa"
 dependencies = [
- "glob",
  "handlebars",
  "normpath",
  "notify",
  "rocket",
+ "walkdir",
 ]
 
 [[package]]
 name = "rocket_http"
-version = "0.5.0-rc.3"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936012c99162a03a67f37f9836d5f938f662e26f2717809761a9ac46432090f4"
+checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e"
 dependencies = [
  "cookie",
  "either",
  "futures",
  "http",
  "hyper",
- "indexmap",
+ "indexmap 2.1.0",
  "log",
  "memchr",
  "pear",
@@ -1486,9 +1495,9 @@ dependencies = [
 [[package]]
 name = "rust_team_data"
 version = "1.0.0"
-source = "git+https://github.com/rust-lang/team#811f92f4395068e1b826b208fd7f2ba677abd452"
+source = "git+https://github.com/rust-lang/team#ae0fe5cb83bbb24c9533c6829f2ca11667e8ab2d"
 dependencies = [
- "indexmap",
+ "indexmap 2.1.0",
  "serde",
 ]
 
@@ -1500,16 +1509,15 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
 name = "rustix"
-version = "0.36.11"
+version = "0.38.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.4.1",
  "errno",
- "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.45.0",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1655,7 +1663,7 @@ version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
 dependencies = [
- "indexmap",
+ "indexmap 1.9.1",
  "ryu",
  "serde",
  "yaml-rust",
@@ -1744,9 +1752,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
 [[package]]
 name = "state"
-version = "0.5.3"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b"
+checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
 dependencies = [
  "loom",
 ]
@@ -1782,7 +1790,7 @@ dependencies = [
  "cfg-if",
  "fastrand",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.2.16",
  "remove_dir_all",
  "winapi",
 ]
@@ -2207,12 +2215,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "walkdir"
-version = "2.3.2"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
 dependencies = [
  "same-file",
- "winapi",
  "winapi-util",
 ]
 
@@ -2345,7 +2352,7 @@ version = "0.44.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.42.2",
 ]
 
 [[package]]
@@ -2363,26 +2370,20 @@ dependencies = [
 
 [[package]]
 name = "windows-sys"
-version = "0.42.0"
+version = "0.45.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets 0.42.2",
 ]
 
 [[package]]
 name = "windows-sys"
-version = "0.45.0"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
 ]
 
 [[package]]
@@ -2391,21 +2392,42 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
 dependencies = [
- "windows_aarch64_gnullvm",
+ "windows_aarch64_gnullvm 0.42.2",
  "windows_aarch64_msvc 0.42.2",
  "windows_i686_gnu 0.42.2",
  "windows_i686_msvc 0.42.2",
  "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm",
+ "windows_x86_64_gnullvm 0.42.2",
  "windows_x86_64_msvc 0.42.2",
 ]
 
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.36.1"
@@ -2418,6 +2440,12 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.36.1"
@@ -2430,6 +2458,12 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.36.1"
@@ -2442,6 +2476,12 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.36.1"
@@ -2454,12 +2494,24 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.36.1"
@@ -2472,6 +2524,12 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
 [[package]]
 name = "winreg"
 version = "0.10.1"
@@ -2521,3 +2579,12 @@ name = "yansi"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
+
+[[package]]
+name = "yansi"
+version = "1.0.0-rc.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377"
+dependencies = [
+ "is-terminal",
+]
diff --git a/Cargo.toml b/Cargo.toml
index cc1942b29..537ee35bd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,8 +13,8 @@ fluent-locale = "0.10.1"
 handlebars-fluent = "0.3.1"
 rand = "0.8"
 regex = "1"
-rocket = "=0.5.0-rc.3"
-rocket_dyn_templates = { version = "=0.1.0-rc.3", features = ["handlebars"] }
+rocket = "0.5.0"
+rocket_dyn_templates = { version = "0.1.0", features = ["handlebars"] }
 serde = { version = "1.0", features = ["derive"] }
 serde_yaml = "0.8.17"
 sass-rs = "0.2.1"
diff --git a/locales/de/tools.ftl b/locales/de/tools.ftl
index 04c4a1321..349d904a9 100644
--- a/locales/de/tools.ftl
+++ b/locales/de/tools.ftl
@@ -28,9 +28,8 @@ tools-automation-cargo-doc-link = Zur Webseite
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/en-US/teams.ftl b/locales/en-US/teams.ftl
index ca6623202..8fa2a8cb7 100644
--- a/locales/en-US/teams.ftl
+++ b/locales/en-US/teams.ftl
@@ -44,9 +44,6 @@ governance-team-devtools-description = Contributing to and creating the Rust dev
 governance-team-docs-rs-name = Docs.rs team
 governance-team-docs-rs-description = Docs.rs, the documentation hosting service for crates
 
-governance-team-ides-name = IDEs and editors team
-governance-team-ides-description = Developing IDEs, editors, and other development tools such as Racer and rust-analyzer
-
 governance-team-infra-name = Infrastructure team
 governance-team-infra-description = Managing the infrastructure supporting the Rust project itself, including CI, releases, bots, and metrics
 
@@ -59,6 +56,9 @@ governance-team-lang-advisors-description = Advising on the development of the R
 governance-team-lang-docs-name = lang-docs team
 governance-team-lang-docs-description = Developing and writing the docs related to the lang team
 
+governance-team-lang-ops-name = lang-ops team
+governance-team-lang-ops-description = Operations for the lang team: preparing agenda, issue triage, scheduling and documenting meetings
+
 governance-team-leadership-council-name = Leadership council
 governance-team-leadership-council-description = Charged with the success of the Rust Project as whole, consisting of representatives from top-level teams
 
@@ -95,9 +95,6 @@ governance-team-project-const-generics-description = Working to advance const ge
 governance-team-project-dyn-upcasting-name = Dyn Upcasting Initiative
 governance-team-project-dyn-upcasting-description = Allowing Upcasting between trait objects
 
-governance-team-project-edition-2021-name = Edition 2021 Project Group
-governance-team-project-edition-2021-description = Managing the Rust 2021 edition
-
 governance-team-project-error-handling-name = Error Handling Project Group
 governance-team-project-error-handling-description = Identifying error handling best practices and consolidating the ecosystem
 
@@ -140,15 +137,24 @@ governance-team-rust-analyzer-description = Rust IDE support and error-resilient
 governance-team-rustdoc-name = Rustdoc team
 governance-team-rustdoc-description = Developing and managing the Rustdoc documentation tool
 
+governance-team-rustdoc-contributors-name = Rustdoc team contributors
+governance-team-rustdoc-contributors-description = Contributing to Rustdoc on a regular basis
+
 governance-team-rustfmt-name = Rustfmt team
 governance-team-rustfmt-description = Designing and implementing rustfmt, a formatting tool for Rust code
 
 governance-team-rustup-name = Rustup team
 governance-team-rustup-description = Designing and implementing rustup
 
+governance-team-spec-name = Specification team
+governance-team-spec-description = Creating and maintaining the specification for the Rust language
+
 governance-team-style-name = Style team
 governance-team-style-description = Defining and evolving the default Rust coding style
 
+governance-team-testing-devex-name = Testing DevEx team
+governance-team-testing-devex-description = Working on the associated libraries, tooling, strategy, and integration points that support the developer experience with writing, executing, and analyzing tests
+
 governance-team-triagebot-name = Triagebot team
 governance-team-triagebot-description = Maintaining and improving the rust-lang/triagebot tooling
 
@@ -161,6 +167,9 @@ governance-team-wg-allocators-description = Paving a path for a standard set of
 governance-team-wg-async-name = Async working group
 governance-team-wg-async-description = Pursuing core language and library support for async-await
 
+governance-team-wg-binary-size-name = Binary size working group
+governance-team-wg-binary-size-description = Improving the binary size of Rust programs and libraries
+
 governance-team-wg-bindgen-name = Bindgen working group
 governance-team-wg-bindgen-description = Developing tools for generating FFI bindings
 
@@ -200,6 +209,9 @@ governance-team-wg-embedded-hal-description = Develops and maintains crates that
 governance-team-wg-embedded-infra-name = Embedded infrastructure team
 governance-team-wg-embedded-infra-description = Managing infrastructure for wg-embedded
 
+governance-team-wg-embedded-libs-name = Embedded libraries team
+governance-team-wg-embedded-libs-description = Develops and maintains libraries for embedded devices
+
 governance-team-wg-embedded-linux-name = Embedded Linux team
 governance-team-wg-embedded-linux-description = The embedded Linux team develops and maintains the core of the embedded Linux crate ecosystem.
 
@@ -209,8 +221,8 @@ governance-team-wg-embedded-msp430-description = Develops and maintains the core
 governance-team-wg-embedded-resources-name = Embedded resources working group
 governance-team-wg-embedded-resources-description = Managing various resources owned by the embedded working group
 
-governance-team-wg-embedded-riscv-name = Embedded RISCV team
-governance-team-wg-embedded-riscv-description = Develops and maintains the core of the embedded RISCV crate ecosystem
+governance-team-wg-embedded-riscv-name = Embedded RISC-V team
+governance-team-wg-embedded-riscv-description = Develops and maintains the core of the embedded RISC-V crate ecosystem
 
 governance-team-wg-embedded-tools-name = Embedded Tools Team
 governance-team-wg-embedded-tools-description = Develops and maintains core embedded tools
@@ -236,15 +248,15 @@ governance-team-wg-inline-asm-description = A working-group project to extend th
 governance-team-wg-llvm-name = LLVM working group
 governance-team-wg-llvm-description = Working with LLVM upstream to represent Rust in its development
 
+governance-team-wg-macros-name = Macros working group
+governance-team-wg-macros-description = Revival and Improving the Rust Macros
+
 governance-team-wg-mir-opt-name = MIR optimizations working group
 governance-team-wg-mir-opt-description = Writing MIR optimizations and refactoring the MIR to be more optimizable
 
 governance-team-wg-parallel-rustc-name = Parallel rustc working group
 governance-team-wg-parallel-rustc-description = Making parallel compilation the default for rustc
 
-governance-team-wg-parselib-name = Parselib working group
-governance-team-wg-parselib-description = Sharing the parser between rustc and rust-analyzer
-
 governance-team-wg-pgo-name = Profile-guided optimization working group
 governance-team-wg-pgo-description = Implementing profile-guided optimization for rustc
 
@@ -269,9 +281,6 @@ governance-team-wg-rustc-dev-guide-description = Making the compiler easier to l
 governance-team-wg-rustc-reading-club-name = Rust Code Reading Club working group
 governance-team-wg-rustc-reading-club-description = Helping new and experienced contributors learn more about rustc
 
-governance-team-wg-rustfix-name = Rustfix working group
-governance-team-wg-rustfix-description = Developing cargo-fix and serving as a point of contact for other teams
-
 governance-team-wg-safe-transmute-name = safe-transmute project group
 governance-team-wg-safe-transmute-description = A working-group project to extend the Rust language to support safe transmute between types
 
@@ -289,3 +298,14 @@ governance-team-wg-triage-description = Triaging repositories under the rust-lan
 
 governance-team-wg-wasm-name = WebAssembly (WASM) working group
 governance-team-wg-wasm-description = Improving on the end-to-end experience of embedding Rust code in JS libraries and apps via WebAssembly
+
+governance-role-council-rep-compiler = Compiler team
+governance-role-council-rep-crates-io = Crates.io team
+governance-role-council-rep-devtools = Dev tools team
+governance-role-council-rep-infra = Infrastructure team
+governance-role-council-rep-lang = Language team
+governance-role-council-rep-launching-pad = Launching pad
+governance-role-council-rep-libs = Library team
+governance-role-council-rep-mods = Moderation team
+governance-role-council-rep-release = Release team
+governance-role-spec-editor = Editor
diff --git a/locales/en-US/tools.ftl b/locales/en-US/tools.ftl
index fed5cec34..456281f24 100644
--- a/locales/en-US/tools.ftl
+++ b/locales/en-US/tools.ftl
@@ -170,9 +170,8 @@ tools-rustup-manual-default-windows = If you are running Windows,<br>download an
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/es/tools.ftl b/locales/es/tools.ftl
index 28a5f85ff..eb6377487 100644
--- a/locales/es/tools.ftl
+++ b/locales/es/tools.ftl
@@ -120,9 +120,8 @@ tools-rustup-manual-default-windows = Si usas Windows, <br>descarga y ejecuta <a
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/fa/tools.ftl b/locales/fa/tools.ftl
index 02aa94aa5..bb36af433 100644
--- a/locales/fa/tools.ftl
+++ b/locales/fa/tools.ftl
@@ -20,9 +20,8 @@ install-other-methods-link = آموزش بیشتر
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/fr/tools.ftl b/locales/fr/tools.ftl
index b545b1c9a..7be6504dd 100644
--- a/locales/fr/tools.ftl
+++ b/locales/fr/tools.ftl
@@ -105,9 +105,8 @@ tools-rustup-manual-default-windows = Si vous utilisez Windows,<br> télécharge
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/it/tools.ftl b/locales/it/tools.ftl
index b6eed15fa..cb0a0fc79 100644
--- a/locales/it/tools.ftl
+++ b/locales/it/tools.ftl
@@ -95,9 +95,8 @@ tools-rustup-manual-default-windows = Se stai usando Windows,<br>scarica ed eseg
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/ja/tools.ftl b/locales/ja/tools.ftl
index 8713c75cc..c06742d59 100644
--- a/locales/ja/tools.ftl
+++ b/locales/ja/tools.ftl
@@ -96,9 +96,8 @@ tools-rustup-manual-default-windows = Windows上であれば、<br><a href="http
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/ko/tools.ftl b/locales/ko/tools.ftl
index e8d832076..4eb759717 100644
--- a/locales/ko/tools.ftl
+++ b/locales/ko/tools.ftl
@@ -55,9 +55,8 @@ tools-rustup-wsl-heading = Linux용 Windows 하위 시스템 (WSL)
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/pl/tools.ftl b/locales/pl/tools.ftl
index 3379e6adc..c78cc7827 100644
--- a/locales/pl/tools.ftl
+++ b/locales/pl/tools.ftl
@@ -40,9 +40,8 @@ tools-rustup-report = Zgłoś problem
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/pt-BR/tools.ftl b/locales/pt-BR/tools.ftl
index 872239d0e..1aa197ede 100644
--- a/locales/pt-BR/tools.ftl
+++ b/locales/pt-BR/tools.ftl
@@ -145,9 +145,8 @@ tools-rustup-manual-default-windows = Se você estiver rodando Windows, <br> fa
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/ru/tools.ftl b/locales/ru/tools.ftl
index 58a174372..7a38e25e6 100644
--- a/locales/ru/tools.ftl
+++ b/locales/ru/tools.ftl
@@ -118,9 +118,8 @@ tools-rustup-manual-default-windows = Если у вас запущен Windows,
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/tr/tools.ftl b/locales/tr/tools.ftl
index 3e0d847eb..c1b7df950 100644
--- a/locales/tr/tools.ftl
+++ b/locales/tr/tools.ftl
@@ -129,9 +129,8 @@ tools-rustup-manual-default-windows = Eğer Windows kullanıyorsanız <br><a hre
 
 tools-editor-vscode = { ENGLISH("VS Code") }
 tools-editor-sublime = { ENGLISH("Sublime Text") }
-tools-editor-atom = { ENGLISH("Atom") }
-tools-editor-idea = { ENGLISH("RustRover") }
+tools-editor-rover = { ENGLISH("RustRover") }
 tools-editor-eclipse = { ENGLISH("Eclipse") }
 tools-editor-vim = { ENGLISH("Vim") }
 tools-editor-emacs = { ENGLISH("Emacs") }
-tools-editor-geany = { ENGLISH("Geany") }
+tools-editor-visual-studio = { ENGLISH("Visual Studio") }
diff --git a/locales/zh-CN/tools.ftl b/locales/zh-CN/tools.ftl
index 868245d98..62cfe5437 100644
--- a/locales/zh-CN/tools.ftl
+++ b/locales/zh-CN/tools.ftl
@@ -4,7 +4,7 @@ tools-page-title = 工具
 tools-editor-support-heading = 主流编辑器支持
 tools-editor-support-description =
     无论您喜欢用命令行还是可视化编辑器,都有适合的 Rust 集成供您选择。
-    您也可以使用 <a href="https://github.com/rust-analyzer/rust-analyzer">Rust DO_NOT_SUBMIT</a>来为自己的编辑器添加 Rust 支持。
+    您也可以使用 <a href="https://github.com/rust-lang/rust-analyzer">rust-analyzer</a> 来为自己的编辑器添加 Rust 支持。
 tools-build-heading = 流畅的构建体验
 tools-build-description = Cargo 是 Rust 的构建工具,它将常用命令集于一身,无需引入其它命令。
 tools-build-install-heading = 安装
@@ -132,9 +132,8 @@ tools-rustup-manual-default-windows = 如果您正在运行 Windows,<br>请下
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/locales/zh-TW/tools.ftl b/locales/zh-TW/tools.ftl
index d07dd7cb2..232cb0418 100644
--- a/locales/zh-TW/tools.ftl
+++ b/locales/zh-TW/tools.ftl
@@ -106,9 +106,8 @@ tools-rustup-manual-default-windows = 如果您的電腦是 Windows 作業系統
 
 tools-editor-vscode = VS Code
 tools-editor-sublime = Sublime Text
-tools-editor-atom = Atom
-tools-editor-idea = RustRover
+tools-editor-rover = RustRover
 tools-editor-eclipse = Eclipse
 tools-editor-vim = Vim
 tools-editor-emacs = Emacs
-tools-editor-geany = Geany
+tools-editor-visual-studio = Visual Studio
diff --git a/src/cache.rs b/src/cache.rs
index 84c71d92a..f24c94b62 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -1,4 +1,5 @@
 use std::error::Error;
+use std::future::Future;
 use std::sync::Arc;
 use std::time::Instant;
 
@@ -10,10 +11,9 @@ const CACHE_TTL_SECS: u64 = 120;
 
 pub type Cache<T> = State<Arc<RwLock<T>>>;
 
-#[async_trait]
 pub trait Cached: Send + Sync + Clone + 'static {
     fn get_timestamp(&self) -> Instant;
-    async fn fetch() -> Result<Self, Box<dyn Error + Send + Sync>>;
+    fn fetch() -> impl Future<Output = Result<Self, Box<dyn Error + Send + Sync>>> + Send;
     async fn get(cache: &Cache<Self>) -> Self {
         let cached = cache.read().await.clone();
         let timestamp = cached.get_timestamp();
diff --git a/src/i18n.rs b/src/i18n.rs
index 211a3e67c..f2153372c 100644
--- a/src/i18n.rs
+++ b/src/i18n.rs
@@ -17,7 +17,7 @@ simple_loader!(create_loader, "./locales/", "en-US", core: "./locales/core.ftl",
 fn add_bundle_functions(bundle: &mut FluentBundle<&'static FluentResource>) {
     bundle
         .add_function("EMAIL", |values, _named| {
-            let email = match values.get(0) {
+            let email = match values.first() {
                 Some(FluentValue::String(ref s)) => s,
                 _ => return FluentValue::None,
             };
@@ -27,7 +27,7 @@ fn add_bundle_functions(bundle: &mut FluentBundle<&'static FluentResource>) {
 
     bundle
         .add_function("ENGLISH", |values, _named| {
-            let text = match values.get(0) {
+            let text = match values.first() {
                 Some(FluentValue::String(ref s)) => s,
                 _ => return FluentValue::None,
             };
@@ -107,6 +107,45 @@ impl Default for TeamHelper {
     }
 }
 
+enum TeamHelperParam {
+    /// `{{team-text team name}}`
+    Name,
+
+    /// `{{team-text team description}}`
+    Description,
+
+    /// `{{team-text team role (lookup member.roles 0)}}`
+    Role(String),
+}
+
+impl TeamHelperParam {
+    fn fluent_id(&self, team_name: &str) -> String {
+        match self {
+            TeamHelperParam::Name => format!("governance-team-{team_name}-name"),
+            TeamHelperParam::Description => format!("governance-team-{team_name}-description"),
+            TeamHelperParam::Role(role_id) => format!("governance-role-{role_id}"),
+        }
+    }
+
+    fn english<'a>(&'a self, team: &'a serde_json::Value) -> &'a str {
+        match self {
+            TeamHelperParam::Name => team["website_data"]["name"].as_str().unwrap(),
+            TeamHelperParam::Description => team["website_data"]["description"].as_str().unwrap(),
+            TeamHelperParam::Role(role_id) => {
+                for role in team["roles"].as_array().unwrap() {
+                    if role["id"] == *role_id {
+                        return role["description"].as_str().unwrap();
+                    }
+                }
+                // This should never happen. The `validate_member_roles` test in
+                // the team repo enforces that `.members.*.roles.*` lines up
+                // with exactly one `.roles.*.id`.
+                role_id
+            }
+        }
+    }
+}
+
 impl HelperDef for TeamHelper {
     fn call<'reg: 'rc, 'rc>(
         &self,
@@ -137,6 +176,25 @@ impl HelperDef for TeamHelper {
                 "{{team-text}} takes only identifier parameters",
             ));
         };
+
+        let param = match id.as_str() {
+            "name" => TeamHelperParam::Name,
+            "description" => TeamHelperParam::Description,
+            "role" => {
+                let Some(role_id) = h.param(2) else {
+                    return Err(RenderError::new(
+                        "{{team-text}} requires a third parameter for the role id",
+                    ));
+                };
+                TeamHelperParam::Role(role_id.value().as_str().unwrap().to_owned())
+            }
+            unrecognized => {
+                return Err(RenderError::new(format!(
+                    "unrecognized {{{{team-text}}}} param {unrecognized:?}",
+                )));
+            }
+        };
+
         let team = rcx
             .evaluate(context, name)
             .map_err(|e| RenderError::from_error(&format!("Cannot find team {}", name), e))?;
@@ -148,22 +206,20 @@ impl HelperDef for TeamHelper {
             .expect("Language must be string");
         let team_name = team.as_json()["name"].as_str().unwrap();
 
-        let fluent_id = format!("governance-team-{}-{}", team_name, id);
-
         // English uses the team data directly, so that it gets autoupdated
         if lang == "en-US" {
-            let english = team.as_json()["website_data"][id].as_str().unwrap();
+            let english = param.english(team.as_json());
             out.write(english)
                 .map_err(|e| RenderError::from_error("failed to render English team data", e))?;
         } else if let Some(value) = self.i18n.lookup_no_default_fallback(
             &lang.parse().expect("language must be valid"),
-            &fluent_id,
+            &param.fluent_id(team_name),
             None,
         ) {
             out.write(&value)
                 .map_err(|e| RenderError::from_error("failed to render translated team data", e))?;
         } else {
-            let english = team.as_json()["website_data"][id].as_str().unwrap();
+            let english = param.english(team.as_json());
             out.write(english)
                 .map_err(|e| RenderError::from_error("failed to render", e))?;
         }
diff --git a/src/main.rs b/src/main.rs
index fe245a909..5be444f35 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,3 @@
-#![cfg_attr(test, deny(warnings))]
-
 #[macro_use]
 extern crate lazy_static;
 extern crate rand;
@@ -102,7 +100,7 @@ struct Context<T: ::serde::Serialize> {
 }
 
 impl<T: ::serde::Serialize> Context<T> {
-    fn new(page: String, title_id: &str, is_landing: bool, data: T, lang: String) -> Self {
+    fn new(page: &str, title_id: &str, is_landing: bool, data: T, lang: String) -> Self {
         let helper = create_loader();
         let title = if title_id.is_empty() {
             "".into()
@@ -111,7 +109,7 @@ impl<T: ::serde::Serialize> Context<T> {
             helper.lookup(&lang, title_id, None)
         };
         Self {
-            page,
+            page: page.to_owned(),
             title,
             parent: LAYOUT,
             is_landing,
@@ -222,8 +220,8 @@ async fn governance(teams_cache: &Cache<RustTeams>) -> Result<Template, Status>
 
 #[get("/governance/<section>/<team>", rank = 2)]
 async fn team(
-    section: String,
-    team: String,
+    section: &str,
+    team: &str,
     teams_cache: &Cache<RustTeams>,
 ) -> Result<Template, Result<Redirect, Status>> {
     render_team(section, team, ENGLISH.into(), teams_cache).await
@@ -239,8 +237,8 @@ async fn governance_locale(
 
 #[get("/<locale>/governance/<section>/<team>", rank = 12)]
 async fn team_locale(
-    section: String,
-    team: String,
+    section: &str,
+    team: &str,
     locale: SupportedLocale,
     teams_cache: &Cache<RustTeams>,
 ) -> Result<Template, Result<Redirect, Status>> {
@@ -258,14 +256,14 @@ fn production_locale(locale: SupportedLocale) -> Template {
 }
 
 #[get("/<category>/<subject>", rank = 4)]
-fn subject(category: Category, subject: String) -> Result<Template, Status> {
+fn subject(category: Category, subject: &str) -> Result<Template, Status> {
     render_subject(category, subject, ENGLISH.into())
 }
 
 #[get("/<locale>/<category>/<subject>", rank = 14)]
 fn subject_locale(
     category: Category,
-    subject: String,
+    subject: &str,
     locale: SupportedLocale,
 ) -> Result<Template, Status> {
     render_subject(category, subject, locale.0)
@@ -284,6 +282,7 @@ fn redirect_bare_en_us() -> Redirect {
 }
 
 #[catch(404)]
+#[allow(clippy::result_large_err)]
 fn not_found(req: &Request) -> Result<Template, Redirect> {
     if let Some(redirect) = crate::redirect::maybe_redirect(req.uri().path()) {
         return Err(redirect);
@@ -304,7 +303,7 @@ fn not_found(req: &Request) -> Result<Template, Redirect> {
 
 fn not_found_locale(lang: String) -> Template {
     let page = "404";
-    let context = Context::new("404".into(), "error404-page-title", false, (), lang);
+    let context = Context::new(page, "error404-page-title", false, (), lang);
     Template::render(page, context)
 }
 
@@ -377,7 +376,7 @@ async fn render_index(
         rust_release_post: String,
     }
 
-    let page = "index".to_string();
+    let page = "index";
     let release_post = rust_version::rust_release_post(release_post_cache).await;
     let data = IndexData {
         rust_version: rust_version::rust_version(version_cache).await,
@@ -387,22 +386,22 @@ async fn render_index(
             String::new()
         },
     };
-    let context = Context::new(page.clone(), "", true, data, lang);
+    let context = Context::new(page, "", true, data, lang);
     Template::render(page, context)
 }
 
 fn render_category(category: Category, lang: String) -> Template {
     let page = category.index();
     let title_id = format!("{}-page-title", category.name());
-    let context = Context::new(category.name().to_string(), &title_id, false, (), lang);
+    let context = Context::new(category.name(), &title_id, false, (), lang);
 
     Template::render(page, context)
 }
 
 fn render_production(lang: String) -> Template {
-    let page = "production/users".to_string();
+    let page = "production/users";
     let context = Context::new(
-        page.clone(),
+        page,
         "production-users-page-title",
         false,
         load_users_data(),
@@ -418,8 +417,8 @@ async fn render_governance(
 ) -> Result<Template, Status> {
     match teams::index_data(teams_cache).await {
         Ok(data) => {
-            let page = "governance/index".to_string();
-            let context = Context::new(page.clone(), "governance-page-title", false, data, lang);
+            let page = "governance/index";
+            let context = Context::new(page, "governance-page-title", false, data, lang);
 
             Ok(Template::render(page, context))
         }
@@ -431,21 +430,21 @@ async fn render_governance(
 }
 
 async fn render_team(
-    section: String,
-    team: String,
+    section: &str,
+    team: &str,
     lang: String,
     teams_cache: &Cache<RustTeams>,
 ) -> Result<Template, Result<Redirect, Status>> {
-    match teams::page_data(&section, &team, teams_cache).await {
+    match teams::page_data(section, team, teams_cache).await {
         Ok(data) => {
-            let page = "governance/group".to_string();
+            let page = "governance/group";
             let name = format!("governance-team-{}-name", data.team.name);
-            let context = Context::new(page.clone(), &name, false, data, lang);
+            let context = Context::new(page, &name, false, data, lang);
             Ok(Template::render(page, context))
         }
         Err(err) => {
             if err.is::<teams::TeamNotFound>() {
-                match (section.as_str(), team.as_str()) {
+                match (section, team) {
                     // Old teams URLs
                     ("teams", "language-and-compiler") | ("teams", "operations") => {
                         Err(Ok(Redirect::temporary("/governance")))
@@ -460,7 +459,7 @@ async fn render_team(
     }
 }
 
-fn render_subject(category: Category, subject: String, lang: String) -> Result<Template, Status> {
+fn render_subject(category: Category, subject: &str, lang: String) -> Result<Template, Status> {
     // Rocket's Template::render method is not really designed to accept arbitrary templates: if a
     // template is missing, it just returns a Status::InternalServerError, without a way to
     // distinguish it from a syntax error in the template itself.
@@ -473,7 +472,7 @@ fn render_subject(category: Category, subject: String, lang: String) -> Result<T
         return Err(Status::NotFound);
     }
 
-    let page = format!("{}/{}", category.name(), subject.as_str());
+    let page = format!("{}/{}", category.name(), subject);
     let title_id = format!("{}-{}-page-title", category.name(), subject);
     let context = Context::new(subject, &title_id, false, (), lang);
 
diff --git a/src/rust_version.rs b/src/rust_version.rs
index a2da75841..b84f64686 100644
--- a/src/rust_version.rs
+++ b/src/rust_version.rs
@@ -42,7 +42,6 @@ impl Default for RustVersion {
     }
 }
 
-#[async_trait]
 impl Cached for RustVersion {
     fn get_timestamp(&self) -> Instant {
         self.1
@@ -68,7 +67,6 @@ impl Default for RustReleasePost {
     }
 }
 
-#[async_trait]
 impl Cached for RustReleasePost {
     fn get_timestamp(&self) -> Instant {
         self.1
diff --git a/src/teams.rs b/src/teams.rs
index 565a06400..12d050f99 100644
--- a/src/teams.rs
+++ b/src/teams.rs
@@ -59,7 +59,7 @@ impl Data {
         self.teams
             .into_iter()
             .filter(|team| team.website_data.is_some())
-            .filter(|team| team.subteam_of.is_none())
+            .filter(|team| matches!(team.subteam_of.as_deref(), None | Some("launching-pad")))
             .map(|team| IndexTeam {
                 url: format!(
                     "{}/{}",
@@ -98,8 +98,12 @@ impl Data {
             .ok_or(TeamNotFound)?;
 
         // Don't show pages for subteams
-        if main_team.subteam_of.is_some() {
-            return Err(TeamNotFound.into());
+        if let Some(subteam) = &main_team.subteam_of {
+            // Launching-pad does not have a page of its own, but we do want
+            // to show the working groups that are inside it.
+            if subteam != "launching-pad" {
+                return Err(TeamNotFound.into());
+            }
         }
 
         // Then find all the subteams, working groups and project groups.
@@ -155,7 +159,7 @@ impl Data {
         let mut subteams = Vec::with_capacity(raw_subteams.len());
         lay_out_subteams_hierarchically(&mut subteams, None, &main_team.name, &raw_subteams);
 
-        fn lay_out_subteams_hierarchically<'a>(
+        fn lay_out_subteams_hierarchically(
             result: &mut Vec<Team>,
             team: Option<&Team>,
             main_team: &str,
@@ -238,7 +242,6 @@ impl Default for RustTeams {
     }
 }
 
-#[async_trait]
 impl Cached for RustTeams {
     fn get_timestamp(&self) -> Instant {
         self.1
@@ -298,17 +301,26 @@ mod tests {
             kind: TeamKind::Team,
             subteam_of: None,
             members: vec![
+                TeamMember {
+                    name: "Jupiter Doe".into(),
+                    github: "jupiterd".into(),
+                    github_id: 123,
+                    is_lead: false,
+                    roles: vec!["convener".to_owned()],
+                },
                 TeamMember {
                     name: "John Doe".into(),
                     github: "johnd".into(),
+                    github_id: 456,
                     is_lead: false,
-                    github_id: 1234,
+                    roles: Vec::new(),
                 },
                 TeamMember {
                     name: "Jane Doe".into(),
                     github: "janed".into(),
+                    github_id: 789,
                     is_lead: true,
-                    github_id: 1234,
+                    roles: Vec::new(),
                 },
             ],
             alumni: Vec::new(),
@@ -322,6 +334,7 @@ mod tests {
                 zulip_stream: None,
                 weight: 0,
             }),
+            roles: Vec::new(),
             github: None,
             discord: vec![],
         }
diff --git a/static/scripts/tools-install.js b/static/scripts/tools-install.js
index 02084f47f..3283aad5e 100644
--- a/static/scripts/tools-install.js
+++ b/static/scripts/tools-install.js
@@ -29,6 +29,7 @@ function detect_platform() {
     if (navigator.platform == "Linux ppc64") {os = "unix";}
     if (navigator.platform == "Linux mips") {os = "unix";}
     if (navigator.platform == "Linux mips64") {os = "unix";}
+    if (navigator.platform == "Linux riscv64") {os = "unix";}
     if (navigator.platform == "Mac") {os = "unix";}
     if (navigator.platform == "Win32") {os = "win";}
     if (navigator.platform == "FreeBSD x86_64") {os = "unix";}
diff --git a/templates/components/tools/editors.html.hbs b/templates/components/tools/editors.html.hbs
index fc39630a5..37bb84555 100644
--- a/templates/components/tools/editors.html.hbs
+++ b/templates/components/tools/editors.html.hbs
@@ -7,16 +7,12 @@
     <a href="https://github.com/rust-lang/rust-enhanced"
        class="button button-secondary">{{fluent "tools-editor-sublime"}}</a>
   </div>
-  <div class="w-25-l w-50-m w-100 pa3">
-    <a href="https://github.com/rust-lang/atom-ide-rust"
-       class="button button-secondary">{{fluent "tools-editor-atom"}}</a>
-  </div>
   <div class="w-25-l w-50-m w-100 pa3">
     <a href="https://jetbrains.com/rust"
-       class="button button-secondary">{{fluent "tools-editor-idea"}}</a>
+       class="button button-secondary">{{fluent "tools-editor-rover"}}</a>
   </div>
   <div class="w-25-l w-50-m w-100 pa3">
-    <a href="https://www.eclipse.org/downloads/packages/release/2019-09/r/eclipse-ide-rust-developers-includes-incubating-components"
+    <a href="https://projects.eclipse.org/projects/tools.corrosion"
        class="button button-secondary">{{fluent "tools-editor-eclipse"}}</a>
   </div>
   <div class="w-25-l w-50-m w-100 pa3">
@@ -28,7 +24,7 @@
        class="button button-secondary">{{fluent "tools-editor-emacs"}}</a>
   </div>
   <div class="w-25-l w-50-m w-100 pa3">
-    <a href="https://geany.org/about/filetypes/"
-       class="button button-secondary">{{fluent "tools-editor-geany"}}</a>
+    <a href="https://rust-analyzer.github.io/manual.html#visual-studio-2022"
+       class="button button-secondary">{{fluent "tools-editor-visual-studio"}}</a>
   </div>
 </div>
diff --git a/templates/governance/group-team.html.hbs b/templates/governance/group-team.html.hbs
index 670037b2b..5fd32b72d 100644
--- a/templates/governance/group-team.html.hbs
+++ b/templates/governance/group-team.html.hbs
@@ -60,6 +60,10 @@
                         </div>
                         {{#if member.is_lead}}
                             <div>{{fluent "governance-user-team-leader"}}</div>
+                        {{else}}
+                            {{#if member.roles}}
+                                <div>{{team-text team role (lookup member.roles 0)}}</div>
+                            {{/if}}
                         {{/if}}
                     </div>
                 </div>