about summary refs log tree commit diff stats
path: root/pkgs/by-name/ba/back/src
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-06 22:08:26 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-06 22:08:26 +0200
commit21b9a461dafeab63893d82a82d7b84ffe3a59c40 (patch)
treedfb8e657e2f36b426d9fba1b1a703431836d92e5 /pkgs/by-name/ba/back/src
parentflake.nix: Use the packaged version of `ragenix` (diff)
downloadnixos-server-21b9a461dafeab63893d82a82d7b84ffe3a59c40.zip
pkgs/back: Remove
Back has been moved out-of-tree.
Diffstat (limited to 'pkgs/by-name/ba/back/src')
-rw-r--r--pkgs/by-name/ba/back/src/cli.rs24
-rw-r--r--pkgs/by-name/ba/back/src/config/mod.rs136
-rw-r--r--pkgs/by-name/ba/back/src/error/mod.rs134
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/dag/mod.rs155
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/format/mod.rs157
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs78
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs79
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs85
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/mod.rs185
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs124
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs51
-rw-r--r--pkgs/by-name/ba/back/src/git_bug/mod.rs28
-rw-r--r--pkgs/by-name/ba/back/src/main.rs53
-rw-r--r--pkgs/by-name/ba/back/src/web/generate/mod.rs225
-rw-r--r--pkgs/by-name/ba/back/src/web/mod.rs127
-rw-r--r--pkgs/by-name/ba/back/src/web/responses.rs50
16 files changed, 0 insertions, 1691 deletions
diff --git a/pkgs/by-name/ba/back/src/cli.rs b/pkgs/by-name/ba/back/src/cli.rs
deleted file mode 100644
index 79f0d63..0000000
--- a/pkgs/by-name/ba/back/src/cli.rs
+++ /dev/null
@@ -1,24 +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::path::PathBuf;
-
-use clap::Parser;
-
-#[derive(Parser, Debug)]
-#[clap(author, version, about, long_about = None)]
-#[allow(clippy::module_name_repetitions)]
-/// An extremely simple git issue tracking system.
-/// Inspired by tvix's panettone
-pub struct Cli {
-    /// The path to the configuration file. The file should be written in JSON.
-    pub config_file: PathBuf,
-}
diff --git a/pkgs/by-name/ba/back/src/config/mod.rs b/pkgs/by-name/ba/back/src/config/mod.rs
deleted file mode 100644
index 832d060..0000000
--- a/pkgs/by-name/ba/back/src/config/mod.rs
+++ /dev/null
@@ -1,136 +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::{
-    fs,
-    path::{Path, PathBuf},
-};
-
-use gix::ThreadSafeRepository;
-use serde::Deserialize;
-use url::Url;
-
-use crate::{
-    error::{self, Error},
-    git_bug::dag::is_git_bug,
-};
-
-#[derive(Deserialize)]
-pub struct BackConfig {
-    /// The url to the source code of back. This is needed, because back is licensed under the
-    /// AGPL.
-    pub source_code_repository_url: Url,
-
-    /// The root url this instance of back is hosted on.
-    /// For example:
-    ///     `issues.foss-syndicate.org`
-    pub root_url: Url,
-
-    project_list: PathBuf,
-
-    /// The path that is the common parent of all the repositories.
-    pub scan_path: PathBuf,
-}
-
-pub struct BackRepositories {
-    repositories: Vec<BackRepository>,
-}
-
-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 struct BackRepository {
-    repo_path: PathBuf,
-}
-
-impl BackRepository {
-    pub fn open(&self, scan_path: &Path) -> Result<ThreadSafeRepository, error::Error> {
-        let path = {
-            let base = scan_path.join(&self.repo_path);
-            if base.is_dir() {
-                base
-            } else {
-                PathBuf::from(base.display().to_string() + ".git")
-            }
-        };
-        let repo = ThreadSafeRepository::open(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
-    }
-}
-
-impl BackConfig {
-    pub fn repositories(&self) -> error::Result<BackRepositories> {
-        let repositories = fs::read_to_string(&self.project_list)
-            .map_err(|err| error::Error::ProjectListRead {
-                error: err,
-                file: self.project_list.to_owned(),
-            })?
-            .lines()
-            .try_fold(vec![], |mut acc, path| {
-                acc.push(BackRepository {
-                    repo_path: PathBuf::from(path),
-                });
-
-                Ok::<_, error::Error>(acc)
-            })?;
-        Ok(BackRepositories { repositories })
-    }
-
-    pub fn from_config_file(path: &Path) -> error::Result<Self> {
-        let value = fs::read_to_string(path).map_err(|err| Error::ConfigRead {
-            file: path.to_owned(),
-            error: err,
-        })?;
-
-        serde_json::from_str(&value).map_err(|err| Error::ConfigParse {
-            file: path.to_owned(),
-            error: err,
-        })
-    }
-}
diff --git a/pkgs/by-name/ba/back/src/error/mod.rs b/pkgs/by-name/ba/back/src/error/mod.rs
deleted file mode 100644
index 8889033..0000000
--- a/pkgs/by-name/ba/back/src/error/mod.rs
+++ /dev/null
@@ -1,134 +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, io, net::SocketAddr, path::PathBuf};
-
-use gix::hash::Prefix;
-use thiserror::Error;
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-#[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,
-    },
-    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 {
-        error: io::Error,
-    },
-
-    TcpBind {
-        addr: SocketAddr,
-        err: io::Error,
-    },
-    TcpAccept {
-        err: io::Error,
-    },
-
-    IssuesPrefixMissing {
-        prefix: Prefix,
-    },
-    IssuesPrefixParse(#[from] gix::hash::prefix::from_hex::Error),
-}
-
-impl Display for Error {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Error::ConfigParse { file, error } => {
-                write!(
-                    f,
-                    "while trying to parse the config file ({}): {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,
-                    "while trying to read the config file ({}): {error}",
-                    file.display()
-                )
-            }
-            Error::RepoOpen {
-                repository_path,
-                error,
-            } => {
-                write!(
-                    f,
-                    "while trying to open the repository ({}): {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, .. } => {
-                write!(f, "while prefixing the refs with a path: {error}")
-            }
-            Error::IssuesPrefixMissing { prefix } => {
-                write!(
-                    f,
-                    "There is no 'issue' associated with the prefix: {prefix}"
-                )
-            }
-            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/git_bug/dag/mod.rs b/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
deleted file mode 100644
index 3d22b04..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/dag/mod.rs
+++ /dev/null
@@ -1,155 +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::path::Path;
-
-use gix::{bstr::ByteSlice, refs::Target, Commit, Id, ObjectId, Repository};
-
-use crate::error;
-
-use super::issue::{
-    entity::{Entity, RawEntity},
-    CollapsedIssue, RawCollapsedIssue,
-};
-
-#[derive(Debug)]
-pub struct Dag {
-    entities: Vec<Entity>,
-}
-
-impl Dag {
-    pub fn collapse(self) -> CollapsedIssue {
-        let raw_collapsed_issue = self.entities.into_iter().rev().fold(
-            RawCollapsedIssue::default(),
-            |mut collapsed_issue, entity| {
-                collapsed_issue.append_entity(entity);
-                collapsed_issue
-            },
-        );
-
-        CollapsedIssue::from(raw_collapsed_issue)
-    }
-
-    /// Construct a DAG from the root child upwards.
-    pub fn construct(repo: &Repository, id: ObjectId) -> Self {
-        let mut entities = vec![];
-
-        let base_commit = repo
-            .find_object(id)
-            .expect("The object with this id should exist.")
-            .try_into_commit()
-            .expect("The git-bug's data model enforces this.");
-
-        entities.push(Self::commit_to_operations(repo, &base_commit));
-
-        let mut current_commit = base_commit;
-        while let Some(parent_id) = Self::try_get_parent(repo, &current_commit) {
-            entities.push(Self::commit_to_operations(repo, &parent_id));
-            current_commit = parent_id;
-        }
-
-        Self {
-            entities: {
-                entities
-                    .into_iter()
-                    .map(|(raw_entity, id)| Entity::from_raw(repo, raw_entity, id))
-                    .collect()
-            },
-        }
-    }
-
-    fn commit_to_operations<'b>(repo: &Repository, id: &Commit<'b>) -> (RawEntity, Id<'b>) {
-        let tree_obj = repo
-            .find_object(
-                id.tree_id()
-                    .expect("All of git-bug's commits should have trees attached to them'"),
-            )
-            .expect("The object with this id should exist.")
-            .try_into_tree()
-            .expect("git-bug's data model enforces this.");
-
-        let ops_ref = tree_obj
-            .find_entry("ops")
-            .expect("All of git-bug's trees should contain a 'ops' json file");
-
-        let issue_data = repo
-            .find_object(ops_ref.object_id())
-            .expect("The object with this id should exist.")
-            .try_into_blob()
-            .expect("The git-bug's data model enforces this.")
-            .data
-            .clone();
-
-        let operations = serde_json::from_str(
-            issue_data
-                .to_str()
-                .expect("git-bug's ensures, that this is valid json."),
-        )
-        .expect("The returned json should be valid");
-
-        (operations, id.id())
-    }
-
-    fn try_get_parent<'a>(repo: &'a Repository, base_commit: &Commit<'a>) -> Option<Commit<'a>> {
-        let count = base_commit.parent_ids().count();
-
-        match count {
-            0 => None,
-            1 => {
-                let parent = base_commit.parent_ids().last().expect("One does exist");
-
-                let parent_id = parent.object().expect("The object exists").id;
-                Some(
-                    repo.find_object(parent_id)
-                        .expect("This is a valid id")
-                        .try_into_commit()
-                        .expect("This should be a commit"),
-                )
-            }
-            other => {
-                unreachable!(
-                    "Each commit, used by git-bug should only have one parent, but found: {other}"
-                );
-            }
-        }
-    }
-}
-
-/// 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/"))
-        .map_err(|err| error::Error::RepoRefsPrefixed { error: err })?
-        .map(|val| {
-            let reference = val.expect("All `git-bug` references in 'refs/bugs' should be objects");
-
-            if let Target::Object(id) = reference.target {
-                Dag::construct(repo, id)
-            } else {
-                unreachable!("All 'refs/bugs/' should contain a clear target.");
-            }
-        })
-        .collect::<Vec<Dag>>();
-
-    Ok(dags)
-}
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
deleted file mode 100644
index ffe44fd..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/format/mod.rs
+++ /dev/null
@@ -1,157 +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 chrono::DateTime;
-use markdown::to_html;
-use serde::Deserialize;
-use serde_json::Value;
-
-/// Markdown content.
-#[derive(Debug, Default, Clone)]
-pub struct MarkDown {
-    value: String,
-}
-
-impl Display for MarkDown {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str(to_html(&self.value).as_str())
-    }
-}
-impl From<&Value> for MarkDown {
-    fn from(value: &Value) -> Self {
-        Self {
-            value: value.as_str().expect("This will exist").to_owned(),
-        }
-    }
-}
-
-/// An UNIX time stamp.
-///
-/// These should only ever be used for human-display, because timestamps are unreliably in a
-/// distributed system.
-/// Because of this reason, there is no `value()` function.
-#[derive(Debug, Default, Clone, Copy)]
-pub struct TimeStamp {
-    value: u64,
-}
-impl From<&Value> for TimeStamp {
-    fn from(value: &Value) -> Self {
-        Self {
-            value: value.as_u64().expect("This must exist"),
-        }
-    }
-}
-impl Display for TimeStamp {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        let date =
-            DateTime::from_timestamp(self.value as i64, 0).expect("This timestamp should be vaild");
-
-        let newdate = date.format("%Y-%m-%d %H:%M:%S");
-        f.write_str(newdate.to_string().as_str())
-    }
-}
-
-/// An UNIX time stamp.
-///
-/// These should only ever be used for human-display, because timestamps are unreliably in a
-/// distributed system.
-///
-/// This one allows underlying access to it's value and is only obtainable via `unsafe` code.
-/// The reason behind this is, that you might need to access this to improve the display for humans
-/// (i.e., sorting by date).
-#[derive(Debug, Default, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
-pub struct UnsafeTimeStamp {
-    value: u64,
-}
-impl TimeStamp {
-    /// # Safety
-    /// This is not really unsafe, but there is just no way your can trust a time stamp in a
-    /// distributed system. As such access to the raw value could lead to bugs.
-    pub unsafe fn to_unsafe(self) -> UnsafeTimeStamp {
-        UnsafeTimeStamp { value: self.value }
-    }
-}
-
-#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)]
-/// A string that should be escaped when injected into html content.
-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 }
-    }
-}
-
-impl From<&Value> for HtmlString {
-    fn from(value: &Value) -> Self {
-        Self {
-            value: value.as_str().expect("This will exist").to_owned(),
-        }
-    }
-}
-impl Display for HtmlString {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str(escape_html(&self.value).as_str())
-    }
-}
-
-// From `tera::escape_html`
-/// Escape HTML following [OWASP](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
-///
-/// Escape the following characters with HTML entity encoding to prevent switching
-/// into any execution context, such as script, style, or event handlers. Using
-/// hex entities is recommended in the spec. In addition to the 5 characters
-/// significant in XML (&, <, >, ", '), the forward slash is included as it helps
-/// to end an HTML entity.
-///
-/// ```text
-/// & --> &amp;
-/// < --> &lt;
-/// > --> &gt;
-/// " --> &quot;
-/// ' --> &#x27;     &apos; is not recommended
-/// / --> &#x2F;     forward slash is included as it helps end an HTML entity
-/// ```
-#[inline]
-pub fn escape_html(input: &str) -> String {
-    let mut output = String::with_capacity(input.len() * 2);
-    for c in input.chars() {
-        match c {
-            '&' => output.push_str("&amp;"),
-            '<' => output.push_str("&lt;"),
-            '>' => output.push_str("&gt;"),
-            '"' => output.push_str("&quot;"),
-            '\'' => output.push_str("&#x27;"),
-            '/' => output.push_str("&#x2F;"),
-            _ => output.push(c),
-        }
-    }
-
-    // Not using shrink_to_fit() on purpose
-    output
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs
deleted file mode 100644
index f2e9af0..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/entity/mod.rs
+++ /dev/null
@@ -1,78 +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::Repository;
-use serde::Deserialize;
-use serde_json::Value;
-
-use super::{
-    identity::{Author, RawAuthor},
-    operation::Operation,
-};
-
-#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
-#[serde(from = "Value")]
-pub struct Id {
-    value: String,
-}
-impl From<Value> for Id {
-    fn from(value: Value) -> Self {
-        Self::from(&value)
-    }
-}
-impl From<&Value> for Id {
-    fn from(value: &Value) -> Self {
-        Self {
-            value: value.as_str().expect("This should  be a string").to_owned(),
-        }
-    }
-}
-impl From<gix::Id<'_>> for Id {
-    fn from(value: gix::Id<'_>) -> Self {
-        Self {
-            value: value.shorten().expect("This should work?").to_string(),
-        }
-    }
-}
-impl Display for Id {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.value.fmt(f)
-        // let shortend = self.value.shorten().expect("This should work.");
-        // f.write_str(shortend.to_string().as_str())
-    }
-}
-
-#[derive(Debug)]
-pub struct Entity {
-    pub id: Id,
-    pub author: Author,
-    pub operations: Vec<Operation>,
-}
-
-impl Entity {
-    pub fn from_raw<'a>(repo: &'a Repository, raw: RawEntity, id: gix::Id<'a>) -> Self {
-        Self {
-            id: Id::from(id),
-            author: Author::construct(repo, raw.author),
-            operations: raw.operations,
-        }
-    }
-}
-
-#[derive(Deserialize)]
-pub struct RawEntity {
-    pub author: RawAuthor,
-
-    #[serde(alias = "ops")]
-    pub operations: Vec<Operation>,
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs
deleted file mode 100644
index bbf483c..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/identity/mod.rs
+++ /dev/null
@@ -1,79 +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::{bstr::ByteSlice, Repository};
-use serde::Deserialize;
-use serde_json::Value;
-
-use crate::{get, git_bug::format::HtmlString};
-
-use super::entity::Id;
-
-#[derive(Debug, Clone)]
-pub struct Author {
-    pub name: HtmlString,
-    pub email: HtmlString,
-    pub id: Id,
-}
-
-impl Display for Author {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "({}, {})", self.name, self.email)
-    }
-}
-
-impl Author {
-    pub fn construct(repo: &Repository, raw: RawAuthor) -> Self {
-        let commit_obj = repo
-            .find_reference(&format!("refs/identities/{}", raw.id))
-            .expect("All authors should also have identities")
-            .peel_to_commit()
-            .expect("All identities should be commits");
-
-        let tree_obj = repo
-            .find_tree(
-                commit_obj
-                    .tree()
-                    .expect("The commit should have an tree associated with it")
-                    .id,
-            )
-            .expect("This should be a tree");
-
-        let data = repo
-            .find_blob(
-                tree_obj
-                    .find_entry("version")
-                    .expect("This entry should exist")
-                    .object()
-                    .expect("This should point to a blob entry")
-                    .id,
-            )
-            .expect("This blob should exist")
-            .data
-            .clone();
-
-        let json: Value = serde_json::from_str(data.to_str().expect("This is encoded json"))
-            .expect("This is valid json");
-
-        Author {
-            name: get! {json, "name"},
-            email: get! {json, "email"},
-            id: raw.id,
-        }
-    }
-}
-
-#[derive(Deserialize)]
-pub struct RawAuthor {
-    id: Id,
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs
deleted file mode 100644
index a971234..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/label/mod.rs
+++ /dev/null
@@ -1,85 +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 serde::Deserialize;
-use sha2::{Digest, Sha256};
-
-use crate::git_bug::format::HtmlString;
-
-#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
-pub struct Label {
-    value: HtmlString,
-}
-
-impl Display for Label {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.value.fmt(f)
-    }
-}
-
-impl Label {
-    /// RGBA from a Label computed in a deterministic way
-    /// This is taken completely from `git_bug`
-    pub fn associate_color(&self) -> Color {
-        // colors from: https://material-ui.com/style/color/
-        let colors = vec![
-            Color::from_rgba(244, 67, 54, 255),   // red
-            Color::from_rgba(233, 30, 99, 255),   // pink
-            Color::from_rgba(156, 39, 176, 255),  // purple
-            Color::from_rgba(103, 58, 183, 255),  // deepPurple
-            Color::from_rgba(63, 81, 181, 255),   // indigo
-            Color::from_rgba(33, 150, 243, 255),  // blue
-            Color::from_rgba(3, 169, 244, 255),   // lightBlue
-            Color::from_rgba(0, 188, 212, 255),   // cyan
-            Color::from_rgba(0, 150, 136, 255),   // teal
-            Color::from_rgba(76, 175, 80, 255),   // green
-            Color::from_rgba(139, 195, 74, 255),  // lightGreen
-            Color::from_rgba(205, 220, 57, 255),  // lime
-            Color::from_rgba(255, 235, 59, 255),  // yellow
-            Color::from_rgba(255, 193, 7, 255),   // amber
-            Color::from_rgba(255, 152, 0, 255),   // orange
-            Color::from_rgba(255, 87, 34, 255),   // deepOrange
-            Color::from_rgba(121, 85, 72, 255),   // brown
-            Color::from_rgba(158, 158, 158, 255), // grey
-            Color::from_rgba(96, 125, 139, 255),  // blueGrey
-        ];
-
-        let hash = Sha256::digest(self.to_string().as_bytes());
-
-        let id: usize = hash
-            .into_iter()
-            .map(|val| val as usize)
-            .fold(0, |acc, val| (acc + val) % colors.len());
-
-        colors[id]
-    }
-}
-
-#[derive(Default, Clone, Copy, Debug)]
-pub struct Color {
-    pub red: u32,
-    pub green: u32,
-    pub blue: u32,
-    pub alpha: u32,
-}
-
-impl Color {
-    pub fn from_rgba(red: u32, green: u32, blue: u32, alpha: u32) -> Self {
-        Self {
-            red,
-            green,
-            blue,
-            alpha,
-        }
-    }
-}
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
deleted file mode 100644
index d382b54..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/mod.rs
+++ /dev/null
@@ -1,185 +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 entity::{Entity, Id};
-use identity::Author;
-use label::Label;
-use operation::Operation;
-use serde_json::Value;
-
-use super::format::{MarkDown, TimeStamp};
-
-pub mod entity;
-pub mod identity;
-pub mod label;
-pub mod operation;
-
-#[derive(Debug, Eq, PartialEq, Copy, Clone)]
-pub enum Status {
-    Open,
-    Closed,
-}
-impl From<&Value> for Status {
-    fn from(value: &Value) -> Self {
-        match value.as_u64().expect("This should be a integer") {
-            1 => Self::Open,
-            2 => Self::Closed,
-            other => unimplemented!("Invalid status string: '{other}'"),
-        }
-    }
-}
-impl Display for Status {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Status::Open => f.write_str("Open"),
-            Status::Closed => f.write_str("Closed"),
-        }
-    }
-}
-
-#[derive(Debug)]
-pub struct CollapsedIssue {
-    pub id: Id,
-    pub author: Author,
-    pub timestamp: TimeStamp,
-    pub title: MarkDown,
-    pub message: MarkDown,
-    pub comments: Vec<Comment>,
-    pub status: Status,
-    pub last_status_change: TimeStamp,
-    pub labels: Vec<Label>,
-}
-impl From<RawCollapsedIssue> for CollapsedIssue {
-    fn from(r: RawCollapsedIssue) -> Self {
-        macro_rules! get {
-            ($name:ident) => {
-                r.$name.expect(concat!(
-                    "'",
-                    stringify!($name),
-                    "' is unset, when trying to collapes an issue! (This is likely a bug)"
-                ))
-            };
-        }
-
-        Self {
-            id: get! {id},
-            author: get! {author},
-            timestamp: get! {timestamp},
-            title: get! {title},
-            message: get! {message},
-            comments: r.comments,
-            status: get! {status},
-            last_status_change: get! {last_status_change},
-            labels: r.labels,
-        }
-    }
-}
-
-#[derive(Debug)]
-pub struct Comment {
-    pub id: Id,
-    pub author: Author,
-    pub timestamp: TimeStamp,
-    pub message: MarkDown,
-}
-
-#[derive(Debug, Default)]
-pub struct RawCollapsedIssue {
-    pub id: Option<Id>,
-    pub author: Option<Author>,
-    pub timestamp: Option<TimeStamp>,
-    pub title: Option<MarkDown>,
-    pub message: Option<MarkDown>,
-    pub status: Option<Status>,
-    pub last_status_change: Option<TimeStamp>,
-
-    // NOTE(@bpeetz): These values set here already, because an issue without these
-    // would be perfectly valid. <2024-12-26>
-    pub labels: Vec<Label>,
-    pub comments: Vec<Comment>,
-}
-
-impl RawCollapsedIssue {
-    pub fn append_entity(&mut self, entity: Entity) {
-        for op in entity.operations {
-            match op {
-                Operation::AddComment { timestamp, message } => {
-                    self.comments.push(Comment {
-                        id: entity.id.clone(),
-                        author: entity.author.clone(),
-                        timestamp,
-                        message,
-                    });
-                }
-                Operation::Create {
-                    timestamp,
-                    title,
-                    message,
-                } => {
-                    self.id = Some(entity.id.clone());
-                    self.author = Some(entity.author.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
-                    self.last_status_change = Some(timestamp);
-                }
-                Operation::EditComment {
-                    timestamp,
-                    target,
-                    message,
-                } => {
-                    let comments = &mut self.comments;
-
-                    let target_comment = comments
-                        .iter_mut()
-                        .find(|comment| comment.id == target)
-                        .expect("The target must be a valid comment");
-
-                    // TODO(@bpeetz): We should probably set a `edited = true` flag here. <2024-12-26>
-                    // TODO(@bpeetz): Should we also change the author? <2024-12-26>
-
-                    target_comment.timestamp = timestamp;
-                    target_comment.message = message;
-                }
-                Operation::LabelChange {
-                    timestamp: _,
-                    added,
-                    removed,
-                } => {
-                    let labels = self.labels.clone();
-
-                    self.labels = labels
-                        .into_iter()
-                        .filter(|val| !removed.contains(val))
-                        .chain(added.into_iter())
-                        .collect();
-                }
-                Operation::SetStatus { timestamp, status } => {
-                    self.status = Some(status);
-                    self.last_status_change = Some(timestamp);
-                }
-                Operation::SetTitle {
-                    timestamp: _,
-                    title,
-                    was: _,
-                } => {
-                    self.title = Some(title);
-                }
-
-                Operation::NoOp {} => unimplemented!(),
-                Operation::SetMetadata {} => unimplemented!(),
-            }
-        }
-    }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs b/pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs
deleted file mode 100644
index 7f861a7..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/operation/mod.rs
+++ /dev/null
@@ -1,124 +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::convert::Infallible;
-
-use operation_type::OperationType;
-use serde::Deserialize;
-use serde_json::Value;
-
-use crate::{
-    get,
-    git_bug::format::{MarkDown, TimeStamp},
-};
-
-use super::{entity, label::Label, Status};
-
-pub mod operation_type;
-
-#[derive(Deserialize, Debug)]
-#[serde(try_from = "Value")]
-pub enum Operation {
-    AddComment {
-        timestamp: TimeStamp,
-        message: MarkDown,
-    },
-    Create {
-        timestamp: TimeStamp,
-        title: MarkDown,
-        message: MarkDown,
-    },
-    EditComment {
-        timestamp: TimeStamp,
-        target: entity::Id,
-        message: MarkDown,
-    },
-    LabelChange {
-        timestamp: TimeStamp,
-        added: Vec<Label>,
-        removed: Vec<Label>,
-    },
-    SetStatus {
-        timestamp: TimeStamp,
-        status: Status,
-    },
-    SetTitle {
-        timestamp: TimeStamp,
-        title: MarkDown,
-        was: MarkDown,
-    },
-
-    // These seem to be just weird non-operation, operations.
-    // defined in: git-bug/entities/bug/operation.go
-    NoOp {},
-    SetMetadata {},
-}
-
-impl TryFrom<Value> for Operation {
-    type Error = Infallible;
-
-    fn try_from(value: Value) -> Result<Self, Self::Error> {
-        let operation_type = OperationType::from_json_int(
-            value
-                .get("type")
-                .expect("Should exist")
-                .as_u64()
-                .expect("This should work"),
-        );
-
-        let op = match operation_type {
-            OperationType::AddComment => Self::AddComment {
-                timestamp: get! {value, "timestamp" },
-                message: get! {value, "message"},
-            },
-            OperationType::Create => Self::Create {
-                timestamp: get! {value, "timestamp"},
-                title: get! {value, "title"},
-                message: get! {value, "message"},
-            },
-            OperationType::EditComment => Self::EditComment {
-                timestamp: get! {value, "timestamp"},
-                target: get! {value, "target"},
-                message: get! {value, "message"},
-            },
-            OperationType::LabelChange => Self::LabelChange {
-                timestamp: get! {value, "timestamp"},
-                added: serde_json::from_value(
-                    value
-                        .get("added")
-                        .expect("This should be available")
-                        .to_owned(),
-                )
-                .expect("This should be parsable"),
-                removed: serde_json::from_value(
-                    value
-                        .get("removed")
-                        .expect("This should be available")
-                        .to_owned(),
-                )
-                .expect("This should be parsable"),
-            },
-            OperationType::SetStatus => Self::SetStatus {
-                timestamp: get! {value, "timestamp"},
-                status: get! {value, "status"},
-            },
-            OperationType::SetTitle => Self::SetTitle {
-                timestamp: get! {value, "timestamp"},
-                title: get! {value, "title"},
-                was: get! {value, "was"},
-            },
-            OperationType::NoOp => Self::NoOp {},
-            OperationType::SetMetadata => Self::SetMetadata {},
-        };
-
-        Ok(op)
-    }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs b/pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs
deleted file mode 100644
index 69d272f..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/issue/operation/operation_type.rs
+++ /dev/null
@@ -1,51 +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>.
-
-pub enum OperationType {
-    AddComment,
-    Create,
-    EditComment,
-    LabelChange,
-    NoOp,
-    SetMetadata,
-    SetStatus,
-    SetTitle,
-}
-
-impl OperationType {
-    // NOTE(@bpeetz): This mapping should always be the same as `git_bug`'s.
-    // The mapping is defined in `git-bug/entities/bug/operation.go`. <2024-12-26>
-    pub fn to_json_int(self) -> u64 {
-        match self {
-            OperationType::Create => 1,
-            OperationType::SetTitle => 2,
-            OperationType::AddComment => 3,
-            OperationType::SetStatus => 4,
-            OperationType::LabelChange => 5,
-            OperationType::EditComment => 6,
-            OperationType::NoOp => 7,
-            OperationType::SetMetadata => 8,
-        }
-    }
-    pub fn from_json_int(value: u64) -> Self {
-        match value {
-            1 => OperationType::Create,
-            2 => OperationType::SetTitle,
-            3 => OperationType::AddComment,
-            4 => OperationType::SetStatus,
-            5 => OperationType::LabelChange,
-            6 => OperationType::EditComment,
-            7 => OperationType::NoOp,
-            8 => OperationType::SetMetadata,
-            other => unimplemented!("The operation type {other} is not recognized."),
-        }
-    }
-}
diff --git a/pkgs/by-name/ba/back/src/git_bug/mod.rs b/pkgs/by-name/ba/back/src/git_bug/mod.rs
deleted file mode 100644
index c0a5372..0000000
--- a/pkgs/by-name/ba/back/src/git_bug/mod.rs
+++ /dev/null
@@ -1,28 +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>.
-
-pub mod dag;
-pub mod format;
-pub mod issue;
-
-#[macro_export]
-macro_rules! get {
-    ($value:expr, $name:expr) => {
-        $value
-            .get($name)
-            .expect(concat!(
-                "Expected field ",
-                stringify!($name),
-                "to exists, but was missing."
-            ))
-            .into()
-    };
-}
diff --git a/pkgs/by-name/ba/back/src/main.rs b/pkgs/by-name/ba/back/src/main.rs
deleted file mode 100644
index 61953c4..0000000
--- a/pkgs/by-name/ba/back/src/main.rs
+++ /dev/null
@@ -1,53 +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::{process, sync::Arc};
-
-use clap::Parser;
-
-use crate::config::BackConfig;
-
-mod cli;
-pub mod config;
-mod error;
-pub mod git_bug;
-mod web;
-
-fn main() -> Result<(), String> {
-    if let Err(err) = server_main() {
-        eprintln!("Error {err}");
-        process::exit(1);
-    } else {
-        Ok(())
-    }
-}
-
-#[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)?;
-
-    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
deleted file mode 100644
index ae783a3..0000000
--- a/pkgs/by-name/ba/back/src/web/generate/mod.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-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.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.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.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.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_url,
-                    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_url,
-                                issue.id
-                            ))
-                            .build()
-                    })
-                    .collect::<Vec<Item>>()
-            })
-            .collect::<Vec<Item>>(),
-    );
-
-    let channel = ChannelBuilder::default()
-        .title("Issues")
-        .link(config.root_url.to_string())
-        .description(format!("The rss feed for issues on {}.", &config.root_url))
-        .items(items)
-        .build();
-    Ok(channel.to_string())
-}
diff --git a/pkgs/by-name/ba/back/src/web/mod.rs b/pkgs/by-name/ba/back/src/web/mod.rs
deleted file mode 100644
index cc087ab..0000000
--- a/pkgs/by-name/ba/back/src/web/mod.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-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,
-        ));
-    }
-
-    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",
-                ))
-            }
-
-            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"));
-
-                    (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()),
-    }
-}
-
-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/responses.rs b/pkgs/by-name/ba/back/src/web/responses.rs
deleted file mode 100644
index e50f8c2..0000000
--- a/pkgs/by-name/ba/back/src/web/responses.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-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,
-        )
-    }
-}