about summary refs log tree commit diff stats
path: root/pkgs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs')
-rw-r--r--pkgs/by-name/ba/back/Cargo.lock973
-rw-r--r--pkgs/by-name/ba/back/Cargo.toml10
-rw-r--r--pkgs/by-name/ba/back/contrib/config.json5
-rw-r--r--pkgs/by-name/ba/back/src/config/mod.rs110
-rw-r--r--pkgs/by-name/ba/back/src/error/mod.rs66
-rw-r--r--pkgs/by-name/ba/back/src/error/responder.rs23
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/dag/mod.rs14
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/format/mod.rs15
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/mod.rs2
-rw-r--r--pkgs/by-name/ba/back/src/main.rs33
-rw-r--r--pkgs/by-name/ba/back/src/web/generate/mod.rs227
-rw-r--r--pkgs/by-name/ba/back/src/web/issue_html.rs166
-rw-r--r--pkgs/by-name/ba/back/src/web/mod.rs297
-rw-r--r--pkgs/by-name/ba/back/src/web/prefix.rs35
-rw-r--r--pkgs/by-name/ba/back/src/web/responses.rs50
-rw-r--r--pkgs/by-name/ba/back/templates/issue.html57
-rw-r--r--pkgs/by-name/ba/back/templates/issues.html60
-rw-r--r--pkgs/by-name/ba/back/templates/repos.html47
18 files changed, 935 insertions, 1255 deletions
diff --git a/pkgs/by-name/ba/back/Cargo.lock b/pkgs/by-name/ba/back/Cargo.lock
index 894a927..228a22f 100644
--- a/pkgs/by-name/ba/back/Cargo.lock
+++ b/pkgs/by-name/ba/back/Cargo.lock
@@ -132,39 +132,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
 [[package]]
-name = "async-stream"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
-dependencies = [
- "async-stream-impl",
- "futures-core",
- "pin-project-lite",
-]
-
-[[package]]
-name = "async-stream-impl"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "async-trait"
-version = "0.1.87"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "atom_syndication"
 version = "0.12.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -178,19 +145,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "atomic"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
-
-[[package]]
-name = "atomic"
-version = "0.6.0"
+name = "atomic-waker"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
-dependencies = [
- "bytemuck",
-]
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
 
 [[package]]
 name = "autocfg"
@@ -202,16 +160,24 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 name = "back"
 version = "0.1.0"
 dependencies = [
+ "bytes",
  "chrono",
  "clap",
  "gix",
+ "http",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "log",
  "markdown",
- "rocket",
+ "rinja",
  "rss",
  "serde",
  "serde_json",
  "sha2",
+ "stderrlog",
  "thiserror",
+ "tokio",
  "url",
 ]
 
@@ -227,14 +193,17 @@ dependencies = [
  "miniz_oxide",
  "object",
  "rustc-demangle",
- "windows-targets 0.52.6",
+ "windows-targets",
 ]
 
 [[package]]
-name = "binascii"
-version = "0.1.4"
+name = "basic-toml"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
+checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "bitflags"
@@ -258,7 +227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
 dependencies = [
  "memchr",
- "regex-automata 0.4.9",
+ "regex-automata",
  "serde",
 ]
 
@@ -269,18 +238,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
 
 [[package]]
-name = "bytemuck"
-version = "1.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
-
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
-[[package]]
 name = "bytes"
 version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -374,17 +331,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
 
 [[package]]
-name = "cookie"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
-dependencies = [
- "percent-encoding",
- "time",
- "version_check",
-]
-
-[[package]]
 name = "core-foundation-sys"
 version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -483,15 +429,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "deranged"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
-dependencies = [
- "powerfmt",
-]
-
-[[package]]
 name = "derive_builder"
 version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -523,39 +460,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "devise"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d"
-dependencies = [
- "devise_codegen",
- "devise_core",
-]
-
-[[package]]
-name = "devise_codegen"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867"
-dependencies = [
- "devise_core",
- "quote",
-]
-
-[[package]]
-name = "devise_core"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7"
-dependencies = [
- "bitflags",
- "proc-macro2",
- "proc-macro2-diagnostics",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "digest"
 version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -592,12 +496,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
 
 [[package]]
-name = "either"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
-
-[[package]]
 name = "encoding_rs"
 version = "0.8.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -638,20 +536,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
-name = "figment"
-version = "0.10.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
-dependencies = [
- "atomic 0.6.0",
- "pear",
- "serde",
- "toml",
- "uncased",
- "version_check",
-]
-
-[[package]]
 name = "filetime"
 version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -695,27 +579,12 @@ dependencies = [
 ]
 
 [[package]]
-name = "futures"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
 name = "futures-channel"
 version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
 dependencies = [
  "futures-core",
- "futures-sink",
 ]
 
 [[package]]
@@ -725,12 +594,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
 
 [[package]]
-name = "futures-io"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
-
-[[package]]
 name = "futures-sink"
 version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -748,28 +611,10 @@ version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
- "futures-channel",
  "futures-core",
- "futures-io",
- "futures-sink",
  "futures-task",
- "memchr",
  "pin-project-lite",
  "pin-utils",
- "slab",
-]
-
-[[package]]
-name = "generator"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
-dependencies = [
- "cc",
- "libc",
- "log",
- "rustversion",
- "windows",
 ]
 
 [[package]]
@@ -784,17 +629,6 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
@@ -802,7 +636,7 @@ dependencies = [
  "cfg-if",
  "libc",
  "wasi 0.13.3+wasi-0.2.2",
- "windows-targets 0.52.6",
+ "windows-targets",
 ]
 
 [[package]]
@@ -882,7 +716,7 @@ dependencies = [
  "gix-utils",
  "itoa",
  "thiserror",
- "winnow 0.6.26",
+ "winnow",
 ]
 
 [[package]]
@@ -978,7 +812,7 @@ dependencies = [
  "smallvec",
  "thiserror",
  "unicode-bom",
- "winnow 0.6.26",
+ "winnow",
 ]
 
 [[package]]
@@ -1269,7 +1103,7 @@ dependencies = [
  "itoa",
  "smallvec",
  "thiserror",
- "winnow 0.6.26",
+ "winnow",
 ]
 
 [[package]]
@@ -1393,7 +1227,7 @@ dependencies = [
  "gix-utils",
  "maybe-async",
  "thiserror",
- "winnow 0.6.26",
+ "winnow",
 ]
 
 [[package]]
@@ -1425,7 +1259,7 @@ dependencies = [
  "gix-validate",
  "memmap2",
  "thiserror",
- "winnow 0.6.26",
+ "winnow",
 ]
 
 [[package]]
@@ -1685,23 +1519,17 @@ dependencies = [
 ]
 
 [[package]]
-name = "glob"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
-
-[[package]]
 name = "h2"
-version = "0.3.26"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
+checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
 dependencies = [
+ "atomic-waker",
  "bytes",
  "fnv",
  "futures-core",
  "futures-sink",
- "futures-util",
- "http 0.2.12",
+ "http",
  "indexmap",
  "slab",
  "tokio",
@@ -1736,12 +1564,6 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
-
-[[package]]
-name = "hermit-abi"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
@@ -1757,9 +1579,9 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "0.2.12"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
 dependencies = [
  "bytes",
  "fnv",
@@ -1767,24 +1589,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "http"
-version = "1.2.0"
+name = "http-body"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
 dependencies = [
  "bytes",
- "fnv",
- "itoa",
+ "http",
 ]
 
 [[package]]
-name = "http-body"
-version = "0.4.6"
+name = "http-body-util"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
 dependencies = [
  "bytes",
- "http 0.2.12",
+ "futures-util",
+ "http",
+ "http-body",
  "pin-project-lite",
 ]
 
@@ -1807,27 +1630,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5c3b1f728c459d27b12448862017b96ad4767b1ec2ec5e6434e99f1577f085b8"
 
 [[package]]
+name = "humansize"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
+dependencies = [
+ "libm",
+]
+
+[[package]]
 name = "hyper"
-version = "0.14.32"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
+checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
 dependencies = [
  "bytes",
  "futures-channel",
- "futures-core",
  "futures-util",
  "h2",
- "http 0.2.12",
+ "http",
  "http-body",
  "httparse",
  "httpdate",
  "itoa",
  "pin-project-lite",
- "socket2",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
  "tokio",
- "tower-service",
- "tracing",
- "want",
 ]
 
 [[package]]
@@ -2015,16 +1858,9 @@ checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
 dependencies = [
  "equivalent",
  "hashbrown 0.15.2",
- "serde",
 ]
 
 [[package]]
-name = "inlinable_string"
-version = "0.1.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
-
-[[package]]
 name = "io-close"
 version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2040,7 +1876,7 @@ version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
 dependencies = [
- "hermit-abi 0.5.0",
+ "hermit-abi",
  "libc",
  "windows-sys 0.59.0",
 ]
@@ -2106,18 +1942,18 @@ dependencies = [
 ]
 
 [[package]]
-name = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-
-[[package]]
 name = "libc"
 version = "0.2.170"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
 
 [[package]]
+name = "libm"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
+
+[[package]]
 name = "libredox"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2163,21 +1999,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
 
 [[package]]
-name = "loom"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
-dependencies = [
- "cfg-if",
- "generator",
- "scoped-tls",
- "serde",
- "serde_json",
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
 name = "markdown"
 version = "1.0.0-alpha.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2187,15 +2008,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "matchers"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
-dependencies = [
- "regex-automata 0.1.10",
-]
-
-[[package]]
 name = "maybe-async"
 version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2228,6 +2040,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
+name = "mime_guess"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
 name = "miniz_oxide"
 version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2248,47 +2076,22 @@ dependencies = [
 ]
 
 [[package]]
-name = "multer"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
-dependencies = [
- "bytes",
- "encoding_rs",
- "futures-util",
- "http 1.2.0",
- "httparse",
- "memchr",
- "mime",
- "spin",
- "tokio",
- "tokio-util",
- "version_check",
-]
-
-[[package]]
 name = "never"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
 
 [[package]]
-name = "nu-ansi-term"
-version = "0.46.0"
+name = "nom"
+version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
- "overload",
- "winapi",
+ "memchr",
+ "minimal-lexical",
 ]
 
 [[package]]
-name = "num-conv"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
-
-[[package]]
 name = "num-traits"
 version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2298,16 +2101,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi 0.3.9",
- "libc",
-]
-
-[[package]]
 name = "object"
 version = "0.36.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2323,12 +2116,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
 
 [[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
-[[package]]
 name = "parking_lot"
 version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2348,30 +2135,7 @@ dependencies = [
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "pear"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467"
-dependencies = [
- "inlinable_string",
- "pear_codegen",
- "yansi",
-]
-
-[[package]]
-name = "pear_codegen"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147"
-dependencies = [
- "proc-macro2",
- "proc-macro2-diagnostics",
- "quote",
- "syn",
+ "windows-targets",
 ]
 
 [[package]]
@@ -2408,21 +2172,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "powerfmt"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
-dependencies = [
- "zerocopy",
-]
-
-[[package]]
 name = "proc-macro2"
 version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2432,19 +2181,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "proc-macro2-diagnostics"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "version_check",
- "yansi",
-]
-
-[[package]]
 name = "prodash"
 version = "29.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2476,36 +2212,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.15",
-]
-
-[[package]]
 name = "redox_syscall"
 version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2515,26 +2221,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "ref-cast"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
-dependencies = [
- "ref-cast-impl",
-]
-
-[[package]]
-name = "ref-cast-impl"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "regex"
 version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2542,17 +2228,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
+ "regex-automata",
+ "regex-syntax",
 ]
 
 [[package]]
@@ -2563,100 +2240,54 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax 0.8.5",
+ "regex-syntax",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
-[[package]]
-name = "regex-syntax"
 version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
-name = "rocket"
-version = "0.5.1"
+name = "rinja"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f"
+checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5"
 dependencies = [
- "async-stream",
- "async-trait",
- "atomic 0.5.3",
- "binascii",
- "bytes",
- "either",
- "figment",
- "futures",
- "indexmap",
- "log",
- "memchr",
- "multer",
- "num_cpus",
- "parking_lot",
- "pin-project-lite",
- "rand",
- "ref-cast",
- "rocket_codegen",
- "rocket_http",
- "serde",
- "state",
- "tempfile",
- "time",
- "tokio",
- "tokio-stream",
- "tokio-util",
- "ubyte",
- "version_check",
- "yansi",
+ "humansize",
+ "itoa",
+ "percent-encoding",
+ "rinja_derive",
 ]
 
 [[package]]
-name = "rocket_codegen"
-version = "0.5.1"
+name = "rinja_derive"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46"
+checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b"
 dependencies = [
- "devise",
- "glob",
- "indexmap",
+ "basic-toml",
+ "memchr",
+ "mime",
+ "mime_guess",
  "proc-macro2",
  "quote",
- "rocket_http",
+ "rinja_parser",
+ "rustc-hash",
+ "serde",
  "syn",
- "unicode-xid",
- "version_check",
 ]
 
 [[package]]
-name = "rocket_http"
-version = "0.5.1"
+name = "rinja_parser"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9"
+checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610"
 dependencies = [
- "cookie",
- "either",
- "futures",
- "http 0.2.12",
- "hyper",
- "indexmap",
- "log",
  "memchr",
- "pear",
- "percent-encoding",
- "pin-project-lite",
- "ref-cast",
+ "nom",
  "serde",
- "smallvec",
- "stable-pattern",
- "state",
- "time",
- "tokio",
- "uncased",
 ]
 
 [[package]]
@@ -2678,6 +2309,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
 [[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
 name = "rustix"
 version = "0.38.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2725,12 +2362,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "scoped-tls"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
-
-[[package]]
 name = "scopeguard"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2769,15 +2400,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "serde_spanned"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
-dependencies = [
- "serde",
-]
-
-[[package]]
 name = "sha1_smol"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2795,15 +2417,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "sharded-slab"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
 name = "shell-words"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2860,42 +2473,31 @@ dependencies = [
 ]
 
 [[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-
-[[package]]
-name = "stable-pattern"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045"
-dependencies = [
- "memchr",
-]
-
-[[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
 [[package]]
-name = "state"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
-dependencies = [
- "loom",
-]
-
-[[package]]
 name = "static_assertions"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
 [[package]]
+name = "stderrlog"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c910772f992ab17d32d6760e167d2353f4130ed50e796752689556af07dc6b"
+dependencies = [
+ "chrono",
+ "is-terminal",
+ "log",
+ "termcolor",
+ "thread_local",
+]
+
+[[package]]
 name = "strsim"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2931,13 +2533,22 @@ checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567"
 dependencies = [
  "cfg-if",
  "fastrand",
- "getrandom 0.3.1",
+ "getrandom",
  "once_cell",
  "rustix 1.0.1",
  "windows-sys 0.59.0",
 ]
 
 [[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
 name = "thiserror"
 version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2968,37 +2579,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "time"
-version = "0.3.39"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
-dependencies = [
- "deranged",
- "itoa",
- "num-conv",
- "powerfmt",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
-
-[[package]]
-name = "time-macros"
-version = "0.2.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
-[[package]]
 name = "tinystr"
 version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3025,16 +2605,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.43.0"
+version = "1.44.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
+checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a"
 dependencies = [
  "backtrace",
  "bytes",
  "libc",
  "mio",
  "pin-project-lite",
- "signal-hook-registry",
  "socket2",
  "tokio-macros",
  "windows-sys 0.52.0",
@@ -3052,17 +2631,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "tokio-stream"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
-dependencies = [
- "futures-core",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
 name = "tokio-util"
 version = "0.7.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3076,128 +2644,31 @@ dependencies = [
 ]
 
 [[package]]
-name = "toml"
-version = "0.8.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
-dependencies = [
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_edit",
-]
-
-[[package]]
-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"
-version = "0.22.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
-dependencies = [
- "indexmap",
- "serde",
- "serde_spanned",
- "toml_datetime",
- "winnow 0.7.3",
-]
-
-[[package]]
-name = "tower-service"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
-
-[[package]]
 name = "tracing"
 version = "0.1.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
 dependencies = [
  "pin-project-lite",
- "tracing-attributes",
  "tracing-core",
 ]
 
 [[package]]
-name = "tracing-attributes"
-version = "0.1.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "tracing-core"
 version = "0.1.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
 dependencies = [
  "once_cell",
- "valuable",
 ]
 
 [[package]]
-name = "tracing-log"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
-dependencies = [
- "log",
- "once_cell",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.3.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
-dependencies = [
- "matchers",
- "nu-ansi-term",
- "once_cell",
- "regex",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing",
- "tracing-core",
- "tracing-log",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
-
-[[package]]
 name = "typenum"
 version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
 
 [[package]]
-name = "ubyte"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea"
-dependencies = [
- "serde",
-]
-
-[[package]]
 name = "uluru"
 version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3207,14 +2678,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "uncased"
-version = "0.9.10"
+name = "unicase"
+version = "2.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
-dependencies = [
- "serde",
- "version_check",
-]
+checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
 
 [[package]]
 name = "unicode-bom"
@@ -3244,12 +2711,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "unicode-xid"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
-
-[[package]]
 name = "url"
 version = "2.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3280,12 +2741,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
-name = "valuable"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
-
-[[package]]
 name = "version_check"
 version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3302,15 +2757,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
-[[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3415,21 +2861,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "windows"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
-dependencies = [
- "windows-targets 0.48.5",
-]
-
-[[package]]
 name = "windows-core"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets 0.52.6",
+ "windows-targets",
 ]
 
 [[package]]
@@ -3444,7 +2881,7 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.6",
+ "windows-targets",
 ]
 
 [[package]]
@@ -3453,22 +2890,7 @@ version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[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",
+ "windows-targets",
 ]
 
 [[package]]
@@ -3477,48 +2899,30 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
  "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
 ]
 
 [[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_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_aarch64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[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_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
@@ -3531,48 +2935,24 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_i686_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[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_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[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_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
-[[package]]
-name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
@@ -3587,15 +2967,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "winnow"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
-dependencies = [
- "memchr",
-]
-
-[[package]]
 name = "wit-bindgen-rt"
 version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3617,15 +2988,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
 
 [[package]]
-name = "yansi"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
-dependencies = [
- "is-terminal",
-]
-
-[[package]]
 name = "yoke"
 version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3655,7 +3017,6 @@ version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
- "byteorder",
  "zerocopy-derive",
 ]
 
diff --git a/pkgs/by-name/ba/back/Cargo.toml b/pkgs/by-name/ba/back/Cargo.toml
index e19bf06..7fabeed 100644
--- a/pkgs/by-name/ba/back/Cargo.toml
+++ b/pkgs/by-name/ba/back/Cargo.toml
@@ -22,16 +22,24 @@ license = "AGPL-3.0-or-later"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
+bytes = "1.10.1"
 chrono = "0.4.40"
 clap = { version = "4.5.31", features = ["derive"] }
 gix = "0.70.0"
+http = "1.2.0"
+http-body-util = "0.1.2"
+hyper = { version = "1.6.0", features = ["http1", "http2", "server"] }
+hyper-util = { version = "0.1.10", features = ["tokio"] }
+log = "0.4.26"
 markdown = "1.0.0-alpha.23"
-rocket = "0.5.1"
+rinja = "0.3.5"
 rss = "2.0.12"
 serde = "1.0.218"
 serde_json = "1.0.140"
 sha2 = "0.10.8"
+stderrlog = "0.6.0"
 thiserror = "2.0.12"
+tokio = { version = "1.44.0", features = ["macros", "net", "rt-multi-thread"] }
 url = { version = "2.5.4", features = ["serde"] }
 
 [profile.release]
diff --git a/pkgs/by-name/ba/back/contrib/config.json b/pkgs/by-name/ba/back/contrib/config.json
index 2347bf2..81d1041 100644
--- a/pkgs/by-name/ba/back/contrib/config.json
+++ b/pkgs/by-name/ba/back/contrib/config.json
@@ -1,5 +1,6 @@
 {
     "source_code_repository_url": "https://git.foss-syndicate.org/vhack.eu/nixos-server/tree/pkgs/by-name/ba/back",
-    "repository_path": "/path/to/your/repository"
-    "root_url": "https://issues.foss-syndicate.org"
+    "root_url": "https://issues.foss-syndicate.org",
+    "scan_path": "/path/to/the/scan/path",
+    "project_list": "/path/to/the/projects.list"
 }
diff --git a/pkgs/by-name/ba/back/src/config/mod.rs b/pkgs/by-name/ba/back/src/config/mod.rs
index 7351ad8..1161ce3 100644
--- a/pkgs/by-name/ba/back/src/config/mod.rs
+++ b/pkgs/by-name/ba/back/src/config/mod.rs
@@ -18,21 +18,97 @@ use gix::ThreadSafeRepository;
 use serde::Deserialize;
 use url::Url;
 
-use crate::error::{self, Error};
+use crate::{
+    error::{self, Error},
+    git_bug::dag::is_git_bug,
+};
 
 pub struct BackConfig {
-    // NOTE(@bpeetz): We do not need to html escape this, as the value must be a valid url. As such
-    // `<tags>` of all kinds _should_ be invalid.  <2024-12-26>
+    /// The url to the source code of back. This is needed, because back is licensed under the
+    /// AGPL.
     pub source_code_repository_url: Url,
-    pub repository: ThreadSafeRepository,
+
+    /// A list of the repositories known to back.
+    /// This list is constructed from the `scan_path` and the `project_list` file.
+    pub repositories: BackRepositories,
+
+    /// The root url this instance of back is hosted on.
+    /// For example:
+    ///     `issues.foss-syndicate.org`
     pub root: Url,
 }
 
+pub struct BackRepositories {
+    repositories: Vec<BackRepository>,
+
+    /// The path that is the common parent of all the repositories.
+    scan_path: PathBuf,
+}
+
+impl BackRepositories {
+    pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
+        self.into_iter()
+    }
+}
+
+impl<'a> IntoIterator for &'a BackRepositories {
+    type Item = <&'a Vec<BackRepository> as IntoIterator>::Item;
+
+    type IntoIter = <&'a Vec<BackRepository> as IntoIterator>::IntoIter;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.repositories.iter()
+    }
+}
+
+impl BackRepositories {
+    /// Try to get the repository at path `path`.
+    /// If no repository was registered/found at `path`, returns an error.
+    pub fn get(&self, path: &Path) -> Result<&BackRepository, error::Error> {
+        self.repositories
+            .iter()
+            .find(|p| p.repo_path == path)
+            .ok_or(error::Error::RepoFind {
+                repository_path: path.to_owned(),
+            })
+    }
+
+    pub fn scan_path(&self) -> &Path {
+        &self.scan_path
+    }
+}
+
+pub struct BackRepository {
+    repo_path: PathBuf,
+}
+
+impl BackRepository {
+    pub fn open(&self, scan_path: &Path) -> Result<ThreadSafeRepository, error::Error> {
+        let repo = ThreadSafeRepository::open(scan_path.join(&self.repo_path)).map_err(|err| {
+            Error::RepoOpen {
+                repository_path: self.repo_path.to_owned(),
+                error: Box::new(err),
+            }
+        })?;
+        if is_git_bug(&repo.to_thread_local())? {
+            Ok(repo)
+        } else {
+            Err(error::Error::NotGitBug {
+                path: self.repo_path.clone(),
+            })
+        }
+    }
+    pub fn path(&self) -> &Path {
+        &self.repo_path
+    }
+}
+
 #[derive(Deserialize)]
 struct RawBackConfig {
     source_code_repository_url: Url,
-    repository_path: PathBuf,
     root_url: Url,
+    project_list: PathBuf,
+    scan_path: PathBuf,
 }
 
 impl BackConfig {
@@ -56,16 +132,26 @@ impl TryFrom<RawBackConfig> for BackConfig {
     type Error = error::Error;
 
     fn try_from(value: RawBackConfig) -> Result<Self, Self::Error> {
-        let repository = {
-            ThreadSafeRepository::open(&value.repository_path).map_err(|err| Error::RepoOpen {
-                repository_path: value.repository_path,
-                error: Box::new(err),
-            })
-        }?;
+        let repositories = fs::read_to_string(&value.project_list)
+            .map_err(|err| error::Error::ProjectListRead {
+                error: err,
+                file: value.project_list,
+            })?
+            .lines()
+            .try_fold(vec![], |mut acc, path| {
+                acc.push(BackRepository {
+                    repo_path: PathBuf::from(path),
+                });
+
+                Ok::<_, Self::Error>(acc)
+            })?;
 
         Ok(Self {
-            repository,
             source_code_repository_url: value.source_code_repository_url,
+            repositories: BackRepositories {
+                repositories,
+                scan_path: value.scan_path,
+            },
             root: value.root_url,
         })
     }
diff --git a/pkgs/by-name/ba/back/src/error/mod.rs b/pkgs/by-name/ba/back/src/error/mod.rs
index 8b71700..8889033 100644
--- a/pkgs/by-name/ba/back/src/error/mod.rs
+++ b/pkgs/by-name/ba/back/src/error/mod.rs
@@ -9,37 +9,53 @@
 // You should have received a copy of the License along with this program.
 // If not, see <https://www.gnu.org/licenses/agpl.txt>.
 
-use std::{fmt::Display, io, path::PathBuf};
+use std::{fmt::Display, io, net::SocketAddr, path::PathBuf};
 
+use gix::hash::Prefix;
 use thiserror::Error;
 
-use crate::web::prefix::BackPrefix;
-
 pub type Result<T> = std::result::Result<T, Error>;
 
-pub mod responder;
-
 #[derive(Error, Debug)]
 pub enum Error {
     ConfigParse {
         file: PathBuf,
         error: serde_json::Error,
     },
+
+    ProjectListRead {
+        file: PathBuf,
+        error: io::Error,
+    },
     ConfigRead {
         file: PathBuf,
         error: io::Error,
     },
-    RocketLaunch(#[from] rocket::Error),
-
+    NotGitBug {
+        path: PathBuf,
+    },
     RepoOpen {
         repository_path: PathBuf,
         error: Box<gix::open::Error>,
     },
+    RepoFind {
+        repository_path: PathBuf,
+    },
     RepoRefsIter(#[from] gix::refs::packed::buffer::open::Error),
-    RepoRefsPrefixed(#[from] std::io::Error),
+    RepoRefsPrefixed {
+        error: io::Error,
+    },
+
+    TcpBind {
+        addr: SocketAddr,
+        err: io::Error,
+    },
+    TcpAccept {
+        err: io::Error,
+    },
 
     IssuesPrefixMissing {
-        prefix: BackPrefix,
+        prefix: Prefix,
     },
     IssuesPrefixParse(#[from] gix::hash::prefix::from_hex::Error),
 }
@@ -54,6 +70,13 @@ impl Display for Error {
                     file.display()
                 )
             }
+            Error::ProjectListRead { file, error } => {
+                write!(
+                    f,
+                    "while trying to read the project.list file ({}): {error}",
+                    file.display()
+                )
+            }
             Error::ConfigRead { file, error } => {
                 write!(
                     f,
@@ -61,9 +84,6 @@ impl Display for Error {
                     file.display()
                 )
             }
-            Error::RocketLaunch(error) => {
-                write!(f, "while trying to start back: {error}")
-            }
             Error::RepoOpen {
                 repository_path,
                 error,
@@ -74,10 +94,24 @@ impl Display for Error {
                     repository_path.display()
                 )
             }
+            Error::NotGitBug { path } => {
+                write!(
+                    f,
+                    "Repository ('{}') has no initialized git-bug data",
+                    path.display()
+                )
+            }
+            Error::RepoFind { repository_path } => {
+                write!(
+                    f,
+                    "failed to find the repository at path: '{}'",
+                    repository_path.display()
+                )
+            }
             Error::RepoRefsIter(error) => {
                 write!(f, "while iteration over the refs in a repository: {error}",)
             }
-            Error::RepoRefsPrefixed(error) => {
+            Error::RepoRefsPrefixed { error, .. } => {
                 write!(f, "while prefixing the refs with a path: {error}")
             }
             Error::IssuesPrefixMissing { prefix } => {
@@ -89,6 +123,12 @@ impl Display for Error {
             Error::IssuesPrefixParse(error) => {
                 write!(f, "The given prefix can not be parsed as prefix: {error}")
             }
+            Error::TcpBind { addr, err } => {
+                write!(f, "while trying to open tcp {addr} for listening: {err}.")
+            }
+            Error::TcpAccept { err } => {
+                write!(f, "while trying to accept a tcp connection: {err}.")
+            }
         }
     }
 }
diff --git a/pkgs/by-name/ba/back/src/error/responder.rs b/pkgs/by-name/ba/back/src/error/responder.rs
deleted file mode 100644
index 7bea961..0000000
--- a/pkgs/by-name/ba/back/src/error/responder.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use rocket::{
-    response::{self, Responder, Response},
-    Request,
-};
-
-use super::Error;
-
-impl<'r> Responder<'r, 'static> for Error {
-    fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
-        Response::build_from(self.to_string().respond_to(req)?).ok()
-    }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
index 9c158a7..3d22b04 100644
--- a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
+++ b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
@@ -123,11 +123,23 @@ impl Dag {
     }
 }
 
+/// Check whether `git-bug` has been initialized in this repository
+pub fn is_git_bug(repo: &Repository) -> error::Result<bool> {
+    Ok(repo
+        .refs
+        .iter()?
+        .prefixed(Path::new("refs/bugs/"))
+        .map_err(|err| error::Error::RepoRefsPrefixed { error: err })?
+        .count()
+        > 0)
+}
+
 pub fn issues_from_repository(repo: &Repository) -> error::Result<Vec<Dag>> {
     let dags = repo
         .refs
         .iter()?
-        .prefixed(Path::new("refs/bugs/"))?
+        .prefixed(Path::new("refs/bugs/"))
+        .map_err(|err| error::Error::RepoRefsPrefixed { error: err })?
         .map(|val| {
             let reference = val.expect("All `git-bug` references in 'refs/bugs' should be objects");
 
diff --git a/pkgs/by-name/ba/back/src/git_bug/format/mod.rs b/pkgs/by-name/ba/back/src/git_bug/format/mod.rs
index b3b6bcc..ffe44fd 100644
--- a/pkgs/by-name/ba/back/src/git_bug/format/mod.rs
+++ b/pkgs/by-name/ba/back/src/git_bug/format/mod.rs
@@ -16,8 +16,8 @@ use markdown::to_html;
 use serde::Deserialize;
 use serde_json::Value;
 
-#[derive(Debug, Default, Clone)]
 /// Markdown content.
+#[derive(Debug, Default, Clone)]
 pub struct MarkDown {
     value: String,
 }
@@ -88,6 +88,19 @@ pub struct HtmlString {
     value: String,
 }
 
+impl From<String> for HtmlString {
+    fn from(value: String) -> Self {
+        Self { value }
+    }
+}
+impl From<&str> for HtmlString {
+    fn from(value: &str) -> Self {
+        Self {
+            value: value.to_owned(),
+        }
+    }
+}
+
 impl From<MarkDown> for HtmlString {
     fn from(value: MarkDown) -> Self {
         Self { value: value.value }
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs
index f27bfec..d382b54 100644
--- a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs
+++ b/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs
@@ -128,7 +128,7 @@ impl RawCollapsedIssue {
                 } => {
                     self.id = Some(entity.id.clone());
                     self.author = Some(entity.author.clone());
-                    self.timestamp = Some(timestamp.clone());
+                    self.timestamp = Some(timestamp);
                     self.title = Some(title);
                     self.message = Some(message);
                     self.status = Some(Status::Open); // This is the default in git_bug
diff --git a/pkgs/by-name/ba/back/src/main.rs b/pkgs/by-name/ba/back/src/main.rs
index 961c39b..61953c4 100644
--- a/pkgs/by-name/ba/back/src/main.rs
+++ b/pkgs/by-name/ba/back/src/main.rs
@@ -9,14 +9,11 @@
 // You should have received a copy of the License along with this program.
 // If not, see <https://www.gnu.org/licenses/agpl.txt>.
 
-use std::process;
+use std::{process, sync::Arc};
 
 use clap::Parser;
-use config::BackConfig;
-use rocket::routes;
-use web::feed;
 
-use crate::web::{closed, open, show_issue, styles};
+use crate::config::BackConfig;
 
 mod cli;
 pub mod config;
@@ -25,7 +22,7 @@ pub mod git_bug;
 mod web;
 
 fn main() -> Result<(), String> {
-    if let Err(err) = rocket_main() {
+    if let Err(err) = server_main() {
         eprintln!("Error {err}");
         process::exit(1);
     } else {
@@ -33,20 +30,24 @@ fn main() -> Result<(), String> {
     }
 }
 
-#[rocket::main]
-async fn rocket_main() -> Result<(), error::Error> {
+#[tokio::main]
+async fn server_main() -> Result<(), error::Error> {
     let args = cli::Cli::parse();
 
+    stderrlog::new()
+        .module(module_path!())
+        .modules(["hyper", "http"])
+        .quiet(false)
+        .show_module_names(false)
+        .color(stderrlog::ColorChoice::Auto)
+        .verbosity(2)
+        .timestamp(stderrlog::Timestamp::Off)
+        .init()
+        .expect("Let's just hope that this does not panic");
+
     let config = BackConfig::from_config_file(&args.config_file)?;
 
-    rocket::build()
-        .mount("/", routes![open, closed, show_issue, styles, feed])
-        .manage(config)
-        .ignite()
-        .await
-        .expect("This error should only happen on a miss-configuration.")
-        .launch()
-        .await?;
+    web::main(Arc::new(config)).await?;
 
     Ok(())
 }
diff --git a/pkgs/by-name/ba/back/src/web/generate/mod.rs b/pkgs/by-name/ba/back/src/web/generate/mod.rs
new file mode 100644
index 0000000..10146bb
--- /dev/null
+++ b/pkgs/by-name/ba/back/src/web/generate/mod.rs
@@ -0,0 +1,227 @@
+use std::{fs, path::Path};
+
+use gix::hash::Prefix;
+use log::info;
+use rinja::Template;
+use url::Url;
+
+use crate::{
+    config::BackConfig,
+    error,
+    git_bug::{
+        dag::issues_from_repository,
+        issue::{CollapsedIssue, Status},
+    },
+};
+
+#[derive(Template)]
+#[template(path = "./issues.html")]
+struct IssuesTemplate {
+    wanted_status: Status,
+    counter_status: Status,
+    issues: Vec<CollapsedIssue>,
+
+    /// The path to the repository
+    repo_path: String,
+
+    /// The URL to `back`'s source code
+    source_code_repository_url: Url,
+}
+pub fn issues(
+    config: &BackConfig,
+    wanted_status: Status,
+    counter_status: Status,
+    repo_path: &Path,
+) -> error::Result<String> {
+    let repository = config
+        .repositories
+        .get(repo_path)?
+        .open(config.repositories.scan_path())?;
+
+    let mut issue_list = issues_from_repository(&repository.to_thread_local())?
+        .into_iter()
+        .map(|issue| issue.collapse())
+        .filter(|issue| issue.status == wanted_status)
+        .collect::<Vec<CollapsedIssue>>();
+
+    // Sort by date descending.
+    // SAFETY:
+    // The time stamp is only used for sorting, so a malicious attacker could only affect the issue
+    // sorting.
+    issue_list.sort_by_key(|issue| unsafe { issue.timestamp.to_unsafe() });
+    issue_list.reverse();
+
+    Ok(IssuesTemplate {
+        wanted_status,
+        counter_status,
+        source_code_repository_url: config.source_code_repository_url.clone(),
+        issues: issue_list,
+        repo_path: repo_path.display().to_string(),
+    }
+    .render()
+    .expect("This should always work"))
+}
+
+use crate::git_bug::format::HtmlString;
+#[derive(Template)]
+#[template(path = "./issue.html")]
+struct IssueTemplate {
+    issue: CollapsedIssue,
+
+    /// The path to the repository
+    repo_path: String,
+
+    /// The URL to `back`'s source code
+    source_code_repository_url: Url,
+}
+pub fn issue(config: &BackConfig, repo_path: &Path, prefix: Prefix) -> error::Result<String> {
+    let repository = config
+        .repositories
+        .get(repo_path)?
+        .open(config.repositories.scan_path())?
+        .to_thread_local();
+
+    let maybe_issue = issues_from_repository(&repository)?
+        .into_iter()
+        .map(|val| val.collapse())
+        .find(|issue| issue.id.to_string().starts_with(&prefix.to_string()));
+
+    match maybe_issue {
+        Some(issue) => Ok(IssueTemplate {
+            issue,
+            repo_path: repo_path.display().to_string(),
+            source_code_repository_url: config.source_code_repository_url.clone(),
+        }
+        .render()
+        .expect("This should always work")),
+        None => Err(error::Error::IssuesPrefixMissing { prefix }),
+    }
+}
+
+#[derive(Template)]
+#[template(path = "./repos.html")]
+struct ReposTemplate {
+    repos: Vec<RepoValue>,
+
+    /// The URL to `back`'s source code
+    source_code_repository_url: Url,
+}
+struct RepoValue {
+    description: String,
+    owner: String,
+    path: String,
+}
+pub fn repos(config: &BackConfig) -> error::Result<String> {
+    let repos: Vec<RepoValue> = config
+        .repositories
+        .iter()
+        .filter_map(
+            |raw_repo| match raw_repo.open(config.repositories.scan_path()) {
+                Ok(repo) => {
+                    let repo = repo.to_thread_local();
+                    let git_config = repo.config_snapshot();
+
+                    let path = raw_repo.path().to_string_lossy().to_string();
+
+                    let owner = git_config
+                        .string("cgit.owner")
+                        .map(|v| v.to_string())
+                        .unwrap_or("<No owner>".to_owned());
+
+                    let description = fs::read_to_string(repo.git_dir().join("description"))
+                        .unwrap_or("<No description>".to_owned());
+
+                    Some(RepoValue {
+                        description,
+                        owner,
+                        path,
+                    })
+                }
+                Err(err) => {
+                    info!(
+                        "Repo '{}' could not be opened: '{err}'",
+                        raw_repo.path().display()
+                    );
+                    None
+                }
+            },
+        )
+        .collect();
+
+    Ok(ReposTemplate {
+        repos,
+        source_code_repository_url: config.source_code_repository_url.clone(),
+    }
+    .render()
+    .expect("this should work"))
+}
+
+pub fn feed(config: &BackConfig, repo_path: &Path) -> error::Result<String> {
+    use rss::{ChannelBuilder, Item, ItemBuilder};
+
+    let repository = config
+        .repositories
+        .get(repo_path)?
+        .open(config.repositories.scan_path())?
+        .to_thread_local();
+
+    let issues: Vec<CollapsedIssue> = issues_from_repository(&repository)?
+        .into_iter()
+        .map(|issue| issue.collapse())
+        .collect();
+
+    // Collect all Items as rss items
+    let mut items: Vec<Item> = issues
+        .iter()
+        .map(|issue| {
+            ItemBuilder::default()
+                .title(issue.title.to_string())
+                .author(issue.author.to_string())
+                .description(issue.message.to_string())
+                .pub_date(issue.timestamp.to_string())
+                .link(format!(
+                    "/{}/{}/issue/{}",
+                    repo_path.display(),
+                    &config.root,
+                    issue.id
+                ))
+                .build()
+        })
+        .collect();
+
+    // Append all comments after converting them to rss items
+    items.extend(
+        issues
+            .iter()
+            .filter(|issue| !issue.comments.is_empty())
+            .flat_map(|issue| {
+                issue
+                    .comments
+                    .iter()
+                    .map(|comment| {
+                        ItemBuilder::default()
+                            .title(issue.title.to_string())
+                            .author(comment.author.to_string())
+                            .description(comment.message.to_string())
+                            .pub_date(comment.timestamp.to_string())
+                            .link(format!(
+                                "/{}/{}/issue/{}",
+                                repo_path.display(),
+                                &config.root,
+                                issue.id
+                            ))
+                            .build()
+                    })
+                    .collect::<Vec<Item>>()
+            })
+            .collect::<Vec<Item>>(),
+    );
+
+    let channel = ChannelBuilder::default()
+        .title("Issues")
+        .link(config.root.to_string())
+        .description(format!("The rss feed for issues on {}.", &config.root))
+        .items(items)
+        .build();
+    Ok(channel.to_string())
+}
diff --git a/pkgs/by-name/ba/back/src/web/issue_html.rs b/pkgs/by-name/ba/back/src/web/issue_html.rs
deleted file mode 100644
index 45c0281..0000000
--- a/pkgs/by-name/ba/back/src/web/issue_html.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use rocket::response::content::RawHtml;
-
-use crate::{
-    config::BackConfig,
-    git_bug::{
-        format::HtmlString,
-        issue::{identity::Author, CollapsedIssue, Comment},
-    },
-};
-
-impl CollapsedIssue {
-    pub fn to_list_entry(&self) -> RawHtml<String> {
-        let comment_list = if self.comments.is_empty() {
-            String::new()
-        } else {
-            let comments_string = if self.comments.len() > 1 {
-                "comments"
-            } else {
-                "comment"
-            };
-
-            format!(
-                r#"
-                <span class="comment-count"> - {} {}</span>
-            "#,
-                self.comments.len(),
-                comments_string
-            )
-        };
-
-        let CollapsedIssue {
-            id,
-            title,
-            message: _,
-            author,
-            timestamp,
-            comments: _,
-            status: _,
-            last_status_change: _,
-            labels: _,
-        } = self;
-
-        let Author { name, email, id: _ } = author;
-
-        RawHtml(format!(
-            r#"
-               <li>
-                  <a href="/issue/{id}">
-                     <p>
-                        <span class="issue-subject">{title}</span>
-                     </p>
-                     <span class="issue-number">{id}</span> - <span class="created-by-at">Opened by <span class="user-name">{name}</span> <span class="user-email">&lt;{email}&gt;</span> at <span class="timestamp">{timestamp}</span></span>{comment_list}                  </a>
-               </li>
-"#,
-        ))
-    }
-
-    pub fn to_html(&self, config: &BackConfig) -> RawHtml<String> {
-        let comments = if self.comments.is_empty() {
-            String::new()
-        } else {
-            let fmt_comments: String = self
-                .comments
-                .iter()
-                .map(|val| {
-                    let Comment {
-                        id,
-                        author,
-                        message,
-                        timestamp,
-                    } = val;
-                    let Author {
-                        name,
-                        email: _,
-                        id: _,
-                    } = author;
-
-                    format!(
-                        r#"
-               <li class="comment" id="{id}">
-                  {message}
-                  <p class="comment-info"><span class="user-name">{name} at {timestamp}</span></p>
-               </li>
-                "#,
-                    )
-                })
-                .collect::<Vec<String>>()
-                .join("\n");
-
-            format!(
-                r#"
-            <ol class="issue-history">
-            {fmt_comments}
-            </ol>
-            "#
-            )
-        };
-
-        {
-            let CollapsedIssue {
-                id,
-                title,
-                message,
-                author,
-                timestamp,
-                comments: _,
-                status: _,
-                last_status_change: _,
-                labels: _,
-            } = self;
-            let Author { name, email, id: _ } = author;
-            let html_title = HtmlString::from(title.clone());
-
-            RawHtml(format!(
-                r#"
-<!DOCTYPE html>
-<html lang="en">
-   <head>
-      <title>{html_title} | Back</title>
-      <link href="/style.css" rel="stylesheet" type="text/css">
-      <meta content="width=device-width,initial-scale=1" name="viewport">
-   </head>
-   <body>
-      <div class="content">
-         <nav>
-         <a href="/issues/open">Open Issues</a>
-         <a href="/issues/closed">Closed Issues</a>
-         </nav>
-         <header>
-            <h1>{title}</h1>
-            <div class="issue-number">{id}</div>
-         </header>
-         <main>
-            <div class="issue-info">
-                <span class="created-by-at">Opened by <span class="user-name">{name}</span> <span class="user-email">&lt;{email}&gt;</span> at <span class="timestamp">{timestamp}</span></span>
-            </div>
-            {message}
-            {comments}
-         </main>
-         <footer>
-            <nav>
-            <a href="/issues/open">Open Issues</a>
-            <a href="{}">Source code</a>
-            <a href="/issues/closed">Closed Issues</a>
-            </nav>
-         </footer>
-      </div>
-   </body>
-</html>
-"#,
-                config.source_code_repository_url
-            ))
-        }
-    }
-}
diff --git a/pkgs/by-name/ba/back/src/web/mod.rs b/pkgs/by-name/ba/back/src/web/mod.rs
index f7a4077..cc087ab 100644
--- a/pkgs/by-name/ba/back/src/web/mod.rs
+++ b/pkgs/by-name/ba/back/src/web/mod.rs
@@ -1,186 +1,127 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use crate::{
-    config::BackConfig,
-    error::{self, Error},
-    git_bug::{
-        dag::issues_from_repository,
-        issue::{CollapsedIssue, Status},
-    },
-};
-use prefix::BackPrefix;
-use rocket::{
-    get,
-    response::content::{RawCss, RawHtml},
-    State,
-};
-
-mod issue_html;
-pub mod prefix;
-
-#[get("/style.css")]
-pub fn styles() -> RawCss<String> {
-    RawCss(include_str!("../../assets/style.css").to_owned())
-}
+use bytes::Bytes;
+use http_body_util::combinators::BoxBody;
+use hyper::{server::conn::http1, service::service_fn, Method, Request, Response, StatusCode};
+use hyper_util::rt::TokioIo;
+use log::{error, info};
+use responses::{html_response, html_response_status, html_response_status_content_type};
+use tokio::net::TcpListener;
+
+use std::{convert::Infallible, net::SocketAddr, path::PathBuf, sync::Arc};
+
+use crate::{config::BackConfig, error, git_bug::issue::Status};
+
+mod generate;
+mod responses;
+
+async fn match_uri(
+    config: Arc<BackConfig>,
+    req: Request<hyper::body::Incoming>,
+) -> Result<Response<BoxBody<Bytes, Infallible>>, hyper::Error> {
+    if req.method() != Method::GET {
+        return Ok(html_response_status(
+            "Only get requests are supported",
+            StatusCode::NOT_ACCEPTABLE,
+        ));
+    }
 
-pub fn issue_list_boilerplate(
-    config: &State<BackConfig>,
-    wanted_status: Status,
-    counter_status: Status,
-) -> error::Result<RawHtml<String>> {
-    let repository = &config.repository;
-
-    let mut issue_list = issues_from_repository(&repository.to_thread_local())?
-        .into_iter()
-        .map(|issue| issue.collapse())
-        .collect::<Vec<CollapsedIssue>>();
-
-    // Sort by date descending.
-    issue_list.sort_by_key(|issue| unsafe { issue.timestamp.to_unsafe() });
-    issue_list.reverse();
-
-    let issue_list_str = issue_list.into_iter().fold(String::new(), |acc, issue| {
-        format!("{}{}", acc, {
-            if issue.status == wanted_status {
-                let issue_entry = issue.to_list_entry();
-                issue_entry.0
-            } else {
-                String::new()
+    let output = || -> Result<Response<BoxBody<Bytes, Infallible>>, error::Error> {
+        match req.uri().path().trim_end_matches("/") {
+            "" => Ok(html_response(generate::repos(&config)?)),
+
+            "/style.css" => Ok(responses::html_response_status_content_type(
+                include_str!("../../assets/style.css"),
+                StatusCode::OK,
+                "text/css",
+            )),
+
+            path if path.ends_with("/issues/open") => {
+                let repo_path = PathBuf::from(
+                    path.strip_suffix("/issues/open")
+                        .expect("This suffix exists")
+                        .strip_prefix("/")
+                        .expect("This also exists"),
+                );
+
+                let issues = generate::issues(&config, Status::Open, Status::Closed, &repo_path)?;
+                Ok(html_response(issues))
+            }
+            path if path.ends_with("/issues/closed") => {
+                let repo_path = PathBuf::from(
+                    path.strip_suffix("/issues/closed")
+                        .expect("This suffix exists")
+                        .strip_prefix("/")
+                        .expect("This also exists"),
+                );
+
+                let issues = generate::issues(&config, Status::Closed, Status::Open, &repo_path)?;
+                Ok(html_response(issues))
+            }
+            path if path.ends_with("/issues/feed") => {
+                let repo_path = PathBuf::from(
+                    path.strip_suffix("/issues/feed")
+                        .expect("This suffix exists")
+                        .strip_prefix("/")
+                        .expect("This also exists"),
+                );
+
+                let feed = generate::feed(&config, &repo_path)?;
+                Ok(html_response_status_content_type(
+                    feed,
+                    StatusCode::OK,
+                    "text/xml",
+                ))
             }
-        })
-    });
-
-    let counter_status_lower = counter_status.to_string().to_lowercase();
-    Ok(RawHtml(format!(
-        r#"
-    <!DOCTYPE html>
-    <html lang="en">
-       <head>
-          <title>Back</title>
-          <link href="/style.css" rel="stylesheet" type="text/css">
-          <meta content="width=device-width,initial-scale=1" name="viewport">
-       </head>
-       <body>
-          <div class="content">
-             <header>
-                <h1>{wanted_status} Issues</h1>
-             </header>
-             <main>
-                <div class="issue-links">
-                   <a href="/issues/{counter_status_lower}/">View {counter_status} issues</a>
-                   <a href="{}">Source code</a>
-                   <!--
-                   <form class="issue-search" method="get">
-                       <input name="search" title="Issue search query" type="search">
-                       <input class="sr-only" type="submit" value="Search Issues">
-                   </form>
-                   -->
-                </div>
-                <ol class="issue-list">
-                {issue_list_str}
-                </ol>
-             </main>
-          </div>
-       </body>
-    </html>
-    "#,
-        config.source_code_repository_url
-    )))
-}
 
-#[get("/issues/open")]
-pub fn open(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
-    issue_list_boilerplate(config, Status::Open, Status::Closed)
-}
-#[get("/issues/closed")]
-pub fn closed(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
-    issue_list_boilerplate(config, Status::Closed, Status::Open)
-}
+            path if path.contains("/issue/") => {
+                let (repo_path, prefix) = {
+                    let split: Vec<&str> = path.split("/issue/").collect();
+
+                    let prefix =
+                        gix::hash::Prefix::from_hex(split[1]).map_err(error::Error::from)?;
+
+                    let repo_path =
+                        PathBuf::from(split[0].strip_prefix("/").expect("This prefix exists"));
 
-#[get("/issues/feed")]
-pub fn feed(config: &State<BackConfig>) -> error::Result<RawHtml<String>> {
-    use rss::{ChannelBuilder, Item, ItemBuilder};
-
-    //Collect all Items as rss items
-    let mut items: Vec<Item> = issues_from_repository(&config.repository.to_thread_local())?
-        .into_iter()
-        .map(|issue| issue.collapse())
-        .map(|issue| {
-            ItemBuilder::default()
-                .title(issue.title.to_string())
-                .author(issue.author.to_string())
-                .description(issue.message.to_string())
-                .pub_date(issue.timestamp.to_string())
-                .link(format!("{}/issue/{}", &config.root.to_string(), issue.id))
-                .build()
-        })
-        .collect();
-    //Append all comments after converting them to rss items
-    items.extend(
-        issues_from_repository(&config.repository.to_thread_local())?
-            .into_iter()
-            .map(|issue| issue.collapse())
-            .filter(|issue| issue.comments.len() > 0)
-            .map(|issue| {
-                issue
-                    .comments
-                    .into_iter()
-                    .map(|comment| {
-                        ItemBuilder::default()
-                            .title(issue.title.to_string())
-                            .author(comment.author.to_string())
-                            .description(comment.message.to_string())
-                            .pub_date(comment.timestamp.to_string())
-                            .link(format!("{}/issue/{}", &config.root.to_string(), issue.id))
-                            .build()
-                    })
-                    .collect::<Vec<Item>>()
-            })
-            .flatten()
-            .collect::<Vec<Item>>(),
-    );
-
-    let channel = ChannelBuilder::default()
-        .title("Issues")
-        .link(config.root.to_string())
-        .description(format!("The rss feed for issues on {}.", config.root))
-        .items(items)
-        .build();
-    Ok(RawHtml(channel.to_string()))
+                    (repo_path, prefix)
+                };
+                Ok(html_response(generate::issue(&config, &repo_path, prefix)?))
+            }
+
+            other => Ok(responses::html_response_status_content_type(
+                format!("'{}' not found", other),
+                StatusCode::NOT_FOUND,
+                "text/plain",
+            )),
+        }
+    };
+    match output() {
+        Ok(response) => Ok(response),
+        Err(err) => Ok(err.into_response()),
+    }
 }
 
-#[get("/issue/<prefix>")]
-pub fn show_issue(
-    config: &State<BackConfig>,
-    prefix: Result<BackPrefix, gix::hash::prefix::from_hex::Error>,
-) -> error::Result<RawHtml<String>> {
-    // NOTE(@bpeetz): Explicitly unwrap the `prefix` here (instead of taking the unwrapped value as
-    // argument), to avoid triggering rockets "errors forward to the next route" feature.
-    // This ensures, that our error message actually reaches the user. <2024-12-26>
-    let prefix = prefix?;
-
-    let repository = config.repository.to_thread_local();
-
-    let all_issues: Vec<CollapsedIssue> = issues_from_repository(&repository)?
-        .into_iter()
-        .map(|val| val.collapse())
-        .collect();
-
-    let maybe_issue = all_issues
-        .iter()
-        .find(|issue| issue.id.to_string().starts_with(&prefix.to_string()));
-
-    match maybe_issue {
-        Some(issue) => Ok(issue.to_html(config)),
-        None => Err(Error::IssuesPrefixMissing { prefix }),
+pub async fn main(config: Arc<BackConfig>) -> Result<(), error::Error> {
+    let addr: SocketAddr = ([127, 0, 0, 1], 8000).into();
+
+    let listener = TcpListener::bind(addr)
+        .await
+        .map_err(|err| error::Error::TcpBind { addr, err })?;
+    info!("Listening on http://{}", addr);
+    loop {
+        let (stream, _) = listener
+            .accept()
+            .await
+            .map_err(|err| error::Error::TcpAccept { err })?;
+        let io = TokioIo::new(stream);
+
+        let local_config = Arc::clone(&config);
+
+        let service = service_fn(move |req| match_uri(Arc::clone(&local_config), req));
+
+        tokio::task::spawn(async move {
+            if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
+                error!("Error serving connection: {:?}", err);
+            }
+        });
     }
 }
diff --git a/pkgs/by-name/ba/back/src/web/prefix.rs b/pkgs/by-name/ba/back/src/web/prefix.rs
deleted file mode 100644
index 5143799..0000000
--- a/pkgs/by-name/ba/back/src/web/prefix.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Back - An extremely simple git issue tracking system. Inspired by tvix's
-// panettone
-//
-// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This file is part of Back.
-//
-// You should have received a copy of the License along with this program.
-// If not, see <https://www.gnu.org/licenses/agpl.txt>.
-
-use std::fmt::Display;
-
-use gix::hash::Prefix;
-use rocket::request::FromParam;
-
-#[derive(Debug)]
-pub struct BackPrefix {
-    prefix: Prefix,
-}
-impl Display for BackPrefix {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.prefix.fmt(f)
-    }
-}
-
-impl<'a> FromParam<'a> for BackPrefix {
-    type Error = gix::hash::prefix::from_hex::Error;
-
-    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
-        let prefix = Prefix::from_hex(param)?;
-
-        Ok(Self { prefix })
-    }
-}
diff --git a/pkgs/by-name/ba/back/src/web/responses.rs b/pkgs/by-name/ba/back/src/web/responses.rs
new file mode 100644
index 0000000..e50f8c2
--- /dev/null
+++ b/pkgs/by-name/ba/back/src/web/responses.rs
@@ -0,0 +1,50 @@
+use std::convert::Infallible;
+
+use bytes::Bytes;
+use http::{Response, StatusCode, Version};
+use http_body_util::{combinators::BoxBody, BodyExt, Full};
+
+use crate::{error, git_bug::format::HtmlString};
+
+pub(super) fn html_response<T: Into<Bytes>>(html_text: T) -> Response<BoxBody<Bytes, Infallible>> {
+    html_response_status(html_text, StatusCode::OK)
+}
+
+pub(super) fn html_response_status<T: Into<Bytes>>(
+    html_text: T,
+    status: StatusCode,
+) -> Response<BoxBody<Bytes, Infallible>> {
+    html_response_status_content_type(html_text, status, "text/html")
+}
+
+pub(super) fn html_response_status_content_type<T: Into<Bytes>>(
+    html_text: T,
+    status: StatusCode,
+    content_type: &str,
+) -> Response<BoxBody<Bytes, Infallible>> {
+    Response::builder()
+        .status(status)
+        .version(Version::HTTP_2)
+        .header("Content-Type", format!("{}; charset=utf-8", content_type))
+        .header("x-content-type-options", "nosniff")
+        .header("x-frame-options", "SAMEORIGIN")
+        .body(full(html_text))
+        .expect("This will always build")
+}
+
+fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, Infallible> {
+    Full::new(chunk.into()).boxed()
+}
+
+// FIXME: Not all errors should return `INTERNAL_SERVER_ERROR`. <2025-03-08>
+impl error::Error {
+    pub fn into_response(self) -> Response<BoxBody<Bytes, Infallible>> {
+        html_response_status(
+            format!(
+                "<h1> Internal server error. </h1> <pre>Error: {}</pre>",
+                HtmlString::from(self.to_string())
+            ),
+            StatusCode::INTERNAL_SERVER_ERROR,
+        )
+    }
+}
diff --git a/pkgs/by-name/ba/back/templates/issue.html b/pkgs/by-name/ba/back/templates/issue.html
new file mode 100644
index 0000000..5b452c5
--- /dev/null
+++ b/pkgs/by-name/ba/back/templates/issue.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <title>{{ HtmlString::from(issue.title.clone()) }} | Back</title>
+        <link
+            href="/style.css"
+            rel="stylesheet"
+            type="text/css" />
+        <meta
+            content="width=device-width,initial-scale=1"
+            name="viewport" />
+    </head>
+    <body>
+        <div class="content">
+            <nav>
+                <a href="/{{repo_path}}/issues/open">Open Issues</a>
+                <a href="/{{repo_path}}/issues/closed">Closed Issues</a>
+            </nav>
+            <header>
+                <h1>{{issue.title|safe}}</h1>
+                <div class="issue-number">{{issue.id}}</div>
+            </header>
+            <main>
+                <div class="issue-info">
+                    <span class="created-by-at"
+                        >Opened by <span class="user-name">{{issue.author.name|safe}}</span>
+                        <span class="user-email">&lt;{{issue.author.email|safe}}&gt;</span> at
+                        <span class="timestamp">{{issue.timestamp}}</span></span
+                    >
+                </div>
+                {{issue.message|safe}} {% if !issue.comments.is_empty() %}
+                <ol class="issue-history">
+                    {% for comment in issue.comments %}
+                    <li
+                        class="comment"
+                        id="{{comment.id}}">
+                        {{comment.message|safe}}
+                        <p class="comment-info">
+                            <span class="user-name"
+                                >{{comment.author.name|safe}} at {{comment.timestamp}}</span
+                            >
+                        </p>
+                    </li>
+                    {% endfor %}
+                </ol>
+                {% endif %}
+            </main>
+            <footer>
+                <nav>
+                    <a href="/{{repo_path}}/issues/open">Open Issues</a>
+                    <a href="{{source_code_repository_url}}">Source code</a>
+                    <a href="/{{repo_path}}/issues/closed">Closed Issues</a>
+                </nav>
+            </footer>
+        </div>
+    </body>
+</html>
diff --git a/pkgs/by-name/ba/back/templates/issues.html b/pkgs/by-name/ba/back/templates/issues.html
new file mode 100644
index 0000000..b6cc9b8
--- /dev/null
+++ b/pkgs/by-name/ba/back/templates/issues.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <title>Back</title>
+        <link
+            href="/style.css"
+            rel="stylesheet"
+            type="text/css" />
+        <meta
+            content="width=device-width,initial-scale=1"
+            name="viewport" />
+    </head>
+    <body>
+        <div class="content">
+            <header>
+                <h1>{{wanted_status}} Issues</h1>
+            </header>
+            <main>
+                <div class="issue-links">
+                    <a href="/{{repo_path}}/issues/{{counter_status|lowercase}}/"
+                        >View {{counter_status}} issues</a
+                    >
+                    <a href="{{source_code_repository_url}}">Source code</a>
+                    <!--
+               <form class="issue-search" method="get">
+                   <input name="search" title="Issue search query" type="search">
+                   <input class="sr-only" type="submit" value="Search Issues">
+               </form>
+               -->
+                </div>
+                <ol class="issue-list">
+                    {% for issue in issues -%}
+                    <li>
+                        <a href="/{{repo_path}}/issue/{{issue.id}}">
+                            <p>
+                                <span class="issue-subject">{{issue.title|safe}}</span>
+                            </p>
+                            <span class="issue-number">{{issue.id}}</span>
+                            <span class="created-by-at"
+                                >Opened by {{ " " }}
+                                <span class="user-name">{{issue.author.name|safe}}</span>
+                                {{ " " }}
+                                <span class="user-email">&lt;{{issue.author.email|safe}}&gt;</span>
+                                {{ "at" }}
+                                <span class="timestamp">{{issue.timestamp}}</span>
+                            </span>
+                            {% if !issue.comments.is_empty() +%}
+                            <span class="comment-count">
+                                - {{issue.comments.len()}}
+                                comment{{issue.comments.len()|pluralize}}</span
+                            >
+                            {%+ endif %}
+                        </a>
+                    </li>
+                    {%- endfor %}
+                </ol>
+            </main>
+        </div>
+    </body>
+</html>
diff --git a/pkgs/by-name/ba/back/templates/repos.html b/pkgs/by-name/ba/back/templates/repos.html
new file mode 100644
index 0000000..dbccba0
--- /dev/null
+++ b/pkgs/by-name/ba/back/templates/repos.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <title>Back</title>
+        <link
+            href="/style.css"
+            rel="stylesheet"
+            type="text/css" />
+        <meta
+            content="width=device-width,initial-scale=1"
+            name="viewport" />
+    </head>
+    <body>
+        <div class="content">
+            <header>
+                <h1>Repositories</h1>
+            </header>
+            <main>
+                <div class="issue-links">
+                    <a href="{{source_code_repository_url}}">Source code</a>
+                    <!--
+               <form class="issue-search" method="get">
+                   <input name="search" title="Issue search query" type="search">
+                   <input class="sr-only" type="submit" value="Search Issues">
+               </form>
+               -->
+                </div>
+                <ol class="issue-list">
+                    {% for repo in repos -%}
+                    <li>
+                        <a href="/{{repo.path}}/issues/open">
+                            <p>
+                                <span class="issue-subject">{{repo.path}}</span>
+                            </p>
+                            <span class="created-by-at">
+                                <span class="timestamp">{{repo.description}}</span>
+                                {{ "-" }}
+                                <span class="user-name">{{repo.owner}}</span>
+                            </span>
+                        </a>
+                    </li>
+                    {%- endfor %}
+                </ol>
+            </main>
+        </div>
+    </body>
+</html>