about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmile <git@emile.space>2024-03-09 22:56:01 +0100
committerEmile <git@emile.space>2024-03-09 22:56:01 +0100
commit55ee036fd7ebed24097c8da1ca8a0b0829264670 (patch)
tree9c600d75a4f59f8ec253c5caadc1f77b2cecb0da
big bang
-rw-r--r--README.md6
-rw-r--r--flake.lock280
-rw-r--r--flake.nix185
-rw-r--r--nix/hosts/caladan/README.md3
-rw-r--r--nix/hosts/caladan/aliases.nix53
-rw-r--r--nix/hosts/caladan/darwin-configuration.nix82
-rw-r--r--nix/hosts/caladan/functions.zsh30
-rw-r--r--nix/hosts/caladan/home_emile.nix180
-rw-r--r--nix/hosts/caladan/home_hydra.nix18
-rw-r--r--nix/hosts/caladan/overlay.nix18
-rw-r--r--nix/hosts/caladan/session_variables.zsh35
-rw-r--r--nix/hosts/caladan/ssh.pub1
-rw-r--r--nix/hosts/chusuk/README.md3
-rw-r--r--nix/hosts/chusuk/configuration.nix141
-rw-r--r--nix/hosts/chusuk/hardware-configuration.nix31
-rw-r--r--nix/hosts/corrino/README.md4
-rw-r--r--nix/hosts/corrino/configuration.nix415
-rw-r--r--nix/hosts/corrino/default.nix3
-rw-r--r--nix/hosts/corrino/emile.space.nix60
-rw-r--r--nix/hosts/corrino/gemini/emile.space.nix16
-rw-r--r--nix/hosts/corrino/hardware-configuration.nix39
-rw-r--r--nix/hosts/corrino/secrets/factorio_password.agebin0 -> 588 bytes
-rw-r--r--nix/hosts/corrino/secrets/grafana_admin_password.age9
-rw-r--r--nix/hosts/corrino/secrets/grafana_database_password.age11
-rw-r--r--nix/hosts/corrino/secrets/grafana_secret_key.age9
-rw-r--r--nix/hosts/corrino/secrets/grafana_smtp_password.age10
-rw-r--r--nix/hosts/corrino/secrets/magic-hash-flag.age10
-rw-r--r--nix/hosts/corrino/secrets/mail_password.age7
-rw-r--r--nix/hosts/corrino/secrets/netbox_secret.age11
-rw-r--r--nix/hosts/corrino/secrets/photoprism_password.age9
-rw-r--r--nix/hosts/corrino/secrets/pretix.agebin0 -> 1487 bytes
-rw-r--r--nix/hosts/corrino/secrets/pretix_postgres_pw.age9
-rw-r--r--nix/hosts/corrino/secrets/storage_box_bx11_password.age7
-rw-r--r--nix/hosts/corrino/secrets/tailscale_authkey.agebin0 -> 524 bytes
-rw-r--r--nix/hosts/corrino/secrets/wireguard_privatekey.agebin0 -> 440 bytes
-rw-r--r--nix/hosts/corrino/ssh.pub1
-rw-r--r--nix/hosts/corrino/www/cs.emile.space.nix56
-rw-r--r--nix/hosts/corrino/www/ctf.emile.space.nix26
-rw-r--r--nix/hosts/corrino/www/emile.space.nix60
-rw-r--r--nix/hosts/corrino/www/events.emile.space.nix59
-rw-r--r--nix/hosts/corrino/www/git.emile.space.nix73
-rw-r--r--nix/hosts/corrino/www/grafana.emile.space.nix217
-rw-r--r--nix/hosts/corrino/www/grafana_full.emile.space.nix440
-rw-r--r--nix/hosts/corrino/www/hydra.emile.space.nix57
-rw-r--r--nix/hosts/corrino/www/jupyter.emile.space.nix60
-rw-r--r--nix/hosts/corrino/www/magic-hash.emile.space.nix33
-rw-r--r--nix/hosts/corrino/www/netbox.emile.space.nix63
-rw-r--r--nix/hosts/corrino/www/pgweb.emile.space.nix21
-rw-r--r--nix/hosts/corrino/www/photo.emile.space.nix33
-rw-r--r--nix/hosts/corrino/www/stream.emile.space.nix24
-rw-r--r--nix/hosts/corrino/www/talks.emile.space.nix97
-rw-r--r--nix/hosts/corrino/www/tickets.emile.space.nix120
-rw-r--r--nix/hosts/corrino/www/tickets.emile.space.nix_chaos.jetzt.nix107
-rw-r--r--nix/hosts/corrino/www/znc.emile.space.nix47
-rw-r--r--nix/hosts/hacknix/README.md46
-rw-r--r--nix/hosts/hacknix/burpsuitepro/default.nix46
-rw-r--r--nix/hosts/hacknix/configuration.nix396
-rw-r--r--nix/hosts/hacknix/hardware-configuration.nix36
-rw-r--r--nix/hosts/hacknix/i3-config.nix139
-rw-r--r--nix/hosts/hacknix/overlay/default.nix16
-rw-r--r--nix/hosts/hacknix/pkgs/helix-2303/Cargo.lock2561
-rw-r--r--nix/hosts/hacknix/pkgs/helix-2303/default.nix55
-rw-r--r--nix/hosts/hacknix/pkgs/radare2-5.8.4/default.nix118
-rw-r--r--nix/hosts/mail/configuration.nix126
-rw-r--r--nix/hosts/mail/hardware-configuration.nix24
-rw-r--r--nix/hosts/mail/mail.nix50
-rw-r--r--nix/lib/default.nix22
-rw-r--r--nix/lib/flake-helper.nix158
-rw-r--r--nix/lib/sec.nix25
-rw-r--r--nix/pkgs/overlay.nix4
-rw-r--r--nix/templates/ctf/flake.nix47
-rw-r--r--nix/users/emile/default.nix13
-rw-r--r--nix/users/emile/ssh.pub1
-rw-r--r--secrets.nix59
-rw-r--r--web/vokobe/.gitignore7
-rw-r--r--web/vokobe/Cargo.lock270
-rw-r--r--web/vokobe/Cargo.toml10
-rw-r--r--web/vokobe/LICENSE21
-rw-r--r--web/vokobe/README.md101
-rw-r--r--web/vokobe/default.nix16
-rw-r--r--web/vokobe/flaaaaake.nix44
-rw-r--r--web/vokobe/src/main.rs922
82 files changed, 8622 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ccedfed
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+# hefe
+
+Growing stuff here!
+
+EDITOR=hx nix run git+https://github.com/ryantm/agenix -- -e nix/hosts/corrino/secrets/pretix.age
+
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..c422573
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,280 @@
+{
+  "nodes": {
+    "agenix": {
+      "inputs": {
+        "darwin": "darwin",
+        "home-manager": "home-manager",
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1707830867,
+        "narHash": "sha256-PAdwm5QqdlwIqGrfzzvzZubM+FXtilekQ/FA0cI49/o=",
+        "ref": "refs/heads/main",
+        "rev": "8cb01a0e717311680e0cbca06a76cbceba6f3ed6",
+        "revCount": 297,
+        "type": "git",
+        "url": "https://github.com/ryantm/agenix"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://github.com/ryantm/agenix"
+      }
+    },
+    "darwin": {
+      "inputs": {
+        "nixpkgs": [
+          "agenix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1700795494,
+        "narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
+        "owner": "lnl7",
+        "repo": "nix-darwin",
+        "rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "lnl7",
+        "ref": "master",
+        "repo": "nix-darwin",
+        "type": "github"
+      }
+    },
+    "darwin_2": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1708737761,
+        "narHash": "sha256-sR/1cYjpgr71ZSrt6Kp5Dg4Ul3mo6pZIG400tuzYks8=",
+        "ref": "refs/heads/master",
+        "rev": "bbde06bed1b72eddff063fa42f18644e90a0121e",
+        "revCount": 1535,
+        "type": "git",
+        "url": "https://github.com/lnl7/nix-darwin"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://github.com/lnl7/nix-darwin"
+      }
+    },
+    "deploy-rs": {
+      "inputs": {
+        "flake-compat": "flake-compat",
+        "nixpkgs": [
+          "nixpkgs-unstable"
+        ],
+        "utils": "utils"
+      },
+      "locked": {
+        "lastModified": 1708091384,
+        "narHash": "sha256-dTGGw2y8wvfjr+J9CjQbfdulOq72hUG17HXVNxpH1yE=",
+        "ref": "master",
+        "rev": "0a0187794ac7f7a1e62cda3dabf8dc041f868790",
+        "revCount": 309,
+        "type": "git",
+        "url": "https://github.com/serokell/deploy-rs"
+      },
+      "original": {
+        "ref": "master",
+        "type": "git",
+        "url": "https://github.com/serokell/deploy-rs"
+      }
+    },
+    "flake-compat": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1696426674,
+        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "hefe-internal": {
+      "locked": {
+        "lastModified": 1700779845,
+        "narHash": "sha256-gWQE4d+JNv7jZMnl298dLMahWRialF+FIjlZnKH3XlE=",
+        "ref": "main",
+        "rev": "509155eeb0c70309cfea6f21f14029265420e067",
+        "revCount": 114,
+        "type": "git",
+        "url": "ssh://gitea@git.emile.space/hanemile/hefe-internal.git"
+      },
+      "original": {
+        "ref": "main",
+        "type": "git",
+        "url": "ssh://gitea@git.emile.space/hanemile/hefe-internal.git"
+      }
+    },
+    "home-manager": {
+      "inputs": {
+        "nixpkgs": [
+          "agenix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1703113217,
+        "narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=",
+        "owner": "nix-community",
+        "repo": "home-manager",
+        "rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "home-manager",
+        "type": "github"
+      }
+    },
+    "home-manager_2": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs-unstable"
+        ]
+      },
+      "locked": {
+        "lastModified": 1706981411,
+        "narHash": "sha256-cLbLPTL1CDmETVh4p0nQtvoF+FSEjsnJTFpTxhXywhQ=",
+        "ref": "release-23.11",
+        "rev": "652fda4ca6dafeb090943422c34ae9145787af37",
+        "revCount": 3175,
+        "type": "git",
+        "url": "https://github.com/nix-community/home-manager"
+      },
+      "original": {
+        "ref": "release-23.11",
+        "type": "git",
+        "url": "https://github.com/nix-community/home-manager"
+      }
+    },
+    "naersk": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs-unstable"
+        ]
+      },
+      "locked": {
+        "lastModified": 1698420672,
+        "narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
+        "ref": "refs/heads/master",
+        "rev": "aeb58d5e8faead8980a807c840232697982d47b9",
+        "revCount": 333,
+        "type": "git",
+        "url": "https://github.com/nix-community/naersk"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://github.com/nix-community/naersk"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1708979614,
+        "narHash": "sha256-FWLWmYojIg6TeqxSnHkKpHu5SGnFP5um1uUjH+wRV6g=",
+        "ref": "release-23.11",
+        "rev": "b7ee09cf5614b02d289cd86fcfa6f24d4e078c2a",
+        "revCount": 556163,
+        "type": "git",
+        "url": "https://github.com/nixos/nixpkgs"
+      },
+      "original": {
+        "ref": "release-23.11",
+        "type": "git",
+        "url": "https://github.com/nixos/nixpkgs"
+      }
+    },
+    "nixpkgs-unstable": {
+      "locked": {
+        "lastModified": 1708847675,
+        "narHash": "sha256-RUZ7KEs/a4EzRELYDGnRB6i7M1Izii3JD/LyzH0c6Tg=",
+        "ref": "nixpkgs-unstable",
+        "rev": "2a34566b67bef34c551f204063faeecc444ae9da",
+        "revCount": 588396,
+        "type": "git",
+        "url": "https://github.com/nixos/nixpkgs"
+      },
+      "original": {
+        "ref": "nixpkgs-unstable",
+        "type": "git",
+        "url": "https://github.com/nixos/nixpkgs"
+      }
+    },
+    "root": {
+      "inputs": {
+        "agenix": "agenix",
+        "darwin": "darwin_2",
+        "deploy-rs": "deploy-rs",
+        "hefe-internal": "hefe-internal",
+        "home-manager": "home-manager_2",
+        "naersk": "naersk",
+        "nixpkgs": "nixpkgs",
+        "nixpkgs-unstable": "nixpkgs-unstable"
+      }
+    },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "systems_2": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "utils": {
+      "inputs": {
+        "systems": "systems_2"
+      },
+      "locked": {
+        "lastModified": 1701680307,
+        "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..0d69e47
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,185 @@
+{
+  inputs = {
+    nixpkgs.url = "git+https://github.com/nixos/nixpkgs?ref=release-23.11";
+    nixpkgs-unstable.url = "git+https://github.com/nixos/nixpkgs?ref=nixpkgs-unstable";
+
+    darwin.url = "git+https://github.com/lnl7/nix-darwin";
+    darwin.inputs.nixpkgs.follows = "nixpkgs";
+
+    deploy-rs.url = "git+https://github.com/serokell/deploy-rs?ref=master";
+    deploy-rs.inputs.nixpkgs.follows = "nixpkgs-unstable";
+
+    agenix.url = "git+https://github.com/ryantm/agenix";
+    agenix.inputs.nixpkgs.follows = "nixpkgs";
+
+    home-manager.url = "git+https://github.com/nix-community/home-manager?ref=release-23.11";
+    home-manager.inputs.nixpkgs.follows = "nixpkgs-unstable";
+
+    naersk.url = "git+https://github.com/nix-community/naersk";
+    naersk.inputs.nixpkgs.follows = "nixpkgs-unstable";
+
+    hefe-internal.url = "git+ssh://gitea@git.emile.space/hanemile/hefe-internal.git?ref=main";
+  };
+
+  outputs = {
+    self,
+    nixpkgs, nixpkgs-unstable, # general packages
+    darwin, # darwin related stuff
+    deploy-rs, # deploy the hosts
+    agenix, # store secrets crypted using age
+    home-manager, # manage my home envs
+    naersk, # build rust stuff
+    hefe-internal, # internal tooling
+    ... }@inputs:
+  let
+    lib = import ./nix/lib inputs;
+    helper = lib.flake-helper;
+  in {
+
+    hosts = {
+      caladan = {
+        system = "aarch64-darwin";
+        sshUser = "hydra";
+        homeManagerEnable = true;
+      };
+      corrino = {
+        system = "x86_64-linux";
+        ip = "corrino";
+        description = "Hetzner AX41 dual 512GB NVME";
+        modules = [ hefe-internal.nixosModules.corrino ];
+        # TODO: install znc irc bouncer
+      };
+      chusuk = {
+        # ip = "chusuk.pinto-pike.ts.net";
+        system = "x86_64-linux";
+        description = "lenovo t480";
+      };
+      hacknix = {
+        # ip = "hacknix.pinto-pike.ts.net"; # clone repo and deploy within
+        system = "x86_64-linux";
+        description = "hacking vm";
+      };
+      mail = {
+        # ip = "mail.pinto-pike.ts.net"; # clone repo and deploy within
+        system = "x86_64-linux";
+        description = "mail server";
+      };
+
+
+      #ecaz = {};
+
+      # gamont = {
+      #   description = "pi 2 tfp"
+      # };
+
+      #kolhar = {}; # nixos vm on caladan
+      #hagal = {}; # apple tv
+      
+      # palma = {
+      #   description = "NAS";
+      # };
+      # lampadas = {
+      #   hostname = "lampadas";
+      #   description = "palma bmc";
+      # };
+
+      # parmentier = {
+      #   description = "ryzen 5 3600 + RTX A2000";
+      # };
+      # lankiveil = {
+      #   hostname = "lankiveil";
+      #   description = "parmentier bmc";
+      # };
+
+      # poritrin = {
+      #   description = "ryzen 5 3600g";
+      # };
+      # lernaeus = {
+      #   hostname = "lernaeus";
+      #   description = "poritrin bmc";
+      # };
+
+      #kaitain = {};
+
+      # futher names: https://neoencyclopedia.fandom.com/wiki/List_of_Dune_planets
+      # Muritan
+      # Naraj
+      # Palma
+      # Parmentier
+      # Poritrin
+      # Richese
+      # Romo
+      # Rossak
+      # Sikun
+      # Synchrony
+      # Tleilax
+      # Tupile
+      # Zanovar
+    };
+
+    nixosConfigurations = helper.mapToNixosConfigurations self.hosts;
+    darwinConfigurations = helper.mapToDarwinConfigurations self.hosts;
+
+    overlays = {
+      emile = import ./nix/pkgs/overlay.nix;
+      default = self.overlays.emile;
+
+      unstable = final: prev: {
+        unstable = import nixpkgs-unstable {
+          system = "x86_64-linux";
+          config.allowUnfree = true;
+        };
+      };
+    };
+
+    deploy.nodes = helper.mapToDeployRsConfiguration self.hosts;
+    deploy.autoRollback = true;
+
+    packages =
+      nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-darwin" ] (system:
+    let
+      pkgs = import nixpkgs {
+        inherit system;
+        overlays = [
+          self.overlays.emile
+
+          # some arguments for packages
+          (_: _: { inherit naersk; })
+        ];
+      };
+    in {
+      inherit (pkgs)
+        vokobe
+        # emu-riscv
+        # emu-mips
+        # emu-x86_64
+        ;
+    });
+
+    hydraJobs = {
+      inherit (self) packages;
+      nixosConfigurations = helper.buildHosts self.nixosConfigurations;
+    };
+
+    templates = {
+      # ; nix nix registry add hefe /Users/emile/Documents/hefe
+      # ; nix flake init -t hefe#ctf
+      ctf = {
+        description = "A basic ctf env with pwn, rev, ... tools";
+        path = ./nix/templates/ctf;
+        welcomeText = ''
+          # A basic CTF env
+          ## Intended usage
+          The intended usage of this flake is...
+
+          ## More info
+          - [Rust language](https://www.rust-lang.org/)
+          - [Rust on the NixOS Wiki](https://nixos.wiki/wiki/Rust)
+          - ...
+        '';
+      };
+    };
+
+    # checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
+  };
+}
diff --git a/nix/hosts/caladan/README.md b/nix/hosts/caladan/README.md
new file mode 100644
index 0000000..733f564
--- /dev/null
+++ b/nix/hosts/caladan/README.md
@@ -0,0 +1,3 @@
+# caladan
+
+m1 macbook air
\ No newline at end of file
diff --git a/nix/hosts/caladan/aliases.nix b/nix/hosts/caladan/aliases.nix
new file mode 100644
index 0000000..5ffff74
--- /dev/null
+++ b/nix/hosts/caladan/aliases.nix
@@ -0,0 +1,53 @@
+{
+  ":q" = "exit";
+  ls = "eza";
+  ytop = "btm";
+
+  # short forms
+  tf = "terraform";
+  h = "mosh hack";
+
+  r2help = ''r2 -qq -c "?*~..." --'';
+  mosh = "mosh --no-init";
+  t = "task";
+  tw = "timew";
+
+  ipa = "scutil --nwi";
+
+  # this can be super nice and super annoying at the same time:
+  # ssh = "kitty +kitten ssh";
+
+  light = "kitty +kitten themes --reload-in=all Ayu Light";
+  dark = "kitty +kitten themes --reload-in=all Ayu";
+
+
+  ".." = "cd ..";
+  "..." = "cd ../..";
+  "...." = "cd ../../..";
+  "....." = "cd ../../../..";
+
+  grep = "grep --color=auto";
+  nix-stray-roots = ''
+    nix-store --gc --print-roots | egrep -v "^(/nix/var|/run/w+-system|{memory)"'';
+
+  holdmybeer = "sudo ";
+
+  servethis = "python3 -m http.server";
+
+  # nmap foo
+  nmap_open_ports = "nmap --open";
+  nmap_list_interfaces = "nmap --iflist";
+  nmap_slow = "sudo nmap -sS -v -T1";
+  nmap_fin = "sudo nmap -sF -v";
+  nmap_full = "sudo nmap -sS -T4 -PE -PP -PS80,443 -PY -g 53 -A -p1-65535 -v";
+  nmap_check_for_firewall = "sudo nmap -sA -p1-65535 -v -T4";
+  nmap_ping_through_firewall = "nmap -PS -PA";
+  nmap_fast = "nmap -F -T5 --version-light --top-ports 300";
+  nmap_detect_versions = "sudo nmap -sV -p1-65535 -O --osscan-guess -T4 -Pn";
+  nmap_check_for_vulns = "nmap --script = vuln";
+  nmap_full_udp = "sudo nmap -sS -sU -T4 -A -v -PE -PS22,25,80 -PA21,23,80,443,3389 ";
+  nmap_traceroute = "sudo nmap -sP -PE -PS22,25,80 -PA21,23,80,3389 -PU -PO --traceroute ";
+  nmap_full_with_scripts = "sudo nmap -sS -sU -T4 -A -v -PE -PP -PS21,22,23,25,80,113,31339 -PA80,113,443,10042 -PO --script all " ;
+  nmap_web_safe_osscan = "sudo nmap -p 80,443 -O -v --osscan-guess --fuzzy ";
+  nmap_ping_scan = "nmap -n -sP";
+}
diff --git a/nix/hosts/caladan/darwin-configuration.nix b/nix/hosts/caladan/darwin-configuration.nix
new file mode 100644
index 0000000..b31b6b3
--- /dev/null
+++ b/nix/hosts/caladan/darwin-configuration.nix
@@ -0,0 +1,82 @@
+{ pkgs, lib, ... }:
+
+{
+  imports = [
+    ./overlay.nix
+  ];
+
+  users.users.emile = {
+    name = "emile";
+    home = "/Users/emile";
+  };
+
+  users.users.hydra = {
+    name = "hydra";
+    home = "/Users/hydra";
+  };
+
+  nix = {
+    useDaemon = true;
+    package = pkgs.nixFlakes;
+    extraOptions = ''
+  		builders-use-substitutes = true
+      auto-optimise-store = true
+    '' + lib.optionalString (pkgs.system == "aarch64-darwin") ''
+      extra-platforms = x86_64-darwin aarch64-darwin
+    '';
+
+    settings = {
+      trusted-public-keys = [
+        "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
+      ];
+      substituters = [
+        "https://cache.nixos.org"
+      ];
+
+      experimental-features = [ "nix-command" "flakes" ];
+    };
+
+    distributedBuilds = true;
+
+  	buildMachines = [
+      {
+        hostName = "corrino.emile.space";
+        system = "x86_64-linux";
+        maxJobs = 1;
+        speedFactor = 2;
+
+        # Feature	      | Derivations requiring it
+        # --------------|-----------------------------------------------------
+        # kvm	          | Everything which builds inside a vm, like NixOS tests
+        # nixos-test	  | Machine can run NixOS tests
+        # big-parallel  | kernel config, libreoffice, evolution, llvm and chromium.
+        # benchmark	    | Machine can generate metrics (Means the builds usually
+        #               | takes the same amount of time)
+
+        # cat /etc/nix/machines
+        # root@corrino  x86_64-linux      /home/nix/.ssh/id_ed25519        8 1     kvm,benchmark
+
+        supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
+        mandatoryFeatures = [ ];
+    	}
+    ];
+  };
+
+  nixpkgs = {
+    config.allowUnfree = true;
+  };
+
+  programs.fish.enable = true;
+
+  services.nix-daemon.enable = true;
+
+  security.pam.enableSudoTouchIdAuth = true;
+
+  environment = {
+    systemPackages = [
+      pkgs.yarr
+    ];
+    shells = with pkgs; [ bashInteractive zsh fish ];
+  };
+
+}
diff --git a/nix/hosts/caladan/functions.zsh b/nix/hosts/caladan/functions.zsh
new file mode 100644
index 0000000..b134ef3
--- /dev/null
+++ b/nix/hosts/caladan/functions.zsh
@@ -0,0 +1,30 @@
+function pmk() {
+	docker run \
+		-v \
+		"$(pwd):/pwn" \
+		--cap-add=SYS_PTRACE \
+		--security-opt seccomp=unconfined \
+		-d \
+		--name $1 \
+		-i \ 
+		ctf_ubuntu22.10;
+}
+
+function pcd() {
+	docker exec \
+		-it \
+		--workdir /pwn \
+		$1 \
+		bash;
+}
+
+function prm() {
+	docker stop $1;
+}
+
+function pls() {
+	docker ps \
+		-a \
+		-f ancestor=ctf_ubuntu22.10 \
+		--format "{{.Names}}";
+}
diff --git a/nix/hosts/caladan/home_emile.nix b/nix/hosts/caladan/home_emile.nix
new file mode 100644
index 0000000..d3428cf
--- /dev/null
+++ b/nix/hosts/caladan/home_emile.nix
@@ -0,0 +1,180 @@
+{ pkgs, lib, ... }:
+
+{
+  home = {
+    stateVersion = "22.11";
+    username = "emile";
+    homeDirectory = "/Users/emile";
+  };
+
+  # let home-manager install and manage itself
+  programs = {
+    home-manager.enable = true;
+
+    direnv = { 
+      enable = true;
+      nix-direnv.enable = true;
+    };
+
+    htop = {
+      enable = true;
+      settings.show_program_with_path = true;
+    };
+
+    zsh = {
+      enable = true;
+      enableCompletion = true;
+      #syntaxHighlighting.enable = true;
+      shellAliases = import ./aliases.nix;
+      enableAutosuggestions = true;
+      oh-my-zsh = {
+        enable = true;
+        plugins = [ "git" "vi-mode" "web-search" "urltools" ];
+      };
+
+      # this has to be added, so we can ssh into the host using deploy-rs and
+      # access the `nix-store` stuff
+      envExtra = ''
+        if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
+          . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
+        fi
+      '';
+
+      initExtraBeforeCompInit = ''
+        ${builtins.readFile ./session_variables.zsh}
+        ${builtins.readFile ./functions.zsh}
+
+        eval "$(direnv hook zsh)"
+
+        setopt autocd 		# cd without needing to use the cd command
+      '';
+    };
+
+    kitty = {
+      enable = true;
+
+      # font = pkgs.iosevka;
+
+      font = {
+        name = "Iosevka Nerd Font";
+        size = 13;
+      };
+
+      settings = {
+        font_size = 12;
+
+        disable_ligatures = "never";
+        close_on_child_death = "yes";
+
+        tab_bar_edge = "top";
+        tab_bar_style = "slant";
+        tab_bar_min_tabs = 1;
+
+        # tab_title_template = "{index}[{layout_name[0:2]}]: {title.replace('emile', 'e')[title.rfind('/')+1:]}";
+        tab_title_template = "{index}[{layout_name[0:2]}]: {title.replace('emile', 'e')}";
+
+        editor = "/Users/emile/.cargo/bin/hx";
+
+        macos_option_as_alt = "no";
+        macos_quit_when_last_window_closed = "yes";
+
+        kitty_mod = "ctrl+shift";
+
+        clear_all_shortcuts = "";
+      };
+
+      keybindings = {
+        "cmd+enter" = "launch --cwd=current --location=split";
+        "cmd+shift+enter" = "launch --cwd=current --location=hsplit";
+
+        "cmd+shift+h" = "move_window left";
+        "cmd+shift+j" = "move_window down";
+        "cmd+shift+k" = "move_window up";
+        "cmd+shift+l" = "move_window right";
+
+        "cmd+shift+m" = "detach_window ask";
+
+        "command+j" = "kitten pass_keys.py neighboring_window bottom command+j";
+        "command+k" = "kitten pass_keys.py neighboring_window top    command+k";
+        "command+h" = "kitten pass_keys.py neighboring_window left   command+h";
+        "command+l" = "kitten pass_keys.py neighboring_window right  command+l";
+        "command+b" = "combine : clear_terminal scroll active : send_text normal,application \x0c";
+
+        # "ctrl+n" = "send_text all \x0e";
+        "ctrl+e" = "send_text all \x01h";
+        "ctrl+n" = "send_text all \x01i";
+        "ctrlshift++n" = "send_text all \x01i";
+
+        "ctrl+left" = "resize_window wider";
+        "ctrl+right" = "resize_window narrower";
+        "ctrl+up" = "resize_window shorter";
+        "ctrl+down" = "resize_window taller";
+      };
+
+      environment = { };
+    };
+  };
+
+  home.packages = with pkgs; [
+    coreutils mpv
+
+    # terminal foo
+    kitty
+    jq ripgrep fd eza lsd tree broot
+    du-dust mktemp htop rsync
+    p7zip imagemagick binwalk lftp
+    graphviz
+
+    git tig 
+
+    # nix related tools
+    deploy-rs
+    cachix
+    nixos-rebuild
+
+    # editor
+    helix
+    nodePackages_latest.typescript-language-server # js language server
+    nil # nix language server
+    nodePackages.yaml-language-server # yaml language server
+
+    # binary foo
+    radare2
+
+    # network foo
+    curl
+    wireguard-tools
+    # tailscale
+
+    # rss foo
+    yarr
+
+    # go foo
+    go delve
+
+    # c foo
+    cmake
+
+    # iot hack foo
+    minicom
+
+    SDL2
+
+    # macos foo
+    # karabiner-elements
+
+    # qemu tooling
+    qemu
+    sphinx #docs
+    virt-manager
+
+    # lisp foo
+    unstable.sbcl
+
+    # infrastructure as code foo
+    terraform ansible
+
+  ] ++ lib.optionals stdenv.isDarwin [
+    m-cli
+  ];
+}
diff --git a/nix/hosts/caladan/home_hydra.nix b/nix/hosts/caladan/home_hydra.nix
new file mode 100644
index 0000000..63d3563
--- /dev/null
+++ b/nix/hosts/caladan/home_hydra.nix
@@ -0,0 +1,18 @@
+{ config, pkgs, ... }:
+
+{
+ home = {
+  stateVersion = "22.11";
+  username = "hydra";
+  homeDirectory = "/Users/hydra";
+ };
+
+ # let home-manager install and manage itself
+ programs = {
+  home-manager.enable = true;
+ };
+
+ home.packages = with pkgs; [
+  tailscale
+ ];
+}
diff --git a/nix/hosts/caladan/overlay.nix b/nix/hosts/caladan/overlay.nix
new file mode 100644
index 0000000..a96e3f3
--- /dev/null
+++ b/nix/hosts/caladan/overlay.nix
@@ -0,0 +1,18 @@
+{ ... }:
+
+{
+  nixpkgs = {
+    overlays = [
+      (self: super: {
+        # helix-2303 = self.callPackage ../../pkgs/helix-2303 { };
+        # r2 = self.callPackage ../../pkgs/radare2-5.8.4 { };
+        # ansel = self.callPackage ../../pkgs/ansel { };
+        # typst = self.callPackage ../pkgs/radare2-5.8.4 { };
+      })
+    ];
+    config = {
+      allowUnfree = true;
+      allowBroken= true;
+    };
+  };
+}
diff --git a/nix/hosts/caladan/session_variables.zsh b/nix/hosts/caladan/session_variables.zsh
new file mode 100644
index 0000000..863f31c
--- /dev/null
+++ b/nix/hosts/caladan/session_variables.zsh
@@ -0,0 +1,35 @@
+export PROMPT="; "		# minimal prompt
+export RPROMPT="%F{green}%/%F{reset}"
+export PROMPT_EOL_MARK="%"  # hide EOL sign ('%')
+
+export EDITOR="hx"
+export LC_ALL="en_US.UTF-8"
+export LANG="en_US.UTF-8"
+
+
+export GOPATH=~/go
+export GOBIN=$GOPATH/bin
+
+# /usr/local/bin is mac specific and where brew installs stuff. As we are
+# making use of brew as fallback so we need to add it
+export PATH="$HOME/.local/bin:$HOME/.nix-profile/bin:/usr/local/bin:$PATH"
+export PATH=$PATH:~/go/bin
+export PATH=$PATH:~/.emacs.d/bin
+export PATH=$PATH:~/bin
+export PATH=$PATH:"/Applications/Racket v8.8/bin"
+export PATH=/Users/emile/.cargo/bin:$PATH
+export PATH=$PATH:/opt/homebrew/bin
+
+# uxn
+export PATH=$PATH:/Users/emile/Documents/projects/uxn/bin
+
+# fzf
+export FZF_BASE=$(whereis fzf | awk '{print $2}' | sed "s/fzf$//g")
+if [ -n "${commands[fzf-share]}" ]; then
+  source "$(fzf-share)/key-bindings.zsh"
+  source "$(fzf-share)/completion.zsh"
+fi
+
+if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
+  . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
+fi
diff --git a/nix/hosts/caladan/ssh.pub b/nix/hosts/caladan/ssh.pub
new file mode 100644
index 0000000..e68dee1
--- /dev/null
+++ b/nix/hosts/caladan/ssh.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew
diff --git a/nix/hosts/chusuk/README.md b/nix/hosts/chusuk/README.md
new file mode 100644
index 0000000..7dcab5f
--- /dev/null
+++ b/nix/hosts/chusuk/README.md
@@ -0,0 +1,3 @@
+# chusuk
+
+t480
diff --git a/nix/hosts/chusuk/configuration.nix b/nix/hosts/chusuk/configuration.nix
new file mode 100644
index 0000000..c9c52e7
--- /dev/null
+++ b/nix/hosts/chusuk/configuration.nix
@@ -0,0 +1,141 @@
+# Edit this configuration file to define what should be installed on
+# your system.  Help is available in the configuration.nix(5) man page
+# and in the NixOS manual (accessible by running ‘nixos-help’).
+
+{ config, pkgs, ... }:
+
+{
+  imports =
+    [ # Include the results of the hardware scan.
+      ./hardware-configuration.nix
+    ];
+
+  # Use the systemd-boot EFI boot loader.
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # Set your time zone.
+  time.timeZone = "Europe/Amsterdam";
+
+  networking = {
+    hostName = "chusuk"; # Define your hostname.
+    wireless.enable = true;  # Enables wireless support via wpa_supplicant.
+
+    # The global useDHCP flag is deprecated, therefore explicitly set to false here.
+    # Per-interface useDHCP will be mandatory in the future, so this generated config
+    # replicates the default behaviour.
+    useDHCP = false;
+    interfaces.enp0s31f6.useDHCP = true;
+    interfaces.wlp3s0.useDHCP = true;
+
+    # Open ports in the firewall.
+    # networking.firewall.allowedTCPPorts = [ ... ];
+    # networking.firewall.allowedUDPPorts = [ ... ];
+    # Or disable the firewall altogether.
+    firewall.enable = true;
+  };
+
+  # Configure network proxy if necessary
+  # networking.proxy.default = "http://user:password@proxy:port/";
+  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
+
+  # Select internationalisation properties.
+  i18n.defaultLocale = "en_US.UTF-8";
+  console = {
+    font = "Lat2-Terminus16";
+    keyMap = "us";
+  };
+
+  # Define a user account. Don't forget to set a password with ‘passwd’.
+  users.users.emile = {
+    isNormalUser = true;
+    extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
+  };
+  users.users.root = {
+    openssh.authorizedKeys.keys = [
+      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew"
+    ];
+  };
+
+  # List packages installed in system profile. To search, run:
+  # $ nix search wget
+  environment = {
+    pathsToLink = [ "/libexec" ];
+    systemPackages = with pkgs; [
+      kitty 
+      vim helix
+      wget htop eza fd du-dust
+      tailscale
+      cryptsetup
+      firefox
+
+      networkmanager
+
+      ###################################3
+      # wayland foo
+      waybar
+      hyprpaper # wallpaper
+
+      tofi rofi
+      dolphin
+      mako
+
+      pipewire
+      wireplumber
+
+      xdg-desktop-portal
+      ###################################3
+    ];
+  };
+
+  # Some programs need SUID wrappers, can be configured further or are
+  # started in user sessions.
+  # programs.mtr.enable = true;
+  # programs.gnupg.agent = {
+  #   enable = true;
+  #   enableSSHSupport = true;
+  # };
+
+  programs = {
+    mosh.enable = true;
+
+    hyprland = {
+      enable = true;
+    };
+  };
+
+  # List services that you want to enable:
+
+  # Enable the OpenSSH daemon.
+  services = {
+    openssh.enable = true;
+    tailscale.enable = true;
+    xserver = {
+      enable = true;
+      desktopManager = {
+        xterm.enable = false;
+      };
+
+      displayManager = {
+        defaultSession = "none+i3";
+      };
+
+      windowManager.i3 = {
+        enable = true;
+        extraPackages = with pkgs; [
+          dmenu i3status i3lock i3blocks
+        ];
+      };
+    };
+  };
+
+  # This value determines the NixOS release from which the default
+  # settings for stateful data, like file locations and database versions
+  # on your system were taken. It‘s perfectly fine and recommended to leave
+  # this value at the release version of the first install of this system.
+  # Before changing this value read the documentation for this option
+  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
+  system.stateVersion = "21.05"; # Did you read the comment?
+
+}
+
diff --git a/nix/hosts/chusuk/hardware-configuration.nix b/nix/hosts/chusuk/hardware-configuration.nix
new file mode 100644
index 0000000..6b34e3b
--- /dev/null
+++ b/nix/hosts/chusuk/hardware-configuration.nix
@@ -0,0 +1,31 @@
+# Do not modify this file!  It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations.  Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports =
+    [ (modulesPath + "/installer/scan/not-detected.nix")
+    ];
+
+  boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-intel" ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" =
+    { device = "/dev/disk/by-uuid/22445c0e-71bd-488f-88e5-0abc60441e58";
+      fsType = "ext4";
+    };
+
+  fileSystems."/boot" =
+    { device = "/dev/disk/by-uuid/29BB-5D28";
+      fsType = "vfat";
+    };
+
+  swapDevices =
+    [ { device = "/dev/disk/by-uuid/d79efda2-1190-428c-8598-6911793175fb"; }
+    ];
+
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+}
diff --git a/nix/hosts/corrino/README.md b/nix/hosts/corrino/README.md
new file mode 100644
index 0000000..edeb74c
--- /dev/null
+++ b/nix/hosts/corrino/README.md
@@ -0,0 +1,4 @@
+# corrino
+
+`corrino.emile.space` is my current (2023-05-28) "main" server for hosting
+services, running build tasks and more.
diff --git a/nix/hosts/corrino/configuration.nix b/nix/hosts/corrino/configuration.nix
new file mode 100644
index 0000000..aed56cf
--- /dev/null
+++ b/nix/hosts/corrino/configuration.nix
@@ -0,0 +1,415 @@
+{ config, pkgs, ... }:
+{
+  imports =
+    [ # Include the results of the hardware scan.
+      ./hardware-configuration.nix
+      # ./age_secrets.nix
+
+      ./www/emile.space.nix
+      ./www/git.emile.space.nix
+      ./www/hydra.emile.space.nix
+      ./www/netbox.emile.space.nix
+      # ./www/grafana.emile.space.nix
+      ./www/photo.emile.space.nix
+
+      
+      # ./www/events.emile.space.nix
+      ./www/tickets.emile.space.nix
+      ./www/talks.emile.space.nix
+      ./www/stream.emile.space.nix
+
+      ./www/pgweb.emile.space.nix
+
+      ./www/ctf.emile.space.nix
+      # ./www/magic-hash.emile.space.nix
+
+      # ./www/znc.emile.space.nix
+
+      ./gemini/emile.space.nix
+    ];
+
+  # Use GRUB2 as the boot loader.
+  # We don't use systemd-boot because Hetzner uses BIOS legacy boot.
+  boot = {
+    #supportsInitrdSecrets = true;
+
+    loader.systemd-boot.enable = false;
+    loader.grub = {
+      enable = true;
+      efiSupport = false;
+      enableCryptodisk = true;
+      device = "nodev";
+      devices = [ "/dev/nvme0n1" "/dev/nvme1n1"];
+    };
+
+    kernelParams = [ "ip=135.181.142.139::135.181.142.129:255.255.255.192:corrino:enp35s0:off:8.8.8.8:8.8.4.4:" ];
+
+    initrd = {
+      kernelModules = [ "dm-snapshot" ];
+
+      availableKernelModules = [ "cryptd" "aesni_intel" "igb" ];#"FIXME Your network driver" ];
+
+      network = {
+        enable = true;
+        ssh = {
+          enable = true;
+      
+          # ssh port during boot for luks decryption
+          port = 2222;
+          authorizedKeys = config.users.users.root.openssh.authorizedKeys.keys;
+          hostKeys = [ "/initrd_ssh_host_ecdsa_key" ];
+        };
+        postCommands = ''
+          echo 'cryptsetup-askpass' >> /root/.profile
+        '';
+      };
+
+      luks = {
+        forceLuksSupportInInitrd = true;
+        devices = {
+          root = {
+            preLVM = true;
+            device = "/dev/md1";
+            allowDiscards = true;
+          };
+        };
+      };
+                  
+      secrets = {
+        "/initrd_ssh_host_ecdsa_key" = "/initrd_ssh_host_ecdsa_key";
+      };
+
+      # The RAIDs are assembled in stage1, so we need to make the config
+      # available there.
+      # services.swraid.mdadmConf = config.environment.etc."mdadm.conf".text;
+    };
+
+    # From the nixos 23.11 release notes changelog breaking changes section:
+    # mdraid support is optional now. This reduces initramfs size and prevents
+    # the potentially undesired automatic detection and activation of software
+    # RAID pools. It is disabled by default in new configurations (determined
+    # by stateVersion), but the appropriate settings will be generated by
+    # nixos-generate-config when installing to a software RAID device, so the
+    # standard installation procedure should be unaffected. If you have custom
+    # configs relying on mdraid, ensure that you use stateVersion correctly or
+    # set boot.swraid.enable manually. On systems with an updated stateVersion
+    # we now also emit warnings if mdadm.conf does not contain the minimum
+    # required configuration necessary to run the dynamically enabled monitoring
+    # daemons.
+    swraid = {
+      enable = true;
+      # mdadmConf = config.environment.etc."mdadm.conf".text;
+      mdadmConf = ''
+        HOMEHOST <ignore>
+        MAILADDR root
+      '';
+    };
+
+    supportedFilesystems = [ "cifs" ];
+  };
+
+  # The mdadm RAID1s were created with 'mdadm --create ... --homehost=hetzner',
+  # but the hostname for each machine may be different, and mdadm's HOMEHOST
+  # setting defaults to '<system>' (using the system hostname).
+  # This results mdadm considering such disks as "foreign" as opposed to
+  # "local", and showing them as e.g. '/dev/md/hetzner:root0'
+  # instead of '/dev/md/root0'.
+  # This is mdadm's protection against accidentally putting a RAID disk
+  # into the wrong machine and corrupting data by accidental sync, see
+  # https://bugzilla.redhat.com/show_bug.cgi?id=606481#c14 and onward.
+  # We do not worry about plugging disks into the wrong machine because
+  # we will never exchange disks between machines, so we tell mdadm to
+  # ignore the homehost entirely.
+  environment = {
+    etc."mdadm.conf".text = ''
+      HOMEHOST <ignore>
+      MAILADDR root
+    '';
+
+    systemPackages = with pkgs; [
+      git
+      du-dust
+      ncdu
+      # helix
+
+      sshfs
+    ];
+  };
+
+  programs = {
+    mosh.enable = true;
+    mtr.enable = true;
+  };
+
+  # create a oneshot job to authenticate to Tailscale
+  systemd.services.tailscale-autoconnect = {
+    description = "Automatic connection to Tailscale";
+
+    # make sure tailscale is running before trying to connect to tailscale
+    after = [ "network-pre.target" "tailscale.service" ];
+    wants = [ "network-pre.target" "tailscale.service" ];
+    wantedBy = [ "multi-user.target" ];
+
+    # set this service as a oneshot job
+    serviceConfig.Type = "oneshot";
+
+    # have the job run this shell script
+    script = with pkgs; ''
+      # wait for tailscaled to settle
+      sleep 2
+
+      # check if we are already authenticated to tailscale
+      status="$(${tailscale}/bin/tailscale status -json | ${jq}/bin/jq -r .BackendState)"
+      if [ $status = "Running" ]; then # if so, then do nothing
+        exit 0
+      fi
+
+      # otherwise authenticate with tailscale
+      ${tailscale}/bin/tailscale up \
+        --advertise-exit-node --exit-node \
+        -authkey tskey-auth-kfswm86CNTRL-QdFyL42rAhJDw7VZ2poVaJgDewQvmUu5K
+    '';
+        # this is an old authkey which I found (was used once, now it's landed here but long expired...). I'm adding an age secret instead, although it isn't used anymore...
+        # -authkey ${config.age.secrets.tailscale_authkey}
+  };
+
+
+  networking = {
+    hostName = "corrino";
+    domain = "emile.space";
+
+    # Network (Hetzner uses static IP assignments, and we don't use DHCP here)
+    useDHCP = false;
+    interfaces = {
+      "enp35s0" = {
+        ipv4.addresses = [
+          { address = "135.181.142.139"; prefixLength = 26; }
+        ];
+      };
+      "enp35s0".ipv6.addresses = [
+        { address = "2a01:4f9:3a:16a4::1"; prefixLength = 64; }
+      ];
+    };
+
+    defaultGateway = "135.181.142.129";
+    defaultGateway6 = { address = "fe80::1"; interface = "enp35s0"; };
+
+    nameservers = [ "8.8.8.8" "8.8.4.4" ];
+
+
+    firewall = {
+      enable = true;
+      allowedTCPPorts = [
+        80 443 # normal web
+      ];
+      allowedUDPPorts = [
+        51820 # wireguard
+      ];
+      allowedUDPPortRanges = [
+        { from = 60000; to = 61000; } # mosh
+      ];
+
+      interfaces."tailscale0".allowedTCPPorts = [
+        8085 # random internal web server port
+      ];
+    };
+
+    nat = {
+      enable = true;
+      enableIPv6 = true;
+      externalInterface = "enp35s0";
+      internalInterfaces = [ "wg0" ];
+    };
+
+    wireguard = {
+      enable = true;
+      interfaces."wg0" = {
+        ips = [ "10.87.0.1/24" ];
+        listenPort = 51820;
+        # This allows the wireguard server to route your traffic to the internet and hence be like a VPN
+        # For this to work you have to set the dnsserver IP of your router (or dnsserver of choice) in your clients
+        postSetup = ''
+          ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.87.0.0/24 -o eth0 -j MASQUERADE
+        '';
+
+        # This undoes the above command
+        postShutdown = ''
+          ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.87.0.0/24 -o eth0 -j MASQUERADE
+        '';
+
+        privateKeyFile = config.age.secrets.wireguard_privatekey.path;
+
+        peers = [
+          # List of allowed peers.
+          { # Emiles-MBA
+            publicKey = "Ebsjn7w2FeUs5lUN6ALoUcF/o9/+SopDL324YJPSCDY=";
+            # List of IPs assigned to this peer within the tunnel subnet. Used to configure routing.
+            allowedIPs = [ "10.87.0.2/32" ];
+          }
+          { # Emiles-IphoneX
+            publicKey = "xGfmwraI0Eh3eFEXjJrd2AYCgUM1uK4Y+FX5ACAQZ3M=";
+            # List of IPs assigned to this peer within the tunnel subnet. Used to configure routing.
+            allowedIPs = [ "10.87.0.3/32" ];
+          }
+        ];
+      };
+    };
+  };
+
+  # Initial empty root password for easy login:
+  users.users = {
+    root = {
+      initialHashedPassword = "";
+      openssh.authorizedKeys.keys = [
+        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew emile@caladan"
+      ];
+      packages = with pkgs; [
+        mdadm
+        tailscale
+
+        # random useful stuff
+        htop
+        git
+        vim
+        fd ripgrep
+      ];
+      extraGroups = [ "docker" "libvirtd" ];
+    };
+
+    hack = {
+      isNormalUser = true;
+      openssh.authorizedKeys.keys = [
+        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew emile@caladan"
+      ];
+      extraGroups = [ "docker" "libvirtd" ];
+    };
+
+    tmpuser1 = {
+      isNormalUser = true;
+      openssh.authorizedKeys.keys = [
+        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew emile@caladan"
+        # "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJMMq7gVuOuJEuarcsss2pb4JJS39zW/Fuow0foyqlV5 noobtracker@noobtracker-linux"
+      ];
+    };
+  };
+
+  services = {
+    openssh = {
+      settings = {
+        PermitRootLogin = "prohibit-password";
+        PasswordAuthentication = false;
+      };
+      enable = true;
+    };
+
+    nginx = {
+      enable = true;
+      recommendedGzipSettings = true;
+      recommendedOptimisation = true;
+      recommendedProxySettings = true;
+      recommendedTlsSettings = true;
+    };
+
+    tailscale = {
+      enable = true;
+
+      # use corrino as a subnet router and an exit node
+      useRoutingFeatures = "both";
+    };
+  };
+  
+  nix = {
+    settings.experimental-features = [ "nix-command" "flakes" ];
+
+    gc = {
+      automatic = true;
+      dates = "daily";
+      options = "--delete-older-than 7d";
+    };
+
+    optimise = {
+      automatic = true;
+      dates = [ "03:45" ];
+    };
+
+    # we need the below in order for hydra to be allowed to access the pages
+    extraOptions = ''
+      allowed-uris = ssh://gitea@git.emile.space git+https://git.emile.space https://git.emile.space https://portswigger-cdn.net https://git.sr.ht/ https://gitlab.com/simple-nixos-mailserver https://github.com/nixos/nixpkgs
+  		builders-use-substitutes = true
+    '';
+
+    buildMachines = [
+      {
+    	 hostName = "localhost";
+    	 system = "x86_64-linux";
+       protocol = "ssh-ng";
+    	 maxJobs = 1;
+    	 supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
+    	}
+      {
+    	 hostName = "caladan";
+    	 system = "aarch64-darwin";
+       protocol = "ssh-ng";
+    	 maxJobs = 1;
+    	 speedFactor = 2;
+    	 supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
+    	 mandatoryFeatures = [ ];
+    	}
+    ];
+
+  	distributedBuilds = true;
+  };
+
+  nixpkgs.config = {
+    allowUnfree = true;
+    permittedInsecurePackages = [
+      # none :D
+    ];
+  };
+
+  security = {
+    acme = {
+      acceptTerms = true;
+      defaults.email = "admin+acme@emile.space";
+    };
+  };
+
+  virtualisation = {
+    docker.enable = true;
+    # libvirtd = {
+    #   enable = true;
+    #   qemu = {
+    #     swtpm.enable = true;
+    #     ovmf.enable = true;
+    #     ovmf.packages = [ pkgs.OVMFFull.fd ];
+    #   };
+    # };
+    # spiceUSBRedirection.enable = true;
+  };
+
+  # programs.virt-manager.enable = true;
+
+  fileSystems."/proc" = {
+    device = "/proc";
+    options = [
+      "nosuid" "nodev" "noexec" "relatime" # normal foo
+      "hidepid=2" # this makes sure users can only see their own processes
+    ];
+  };
+
+  fileSystems."/mnt/storagebox-bx11" = {
+    device = "//u331921.your-storagebox.de/backup";
+    fsType = "cifs";
+    options =
+      let
+        automount_opts = "_netdev,x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
+      in ["${automount_opts},credentials=${config.age.secrets.storage_box_bx11_password.path}"];
+  };
+
+  # FIXME
+  # This value determines the NixOS release with which your system is to be
+  # compatible, in order to avoid breaking some software such as database
+  # servers. You should change this only after NixOS release notes say you
+  # should.
+  system.stateVersion = "22.11"; # Did you read the comment?
+}
diff --git a/nix/hosts/corrino/default.nix b/nix/hosts/corrino/default.nix
new file mode 100644
index 0000000..87486c5
--- /dev/null
+++ b/nix/hosts/corrino/default.nix
@@ -0,0 +1,3 @@
+{
+	sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGFzoWTW3c7CKWx5t0OZzTfKTlC6R2VHXczVatgYI57N";
+}
diff --git a/nix/hosts/corrino/emile.space.nix b/nix/hosts/corrino/emile.space.nix
new file mode 100644
index 0000000..9cca880
--- /dev/null
+++ b/nix/hosts/corrino/emile.space.nix
@@ -0,0 +1,60 @@
+{
+  services.nginx.virtualHosts."emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    # kTLS = true;
+
+    locations = {
+      "/" = {
+        root = "/var/www/emile.space";
+        extraConfig = ''
+          add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+        ''; 
+      };
+
+      #"/.well-known" = {
+      #  root = "/var/www/emile.space";
+      #  extraConfig = ''
+      #    autoindex on;
+      #  '';
+      #};
+
+      ## I ran a matrix homeserver for some time, then stopped, but the other
+      ## homeserver don't know and don't stop sending me requests (5e5 a day or
+      ## so).
+      #"/.well-known/matrix/server".extraConfig = ''
+      #  return 410;
+      #'';
+    };
+  };
+
+  # services.stargazer = {
+  #   enable = true;
+  #   user = "stargazer";
+  #   group = "stargazer";
+
+  #   certLifetime = "1m";
+  #   store = /var/lib/gemini/certs;
+
+  #   genCerts = true;
+  #   regenCerts = true;
+  #   responseTimeout = 0;
+  #   requestTimeout = 5;
+
+  #   routes = [
+  #     {
+  #       route = "emile.space";
+  #       root = "/srv/gemini/emile.space";
+  #     }
+  #   ];
+
+  #   listen = [ "0.0.0.0" "[2002:a00:1::]" ];
+
+  #   ipLogPartial = false;
+  #   ipLog = false;
+  #   connectionLogging = false;
+
+  #   certOrg = "emile.space";
+  # };
+}
diff --git a/nix/hosts/corrino/gemini/emile.space.nix b/nix/hosts/corrino/gemini/emile.space.nix
new file mode 100644
index 0000000..f7116ae
--- /dev/null
+++ b/nix/hosts/corrino/gemini/emile.space.nix
@@ -0,0 +1,16 @@
+{ ... }:
+
+{
+  services.agate = {
+    # TODO: fix link generation in vokobe
+    enable = true;
+    contentDir = "/var/www/emile.space";
+    hostnames = [
+      "emile.space"
+    ];
+    addresses = [
+      "0.0.0.0:1965"
+    ];
+  };
+  networking.firewall.allowedTCPPorts = [ 1965 ];
+}
diff --git a/nix/hosts/corrino/hardware-configuration.nix b/nix/hosts/corrino/hardware-configuration.nix
new file mode 100644
index 0000000..b4e8c1e
--- /dev/null
+++ b/nix/hosts/corrino/hardware-configuration.nix
@@ -0,0 +1,39 @@
+
+	# Do not modify this file!  It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations.  Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports =
+    [ (modulesPath + "/installer/scan/not-detected.nix")
+    ];
+
+  boot.initrd.availableKernelModules = [ "ahci" "nvme" ];
+  boot.initrd.kernelModules = [ "dm-snapshot" ];
+  boot.kernelModules = [ "kvm-amd" ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" =
+    { device = "/dev/disk/by-uuid/4d372699-9d47-44bf-a68e-eeb126fb7ad6";
+      fsType = "ext4";
+    };
+
+  fileSystems."/boot" =
+    { device = "/dev/disk/by-uuid/726db4ba-5b90-47e2-b924-72623f02585a";
+      fsType = "ext4";
+    };
+
+  swapDevices = [ ];
+
+  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+  # (the default) this is the recommended approach. When using systemd-networkd it's
+  # still possible to use this option, but it's recommended to use it in conjunction
+  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+  networking.useDHCP = lib.mkDefault true;
+  # networking.interfaces.eth0.useDHCP = lib.mkDefault true;
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+  powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
+  hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/nix/hosts/corrino/secrets/factorio_password.age b/nix/hosts/corrino/secrets/factorio_password.age
new file mode 100644
index 0000000..7f02410
--- /dev/null
+++ b/nix/hosts/corrino/secrets/factorio_password.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/grafana_admin_password.age b/nix/hosts/corrino/secrets/grafana_admin_password.age
new file mode 100644
index 0000000..52fb988
--- /dev/null
+++ b/nix/hosts/corrino/secrets/grafana_admin_password.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q SSBiaII5ILPTF1He6qWvze7l8CpnPVFa63m8TCXRsmw
+s4ey6wROQcE3VTw6zGlsg5sK7Zgw0JXIgjX8DHRGlQc
+-> ssh-ed25519 m8VklA T1KaNA2bHqTNtXgb3MxkuavpOR9lmvhNQjHK8V3sc04
+PIHSYhLHAC+JBUEQpCVb6C9CYOPCsJtzu9iQ0xGcg1U
+-> pV-grease
+oIQ
+--- XSICvGbcTxVppwbDi7vH/CUEPxd6pHfL005t7t9J0jc
+!PÎY›oºi]µËí*.9HÜE8zÝ@ÄöºgkÛö]ù„`Î8KÝÿ6¶0™³v3[djüÚ5–ûëxå
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/grafana_database_password.age b/nix/hosts/corrino/secrets/grafana_database_password.age
new file mode 100644
index 0000000..69d76d9
--- /dev/null
+++ b/nix/hosts/corrino/secrets/grafana_database_password.age
@@ -0,0 +1,11 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q FrSCB6WTRKb54AGqjFeIDv62uxFT1HvXFQ22BVzwGR0
+/MplLgIhHZJdg+jXn+w3bSs0MyJUXEz+SxWcN/lYngM
+-> ssh-ed25519 m8VklA 8D9irb+/PKIWFhm/YRpJ5Fd7Gne1ie8Y9XxZj36WtBY
+/V30odo5GRJSgigbUZ8ngT1H8Akm3VzUM399RcBcyC8
+-> Iv)bM-grease :v "isNt%u 8W][}
+Slv5G4yrVQ94QVVU4qDo/cYCohRBSxAJdE0IuZMZTbqoc/BOJ7cLuUKNC3l8V0Fg
+pUbO
+--- iyg9ZkWkjQBrUUdG+mV5NlQW9l2F7NeCyOitAf6Ggb4
+«ï6·!àá{J½ÿÈ
|c}Ž(4ñm¦^ÃÚ,\-w ã‘
+•AˆïV7~SÛÈÀ¥b]/ÏýO'L`+µ”Ú*ãì•
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/grafana_secret_key.age b/nix/hosts/corrino/secrets/grafana_secret_key.age
new file mode 100644
index 0000000..b76483e
--- /dev/null
+++ b/nix/hosts/corrino/secrets/grafana_secret_key.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q RTJU0rMUPybqjF0DpYvb74/xvmmybM3gVXqzwGgqzxs
+QtuBSKXrbZPfqkkKQdarK9i1R5ykxJ0shmK7OCutpPY
+-> ssh-ed25519 m8VklA 2/9r5xZ9Ta/4PO0LvoerlXANl/k58s1eq8QlUuOPrjA
+pS0Q+rRnBdzrEJVYRhua/PkHRGi6xmiMQQZDXYdntKY
+-> -MxO/.cF-grease Xm7e:5
+d6qhrhkhcqjBj4bSsBT1qqxoG/PUKKjZJ4V2QcHBwi57NwtbD4mktKTMfWai
+--- cc8nZhs0oc2YDB0mvAnrQe0Drg5xi1vy0Qx8dm/5AWg
+‘Ë!fÚÆìù„—Œ+P"j=3I„Àb'H ÙÙ@ZãPªy­à–°ÛuÂN½‚=mZÙaj<Ž!~‡=­
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/grafana_smtp_password.age b/nix/hosts/corrino/secrets/grafana_smtp_password.age
new file mode 100644
index 0000000..dd27bc4
--- /dev/null
+++ b/nix/hosts/corrino/secrets/grafana_smtp_password.age
@@ -0,0 +1,10 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q 4bKX/wv0cMPXZzRJM8LqNw7F1GoYUwJh8AlALIQkqgY
+xYPghPguhfrXKPq1EBwEZWV/imO1ZI6taj1WIbQ8JqU
+-> ssh-ed25519 m8VklA NBPHb4cZeJ9wFZQyUu0ikWLoO1RlXkEz6LOMqzhnfRQ
+/2//XHuIxlVk4klgYPBdXSWpa4cIpfsRHE7duXJ3P0s
+-> P_YApO--grease s" PT MLCA Hz{}~
+brb+DX4hyKS2Pckyt6UC5yAD4mCfufypzNkRYw70adWlp+YEXA
+--- jt3ZEMFoOlikvQD/4XxoD5l8jyrg7f2UtzfMuBOj/tY
+ºKôÿ_Ö‹u8ŸÒwû‚'€o#ã%b‡Ðü.°dPäç‹9_gµ9Ü55éR»VPÐD¿Å»Cž3©—ê;âvΚZ
+C!7%’t­·*ÍS°½ÿ£ŒöÈ’:t
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/magic-hash-flag.age b/nix/hosts/corrino/secrets/magic-hash-flag.age
new file mode 100644
index 0000000..e0a56b4
--- /dev/null
+++ b/nix/hosts/corrino/secrets/magic-hash-flag.age
@@ -0,0 +1,10 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q 82J5AdcqPHQ9uUNFmiRLHNhXUm/Npwee2NssXBmW2hE
+FUjeBE4LFw1u7jfHyrONEMJUMsDlzU06ghhzOaIq2t8
+-> ssh-ed25519 m8VklA o0t4X6iD7QN69F5DBTkxHDLvtKjOCHVHbas1a2WLfGI
+mfKoJBja+ZuJzPyfdlRXl6hsiUF1d5OmIx3MXwd6Kjw
+-> beyszd-grease ^PZ
+Y+VrEhNbCrjAxVvGIlTsh73ojA9eQms4hbl5RzTf4Ykx3k10lrq1kXGVIt1c1G0+
+1ljVNk7mCQ1+YbudED68Vsz1rhA/3gxcd+5hdIQZPFkbQ4y6
+--- RA3DdqkgplKJhsClt14A7zEZiga/s9+l/V5/rC9II80
+S²üAí/<€àÖ‡	¸OSÇ[öQ¸EžÝ’(ÃŒñ…ª°ÁTÏ7[?yu	RÁ?w+Q4ö\Ëm&öÙ§ÇGÅ¥ÒÄÊÊÔŒžz‡‰y®Á]*"&qº‡ê·%ø
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/mail_password.age b/nix/hosts/corrino/secrets/mail_password.age
new file mode 100644
index 0000000..5601304
--- /dev/null
+++ b/nix/hosts/corrino/secrets/mail_password.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q DNAJBlZ9d46k703peMMsEVTRvCGfGOJ0VnchUb8dsl4
+xN3l7wkznSCThKVXsic0ix9mSB510w1AFCH3taZIUlk
+-> ssh-ed25519 gvwQ2Q BO6dxNpeWETkukjpD5g+U3tlHnLIPknb4+emb6cfACQ
+j4VYw1trP0rPtSQRYO7nBYyYNkaAbUO3oh1WbPKT6eg
+--- sB9tKpo8a5RT3eam8Cyejdg0Kg66YvSmoY+bQtSpCvk
+oä@+Ì@Åpq§Ç¼\OïY†8X”HÔ[9——¤…ñfçš<=.m˜ÿÆFöZÜ
Ñî­€á7—!€®¼>iÎH>Iä¨=~fœª¾u}]OÒÂÚ¬Ð2
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/netbox_secret.age b/nix/hosts/corrino/secrets/netbox_secret.age
new file mode 100644
index 0000000..8f24203
--- /dev/null
+++ b/nix/hosts/corrino/secrets/netbox_secret.age
@@ -0,0 +1,11 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q qEh2F+z0JcOwGITW2vwI+pCc0gEX5bArBkJ2+tUsORc
+gHGoC7aE6KGQFxxQ/vXle0H8VMfeHuV6iRWCZZx977Q
+-> ssh-ed25519 m8VklA wdnUaCTsF+GvQNPViRTOEfc9ytlsrwGXSvi43+288kg
+qxJsSryGAnFyDPrraVjSH29GlgiSsonvg+VM8EpDZ7k
+-> y1&ZO-grease t x I>Fl(`}
+6pzix/Gj077lu+LBkaoWN987JuYbOF2fzpAT3oIi65NTK22yjn063E1k2Utkb6vq
+J4hcpDXKRHpdUE9mor7k7kS3Mwlt4aQrudmJ2I1bZIy/pox6gcICafokmsAeZODR
+vw
+--- wi8HjK7ujilpSz4d3A3velISw+J8oiSVJk0JvKPb21k
+>ÔǪä˜UNK˜輋œ'²xCÚÍÉ>¬c¡cr¶¡5FJ*®«‹h±ømeLÁ/4¢«ã2¤Ü
LÜÉ>&¯ñ[ÞŽ&¦N(¼4ÂOwøÌ:”\êP\–B¹è
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/photoprism_password.age b/nix/hosts/corrino/secrets/photoprism_password.age
new file mode 100644
index 0000000..4b0ad76
--- /dev/null
+++ b/nix/hosts/corrino/secrets/photoprism_password.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q KdqNkMZUY5J1JRtoQ2KFgMafkcG29UM+uNXZSlmbji0
+FsDbzx0zMNQ3lGIm2JmmAf7b8h+qbeOG0QRPRP+ighs
+-> ssh-ed25519 m8VklA 9X1J6AOCzhhVYAJiMB9hWWOWf2eLVDUkHXxOj8UvbHo
+e2R2ICTBOiKOO392NkoOQSBlJLiQA+H18dJRIrjtSUQ
+-> movYQ$-grease zm=!&Kz
+rbksk8Roi2pC/P4b
+--- +WxRRQ9MlQlp6zdJJhrbdM0k1YJrRlWgpHF9uuvmTko
+Ò8kW‰ËM—[ów=ˆê¨õ£-qW¡¥½ùÚ•^LœhåâD¸àÐúÞT£AõU%z—ƒ!eÝqÉŠèÿ&Údƒnþ"ƒ,g@B]<£pvà„~Îàÿ8À?4½sr-T
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/pretix.age b/nix/hosts/corrino/secrets/pretix.age
new file mode 100644
index 0000000..4be5d7c
--- /dev/null
+++ b/nix/hosts/corrino/secrets/pretix.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/pretix_postgres_pw.age b/nix/hosts/corrino/secrets/pretix_postgres_pw.age
new file mode 100644
index 0000000..822b221
--- /dev/null
+++ b/nix/hosts/corrino/secrets/pretix_postgres_pw.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q brYZ6kTQTDViC7girn6bcdKYBW6JAKHDtRe8CpPhJkw
+HQCJSPZ0ZUG5LeoCEatTMBBVlQ/p33cWsR2HzPXKAf8
+-> ssh-ed25519 m8VklA YTjOBL2U7dsnW2Gvu/Hbd6MFtoA9//uhBGCb7aXYzno
+oG67Syidm5PeTahPLm7vRzm147tUvU8U3WGA2Ej/zOA
+-> fD[[4QR-grease 1'8$Jt +^, Q+F:D(p Ks
+64cLnDft3WoVEE5AfxgIkdY
+--- z2zfa+/k3RtxoRfJyiEV6j7HMUgLOog697UART4Mio4
+ÜÕh]PTN¿ÎÀÇl“ª:©Ôûû1Å»¾lˆŒ)î3‡þ䣈¢‰ð4ÃêšÈ(©ÚH[ÃzQÒؤ۠ÙÚõ¹&
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/storage_box_bx11_password.age b/nix/hosts/corrino/secrets/storage_box_bx11_password.age
new file mode 100644
index 0000000..a02a210
--- /dev/null
+++ b/nix/hosts/corrino/secrets/storage_box_bx11_password.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q wKf98LIfuGnY5Tz0SDT4BF1RhU44BrGOoHEd7p0AGgY
+DX/MAYTMp1MxXJaFN8R6crggEyhCIb+apdKJ27YnwRw
+-> ssh-ed25519 m8VklA 4hvYcyt/NuIq7fH7nWq7vnYeeGDDDwok0njyb53e+ys
+SOEU0i6khudr5n16QyH+zlIXka2btCGNDGFi1ccIZlI
+--- +2ZDWzJj8rqh0oAnVz1L46qHnepK3oR7epAhJ1Z7jFs
+™Gð2Í$#=²Ó0–¶øñ„.haØcŽ\'*fóZ,;9áv×\€'‹aó»ÌÁUSáÚ ˜{æ´E™4§¦ùlþÛ“âÕ´Òÿ"ZadA°óVi­2†Y7e|q´×±¯è¥
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/tailscale_authkey.age b/nix/hosts/corrino/secrets/tailscale_authkey.age
new file mode 100644
index 0000000..8102f1a
--- /dev/null
+++ b/nix/hosts/corrino/secrets/tailscale_authkey.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/wireguard_privatekey.age b/nix/hosts/corrino/secrets/wireguard_privatekey.age
new file mode 100644
index 0000000..b92fbe5
--- /dev/null
+++ b/nix/hosts/corrino/secrets/wireguard_privatekey.age
Binary files differdiff --git a/nix/hosts/corrino/ssh.pub b/nix/hosts/corrino/ssh.pub
new file mode 100644
index 0000000..73387ce
--- /dev/null
+++ b/nix/hosts/corrino/ssh.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGFzoWTW3c7CKWx5t0OZzTfKTlC6R2VHXczVatgYI57N
diff --git a/nix/hosts/corrino/www/cs.emile.space.nix b/nix/hosts/corrino/www/cs.emile.space.nix
new file mode 100644
index 0000000..de4d67e
--- /dev/null
+++ b/nix/hosts/corrino/www/cs.emile.space.nix
@@ -0,0 +1,56 @@
+# Run sourcegraph, including its entire machinery, in a container.
+# Running it outside of a container is a futile endeavour for now.
+
+# adapted from https://cs.tvl.fyi/depot/-/blob/ops/modules/sourcegraph.nix
+
+{ ... }:
+
+{
+  services.nginx.virtualHosts."cs.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:3463";
+
+        extraConfig = ''
+          location = / {
+            return 301 https://cs.emile.space/hefe;
+          }
+
+          location / {
+            proxy_set_header X-Sg-Auth "Anonymous";
+            proxy_pass http://localhost:7080;
+          }
+
+          location /users/Anonymous/settings {
+            return 301 https://cs.emile.space;
+          }
+        '';
+      };
+    };
+  };
+
+  virtualisation.oci-containers.backend = "docker";
+  virtualisation.oci-containers.containers.sourcegraph = {
+    image = "sourcegraph/server:5.1.1";
+
+    ports = [
+      "127.0.0.1:3463:7080"
+    ];
+
+    volumes = [
+      "/var/lib/sourcegraph/etc:/etc/sourcegraph"
+      "/var/lib/sourcegraph/data:/var/opt/sourcegraph"
+    ];
+
+    # Sourcegraph needs a higher nofile limit, it logs warnings
+    # otherwise (unclear whether it actually affects the service).
+    extraOptions = [
+      "--ulimit"
+      "nofile=10000:10000"
+    ];
+  };
+}
+
diff --git a/nix/hosts/corrino/www/ctf.emile.space.nix b/nix/hosts/corrino/www/ctf.emile.space.nix
new file mode 100644
index 0000000..c4de8c5
--- /dev/null
+++ b/nix/hosts/corrino/www/ctf.emile.space.nix
@@ -0,0 +1,26 @@
+{ ... }:
+
+{
+  services.nginx.virtualHosts."ctf.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:8338";
+      };
+    };
+  };
+
+  virtualisation.oci-containers = {
+    backend = "docker";
+    containers = {
+      "ctfd" = {
+        image = "ctfd/ctfd";
+        ports = [
+          "8338:8000"
+        ];
+      };
+    };
+  };
+}
diff --git a/nix/hosts/corrino/www/emile.space.nix b/nix/hosts/corrino/www/emile.space.nix
new file mode 100644
index 0000000..9cca880
--- /dev/null
+++ b/nix/hosts/corrino/www/emile.space.nix
@@ -0,0 +1,60 @@
+{
+  services.nginx.virtualHosts."emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    # kTLS = true;
+
+    locations = {
+      "/" = {
+        root = "/var/www/emile.space";
+        extraConfig = ''
+          add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+        ''; 
+      };
+
+      #"/.well-known" = {
+      #  root = "/var/www/emile.space";
+      #  extraConfig = ''
+      #    autoindex on;
+      #  '';
+      #};
+
+      ## I ran a matrix homeserver for some time, then stopped, but the other
+      ## homeserver don't know and don't stop sending me requests (5e5 a day or
+      ## so).
+      #"/.well-known/matrix/server".extraConfig = ''
+      #  return 410;
+      #'';
+    };
+  };
+
+  # services.stargazer = {
+  #   enable = true;
+  #   user = "stargazer";
+  #   group = "stargazer";
+
+  #   certLifetime = "1m";
+  #   store = /var/lib/gemini/certs;
+
+  #   genCerts = true;
+  #   regenCerts = true;
+  #   responseTimeout = 0;
+  #   requestTimeout = 5;
+
+  #   routes = [
+  #     {
+  #       route = "emile.space";
+  #       root = "/srv/gemini/emile.space";
+  #     }
+  #   ];
+
+  #   listen = [ "0.0.0.0" "[2002:a00:1::]" ];
+
+  #   ipLogPartial = false;
+  #   ipLog = false;
+  #   connectionLogging = false;
+
+  #   certOrg = "emile.space";
+  # };
+}
diff --git a/nix/hosts/corrino/www/events.emile.space.nix b/nix/hosts/corrino/www/events.emile.space.nix
new file mode 100644
index 0000000..bb4db38
--- /dev/null
+++ b/nix/hosts/corrino/www/events.emile.space.nix
@@ -0,0 +1,59 @@
+{ ... }:
+
+{
+  services.nginx.virtualHosts."events.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        extraConfig = ''
+          proxy_pass http://[::1]:4000;
+        '';
+      };
+    };
+  };
+
+  # Create users:
+  #
+  # go into the mobilizon-launchers directory within the nix store (systemctl
+  # status mobilizon..., you'll find it there somehow)
+  #
+  # ; sudo -u mobilizon ./bin/mobilizon_ctl users.new emile@emile.space --moderator --admin
+
+  services = {
+    mobilizon = {
+      enable = true;
+      settings.":mobilizon" = {
+        "Mobilizon.Web.Endpoint" = {
+          url.host = "events.emile.space";
+          http.port = 4000;
+
+          # The IP address to listen on. Defaults to [::1] notated as a byte
+          # tuple.
+          # (Yes, this is an elexir application and they've mapped the type system
+          # into nix)
+          http.ip = {
+            _elixirType = "tuple";
+            value = [ 0 0 0 0 0 0 0 1 ];
+          };
+
+          has_reverse_proxy = true;
+        };
+
+        "Mobilizon.Storage.Repo" = {
+          username = "mobilizon";
+          socket_dir = "/var/run/postgresql";
+          database = "mobilizon_prod";
+        };
+
+        ":instance" = rec {
+          name = "events.emile.space";
+          hostname = "emile.space";
+          email_reply_to = email_from;
+          email_from = "noreply@$emile.space";
+        };
+      };
+    };
+  };
+}
diff --git a/nix/hosts/corrino/www/git.emile.space.nix b/nix/hosts/corrino/www/git.emile.space.nix
new file mode 100644
index 0000000..2c7d64e
--- /dev/null
+++ b/nix/hosts/corrino/www/git.emile.space.nix
@@ -0,0 +1,73 @@
+{ pkgs, config, ... }:
+
+let
+  cfg = config.services.gitea;
+in {
+  services.nginx.virtualHosts."git.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:3000";
+      };
+    };
+  };
+
+  services.gitea = rec {
+    enable = true;
+
+    appName = "git.emile.space";
+
+    # unstable in order to use the 1.20... version
+    #package = pkgs.forgejo;
+    package = pkgs.unstable.forgejo;
+
+    stateDir = "/var/lib/gitea";
+    repositoryRoot = "${stateDir}/repositories";
+
+    settings = {
+      service.DISABLE_REGISTRATION = true;
+
+      DEFAULT = {
+        WORK_PATH = "/var/lib/gitea";
+      };
+
+      server = {
+        DOMAIN = pkgs.lib.mkForce "git.emile.space";
+        ROOT_URL = pkgs.lib.mkForce "https://git.emile.space";
+
+        #START_SSH_SERVER = true;
+        BUILTIN_SSH_SERVER_USER = "git";
+        SSH_USER = "gitea";
+        SSH_DOMAIN = "git.emile.space";
+
+        REPO_INDEXER_ENABLED = true;
+      };
+
+      indexer = {
+        REPO_INDEXER_ENABLED = true;
+        ISSUE_INDEXER_PATH = "${stateDir}/indexers/issues.bleve";
+        REPO_INDEXER_PATH = "${stateDir}/indexers/repos.bleve";
+        MAX_FILE_SIZE = 1048576;
+        REPO_INDEXER_INCLUDE = "";
+        REPO_INDEXER_EXCLUDE = "resources/bin/**";
+      };
+
+      #federation = {
+      #  enable = true;
+      #  share_user_statistics = true;
+      #  max_size = 4;
+      #};
+    };
+  };
+
+  users.users.git = {
+    isSystemUser = true;
+    useDefaultShell = true;
+    group = "git";
+    extraGroups = [ "gitea" ];
+    home = cfg.stateDir;
+  };
+  users.groups.git = { };
+}
diff --git a/nix/hosts/corrino/www/grafana.emile.space.nix b/nix/hosts/corrino/www/grafana.emile.space.nix
new file mode 100644
index 0000000..0f73147
--- /dev/null
+++ b/nix/hosts/corrino/www/grafana.emile.space.nix
@@ -0,0 +1,217 @@
+{ config, ... }:
+
+{
+  services = {
+    nginx.virtualHosts."grafana.emile.space" = {
+      addSSL = true;
+      enableACME = true;
+      locations."/" = {
+          proxyPass = "http://${toString config.services.grafana.settings.server.http_addr}:${toString config.services.grafana.settings.server.http_port}/";
+          proxyWebsockets = true;
+      };
+    };
+
+    grafana = {
+      enable = true;
+      settings = {
+        server = {
+          http_addr = "127.0.0.1";
+          http_port = 3002;
+          domain = "grafana.emile.space";
+          root_url = "https://grafana.emile.space/";
+        };
+      };
+
+      provision = {
+        datasources = {
+          settings = {
+            datasources = [
+              {
+                url = "http://localhost:${toString config.services.prometheus.port}";
+                type = "prometheus";
+                name = "Prometheus";
+                editable = false;
+                access = "proxy"; # server = "proxy", browser = "direct"
+              }
+              {
+                name = "loki";
+                url = "http://localhost:${toString config.services.loki.configuration.server.http_listen_port}";
+                type = "loki";
+              }
+            ];
+          };
+        };
+      };
+    };
+
+    prometheus = {
+      enable = true;
+      retentionTime = "356d";
+      port = 9003;
+
+      exporters = {
+        node = {
+          enable = true;
+          enabledCollectors = [ "systemd" ];
+          port = 9002;
+        };
+      };
+      scrapeConfigs = [
+        {
+          job_name = "corrino";
+          static_configs = [{
+            targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.node.port}" ];
+          }];
+        }
+      ];
+    };
+
+    loki = {
+      enable = true;
+      configuration = {
+        auth_enabled = false;
+        server = {
+          http_listen_port = 9004;
+        };
+
+        limits_config = {
+          reject_old_samples = true;
+          reject_old_samples_max_age = "7d";
+          max_global_streams_per_user = 100000;
+        };
+
+        common = {
+          instance_addr = "127.0.0.1";
+          ring = {
+            instance_addr = "127.0.0.1";
+            kvstore.store = "inmemory";
+          };
+          replication_factor = 1;
+          path_prefix = "/tmp/loki";
+        };
+
+        schema_config.configs = [{
+          from = "2023-05-09";
+          store = "boltdb-shipper";
+          object_store = "filesystem";
+          schema = "v11";
+          index = {
+            prefix = "index_";
+            period = "24h";
+          };
+        }];
+      };
+    };
+  };
+
+  # allow the promtail user to read the nginx access files
+  users.users.promtail.extraGroups = [ "nginx" ];
+
+  services = {
+    promtail = {
+      enable = true;
+      configuration = {
+        server = {
+          http_listen_port = 9005;
+          grpc_listen_port = 0;
+        };
+        positions.filename = "/tmp/positions.yml";
+        clients = [{
+          url = "http://localhost:${toString config.services.loki.configuration.server.http_listen_port}/loki/api/v1/push";
+        }];
+        scrape_configs = [
+
+          # systemd
+          {
+            job_name = "journal";
+            journal = {
+              max_age = "12h";
+              labels = {
+                job = "systemd-journal";
+                host = config.networking.hostName;
+              };
+            };
+            relabel_configs = [
+              {
+                source_labels = [ "__journal__systemd_unit" ];
+                target_label = "unit";
+              }
+            ];
+          }
+
+          # nginx error log
+          {
+            job_name = "nginx-error-logs";
+            static_configs = [{
+              targets = [ "localhost" ];
+              labels = {
+                job = "nginx-error-logs";
+                host = "corrino";
+                __path__ = "/var/log/nginx/*error.log";
+              };
+            }];
+          }
+
+          # nginx
+          {
+            job_name = "nginx";
+            static_configs = [
+              {
+                targets = [ "localhost" ];
+                labels = {
+                  job = "nginx";
+                  host = "corrino";
+                  __path__ = "/var/log/nginx/*access.log";
+                };   
+              }
+            ];
+            pipeline_stages = [
+              # {
+              #   regex = {
+              #     expression = "(?:[0-9]{1,3}\.){3}([0-9]{1,3})";
+              #     replace = "***";
+              #   };
+              # }
+              {
+                regex = {
+                  expression = ''(?P<remote_addr>.+) - - \[(?P<time_local>.+)\] "(?P<method>.+) (?P<url>.+) (HTTP\/(?P<version>\d.\d))" (?P<status>\d{3}) (?P<body_bytes_sent>\d+) (["](?P<http_referer>(\-)|(.+))["]) (["](?P<http_user_agent>.+)["])'';
+                };
+              }
+              {
+                labels = {
+                  remote_addr = null;
+                  time_local = null;
+                  method = null;
+                  url = null;
+                  status = null;
+                  body_bytes_sent = null;
+                  http_referer = null;
+                  http_user_agent = null;
+                };
+              }
+              # {
+              #   timestamp = {
+              #     source = "time_local";
+              #     format = "02/Jan/2006:15:04:05 -0700";
+              #   };
+              # }
+              {
+                drop = {
+                  source = "url";
+                  expression = ''/(_matrix|.well-known|notifications|api|identity).*'';
+                };
+              }
+              {
+                drop = {
+                  source = "url";
+                  expression = ''grafana.*'';
+                };
+              }
+            ];
+          }
+
+        ];
+      };
+    };
+  };
+}
\ No newline at end of file
diff --git a/nix/hosts/corrino/www/grafana_full.emile.space.nix b/nix/hosts/corrino/www/grafana_full.emile.space.nix
new file mode 100644
index 0000000..8a9aa02
--- /dev/null
+++ b/nix/hosts/corrino/www/grafana_full.emile.space.nix
@@ -0,0 +1,440 @@
+{ pkgs, config, ... }:
+
+let
+  cfg = config.services.grafana;
+in {
+  services.nginx.virtualHosts."git.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:3000";
+      };
+    };
+  };
+
+  services = {
+    grafana = {
+      enable = true;
+      package = pkgs.grafana;
+      #declarativePlugins = with pkgs.grafanaPlugins; [
+      #  grafana-piechart-panel
+      #];
+      dataDir = "/var/lib/grafana";
+
+      settings = {
+        users = {
+          # Viewers can access and use Explore and perform temporary edits on panels in dashboards they have access to. They cannot save their changes.
+          viewers_can_edit = true;
+
+          # Require email validation before sign up completes
+          verify_email_enabled = false;
+
+          # The duration in time a user invitation remains valid before expiring. This setting should be expressed as a duration. Examples: 6h (hours), 2d (days), 1w (week). The minimum supported duration is 15m (15 minutes).
+          user_invite_max_lifetime_duration = "24h";
+
+          # Text used as placeholder text on login page for password input.
+          password_hint = "password";
+
+          # Text used as placeholder text on login page for login/username input.
+          login_hint = "email or username";
+
+          # Path to a custom home page. Users are only redirected to this if the default home dashboard is used. It should match a frontend route and contain a leading slash.
+          home_page = "";
+
+          # This is a comma-separated list of usernames. Users specified here are hidden in the Grafana UI. They are still visible to Grafana administrators and to themselves.
+          hidden_users = "";
+
+          # Editors can administrate dashboards, folders and teams they create.
+          editors_can_admin = false;
+
+          # Sets the default UI theme. system matches the user’s system theme.
+          default_theme = "system";
+
+          # This setting configures the default UI language, which must be a supported IETF language tag, such as en-US.
+          default_language = "en-US";
+
+          # The role new users will be assigned for the main organization (if the auto_assign_org setting is set to true).
+          # one of "Viewer", "Editor", "Admin"
+          auto_assign_org_role = "Viewer";
+
+          # Set this value to automatically add new users to the provided org. This requires auto_assign_org to be set to true. Please make sure that this organization already exists.
+          auto_assign_org_id = 1;
+
+          # Set to true to automatically add new users to the main organization (id 1). When set to false, new users automatically cause a new organization to be created for that new user. The organization will be created even if the allow_org_create setting is set to false.
+          auto_assign_org = true;
+
+          # Set to false to prohibit users from being able to sign up / create user accounts. The admin user can still create users.
+          allow_sign_up = false;
+
+          # Set to false to prohibit users from creating new organizations.
+          allow_org_create = false; 
+        };
+
+        smtp = {
+          # User used for authentication.
+          user = "mail";
+
+          # StartTLS policy when connecting to server.
+          # null or one of "OpportunisticStartTLS", "MandatoryStartTLS", "NoStartTLS"
+          startTLS_policy = null;
+        
+          # Verify SSL for SMTP server.
+          skip_verify = false;
+
+        # Password used for authentication. Please note that the contents of this option will end up in a world-readable Nix store. Use the file provider pointing at a reasonably secured file in the local filesystem to work around that. Look at the documentation for details: https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider
+          password = "";
+
+          # File path to a key file.
+          key_file = "$__file{${config.age.secrets.grafana_smtp_password.path}}";
+
+          # Host to connect to.
+          host = "localhost:25";
+
+          # Name to be used as client identity for EHLO in SMTP dialog.
+          from_name = "Grafana";
+
+          # Address used when sending out emails
+          from_address = "admin@grafana.localhost";
+
+          # Whether to enable SMTP
+          enabled = true;
+
+          # Name to be used as client identity for EHLO in SMTP dialog
+          ehlo_identity = null;
+
+          # File path to a cert file
+          cert_file = null;
+        };
+        server = {
+          # Root path for static assets.
+          #static_root_path = "${package}/share/grafana/public";
+
+          # Mode where the socket should be set when protocol=socket. Make sure that Grafana process is the file owner before you change this setting.
+          socket_mode = "0660";
+
+          # GID where the socket should be set when protocol=socket. Make sure that the target group is in the group of Grafana process and that Grafana process is the file owner before you change this setting. It is recommended to set the gid as http server user gid. Not set when the value is -1.
+          socket_gid = -1;
+
+          # Path where the socket should be created when protocol=socket. Make sure that Grafana has appropriate permissions before you change this setting.
+          socket = "/run/grafana/grafana.sock";
+
+          # Serve Grafana from subpath specified in the root_url setting. By default it is set to false for compatibility reasons.
+          # 
+          # By enabling this setting and using a subpath in root_url above, e.g. root_url = "http://localhost:3000/grafana", Grafana is accessible on http://localhost:3000/grafana. If accessed without subpath, Grafana will redirect to an URL with the subpath.
+          serve_from_sub_path = false;
+
+          # Set to true for Grafana to log all HTTP requests (not just errors). These are logged as Info level events to the Grafana log.
+          router_logging = false;
+
+          # This is the full URL used to access Grafana from a web browser. This is important if you use Google or GitHub OAuth authentication (for the callback URL to be correct).
+          # 
+          # This setting is also important if you have a reverse proxy in front of Grafana that exposes it through a subpath. In that case add the subpath to the end of this URL setting.
+          root_url = "%(protocol)s://%(domain)s:%(http_port)s/";
+
+          # Sets the maximum time using a duration format (5s/5m/5ms) before timing out read of an incoming request and closing idle connections. 0 means there is no timeout for reading the request.
+          read_timeout = 0;
+
+          # Which protocol to listen.
+          # one of "http", "https", "h2", "socket"
+          protocol = "http";
+
+          # Listening port.
+          http_port = "3000";
+
+          # Listening address.
+          # This setting intentionally varies from upstream’s default to be a bit more secure by default.
+          http_addr = "127.0.0.1";
+
+          # Redirect to correct domain if the host header does not match the domain. Prevents DNS rebinding attacks.
+          enforce_domain = true;
+
+          # Set this option to true to enable HTTP compression, this can improve transfer speed and bandwidth utilization. It is recommended that most users set it to true. By default it is set to false for compatibility reasons.
+          enable_gzip = true;
+
+          # The public facing domain name used to access grafana from a browser.
+          # This setting is only used in the default value of the root_url setting. If you set the latter manually, this option does not have to be specified.
+          domain = "grafana.emile.space";
+
+          # Path to the certificate key file (if protocol is set to https or h2).
+          cert_key = null;
+
+          # Path to the certificate file (if protocol is set to https or h2).
+          cert_file = null;
+
+          # Specify a full HTTP URL address to the root of your Grafana CDN assets. Grafana will add edition and version paths.
+          # 
+          # For example, given a cdn url like https://cdn.myserver.com grafana will try to load a javascript file from http://cdn.myserver.com/grafana-oss/7.4.0/public/build/app.<hash>.js.
+          cdn_url = null;
+        };
+
+        security = {
+          # Set to false to disable the X-XSS-Protection header, which tells browsers to stop pages from loading when they detect reflected cross-site scripting (XSS) attacks.
+          x_xss_protection = true;
+
+          # Set to false to disable the X-Content-Type-Options response header. The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed.
+          x_content_type_options = true;
+
+          # Set to true to enable HSTS includeSubDomains option. Only applied if strict_transport_security is enabled.
+          strict_transport_security_subdomains = true;
+
+          # Set to true to enable HSTS preloading option. Only applied if strict_transport_security is enabled.
+          strict_transport_security_preload = true;
+
+          # Sets how long a browser should cache HSTS in seconds. Only applied if strict_transport_security is enabled.
+          strict_transport_security_max_age_seconds = 86400;
+
+          # Set to true if you want to enable HTTP Strict-Transport-Security (HSTS) response header. Only use this when HTTPS is enabled in your configuration, or when there is another upstream system that ensures your application does HTTPS (like a frontend load balancer). HSTS tells browsers that the site should only be accessed using HTTPS.
+          strict_transport_security = true;
+
+          # Secret key used for signing. Please note that the contents of this option will end up in a world-readable Nix store. Use the file provider pointing at a reasonably secured file in the local filesystem to work around that. Look at the documentation for details: https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider
+          secret_key = "$__file{${config.age.secrets.grafana_secret_key.path}}";
+
+          # Disable creation of admin user on first start of Grafana.
+          disable_initial_admin_creation = false;
+
+          # Set to true to disable the use of Gravatar for user profile images.
+          disable_gravatar = false;
+
+          # Set to true to disable brute force login protection.
+          disable_brute_force_login_protection = false;
+
+          # Define a whitelist of allowed IP addresses or domains, with ports, to be used in data source URLs with the Grafana data source proxy. Format: ip_or_domain:port separated by spaces. PostgreSQL, MySQL, and MSSQL data sources do not use the proxy and are therefore unaffected by this setting.
+          data_source_proxy_whitelist = [];
+
+          # List of additional allowed URLs to pass by the CSRF check. Suggested when authentication comes from an IdP.
+          csrf_trusted_origins = [];
+
+          # List of allowed headers to be set by the user. Suggested to use for if authentication lives behind reverse proxies.
+          csrf_additional_headers = [];
+
+          # Set to true if you host Grafana behind HTTPS.
+          cookie_secure = true;
+
+          # Sets the SameSite cookie attribute and prevents the browser from sending this cookie along with cross-site requests. The main goal is to mitigate the risk of cross-origin information leakage. This setting also provides some protection against cross-site request forgery attacks (CSRF), read more about SameSite here. Using value disabled does not add any SameSite attribute to cookies.
+          # one of "lax", "strict", "none", "disabled"
+          cookie_samesite = "strict";
+
+          # Set to true to add the Content-Security-Policy-Report-Only header to your requests. CSP in Report Only mode enables you to experiment with policies by monitoring their effects without enforcing them. You can enable both policies simultaneously.
+          content_security_policy_report_only = false;
+
+          # Set to true to add the Content-Security-Policy header to your requests. CSP allows to control resources that the user agent can load and helps prevent XSS attacks.
+          content_security_policy = true;
+
+          # When false, the HTTP header X-Frame-Options: deny will be set in Grafana HTTP responses which will instruct browsers to not allow rendering Grafana in a <frame>, <iframe>, <embed> or <object>. The main goal is to mitigate the risk of Clickjacking.
+          allow_embedding = false;
+
+          # Default admin username.
+          admin_user = "admin";
+
+          # Default admin password. Please note that the contents of this option will end up in a world-readable Nix store. Use the file provider pointing at a reasonably secured file in the local filesystem to work around that. Look at the documentation for details: https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider
+          admin_password = "$__file{${config.age.secrets.grafana_admin_password.path}}";
+
+          # The email of the default Grafana Admin, created on startup.
+          admin_email = "admin@emile.space";
+        };
+
+        paths = {
+          # Folder that contains provisioning config files that grafana will apply on startup and while running. Don’t change the value of this option if you are planning to use services.grafana.provision options.
+          # provisioning = ...
+
+          # Directory where grafana will automatically scan and look for plugins
+          plugins = "${cfg.dataDir}/plugins";
+        };
+
+        database = {
+
+          # For sqlite3 only. Setting to enable/disable Write-Ahead Logging.
+          # https://sqlite.org/wal.html
+          wal = false;
+
+          # The database user (not applicable for sqlite3).
+          user = "root";
+
+          # Database type.
+          # one of "mysql", "sqlite3", "postgres"
+          type = "sqlite3";
+
+          # This setting applies to sqlite3 only and controls the number of times the system retries a transaction when the database is locked.
+          transaction_retries = 5;
+
+          # For Postgres, use either disable, require or verify-full. For MySQL, use either true, false, or skip-verify.
+          # one of "disable", "require", "verify-full", "true", "false", "skip-verify"
+          ssl_mode = "disable";
+
+          # The common name field of the certificate used by the mysql or postgres server. Not necessary if ssl_mode is set to skip-verify.
+          server_cert_name = null;
+
+          # This setting applies to sqlite3 only and controls the number of times the system retries a query when the database is locked.
+          query_retries = 0;
+
+          # Only applicable to sqlite3 database. The file path where the database will be stored.
+          path = "${config.services.grafana.dataDir}/data/grafana.db";
+
+          # The database user’s password (not applicable for sqlite3).
+          # Please note that the contents of this option will end up in a world-readable Nix store. Use the file provider pointing at a reasonably secured file in the local filesystem to work around that. Look at the documentation for details: https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider
+          password = "$__file{${config.age.secrets.grafana_database_password.path}}";
+
+          # The name of the Grafana database.
+          name = "grafana";
+
+          # The maximum number of open connections to the database.
+          # 0 = unlimited (I'm just assuming this, everything else would be weird)
+          max_open_conn = 0;
+
+          # The maximum number of connections in the idle connection pool.
+          max_idle_conn = 2;
+
+          # Set to true to log the sql calls and execution times
+          log_queries = false;
+
+          # For mysql, if the migrationLocking feature toggle is set, specify the time (in seconds) to wait before failing to lock the database for the migrations.
+          locking_attempt_timeout_sec = 0;
+
+          # Only the MySQL driver supports isolation levels in Grafana. In case the value is empty, the driver’s default isolation level is applied.
+          # null or one of "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ", "SERIALIZABLE"
+          isolation_level = null;
+
+          # Only applicable to MySQL or Postgres. Includes IP or hostname and port or in case of Unix sockets the path to it. For example, for MySQL running on the same host as Grafana: host = "127.0.0.1:3306" or with Unix sockets: host = "/var/run/mysqld/mysqld.sock"
+          host = "127.0.0.1:3306";
+
+          # Sets the maximum amount of time a connection may be reused. The default is 14400 (which means 14400 seconds or 4 hours). For MySQL, this setting should be shorter than the wait_timeout variable.
+          conn_max_lifetime = 14400;
+
+          # The path to the client key. Only if server requires client authentication.
+          client_key_path = null;
+
+          # The path to the client cert. Only if server requires client authentication.
+          client_cert_path = null;
+
+          # For sqlite3 only. Shared cache setting used for connecting to the database.
+          # one of "private", "shared"
+          cache_mode = "private";
+
+          # The path to the CA certificate to use.
+          ca_cert_path = null;
+        };
+
+        analytics = {
+          # When enabled Grafana will send anonymous usage statistics to stats.grafana.org. No IP addresses are being tracked, only simple counters to track running instances, versions, dashboard and error counts. Counters are sent every 24 hours.
+          reporting_enabled = true;
+
+          # Set to false to remove all feedback links from the UI.
+          feedback_links_enabled = true;
+
+          # When set to false, disables checking for new versions of Grafana from Grafana’s GitHub repository. When enabled, the check for a new version runs every 10 minutes. It will notify, via the UI, when a new version is available. The check itself will not prompt any auto-updates of the Grafana software, nor will it send any sensitive information.
+          check_for_updates = true;
+
+          # When set to false, disables checking for new versions of installed plugins from https://grafana.com. When enabled, the check for a new plugin runs every 10 minutes. It will notify, via the UI, when a new plugin update exists. The check itself will not prompt any auto-updates of the plugin, nor will it send any sensitive information.
+          # check_for_plugin_updates = ...
+        };
+      };
+
+      #provision = {
+      #  notifiers.*.uid
+      #  notifiers.*.type
+      #  notifiers.*.settings
+      #  notifiers.*.send_reminder
+      #  notifiers.*.secure_settings
+      #  notifiers.*.org_name
+      #  notifiers.*.org_id
+      #  notifiers.*.name
+      #  notifiers.*.is_default
+      #  notifiers.*.frequency
+      #  notifiers.*.disable_resolve_message
+      #  notifiers
+      #  enable
+      #  datasources.settings.deleteDatasources.*.orgId
+      #  datasources.settings.deleteDatasources.*.name
+      #  datasources.settings.deleteDatasources
+      #  datasources.settings.datasources.*.url
+      #  datasources.settings.datasources.*.uid
+      #  datasources.settings.datasources.*.type
+      #  datasources.settings.datasources.*.secureJsonData
+      #  datasources.settings.datasources.*.name
+      #  datasources.settings.datasources.*.jsonData
+      #  datasources.settings.datasources.*.editable
+      #  datasources.settings.datasources.*.access
+      #  datasources.settings.datasources
+      #  datasources.settings.apiVersion
+      #  datasources.settings
+      #  datasources.path
+      #  datasources
+      #  dashboards.settings.providers.*.type
+      #  dashboards.settings.providers.*.options.path
+      #  dashboards.settings.providers.*.name
+      #  dashboards.settings.providers
+      #  dashboards.settings.apiVersion
+      #  dashboards.settings
+      #  dashboards.path
+      #  dashboards
+      #  alerting.templates.settings.templates.*.template
+      #  alerting.templates.settings.templates.*.name
+      #  alerting.templates.settings.templates
+      #  alerting.templates.settings.deleteTemplates.*.orgId
+      #  alerting.templates.settings.deleteTemplates.*.name
+      #  alerting.templates.settings.deleteTemplates
+      #  alerting.templates.settings.apiVersion
+      #  alerting.templates.settings
+      #  alerting.templates.path
+      #  alerting.rules.settings.groups.*.name
+      #  alerting.rules.settings.groups.*.interval
+      #  alerting.rules.settings.groups.*.folder
+      #  alerting.rules.settings.groups
+      #  alerting.rules.settings.deleteRules.*.uid
+      #  alerting.rules.settings.deleteRules.*.orgId
+      #  alerting.rules.settings.deleteRules
+      #  alerting.rules.settings.apiVersion
+      #  alerting.rules.settings
+      #  alerting.rules.path
+      #  alerting.policies.settings.resetPolicies
+      #  alerting.policies.settings.policies
+      #  alerting.policies.settings.apiVersion
+      #  alerting.policies.settings
+      #  alerting.policies.path
+      #  alerting.muteTimings.settings.muteTimes.*.name
+      #  alerting.muteTimings.settings.muteTimes
+      #  alerting.muteTimings.settings.deleteMuteTimes.*.orgId
+      #  alerting.muteTimings.settings.deleteMuteTimes.*.name
+      #  alerting.muteTimings.settings.deleteMuteTimes
+      #  alerting.muteTimings.settings.apiVersion
+      #  alerting.muteTimings.settings
+      #  alerting.muteTimings.path
+      #  alerting.contactPoints.settings.deleteContactPoints.*.uid
+      #  alerting.contactPoints.settings.deleteContactPoints.*.orgId
+      #  alerting.contactPoints.settings.deleteContactPoints
+      #  alerting.contactPoints.settings.contactPoints.*.name
+      #  alerting.contactPoints.settings.contactPoints
+      #  alerting.contactPoints.settings.apiVersion
+      #  alerting.contactPoints.settings
+      #  alerting.contactPoints.path
+      #};
+
+      #services.grafana-agent.enable
+      #services.grafana_reporter.port
+      #services.grafana_reporter.addr
+      #services.grafana-agent.package
+      #services.grafana-agent.settings
+      #services.grafana_reporter.enable
+      #services.grafana-agent.extraFlags
+      #services.grafana-agent.credentials
+      #services.grafana_reporter.templateDir
+      #services.grafana_reporter.grafana.port
+      #services.grafana_reporter.grafana.addr
+      #services.grafana-image-renderer.enable
+      #services.grafana-image-renderer.verbose
+      #services.grafana-image-renderer.settings
+      #services.grafana-image-renderer.chromium
+      #services.grafana_reporter.grafana.protocol
+      #services.grafana-image-renderer.provisionGrafana
+      #services.grafana-image-renderer.settings.service.port
+      #services.grafana-image-renderer.settings.service.logging.level
+      #services.grafana-image-renderer.settings.rendering.width
+      #services.grafana-image-renderer.settings.rendering.mode
+      #services.grafana-image-renderer.settings.rendering.height
+      #services.grafana-image-renderer.settings.rendering.args
+    };
+  };
+
+}
\ No newline at end of file
diff --git a/nix/hosts/corrino/www/hydra.emile.space.nix b/nix/hosts/corrino/www/hydra.emile.space.nix
new file mode 100644
index 0000000..2607ac0
--- /dev/null
+++ b/nix/hosts/corrino/www/hydra.emile.space.nix
@@ -0,0 +1,57 @@
+ { ... }:
+
+{
+  services.nginx.virtualHosts."hydra.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:3001";
+      };
+    };
+  };
+
+  # make hydra send emails
+  services.postfix = {
+    enable = true;
+    setSendmail = true;
+  };
+
+  services.hydra = {
+    enable = true;
+
+    listenHost = "*";
+    port = 3001;
+    hydraURL = "https://hydra.emile.space"; # externally visible URL
+
+    # Directory that holds Hydra garbage collector roots.
+    gcRootsDir = "/nix/var/nix/gcroots/hydra";
+
+
+    # a standalone hydra will require you to unset the buildMachinesFiles list to avoid using a nonexistant /etc/nix/hosts
+    buildMachinesFiles = [];
+    # you will probably also want, otherwise *everything* will be built from scratch
+    useSubstitutes = true;
+
+
+    # notification settings
+    smtpHost = "mail.emile.space";
+    notificationSender = "hydra@emile.space";
+
+    # Threshold of minimum disk space (GiB) to determine if the evaluator should run or not.
+    minimumDiskFreeEvaluator = 20;
+
+    # Threshold of minimum disk space (GiB) to determine if the queue runner should run or not.
+    minimumDiskFree = 20;
+
+    # Path to a file containing the logo of your Hydra instance
+    # logo = ;
+
+    extraConfig = ''
+      <git-input>
+        timeout = 3600
+      </git-input>
+    '';
+  };
+}
diff --git a/nix/hosts/corrino/www/jupyter.emile.space.nix b/nix/hosts/corrino/www/jupyter.emile.space.nix
new file mode 100644
index 0000000..d1d951c
--- /dev/null
+++ b/nix/hosts/corrino/www/jupyter.emile.space.nix
@@ -0,0 +1,60 @@
+{ pkgs, lib, config, ... }:
+
+{
+  services.nginx.virtualHosts."jupyter.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:8004";
+      };
+    };
+  };
+
+  services.jupyter = rec {
+    enable = true;
+
+    ip = "127.0.0.1";
+    port = 8004;
+
+    # ; python3
+    # >>> from notebook.auth import passwd
+    # >>> passwd("the_password_here")
+    password = "'argon2:$argon2id$v=19$m=10240,t=10,p=8$WdU+DaBjTaiV1IQDRJUczg$N734yZ45++Kgl26lFEZau58ru8e7P/IgL9N6sf+kw9E'";
+
+    notebookConfig = ''
+      c.NotebookApp.allow_remote_access = True
+      c.NotebookApp.allow_origin = '*'
+    '';
+
+    kernels = {
+      python3 = let
+        env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
+                ipykernel
+              ]));
+      in {
+        displayName = "Python 3";
+        argv = [
+          "${env.interpreter}"
+          "-m"
+          "ipykernel_launcher"
+          "-f"
+          "{connection_file}"
+        ];
+        language = "python";
+        #logo32 = "${env.sitePackages}/ipykernel/resources/logo-32x32.png";
+        #logo64 = "${env.sitePackages}/ipykernel/resources/logo-64x64.png";
+        extraPaths = {
+          "cool.txt" = pkgs.writeText "cool" "cool content";
+        };
+      };
+    };
+
+    group = "jupyter";
+    user = "jupyter";
+  };
+
+  users.users.jupyter.group = "jupyter";
+  users.groups.jupyter = {};
+}
\ No newline at end of file
diff --git a/nix/hosts/corrino/www/magic-hash.emile.space.nix b/nix/hosts/corrino/www/magic-hash.emile.space.nix
new file mode 100644
index 0000000..05446ea
--- /dev/null
+++ b/nix/hosts/corrino/www/magic-hash.emile.space.nix
@@ -0,0 +1,33 @@
+{ config, ... }:
+
+{
+  services.nginx.virtualHosts."magic-hash.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:8339";
+      };
+    };
+  };
+
+  virtualisation.oci-containers = {
+    backend = "docker";
+    containers = {
+      "ctfd" = {
+        image = "magic-hash";
+        ports = [
+          "8338:80"
+        ];
+        environment = {
+
+          # this is not encouraged, but should work for the weekend (this is a
+          # flag, not a password, so even if it get's leaked, the worst that
+          # can happen is that people could enter it somewhere)
+          "FLAG" = builtins.readFile config.age.secrets.magic-hash-flag.path;
+        };
+      };
+    };
+  };
+}
diff --git a/nix/hosts/corrino/www/netbox.emile.space.nix b/nix/hosts/corrino/www/netbox.emile.space.nix
new file mode 100644
index 0000000..a86209c
--- /dev/null
+++ b/nix/hosts/corrino/www/netbox.emile.space.nix
@@ -0,0 +1,63 @@
+{ config, pkgs, ... }:
+
+{
+  services.nginx.virtualHosts."netbox.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+    kTLS = true;
+
+    locations."/" = {
+      proxyPass = "http://[::1]:8001";
+      proxyWebsockets = true;
+    };
+    locations."/static/".root = "${config.services.netbox.dataDir}";
+  };
+
+  users.users.nginx.extraGroups = [ "netbox" ];
+
+  environment.systemPackages = with pkgs; [ netbox ];
+
+  services.netbox = {
+    enable = true;
+    package = pkgs.netbox_3_6; # nixos 23.11 now has netbox 3.6
+    dataDir = "/var/lib/netbox";
+    settings.ALLOWED_HOSTS = [ "*" ];
+    enableLdap = false;
+    settings = {};
+    secretKeyFile = config.age.secrets.netbox_secret.path;
+    port = 8001;
+    listenAddress = "[::1]";
+  };
+
+  age.secrets.netbox_secret = {
+    mode = "440";
+    owner = "netbox";
+    group = "netbox";
+  };
+
+  #services.netbox = {
+  #  enable = true;
+  #  listenAddress = "[::1]";
+  #  secretKeyFile = config.age.secrets.netbox_secret.path;
+  #  package = pkgs.netbox.override { python3 = pkgs.python310; };
+  #  # extraConfig = ''
+  #  #   # REMOTE_AUTH_BACKEND = 'social_core.backends.open_id_connect.OpenIdConnectAuth'
+  #  #   # SOCIAL_AUTH_OIDC_OIDC_ENDPOINT = 'https://auth.c3voc.de'
+
+  #  #   EXEMPT_VIEW_PERMISSIONS = ['*']
+  #  # '';
+  #};
+
+  # add nginx to the netbox group so it can read /var/lib/nginx/static
+  # users = {
+  #   groups."netbox" = {};
+  #   users = {
+  #     netbox = {
+  #       isNormalUser = true;
+  #       group = "netbox";
+  #     };
+  #   };
+  # };
+  # users.users.nginx.extraGroups = [ "netbox" ];
+}
+
diff --git a/nix/hosts/corrino/www/pgweb.emile.space.nix b/nix/hosts/corrino/www/pgweb.emile.space.nix
new file mode 100644
index 0000000..522a6bf
--- /dev/null
+++ b/nix/hosts/corrino/www/pgweb.emile.space.nix
@@ -0,0 +1,21 @@
+{ pkgs, ... }:
+
+{
+  services.nginx.virtualHosts."pgweb.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:5432";
+      };
+    };
+  };
+
+  environment.systemPackages = with pkgs; [ pgweb ];
+
+  # systemd.services.pgweb = {
+  #   wantedBy = [ "multi-user.target" ];
+  #   serviceConfig.ExecStart = "${pkgs.pgweb}/bin/pwgeb";
+  # };
+}
diff --git a/nix/hosts/corrino/www/photo.emile.space.nix b/nix/hosts/corrino/www/photo.emile.space.nix
new file mode 100644
index 0000000..7f2e9ca
--- /dev/null
+++ b/nix/hosts/corrino/www/photo.emile.space.nix
@@ -0,0 +1,33 @@
+{ config, ... }:
+
+{
+  services.nginx.virtualHosts."photo.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:2342";
+        proxyWebsockets = true;
+      };
+    };
+  };
+
+  services.photoprism = {
+    enable = true;
+
+    address = "127.0.0.1";
+    port = 2342;
+
+    passwordFile = config.age.secrets.photoprism_password.path;
+
+    # originalsPath = "/data/photos";
+    originalsPath = "/mnt/storagebox-bx11/photos";
+
+    settings = {
+      PHOTOPRISM_ADMIN_USER = "root";
+      PHOTOPRISM_DEFAULT_LOCALE = "en";
+      PHOTOPRISM_SITE_URL = "https://photo.emile.space";
+    };
+  };
+}
diff --git a/nix/hosts/corrino/www/stream.emile.space.nix b/nix/hosts/corrino/www/stream.emile.space.nix
new file mode 100644
index 0000000..7340d4f
--- /dev/null
+++ b/nix/hosts/corrino/www/stream.emile.space.nix
@@ -0,0 +1,24 @@
+{ ... }:
+
+{
+  services.nginx.virtualHosts."stream.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:8080";
+        proxyWebsockets = true;
+      };
+    };
+  };
+
+  services.owncast = {
+    enable = true;
+    openFirewall = true;
+    listen = "0.0.0.0";
+    dataDir = "/var/lib/owncast";
+    rtmp-port = 1935;
+    port = 8080; # web interface
+  };
+}
diff --git a/nix/hosts/corrino/www/talks.emile.space.nix b/nix/hosts/corrino/www/talks.emile.space.nix
new file mode 100644
index 0000000..4833fa7
--- /dev/null
+++ b/nix/hosts/corrino/www/talks.emile.space.nix
@@ -0,0 +1,97 @@
+{ config, pkgs, ... }:
+
+let
+  pretalx_config = pkgs.writeText "/etc/pretalx.cfg" ''
+    [filesystem]
+    media = /public/media
+    data = /public/data
+    static = /pretalx/src/static.dist
+
+    [site]
+    ; never run debug in production
+    debug = True
+    url = https://talks.emile.space
+
+    [database]
+    backend=sqlite3
+
+    [mail]
+    from = pretalx@emile.space
+    host = mail.emile.space
+    port = 1025
+    user = mail
+    password=${config.age.secrets.mail_password.path}
+    tls = True
+    ssl = False
+
+    [celery]
+    backend=redis+socket:///pretalx/redis.sock?virtual_host=1
+    broker=redis+socket:///pretalx/redis.sock?virtual_host=2
+
+    [redis]
+    location=unix:///pretalx/redis.sock?db=0
+    ; Remove the following line if you are unsure about your redis' security
+    ; to reduce impact if redis gets compromised.
+    sessions=true    
+  ''; 
+in {
+  services.nginx.virtualHosts."talks.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        extraConfig = ''
+          proxy_pass http://127.0.0.1:8350;
+
+          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+          proxy_set_header Host $host;
+        '';
+      };
+      "/media/" = {
+        root = "/var/pretalx-public/";
+      };
+      "/static/" = {
+        root = "/var/pretalx-public/";
+      };
+    };
+  };
+
+  virtualisation.oci-containers.containers = {
+    pretalx = {
+      image = "pretalx/standalone:latest";
+      ports = [
+        "127.0.0.1:8350:80"
+      ];
+      volumes = [
+        "/var/pretalx-data:/data" # {static, media}
+        "/var/pretalx-public:/public"
+        "/var/pretalx-public/static:/pretalx/src/static.dist"
+
+        # "/var/pretalx-public-media:/public/media"
+        "${pretalx_config}:/etc/pretalx/pretalx.cfg:ro"
+        "/run/redis-pretalx/redis.sock:/pretalx/redis.sock"
+      ];
+    };
+  };
+
+  services.redis.vmOverCommit = true;
+  services.redis.servers."pretalx" = {
+    enable = true;
+    port = 0;
+    unixSocketPerm = 666;
+    user = "pretalxuser";
+  };
+
+  users = {
+    groups."pretalxuser" = {};
+    users."pretalxuser" = {
+      #isNormalUser = true; # we're setting the uid manually, nix should detect this, but whatever...
+      uid = 999;
+      group = "pretalxuser";
+      description = "The user for pretalx. Created, as we need a user to set the permissions for the redis unix socket";
+    };
+  };
+
+  # 15,45 * * * * docker exec pretalx-app pretalx runperiodic
+}
diff --git a/nix/hosts/corrino/www/tickets.emile.space.nix b/nix/hosts/corrino/www/tickets.emile.space.nix
new file mode 100644
index 0000000..f479263
--- /dev/null
+++ b/nix/hosts/corrino/www/tickets.emile.space.nix
@@ -0,0 +1,120 @@
+{ config, pkgs, ... }:
+
+# Future People: This place is not a place of honor... no highly esteemed deed
+# is commemorated here... nothing valued is here...
+# Look at the docker volumes section: You'll have to build and fail a few
+# times... sorry
+
+let
+  # pretix_config = config.age.secrets.pretix.path;
+
+  pretix_config = pkgs.writeText "pretix.cfg" ''
+    [pretix]
+    instance_name=tickets.emile.space
+    url=https://tickets.emile.space
+    currency=EUR
+    ; DO NOT change the following value, it has to be set to the location of the
+    ; directory *inside* the docker container
+    datadir=/data
+    cookie_domain=tickets.emile.space
+    trust_x_forwarded_for=on
+    trust_x_forwarded_proto=on
+
+    [database]
+    backend=sqlite3
+
+    [mail]
+    ; See config file documentation for more options
+    from=tickets@emile.space
+    ; This is the default IP address of your docker host in docker's virtual
+    ; network. Make sure postfix listens on this address.
+    host=mail.emile.space
+    user=mail
+    password=${config.age.secrets.mail_password.path}
+    port=1025
+    tls=on
+    ssl=off
+
+    [redis]
+    location=unix:///pretix/redis.sock?db=0
+    ; Remove the following line if you are unsure about your redis' security
+    ; to reduce impact if redis gets compromised.
+    sessions=true
+
+    [celery]
+    backend=redis+socket:///pretix/redis.sock?virtual_host=1
+    broker=redis+socket:///pretix/redis.sock?virtual_host=2
+  '';
+in {
+  services.nginx.virtualHosts."tickets.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    # serverAliases = [
+    #   "falscher-ha.se"
+    # ];
+
+    locations = {
+      "/" = {
+        extraConfig = ''
+          proxy_pass http://127.0.0.1:8349;
+
+          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+          proxy_set_header Host $host;
+        '';
+      };
+    };
+  };
+
+  virtualisation.oci-containers.containers = {
+    pretix = {
+      image = "pretix/standalone:stable";
+      ports = [
+        "127.0.0.1:8349:80"
+      ];
+      volumes = [
+        "/var/pretix-data:/data"
+        "/etc/pretix:/etc/pretix"
+        "/run/redis-pretix/redis.sock:/pretix/redis.sock"
+        # "/run/redis:/var/run/redis"
+
+        # update the below manually using the result from
+        # ; readlink /etc/static/pretix.cfg
+        # after building and failing once
+        # (yes, I'm so annoyed that I can't mount symlinks into docker containers)
+        # "/nix/store/vch1g88b5za1ab79cikil3n7wqrl8wxg-etc-pretix.cfg:/etc/pretix/pretix.cfg"
+        # "/nix/store/rcxvnbg7iqb1z011ybanj3982153xi70-etc-pretix.cfg:/etc/pretix/pretix.cfg"
+        "${pretix_config}:/etc/pretix/pretix.cfg"
+      ];
+      extraOptions = [
+        # "--sysctl net.core.somaxconn=4096"
+      ];
+    };
+  };
+
+
+  services.redis.vmOverCommit = true;
+  services.redis.servers."pretix" = {
+    enable = true;
+    port = 0;
+    unixSocketPerm = 666;
+    user = "pretixuser";
+  };
+
+  users = {
+    groups."pretixuser" = {};
+    users."pretixuser" = {
+      isNormalUser = true; # we're setting the uid manually, nix should detect this, but whatever...
+      uid = 15371;
+      group = "pretixuser";
+      description = "The user for pretix. Created, as we need a user to set the permissions for the redis unix socket";
+    };
+  };
+
+
+  # Allow access to the unix socket for the "redis" group.
+  # services.redis.settings.unixsocketperm = "770";
+
+  # trace: warning: The option `services.redis.settings' defined in `/nix/store/ib5271hcbjqrxb0yrmrjcypvpacmnp2s-source/ops/modules/www/tickets.emile.space.nix' has been renamed to `services.redis.servers."".settings'.
+
+}
diff --git a/nix/hosts/corrino/www/tickets.emile.space.nix_chaos.jetzt.nix b/nix/hosts/corrino/www/tickets.emile.space.nix_chaos.jetzt.nix
new file mode 100644
index 0000000..5b7d99a
--- /dev/null
+++ b/nix/hosts/corrino/www/tickets.emile.space.nix_chaos.jetzt.nix
@@ -0,0 +1,107 @@
+{ config, ... }:
+
+# Future People: This place is not a place of honor... no highly esteemed deed
+# is commemorated here... nothing valued is here...
+# Look at the docker volumes section: You'll have to build and fail a few
+# times... sorry
+
+{
+  services.nginx.virtualHosts."tickets.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        extraConfig = ''
+          proxy_pass http://127.0.0.1:8349;
+
+          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+          proxy_set_header Host $host;
+        '';
+      };
+    };
+  };
+
+  environment.etc."pretix.cfg".text = ''
+    [pretix]
+    instance_name=tickets.emile.space
+    url=https://tickets.emile.space
+    currency=EUR
+    ; DO NOT change the following value, it has to be set to the location of the
+    ; directory *inside* the docker container
+    datadir=/data
+    cookie_domain=tickets.emile.space
+    trust_x_forwarded_for=on
+    trust_x_forwarded_proto=on
+
+    [database]
+    backend=sqlite3
+
+    [mail]
+    ; See config file documentation for more options
+    from=tickets@emile.space
+    ; This is the default IP address of your docker host in docker's virtual
+    ; network. Make sure postfix listens on this address.
+    host=mail.emile.space
+    user=mail
+
+    ; something like this or so...
+    ;password=${builtins.readFile config.age.secrets.mailserver_credz.path}
+    ;password=this_is_an_example_password_changeme
+
+    port=1025
+    tls=on
+    ssl=off
+
+    [redis]
+    location=unix:///pretix/redis.sock?db=0
+    ; Remove the following line if you are unsure about your redis' security
+    ; to reduce impact if redis gets compromised.
+    sessions=true
+
+    [celery]
+    backend=redis+socket:///pretix/redis.sock?virtual_host=1
+    broker=redis+socket:///pretix/redis.sock?virtual_host=2
+  '';
+
+  virtualisation.oci-containers.containers = {
+    pretix = {
+      image = "pretix/standalone:stable";
+      ports = [
+        "127.0.0.1:8349:80"
+      ];
+      volumes = [
+        "/var/pretix-data:/data"
+        "/etc/pretix:/etc/pretix"
+        "/run/redis-pretix/redis.sock:/pretix/redis.sock"
+
+        # update the below manually using the result from
+        # ; readlink /etc/static/pretix.cfg
+        # after building and failing once
+        # (yes, I'm so annoyed that I can't mount symlinks into docker containers)
+        # "/nix/store/vch1g88b5za1ab79cikil3n7wqrl8wxg-etc-pretix.cfg:/etc/pretix/pretix.cfg"
+        "/nix/store/rcxvnbg7iqb1z011ybanj3982153xi70-etc-pretix.cfg:/etc/pretix/pretix.cfg"
+      ];
+    };
+  };
+
+
+  services.redis.vmOverCommit = true;
+  services.redis.servers."pretix" = {
+    enable = true;
+    port = 0;
+    unixSocketPerm = 666;
+    user = "pretixuser";
+  };
+
+  users = {
+    groups."pretixuser" = {};
+    users."pretixuser" = {
+      isNormalUser = true; # we're setting the uid manually, nix should detect
+                           # this, but whatever...
+      uid = 15371;
+      group = "pretixuser";
+      description = "The user for pretix. Created, as we need a user to set the permissions for the redis unix socket";
+    };
+  };
+}
diff --git a/nix/hosts/corrino/www/znc.emile.space.nix b/nix/hosts/corrino/www/znc.emile.space.nix
new file mode 100644
index 0000000..7a790ed
--- /dev/null
+++ b/nix/hosts/corrino/www/znc.emile.space.nix
@@ -0,0 +1,47 @@
+{ ... }:
+
+{
+  services.nginx.virtualHosts."znc.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+
+    locations = {
+      "/" = {
+        proxyPass = "http://127.0.0.1:5000";
+      };
+    };
+  };
+
+  services.znc = {
+    enable = true;
+    openFirewall = true;
+    useLegacyConfig = false;
+
+    config = {
+      LoadModule = [ ];
+      User.Emile = {
+        Admin = true;
+        Nick = "hanemile";
+        RealName = "Emile";
+        # QuitMsg = "iowait()";
+        LoadModule = [ "chansaver" "controlpanel" ];
+
+        Network.libera = {
+          Server = "irc.libera.chat +6697";
+          LoadModule = [ "simple_away" ];
+          Chan = {
+            "#nixos" = { Detached = false; };
+            "##linux" = { Disabled = true; };
+          };
+        };
+
+        Pass.password = { # hunter2
+          Method = "sha256";
+          Hash =
+            "31357a874d929871b7c2267721501aaa1f3c570ddc72eb6fb6d065fe72dbc2e4";
+          Salt = "Oo1du8jahquataexai6Eiph9OcohpoL3";
+        };
+      };
+    };
+  };
+}
diff --git a/nix/hosts/hacknix/README.md b/nix/hosts/hacknix/README.md
new file mode 100644
index 0000000..a695e7e
--- /dev/null
+++ b/nix/hosts/hacknix/README.md
@@ -0,0 +1,46 @@
+# hacknix
+
+A nixos setup for hacking (pentesting).
+
+This has...
+
+- burp pro installed
+- the office ca cert installed
+- common tools installed
+
+## Installation
+
+- download the minimal NixOS iso
+    - https://channels.nixos.org/nixos-22.11/latest-nixos-minimal-x86_64-linux.iso
+- create a new VM in Virtualbox
+    - call it whatever you like
+    - select the previously downloaded iso image
+    - set the type to `linux`
+    - set the version to `other`
+    - give it some RAM
+    - give it some CORES
+    - enable EFI!
+    - give it some storage
+- boot the vm, then...
+    - `sudo -i`
+    - `parted /dev/sda -- mklabel gpt`
+    - `parted /dev/sda -- mklabel primary 512MB 100%`
+    - `parted /dev/sda -- mklabel ESP fat32 1MB 512MB`
+    - `parted /dev/sda -- mklabel set 3 esp on`
+    - `pvcreate /dev/sda1`
+    - `vgcreate pool /dev/sda1`
+    - `lvcreate -L 30GB -n home pool`
+    - `mkfs.ext4 /dev/pool/home`
+    - `mount /dev/disk/by-label/nixos /mnt`
+    - `mkdir -p /mnt/boot`
+    - `mount /dev/disk/by-label/boot /mnt/boot`
+    - `nixos-generate-config --root /mnt`
+    - edit the nixos configuration (this is just preliminary, we're going to use the one in this repo further on)
+    - `nixos-install`
+        - set a root password
+    - `reboot`
+- login
+    - `git clone https://<git>/<user>/hacknix /etc/nixos`
+        - this will probably fail due to missing certs...
+    - `sudo nixos-rebuild switch`
+
diff --git a/nix/hosts/hacknix/burpsuitepro/default.nix b/nix/hosts/hacknix/burpsuitepro/default.nix
new file mode 100644
index 0000000..0365bb9
--- /dev/null
+++ b/nix/hosts/hacknix/burpsuitepro/default.nix
@@ -0,0 +1,46 @@
+{ pkgs, nixpkgs, ... }:
+
+pkgs.stdenvNoCC.mkDerivation rec {
+  pname = "burpsuitepro";
+  version = "2023.3.2";
+
+  src = builtins.fetchurl {
+    name = "burpsuite.jar";
+    url = "https://portswigger-cdn.net/burp/releases/download?product=pro&version=2023.5.4&type=Jar";
+    sha256 = "sha256:1zlcrs3xg5z5kbdnfszrk8bsw0h4hlpfmbf7j0qb6hzncsp8j5p0";
+  };
+
+  dontUnpack = true;
+  dontBuild = true;
+  installPhase = ''
+    runHook preInstall
+    mkdir -p $out/bin
+    echo '#!${pkgs.runtimeShell}
+    eval "$(${pkgs.unzip}/bin/unzip -p ${src} chromium.properties)"
+    mkdir -p "$HOME/.BurpSuite/burpbrowser/$linux64"
+    ln -sf "${pkgs.chromium}/bin/chromium" "$HOME/.BurpSuite/burpbrowser/$linux64/chrome"
+    exec ${pkgs.jdk19}/bin/java -jar ${src} "$@"' > $out/bin/burpsuitepro
+    chmod +x $out/bin/${pname}
+    runHook postInstall
+  '';
+
+
+  preferLocalBuild = true;
+
+  meta = with nixpkgs.lib; {
+    description = "An integrated platform for performing security testing of web applications";
+    longDescription = ''
+      Burp Suite is an integrated platform for performing security testing of web applications.
+      Its various tools work seamlessly together to support the entire testing process, from
+      initial mapping and analysis of an application's attack surface, through to finding and
+      exploiting security vulnerabilities.
+    '';
+    homepage = "https://portswigger.net/burp/";
+    downloadPage = "https://portswigger.net/burp/freedownload";
+    sourceProvenance = with sourceTypes; [ binaryBytecode ];
+    #license = licenses.unfree;
+    platforms = pkgs.jdk19.meta.platforms;
+    hydraPlatforms = [];
+    maintainers = with maintainers; [ hanemile ];
+  };
+}
diff --git a/nix/hosts/hacknix/configuration.nix b/nix/hosts/hacknix/configuration.nix
new file mode 100644
index 0000000..48aa1e0
--- /dev/null
+++ b/nix/hosts/hacknix/configuration.nix
@@ -0,0 +1,396 @@
+# Edit ths configuration file to define what should be installed on
+# your system.  Help is available in the configuration.nix(5) man page
+# and in the NixOS manual (accessible by running ‘nixos-help’).
+
+{ nixpkgs, nixpkgs-unstable, config, lib, pkgs, ... }:
+
+let
+  burppro = pkgs.callPackage ./burpsuitepro { inherit pkgs; nixpkgs=pkgs; };
+  # TODO: pull licence from git
+in {
+  imports =
+    [ # Include the results of the hardware scan.
+      ./hardware-configuration.nix
+      ./overlay
+    ];
+
+  nixpkgs = {
+    config.allowUnfree = true; # for virtualisation.virtualbox
+  };
+
+  # Use the systemd-boot EFI boot loader.
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  networking.hostName = "hacknix";
+
+  # Set your time zone.
+  time.timeZone = "Europe/Berlin";
+
+  i18n.defaultLocale = "en_US.UTF-8";
+
+  # fileSystems."/home/hack/Documents/datapool.lan" = {
+  #   device = "datapool.lan:/mnt/data/dump";
+  #   fsType = "nfs";
+  # };
+
+  services = {
+    dbus.enable = true;
+    xserver = {
+    enable = true;
+
+     # Keyboard settings
+     layout = "us";
+     xkbOptions = "caps:compose";
+
+     desktopManager = {
+       xterm.enable = false;
+
+       # we don't use the xfce interface, only the fancy desktopManager
+       # settings and the session
+       xfce = {
+         enable = true;
+         noDesktop = true;
+         enableXfwm = false;
+       };
+     };
+
+     # default display manager when logging in
+     displayManager = {
+       defaultSession = "xfce+i3";
+       sessionCommands = ''
+       '';
+     };
+
+     windowManager.i3 = {
+       enable = true;
+       configFile = "/etc/i3.conf"; # see environment.etc."i3.conf".text
+       extraPackages = with pkgs; [
+         dmenu
+         i3status i3blocks
+       ];
+     };
+    };
+  };
+
+  environment.etc."i3.conf".text = pkgs.callPackage ./i3-config.nix {};
+
+  # Enable CUPS to print documents.
+  # services.printing.enable = true;
+
+  # Enable sound.
+  # sound.enable = true;
+  # hardware.pulseaudio.enable = true;
+  hardware.opengl.enable = true;
+
+  # Enable touchpad support (enabled default in most desktopManager).
+  # services.xserver.libinput.enable = true;
+
+  users.users.hack = {
+    isNormalUser = true;
+    extraGroups = [
+      "wheel" # Enable ‘sudo’ for the user.
+      "vboxsf" # Allow access to the shared /pentest folder mounted in via virtualbox
+      "docker" # access to the docker socket
+    ];
+    shell = pkgs.zsh;
+  };
+
+  environment = {
+    shellAliases = {
+      #ls = "lsd";
+      ls = "eza";
+    };
+    systemPackages = with pkgs; [
+      unstable.obsidian
+
+      kitty 
+
+      # editors
+      vim
+
+      helix
+        marksman # markdown lsp
+        cuelsp # cue lsp
+        terraform-lsp terraform-ls # terraform lsp
+        rnix-lsp # nix lsp
+
+      # command line tools
+      fd
+      ripgrep
+      htop
+      fzf
+      jq
+      eza
+      lsd
+      du-dust
+      pwgen
+
+      # x11 foo
+      arandr
+      feh
+
+      # shell
+      zsh oh-my-zsh
+
+      # browser
+      chromium
+      firefox
+
+      # programming languages
+      go
+      gopls # (Official language server for the Go language)
+      go-outline # (Utility to extract JSON representation of declarations from a Go source file)
+      go-tools # staticcheck (A collection of tools and libraries for working with Go code, including linters and static analysis)
+      gocode-gomod # (An autocompletion daemon for the Go programming language)
+      gotest # (go test with colors)
+      gotests # (Generate Go tests from your source code)
+      gomodifytags # (Go tool to modify struct field tags)
+      impl # (Generate method stubs for implementing an interface)
+      delve # dlv (debugger for the Go programming language)
+
+      (pkgs.python3.withPackages (ps: with ps; [
+        pwntools
+        requests 
+        tqdm 
+        beautifulsoup4
+        mitmproxy
+
+        (
+          buildPythonPackage rec {
+            pname = "pandoc";
+            version = "2.3";
+            src = fetchPypi {
+              inherit pname version;
+              sha256 = "sha256-53LCxthxFGiUV5go268e/VOOtk/H5x1KazoRoYuu+Q0=";
+            };
+            doCheck = false;
+            propagatedBuildInputs = [
+              # pkgs.python310Packages.ply
+              # pkgs.python310Packages.plumbum
+              # Specify dependencies
+              #pkgs.python3Packages.numpy
+            ];
+          }
+        )
+      ]))
+
+      # dev
+      vscode
+      docker-compose
+
+      # analysis
+      binwalk
+      file
+
+      # communication
+      element-desktop
+
+      # view pdfs
+      zathura okular
+
+      # infra 
+      cue
+      cuetools
+      
+      #radare2
+      r2
+      capstone # Advanced disassembly library
+      keystone # Lightweight multi-platform, multi-architecture assembler framework
+      unicorn # Lightweight multi-platform CPU emulator library
+
+      # hashicorp stuff
+      # vault vault-bin vaultenv vault-medusa
+      # nomad_1_4
+      # consul
+      # terraform
+
+      #unstable.mitmproxy
+      #mitmproxy_bs4
+
+      dex
+      xss-lock
+      networkmanagerapplet
+
+      p7zip
+      m4
+
+      libreoffice
+
+      pandoc
+      tmux
+
+      python311Packages.python-lsp-server
+    ] ++ [
+      burppro
+    ]; 
+  };
+
+  fonts.packages = with pkgs; [
+    ubuntu_font_family # the font used in the "Sogeti" logo
+    #nerdfonts
+    #font-awesome
+    #powerline-fonts
+  ];
+
+  # Some programs need SUID wrappers, can be configured further or are
+  # started in user sessions.
+  programs = {
+ 
+    vim.defaultEditor = true;
+
+    htop = {
+      enable = true;
+      settings = {
+        hide_kernel_threads = true; 
+      };
+    };
+
+    #fish.enable = true;
+    zsh = {
+      enable = true;
+      syntaxHighlighting = {
+        enable = true;
+      };
+      ohMyZsh = {
+        enable = true;
+        plugins = [ "nmap" ];
+      };
+
+      # this par in ~/.zshrc:
+      # 
+      # PROMPT="; "
+      # RPROMPT="%F{green}%/%F{reset}"
+      # ZSH_THEME=
+      # PATH=$PATH:/home/hack/.cargo/bin
+    };
+
+    chromium = {
+      enable = true;
+      homepageLocation = "https://emile.space";
+      extraOpts = {
+        "ClientCertificateManagementAllowed" = 0; 
+      };
+    };
+
+    git = {
+      enable = true;
+      config = {
+        core.editor = "vim";
+        user = {
+          name = "Emile Hansmaennel";
+          email = "emile.hansmaennel@sogeti.com";
+        };
+      };
+    };
+  };
+
+  # virtualbox guest additions
+  virtualisation.virtualbox.guest.enable = true;
+  virtualisation.virtualbox.guest.x11 = true;
+
+  nix = {
+    settings.experimental-features = [
+      "nix-command"
+      "flakes"
+    ];
+
+    gc = {
+      automatic = true;
+      dates = "12:00"; # daily, docs on format in `man 7 systemd.time`
+      persistent = true;
+    };
+
+    settings = {
+      substituters = [
+        "https://nix-community.cachix.org"
+        "https://cache.nixos.org/"
+      ];
+      trusted-public-keys = [
+        "nixbinarycache.lan:JDjlVLc+5VUKOtFAFBGCDtlgVpLEaaR2JdTw2mQUIb8="
+        "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
+      ];
+    };
+  };
+
+  # the office root_ca
+  security.pki.certificates = [
+    # office.lan
+    ''
+      *.office.lan
+      ============
+      -----BEGIN CERTIFICATE-----
+      MIIBezCCASGgAwIBAgIQdkxWKinry5WWfV2CTRRHfzAKBggqhkjOPQQDAjAcMRow
+      GAYDVQQDExFPZmZpY2UgQ0EgUm9vdCBDQTAeFw0yMDEwMjYxMjQ2MTlaFw0zMDEw
+      MjYxMjQ2MTlaMBwxGjAYBgNVBAMTEU9mZmljZSBDQSBSb290IENBMFkwEwYHKoZI
+      zj0CAQYIKoZIzj0DAQcDQgAEZ/Ac4kmThYXE0ZUBWvTSvgi4fcR19dgL2hROxSfH
+      2RLW7hQzArloxhOzs+28VttiVh13lB4rSCvHe3TGA44c5KNFMEMwDgYDVR0PAQH/
+      BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFE0i80PVvdecDvDp
+      MpO2VtGluzxcMAoGCCqGSM49BAMCA0gAMEUCIQDP9Z1J3Z++6atOdHNTqd0PZ/pi
+      w7HjGPxpRneD4/3vTwIgSoE5Gb3umt+FxIvv9WDFlsWSVRJ5wE6KpCkdGWWzWuU=
+      -----END CERTIFICATE-----
+    ''
+  ];
+
+  # Enable the OpenSSH daemon.
+  # services.openssh.enable = true;
+
+  networking = {
+    nameservers = [
+      "192.168.1.1"
+      #"8.8.8.8"
+    ];
+
+    hosts = {
+      # 127.0.0.1 localhost
+      # ::1 localhost
+    };
+    
+    firewall = {
+      enable = true;
+
+      # open further TCP and/or UDP ports in the firewall
+      allowedTCPPorts = [ 80 443 8123 8080 ];
+      #allowedUDPPorts = [ 53 ];
+    };
+
+    wg-quick.interfaces = {
+      "wg0" = {
+        address = [
+          "10.10.10.12/24" # our IP
+        ];
+        dns = [ "192.168.1.1" ];
+        mtu = 1380;
+        listenPort = 51820;
+
+        # TODO: add private key to repo using agenix, then link here
+        privateKeyFile = "/etc/wireguard/private_key";
+
+        peers = [
+          {
+            publicKey = "9+4OWuqZ0rZsi/oaaXd3YhE1p+Z0tbxwfNbcDnVqRxg=";
+            allowedIPs = [ "0.0.0.0/0" ];
+            endpoint = "PUBLIC_IP:51820";
+            persistentKeepalive = 25;
+          }
+        ];
+      };
+    };
+  };
+
+  virtualisation.docker.enable = true;
+
+  # Copy the NixOS configuration file and link it from the resulting system
+  # (/run/current-system/configuration.nix). This is useful in case you
+  # accidentally delete configuration.nix.
+  # system.copySystemConfiguration = true;
+
+  # This value determines the NixOS release from which the default
+  # settings for stateful data, like file locations and database versions
+  # on your system were taken. It‘s perfectly fine and recommended to leave
+  # this value at the release version of the first install of this system.
+  # Before changing this value read the documentation for this option
+  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
+  system.stateVersion = "22.11"; # Did you read the comment?
+}
+
diff --git a/nix/hosts/hacknix/hardware-configuration.nix b/nix/hosts/hacknix/hardware-configuration.nix
new file mode 100644
index 0000000..d887abc
--- /dev/null
+++ b/nix/hosts/hacknix/hardware-configuration.nix
@@ -0,0 +1,36 @@
+# Do not modify this file!  It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations.  Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports = [ ];
+
+  boot.initrd.availableKernelModules = [ "ata_piix" "ohci_pci" "ehci_pci" "ahci" "sd_mod" "sr_mod" ];
+  boot.initrd.kernelModules = [ "dm-snapshot" ];
+  boot.kernelModules = [ ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" =
+    { device = "/dev/disk/by-uuid/0c22b35c-1d78-4186-aff0-62282e832ad7";
+      fsType = "ext4";
+    };
+
+  fileSystems."/boot" =
+    { device = "/dev/disk/by-uuid/9545-D744";
+      fsType = "vfat";
+    };
+
+  swapDevices = [ ];
+
+  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+  # (the default) this is the recommended approach. When using systemd-networkd it's
+  # still possible to use this option, but it's recommended to use it in conjunction
+  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+  networking.useDHCP = lib.mkDefault true;
+  # networking.interfaces.enp0s3.useDHCP = lib.mkDefault true;
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+  virtualisation.virtualbox.guest.enable = true;
+}
diff --git a/nix/hosts/hacknix/i3-config.nix b/nix/hosts/hacknix/i3-config.nix
new file mode 100644
index 0000000..ea06d2d
--- /dev/null
+++ b/nix/hosts/hacknix/i3-config.nix
@@ -0,0 +1,139 @@
+{}:
+
+''
+set $mod Mod1
+
+font pango:monospace 8
+
+exec --no-startup-id dex --autostart --environment i3
+exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
+exec --no-startup-id nm-applet
+
+set $refresh_i3status killall -SIGUSR1 i3status
+bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $refresh_i3status
+bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -10% && $refresh_i3status
+bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $refresh_i3status
+bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ toggle && $refresh_i3status
+
+floating_modifier $mod
+
+tiling_drag modifier titlebar
+
+#bindsym $mod+Return exec i3-sensible-terminal
+bindsym $mod+Return exec kitty 
+
+bindsym $mod+Shift+q kill
+
+bindsym $mod+d exec --no-startup-id dmenu_run
+
+bindsym $mod+h focus left
+bindsym $mod+j focus down
+bindsym $mod+k focus up
+bindsym $mod+l focus right
+
+bindsym $mod+Left focus left
+bindsym $mod+Down focus down
+bindsym $mod+Up focus up
+bindsym $mod+Right focus right
+
+bindsym $mod+Shift+h move left
+bindsym $mod+Shift+j move down
+bindsym $mod+Shift+k move up
+bindsym $mod+Shift+l move right
+
+bindsym $mod+Shift+Left move left
+bindsym $mod+Shift+Down move down
+bindsym $mod+Shift+Up move up
+bindsym $mod+Shift+Right move right
+
+bindsym $mod+b split h
+
+bindsym $mod+v split v
+
+bindsym $mod+f fullscreen toggle
+
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+e layout toggle split
+
+bindsym $mod+Shift+space floating toggle
+
+bindsym $mod+space focus mode_toggle
+
+bindsym $mod+a focus parent
+
+
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+set $ws5 "5"
+set $ws6 "6"
+set $ws7 "7"
+set $ws8 "8"
+set $ws9 "9"
+set $ws10 "10"
+
+bindsym $mod+1 workspace number $ws1
+bindsym $mod+2 workspace number $ws2
+bindsym $mod+3 workspace number $ws3
+bindsym $mod+4 workspace number $ws4
+bindsym $mod+5 workspace number $ws5
+bindsym $mod+6 workspace number $ws6
+bindsym $mod+7 workspace number $ws7
+bindsym $mod+8 workspace number $ws8
+bindsym $mod+9 workspace number $ws9
+bindsym $mod+0 workspace number $ws10
+
+bindsym $mod+Shift+1 move container to workspace number $ws1
+bindsym $mod+Shift+2 move container to workspace number $ws2
+bindsym $mod+Shift+3 move container to workspace number $ws3
+bindsym $mod+Shift+4 move container to workspace number $ws4
+bindsym $mod+Shift+5 move container to workspace number $ws5
+bindsym $mod+Shift+6 move container to workspace number $ws6
+bindsym $mod+Shift+7 move container to workspace number $ws7
+bindsym $mod+Shift+8 move container to workspace number $ws8
+bindsym $mod+Shift+9 move container to workspace number $ws9
+bindsym $mod+Shift+0 move container to workspace number $ws10
+
+bindsym $mod+Shift+c reload
+bindsym $mod+Shift+r restart
+bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
+
+mode "resize" {
+        # These bindings trigger as soon as you enter the resize mode
+
+        # Pressing left will shrink the window’s width.
+        # Pressing right will grow the window’s width.
+        # Pressing up will shrink the window’s height.
+        # Pressing down will grow the window’s height.
+        bindsym h resize shrink width 10 px or 10 ppt
+        bindsym j resize grow height 10 px or 10 ppt
+        bindsym k resize shrink height 10 px or 10 ppt
+        bindsym l resize grow width 10 px or 10 ppt
+
+        # same bindings, but for the arrow keys
+        bindsym Left resize shrink width 10 px or 10 ppt
+        bindsym Down resize grow height 10 px or 10 ppt
+        bindsym Up resize shrink height 10 px or 10 ppt
+        bindsym Right resize grow width 10 px or 10 ppt
+
+        # back to normal: Enter or Escape or $mod+r
+        bindsym Return mode "default"
+        bindsym Escape mode "default"
+        bindsym $mod+r mode "default"
+}
+
+bindsym $mod+r mode "resize"
+
+bar {
+	position top
+        status_command i3status
+}
+
+default_border pixel 1
+hide_edge_borders smart
+smart_borders on
+
+bindsym $mod+shift+m border toggle
+''
diff --git a/nix/hosts/hacknix/overlay/default.nix b/nix/hosts/hacknix/overlay/default.nix
new file mode 100644
index 0000000..0dc3509
--- /dev/null
+++ b/nix/hosts/hacknix/overlay/default.nix
@@ -0,0 +1,16 @@
+{ ... }:
+
+{
+  nixpkgs = {
+    overlays = [
+      (self: super: {
+        #helix-2303 = self.callPackage ../pkgs/helix-2303 { };
+        r2 = self.callPackage ../pkgs/radare2-5.8.4 { };
+      })
+    ];
+    config = {
+      allowUnfree = true;
+      allowBroken= true;
+    };
+  };
+}
diff --git a/nix/hosts/hacknix/pkgs/helix-2303/Cargo.lock b/nix/hosts/hacknix/pkgs/helix-2303/Cargo.lock
new file mode 100644
index 0000000..c3421fb
--- /dev/null
+++ b/nix/hosts/hacknix/pkgs/helix-2303/Cargo.lock
@@ -0,0 +1,2561 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.70"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
+
+[[package]]
+name = "arc-swap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
+
+[[package]]
+name = "bstr"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09"
+dependencies = [
+ "memchr",
+ "once_cell",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "btoi"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
+[[package]]
+name = "bytecount"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cassowary"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chardetng"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea"
+dependencies = [
+ "cfg-if",
+ "encoding_rs",
+ "memchr",
+]
+
+[[package]]
+name = "chrono"
+version = "0.4.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
+dependencies = [
+ "iana-time-zone",
+ "num-integer",
+ "num-traits",
+ "winapi",
+]
+
+[[package]]
+name = "clipboard-win"
+version = "4.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
+dependencies = [
+ "error-code",
+ "str-buf",
+ "winapi",
+]
+
+[[package]]
+name = "clru"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807"
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "content_inspector"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossterm"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
+dependencies = [
+ "bitflags 1.3.2",
+ "crossterm_winapi",
+ "futures-core",
+ "libc",
+ "mio",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "cxx"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "dirs"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dunce"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
+
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "encoding_rs_io"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
+dependencies = [
+ "encoding_rs",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "error-code"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
+dependencies = [
+ "libc",
+ "str-buf",
+]
+
+[[package]]
+name = "etcetera"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d017fce18e4e9bfa75e1db51f49f4487bd3f8a7df509b24a46474a956ee962fd"
+dependencies = [
+ "cfg-if",
+ "dirs-next",
+ "thiserror",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fern"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "filetime"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.2.16",
+ "windows-sys",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "fuzzy-matcher"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
+dependencies = [
+ "thread_local",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gix"
+version = "0.43.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c256ea71cc1967faaefdaad15f334146b7c806f12460dcafd3afed845c8c78dd"
+dependencies = [
+ "gix-actor",
+ "gix-attributes",
+ "gix-config",
+ "gix-credentials",
+ "gix-date",
+ "gix-diff",
+ "gix-discover",
+ "gix-features",
+ "gix-glob",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-index",
+ "gix-lock",
+ "gix-mailmap",
+ "gix-object",
+ "gix-odb",
+ "gix-pack",
+ "gix-path",
+ "gix-prompt",
+ "gix-ref",
+ "gix-refspec",
+ "gix-revision",
+ "gix-sec",
+ "gix-tempfile",
+ "gix-traverse",
+ "gix-url",
+ "gix-validate",
+ "gix-worktree",
+ "log",
+ "once_cell",
+ "signal-hook",
+ "smallvec",
+ "thiserror",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "gix-actor"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc22b0cdc52237667c301dd7cdc6ead8f8f73c9f824e9942c8ebd6b764f6c0bf"
+dependencies = [
+ "bstr",
+ "btoi",
+ "gix-date",
+ "itoa",
+ "nom",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-attributes"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2231a25934a240d0a4b6f4478401c73ee81d8be52de0293eedbc172334abf3e1"
+dependencies = [
+ "bstr",
+ "gix-features",
+ "gix-glob",
+ "gix-path",
+ "gix-quote",
+ "thiserror",
+ "unicode-bom",
+]
+
+[[package]]
+name = "gix-bitmap"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "024bca0c7187517bda5ea24ab148c9ca8208dd0c3e2bea88cdb2008f91791a6d"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "gix-chunk"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0d39583cab06464b8bf73b3f1707458270f0e7383cb24c3c9c1a16e6f792978"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "gix-command"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2c6f75c1e0f924de39e750880a6e21307194bb1ab773efe3c7d2d787277f8ab"
+dependencies = [
+ "bstr",
+]
+
+[[package]]
+name = "gix-config"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fbad5ce54a8fc997acc50febd89ec80fa6e97cb7f8d0654cb229936407489d8"
+dependencies = [
+ "bstr",
+ "gix-config-value",
+ "gix-features",
+ "gix-glob",
+ "gix-path",
+ "gix-ref",
+ "gix-sec",
+ "log",
+ "memchr",
+ "nom",
+ "once_cell",
+ "smallvec",
+ "thiserror",
+ "unicode-bom",
+]
+
+[[package]]
+name = "gix-config-value"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d09154c0c8677e4da0ec35e896f56ee3e338e741b9599fae06075edd83a4081c"
+dependencies = [
+ "bitflags 1.3.2",
+ "bstr",
+ "gix-path",
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-credentials"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "750b684197374518ea057e0a0594713e07683faa0a3f43c0f93d97f64130ad8d"
+dependencies = [
+ "bstr",
+ "gix-command",
+ "gix-config-value",
+ "gix-path",
+ "gix-prompt",
+ "gix-sec",
+ "gix-url",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-date"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b96271912ce39822501616f177dea7218784e6c63be90d5f36322ff3a722aae2"
+dependencies = [
+ "bstr",
+ "itoa",
+ "thiserror",
+ "time",
+]
+
+[[package]]
+name = "gix-diff"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "103a0fa79b0d438f5ecb662502f052e530ace4fe1fe8e1c83c0c6da76d728e67"
+dependencies = [
+ "gix-hash",
+ "gix-object",
+ "imara-diff",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-discover"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eba8ba458cb8f4a6c33409b0fe650b1258655175a7ffd1d24fafd3ed31d880b"
+dependencies = [
+ "bstr",
+ "dunce",
+ "gix-hash",
+ "gix-path",
+ "gix-ref",
+ "gix-sec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-features"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b76f9a80f6dd7be66442ae86e1f534effad9546676a392acc95e269d0c21c22"
+dependencies = [
+ "crc32fast",
+ "flate2",
+ "gix-hash",
+ "libc",
+ "once_cell",
+ "prodash",
+ "sha1_smol",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "gix-glob"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93e43efd776bc543f46f0fd0ca3d920c37af71a764a16f2aebd89765e9ff2993"
+dependencies = [
+ "bitflags 1.3.2",
+ "bstr",
+]
+
+[[package]]
+name = "gix-hash"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0c5a9f4d621d4f4ea046bb331df5c746ca735b8cae5b234cc2be70ee4dbef0"
+dependencies = [
+ "hex",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-hashtable"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9609c1b8f36f12968e6a6098f7cdb52004f7d42d570f47a2d6d7c16612f19acb"
+dependencies = [
+ "gix-hash",
+ "hashbrown 0.13.2",
+ "parking_lot",
+]
+
+[[package]]
+name = "gix-index"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "717ab601ece7921f59fe86849dbe27d44a46ebb883b5885732c4f30df4996177"
+dependencies = [
+ "bitflags 1.3.2",
+ "bstr",
+ "btoi",
+ "filetime",
+ "gix-bitmap",
+ "gix-features",
+ "gix-hash",
+ "gix-lock",
+ "gix-object",
+ "gix-traverse",
+ "itoa",
+ "memmap2",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-lock"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41b80172055c5d8017a48ddac5cc7a95421c00211047db0165c97853c4f05194"
+dependencies = [
+ "fastrand",
+ "gix-tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-mailmap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b66aea5e52875cd4915f4957a6f4b75831a36981e2ec3f5fad9e370e444fe1a"
+dependencies = [
+ "bstr",
+ "gix-actor",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-object"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df068db9180ee935fbb70504848369e270bdcb576b05c0faa8b9fd3b86fc017"
+dependencies = [
+ "bstr",
+ "btoi",
+ "gix-actor",
+ "gix-features",
+ "gix-hash",
+ "gix-validate",
+ "hex",
+ "itoa",
+ "nom",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-odb"
+version = "0.43.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83af2e3e36005bfe010927f0dff41fb5acc3e3d89c6f1174135b3a34086bda2"
+dependencies = [
+ "arc-swap",
+ "gix-features",
+ "gix-hash",
+ "gix-object",
+ "gix-pack",
+ "gix-path",
+ "gix-quote",
+ "parking_lot",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-pack"
+version = "0.33.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9401911c7fe032ad7b31c6a6b5be59cb283d1d6c999417a8215056efe6d635f3"
+dependencies = [
+ "clru",
+ "gix-chunk",
+ "gix-diff",
+ "gix-features",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "gix-path",
+ "gix-tempfile",
+ "gix-traverse",
+ "memmap2",
+ "parking_lot",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-path"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32370dce200bb951df013e03dff35b4233fc7a89458642b047629b91734a7e19"
+dependencies = [
+ "bstr",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-prompt"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f3034d4d935aef2c7bf719aaa54b88c520e82413118d886ae880a31d5bdee57"
+dependencies = [
+ "gix-command",
+ "gix-config-value",
+ "nix",
+ "parking_lot",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-quote"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a282f5a8d9ee0b09ec47390ac727350c48f2f5c76d803cd8da6b3e7ad56e0bcb"
+dependencies = [
+ "bstr",
+ "btoi",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-ref"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e909396ed3b176823991ccc391c276ae2a015e54edaafa3566d35123cfac9d"
+dependencies = [
+ "gix-actor",
+ "gix-features",
+ "gix-hash",
+ "gix-lock",
+ "gix-object",
+ "gix-path",
+ "gix-tempfile",
+ "gix-validate",
+ "memmap2",
+ "nom",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-refspec"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aba332462bda2e8efeae4302b39a6ed01ad56ef772fd5b7ef197cf2798294d65"
+dependencies = [
+ "bstr",
+ "gix-hash",
+ "gix-revision",
+ "gix-validate",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-revision"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b12fc4bbc3161a5b2d68079fce93432cef8771ff88ca017abb01187fddfc41a1"
+dependencies = [
+ "bstr",
+ "gix-date",
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-sec"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8ffa5bf0772f9b01de501c035b6b084cf9b8bb07dec41e3afc6a17336a65f47"
+dependencies = [
+ "bitflags 1.3.2",
+ "dirs",
+ "gix-path",
+ "libc",
+ "windows 0.43.0",
+]
+
+[[package]]
+name = "gix-tempfile"
+version = "5.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2ceb30a610e3f5f2d5f9a5114689fde507ba9417705a8cf3429604275b2153c"
+dependencies = [
+ "libc",
+ "once_cell",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-registry",
+ "tempfile",
+]
+
+[[package]]
+name = "gix-traverse"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd9a4a07bb22168dc79c60e1a6a41919d198187ca83d8a5940ad8d7122a45df3"
+dependencies = [
+ "gix-hash",
+ "gix-hashtable",
+ "gix-object",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-url"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6a22b4b32ad14d68f7b7fb6458fa58d44b01797d94c1b8f4db2d9c7b3c366b5"
+dependencies = [
+ "bstr",
+ "gix-features",
+ "gix-path",
+ "home",
+ "thiserror",
+ "url",
+]
+
+[[package]]
+name = "gix-validate"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd629d3680773e1785e585d76fd4295b740b559cad9141517300d99a0c8c049"
+dependencies = [
+ "bstr",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-worktree"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54ec9a000b4f24af706c3cc680c7cda235656cbe3216336522f5692773b8a301"
+dependencies = [
+ "bstr",
+ "gix-attributes",
+ "gix-features",
+ "gix-glob",
+ "gix-hash",
+ "gix-index",
+ "gix-object",
+ "gix-path",
+ "io-close",
+ "thiserror",
+]
+
+[[package]]
+name = "globset"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "fnv",
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "grep-matcher"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3902ca28f26945fe35cad349d776f163981d777fee382ccd6ef451126f51b319"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "grep-regex"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "997598b41d53a37a2e3fc5300d5c11d825368c054420a9c65125b8fe1078463f"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "grep-matcher",
+ "log",
+ "regex",
+ "regex-syntax",
+ "thread_local",
+]
+
+[[package]]
+name = "grep-searcher"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5601c4b9f480f0c9ebb40b1f6cbf447b8a50c5369223937a6c5214368c58779f"
+dependencies = [
+ "bstr",
+ "bytecount",
+ "encoding_rs",
+ "encoding_rs_io",
+ "grep-matcher",
+ "log",
+ "memmap2",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash 0.7.6",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash 0.8.3",
+]
+
+[[package]]
+name = "helix-core"
+version = "0.6.0"
+dependencies = [
+ "ahash 0.8.3",
+ "arc-swap",
+ "bitflags 2.0.2",
+ "chrono",
+ "dunce",
+ "encoding_rs",
+ "etcetera",
+ "hashbrown 0.13.2",
+ "helix-loader",
+ "imara-diff",
+ "indoc",
+ "log",
+ "once_cell",
+ "quickcheck",
+ "regex",
+ "ropey",
+ "serde",
+ "serde_json",
+ "slotmap",
+ "smallvec",
+ "smartstring",
+ "textwrap",
+ "toml",
+ "tree-sitter",
+ "unicode-general-category",
+ "unicode-segmentation",
+ "unicode-width",
+]
+
+[[package]]
+name = "helix-dap"
+version = "0.6.0"
+dependencies = [
+ "anyhow",
+ "fern",
+ "helix-core",
+ "log",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "which",
+]
+
+[[package]]
+name = "helix-loader"
+version = "0.6.0"
+dependencies = [
+ "anyhow",
+ "cc",
+ "etcetera",
+ "libloading",
+ "log",
+ "once_cell",
+ "serde",
+ "threadpool",
+ "toml",
+ "tree-sitter",
+]
+
+[[package]]
+name = "helix-lsp"
+version = "0.6.0"
+dependencies = [
+ "anyhow",
+ "futures-executor",
+ "futures-util",
+ "helix-core",
+ "helix-loader",
+ "helix-parsec",
+ "log",
+ "lsp-types",
+ "parking_lot",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "which",
+]
+
+[[package]]
+name = "helix-parsec"
+version = "0.6.0"
+
+[[package]]
+name = "helix-term"
+version = "0.6.0"
+dependencies = [
+ "anyhow",
+ "arc-swap",
+ "chrono",
+ "content_inspector",
+ "crossterm",
+ "fern",
+ "futures-util",
+ "fuzzy-matcher",
+ "grep-regex",
+ "grep-searcher",
+ "helix-core",
+ "helix-dap",
+ "helix-loader",
+ "helix-lsp",
+ "helix-tui",
+ "helix-vcs",
+ "helix-view",
+ "ignore",
+ "indoc",
+ "libc",
+ "log",
+ "once_cell",
+ "pulldown-cmark",
+ "serde",
+ "serde_json",
+ "signal-hook",
+ "signal-hook-tokio",
+ "smallvec",
+ "tempfile",
+ "tokio",
+ "tokio-stream",
+ "toml",
+ "which",
+]
+
+[[package]]
+name = "helix-tui"
+version = "0.6.0"
+dependencies = [
+ "bitflags 2.0.2",
+ "cassowary",
+ "crossterm",
+ "helix-core",
+ "helix-view",
+ "log",
+ "once_cell",
+ "serde",
+ "termini",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "helix-vcs"
+version = "0.6.0"
+dependencies = [
+ "anyhow",
+ "arc-swap",
+ "gix",
+ "helix-core",
+ "imara-diff",
+ "log",
+ "parking_lot",
+ "tempfile",
+ "tokio",
+]
+
+[[package]]
+name = "helix-view"
+version = "0.6.0"
+dependencies = [
+ "anyhow",
+ "arc-swap",
+ "bitflags 2.0.2",
+ "chardetng",
+ "clipboard-win",
+ "crossterm",
+ "futures-util",
+ "helix-core",
+ "helix-dap",
+ "helix-loader",
+ "helix-lsp",
+ "helix-tui",
+ "helix-vcs",
+ "libc",
+ "log",
+ "once_cell",
+ "parking_lot",
+ "serde",
+ "serde_json",
+ "slotmap",
+ "tokio",
+ "tokio-stream",
+ "toml",
+ "url",
+ "which",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "home"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "716f12fbcfac6ffab0a5e9ec51d0a0ff70503742bb2dc7b99396394c9dc323f0"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows 0.47.0",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
+[[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "ignore"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
+dependencies = [
+ "globset",
+ "lazy_static",
+ "log",
+ "memchr",
+ "regex",
+ "same-file",
+ "thread_local",
+ "walkdir",
+ "winapi-util",
+]
+
+[[package]]
+name = "imara-diff"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8"
+dependencies = [
+ "ahash 0.8.3",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "indoc"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690"
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-close"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "js-sys"
+version = "0.3.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.140"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d"
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "lsp-types"
+version = "0.94.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237"
+dependencies = [
+ "bitflags 1.3.2",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "url",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memmap2"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
+dependencies = [
+ "libc",
+]
+
+[[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.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+ "static_assertions",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec",
+ "windows-sys",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prodash"
+version = "23.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9516b775656bc3e8985e19cd4b8c0c0de045095074e453d2c0a513b5f978392d"
+
+[[package]]
+name = "pulldown-cmark"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
+dependencies = [
+ "bitflags 1.3.2",
+ "memchr",
+ "unicase",
+]
+
+[[package]]
+name = "quickcheck"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "rand",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall 0.2.16",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "ropey"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53ce7a2c43a32e50d666e33c5a80251b31147bb4b49024bcab11fb6f20c671ed"
+dependencies = [
+ "smallvec",
+ "str_indices",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75"
+dependencies = [
+ "bitflags 1.3.2",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "scratch"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
+
+[[package]]
+name = "serde"
+version = "1.0.159"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.159"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
+[[package]]
+name = "signal-hook"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signal-hook-tokio"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e"
+dependencies = [
+ "futures-core",
+ "libc",
+ "signal-hook",
+ "tokio",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "slotmap"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "smartstring"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
+dependencies = [
+ "autocfg",
+ "static_assertions",
+ "version_check",
+]
+
+[[package]]
+name = "smawk"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
+
+[[package]]
+name = "socket2"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "str-buf"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
+
+[[package]]
+name = "str_indices"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall 0.3.5",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "termini"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c0f7ecb9c2a380d2686a747e4fc574043712326e8d39fbd220ab3bd29768a12"
+dependencies = [
+ "dirs-next",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+dependencies = [
+ "smawk",
+ "unicode-linebreak",
+ "unicode-width",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "time"
+version = "0.3.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
+dependencies = [
+ "itoa",
+ "libc",
+ "num_threads",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+
+[[package]]
+name = "time-macros"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
+dependencies = [
+ "time-core",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
+dependencies = [
+ "autocfg",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "tree-sitter"
+version = "0.20.9"
+source = "git+https://github.com/tree-sitter/tree-sitter?rev=c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14#c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14"
+dependencies = [
+ "cc",
+ "regex",
+]
+
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
+name = "unicode-bom"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63ec69f541d875b783ca40184d655f2927c95f0bffd486faa83cd3ac3529ec32"
+
+[[package]]
+name = "unicode-general-category"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
+dependencies = [
+ "hashbrown 0.12.3",
+ "regex",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "url"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "walkdir"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+
+[[package]]
+name = "which"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2649ff315bee4c98757f15dac226efe3d81927adbb6e882084bb1ee3e0c330a7"
+dependencies = [
+ "windows-targets 0.47.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f8996d3f43b4b2d44327cd71b7b0efd1284ab60e6e9d0e8b630e18555d87d3e"
+dependencies = [
+ "windows_aarch64_gnullvm 0.47.0",
+ "windows_aarch64_msvc 0.47.0",
+ "windows_i686_gnu 0.47.0",
+ "windows_i686_msvc 0.47.0",
+ "windows_x86_64_gnu 0.47.0",
+ "windows_x86_64_gnullvm 0.47.0",
+ "windows_x86_64_msvc 0.47.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831d567d53d4f3cb1db332b68e6e2b6260228eb4d99a777d8b2e8ed794027c90"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a42d54a417c60ce4f0e31661eed628f0fa5aca73448c093ec4d45fab4c51cdf"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1925beafdbb22201a53a483db861a5644123157c1c3cee83323a2ed565d71e3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a8ef8f2f1711b223947d9b69b596cf5a4e452c930fb58b6fc3fdae7d0ec6b31"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7acaa0c2cf0d2ef99b61c308a0c3dbae430a51b7345dedec470bd8f53f5a3642"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a0628f71be1d11e17ca4a0e9e15b3a5180f6fbf1c2d55e3ba3f850378052c1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d6e62c256dc6d40b8c8707df17df8d774e60e39db723675241e7c15e910bce7"
+
+[[package]]
+name = "winnow"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "xtask"
+version = "0.6.0"
+dependencies = [
+ "helix-core",
+ "helix-loader",
+ "helix-term",
+ "helix-view",
+ "toml",
+]
diff --git a/nix/hosts/hacknix/pkgs/helix-2303/default.nix b/nix/hosts/hacknix/pkgs/helix-2303/default.nix
new file mode 100644
index 0000000..bdbc112
--- /dev/null
+++ b/nix/hosts/hacknix/pkgs/helix-2303/default.nix
@@ -0,0 +1,55 @@
+{ fetchzip, lib, rustPlatform, installShellFiles, makeWrapper }:
+
+rustPlatform.buildRustPackage rec {
+  pname = "helix";
+  version = "23.03";
+
+  # This release tarball includes source code for the tree-sitter grammars,
+  # which is not ordinarily part of the repository.
+  src = fetchzip {
+    url = "https://github.com/helix-editor/helix/releases/download/${version}/helix-${version}-source.tar.xz";
+    hash = "sha256-FtY2V7za3WGeUaC2t2f63CcDUEg9zAS2cGUWI0YeGwk=";
+    stripRoot = false;
+  };
+
+  # should be removed, when tree-sitter is not used as a git checkout anymore
+  cargoLock = {
+    lockFile = ./Cargo.lock;
+    outputHashes = {
+      "tree-sitter-0.20.9" = "sha256-/PaFaASOT0Z8FpipX5uiRCjnv1kyZtg4B9+TnHA0yTY=";
+    };
+  };
+  #cargoHash = "sha256-+KnBQA7gYLu2O/5vbY5cdEj9hni0Cn3cWPYswBi4934=";
+  cargoHash = lib.fakeHash;
+
+  cargoDeps = rustPlatform.importCargoLock {
+    lockFile = ./Cargo.lock;
+    outputHashes = {
+      "tree-sitter-0.20.9" = "sha256-/PaFaASOT0Z8FpipX5uiRCjnv1kyZtg4B9+TnHA0yTY=";
+    };
+  };
+
+  nativeBuildInputs = [ installShellFiles makeWrapper ];
+
+  postInstall = ''
+    # not needed at runtime
+    rm -r runtime/grammars/sources
+    mkdir -p $out/lib
+    cp -r runtime $out/lib
+    installShellCompletion contrib/completion/hx.{bash,fish,zsh}
+    mkdir -p $out/share/{applications,icons/hicolor/256x256/apps}
+    cp contrib/Helix.desktop $out/share/applications
+    cp contrib/helix.png $out/share/icons/hicolor/256x256/apps
+  '';
+  postFixup = ''
+    wrapProgram $out/bin/hx --set HELIX_RUNTIME $out/lib/runtime
+  '';
+
+  meta = with lib; {
+    description = "A post-modern modal text editor";
+    homepage = "https://helix-editor.com";
+    license = licenses.mpl20;
+    mainProgram = "hx";
+    maintainers = with maintainers; [ danth yusdacra ];
+  };
+}
diff --git a/nix/hosts/hacknix/pkgs/radare2-5.8.4/default.nix b/nix/hosts/hacknix/pkgs/radare2-5.8.4/default.nix
new file mode 100644
index 0000000..cbd6a56
--- /dev/null
+++ b/nix/hosts/hacknix/pkgs/radare2-5.8.4/default.nix
@@ -0,0 +1,118 @@
+{ lib
+, stdenv
+, fetchFromGitHub
+, fetchpatch
+, buildPackages
+, pkg-config
+, meson
+, ninja
+, libusb-compat-0_1
+, readline
+, libewf
+, perl
+, zlib
+, openssl
+, libuv
+, file
+, libzip
+, xxHash
+, gtk2
+, vte
+, gtkdialog
+, python3
+, ruby
+, lua
+, lz4
+, capstone
+, useX11 ? false
+, rubyBindings ? false
+, luaBindings ? false
+}:
+
+let
+  # FIXME: Compare revision with
+  # https://github.com/radareorg/radare2/blob/master/libr/arch/p/arm/v35/Makefile#L26-L27
+  arm64 = fetchFromGitHub {
+    owner = "radareorg";
+    repo = "vector35-arch-arm64";
+    rev = "55d73c6bbb94448a5c615933179e73ac618cf876";
+    hash = "sha256-pZxxp5xDg8mgkGEx7LaBSoKxNPyggFYA4um9YaO20LU=";
+  };
+  armv7 = fetchFromGitHub {
+    owner = "radareorg";
+    repo = "vector35-arch-armv7";
+    rev = "f270a6cc99644cb8e76055b6fa632b25abd26024";
+    hash = "sha256-YhfgJ7M8ys53jh1clOzj0I2yfJshXQm5zP0L9kMYsmk=";
+  };
+in
+stdenv.mkDerivation rec {
+  pname = "radare2";
+  version = "5.8.4";
+
+  src = fetchFromGitHub {
+    owner = "radare";
+    repo = "radare2";
+    rev = "refs/tags/${version}";
+    hash = "sha256-Fbluq3Q/BgPwTVNKW28FJL+Ok46hDiBjwFt6KwN4anc=";
+  };
+
+  preBuild = ''
+    pushd ../libr/arch/p/arm/v35
+    cp -r ${arm64} arch-arm64
+    chmod -R +w arch-arm64
+
+    cp -r ${armv7} arch-armv7
+    chmod -R +w arch-armv7
+    popd
+  '';
+
+  postFixup = lib.optionalString stdenv.isDarwin ''
+    install_name_tool -add_rpath $out/lib $out/lib/libr_io.${version}.dylib
+  '';
+
+  mesonFlags = [
+   "-Duse_sys_capstone=true"
+   "-Duse_sys_magic=true"
+   "-Duse_sys_zip=true"
+   "-Duse_sys_xxhash=true"
+   "-Duse_sys_lz4=true"
+   "-Dr2_gittap=${version}"
+  ];
+
+  enableParallelBuilding = true;
+  depsBuildBuild = [ buildPackages.stdenv.cc ];
+
+  strictDeps = true;
+
+  nativeBuildInputs = [ pkg-config meson ninja python3 ];
+  buildInputs = [
+    capstone
+    file
+    readline
+    libusb-compat-0_1
+    libewf
+    perl
+    zlib
+    openssl
+    libuv
+    lz4
+  ] ++ lib.optionals useX11 [ gtkdialog vte gtk2 ]
+    ++ lib.optionals rubyBindings [ ruby ]
+    ++ lib.optionals luaBindings [ lua ];
+
+  propagatedBuildInputs = [
+    # radare2 exposes r_lib which depends on these libraries
+    file # for its list of magic numbers (`libmagic`)
+    libzip
+    xxHash
+  ];
+
+  meta = with lib; {
+    description = "UNIX-like reverse engineering framework and command-line tools";
+    homepage = "https://radare.org";
+    changelog = "https://github.com/radareorg/radare2/releases/tag/${version}";
+    license = licenses.gpl2Plus;
+    maintainers = with maintainers; [ azahi raskin makefu mic92 arkivm ];
+    platforms = platforms.unix;
+  };
+}
diff --git a/nix/hosts/mail/configuration.nix b/nix/hosts/mail/configuration.nix
new file mode 100644
index 0000000..564025c
--- /dev/null
+++ b/nix/hosts/mail/configuration.nix
@@ -0,0 +1,126 @@
+# Edit this configuration file to define what should be installed on
+# your system.  Help is available in the configuration.nix(5) man page
+# and in the NixOS manual (accessible by running ‘nixos-help’).
+
+{ config, pkgs, ... }:
+
+{
+  imports =
+    [ # Include the results of the hardware scan.
+      ./hardware-configuration.nix
+      ./mail.nix
+    ];
+
+  # Use the GRUB 2 boot loader.
+  boot.loader.grub.enable = true;
+  # boot.loader.grub.version = 2;
+  # boot.loader.grub.efiSupport = true;
+  # boot.loader.grub.efiInstallAsRemovable = true;
+  # boot.loader.efi.efiSysMountPoint = "/boot/efi";
+  # Define on which hard drive you want to install Grub.
+  # boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
+
+  # networking.hostName = "nixos"; # Define your hostname.
+  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
+
+  # Set your time zone.
+  # time.timeZone = "Europe/Amsterdam";
+
+  # The global useDHCP flag is deprecated, therefore explicitly set to false here.
+  # Per-interface useDHCP will be mandatory in the future, so this generated config
+  # replicates the default behaviour.
+  networking.useDHCP = false;
+  networking.interfaces.ens3.useDHCP = true;
+
+  # Configure network proxy if necessary
+  # networking.proxy.default = "http://user:password@proxy:port/";
+  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
+
+  # Select internationalisation properties.
+  # i18n.defaultLocale = "en_US.UTF-8";
+  # console = {
+  #   font = "Lat2-Terminus16";
+  #   keyMap = "us";
+  # };
+
+  # Enable the X11 windowing system.
+  # services.xserver.enable = true;
+
+
+  
+
+  # Configure keymap in X11
+  # services.xserver.layout = "us";
+  # services.xserver.xkbOptions = "eurosign:e";
+
+  # Enable CUPS to print documents.
+  # services.printing.enable = true;
+
+  # Enable sound.
+  # sound.enable = true;
+  # hardware.pulseaudio.enable = true;
+
+  # Enable touchpad support (enabled default in most desktopManager).
+  # services.xserver.libinput.enable = true;
+
+  # Define a user account. Don't forget to set a password with ‘passwd’.
+  # users.users.jane = {
+  #   isNormalUser = true;
+  #   extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
+  # };
+
+  # List packages installed in system profile. To search, run:
+  # $ nix search wget
+  # environment.systemPackages = with pkgs; [
+  #   vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
+  #   wget
+  #   firefox
+  # ];
+
+  # Some programs need SUID wrappers, can be configured further or are
+  # started in user sessions.
+  # programs.mtr.enable = true;
+  # programs.gnupg.agent = {
+  #   enable = true;
+  #   enableSSHSupport = true;
+  # };
+
+  # List services that you want to enable:
+
+  # Enable the OpenSSH daemon.
+  # services.openssh.enable = true;
+
+  # Open ports in the firewall.
+  # networking.firewall.allowedTCPPorts = [ ... ];
+  # networking.firewall.allowedUDPPorts = [ ... ];
+  # Or disable the firewall altogether.
+  # networking.firewall.enable = false;
+
+  # This value determines the NixOS release from which the default
+  # settings for stateful data, like file locations and database versions
+  # on your system were taken. It‘s perfectly fine and recommended to leave
+  # this value at the release version of the first install of this system.
+  # Before changing this value read the documentation for this option
+  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
+  system.stateVersion = "21.11"; # Did you read the comment?
+
+
+
+
+  boot.loader.grub.devices = [ "/dev/sda" ];
+
+  # Initial empty root password for easy login:
+  users.users.root.initialHashedPassword = "";
+  services.openssh.settings.PermitRootLogin = "prohibit-password";
+
+  services.openssh.enable = true;
+
+  users.users.root.openssh.authorizedKeys.keys = [
+    # Replace this by your SSH pubkey!
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew"
+  ];
+
+  security.acme.acceptTerms = true;
+  security.acme.certs."mail.emile.space".email = "security@emile.space";
+}
+
diff --git a/nix/hosts/mail/hardware-configuration.nix b/nix/hosts/mail/hardware-configuration.nix
new file mode 100644
index 0000000..2653297
--- /dev/null
+++ b/nix/hosts/mail/hardware-configuration.nix
@@ -0,0 +1,24 @@
+# Do not modify this file!  It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations.  Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports =
+    [ (modulesPath + "/profiles/qemu-guest.nix")
+    ];
+
+  boot.initrd.availableKernelModules = [ "ata_piix" "virtio_pci" "virtio_scsi" "xhci_pci" "sd_mod" "sr_mod" ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" =
+    { device = "/dev/disk/by-uuid/eccc47d3-7cee-4af5-822a-4ae0b302cb10";
+      fsType = "ext4";
+    };
+
+  swapDevices = [ ];
+
+  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/nix/hosts/mail/mail.nix b/nix/hosts/mail/mail.nix
new file mode 100644
index 0000000..4224e04
--- /dev/null
+++ b/nix/hosts/mail/mail.nix
@@ -0,0 +1,50 @@
+{ ... }:
+let
+  release = "nixos-23.05";
+in {
+  imports = [
+    (builtins.fetchTarball {
+      # Pick a commit from the branch you are interested in
+      url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz";
+      # And set its hash
+      sha256 = "1ngil2shzkf61qxiqw11awyl81cr7ks2kv3r3k243zz7v2xakm5c";
+    })
+  ];
+
+  mailserver = {
+    enable = true;
+    fqdn = "mail.emile.space";
+    domains = [ "emile.space" ];
+
+    # A list of all login accounts. To create the password hashes, use
+    # nix run nixpkgs.apacheHttpd -c htpasswd -nbB "" "super secret password" | cut -d: -f2
+    loginAccounts = {
+        "mail@emile.space" = {
+            hashedPasswordFile = "/etc/nixos/keys/mail";
+            aliases = ["@emile.space"];
+        };
+    };
+
+    localDnsResolver = false;
+
+    # Use Let's Encrypt certificates. Note that this needs to set up a stripped
+    # down nginx and opens port 80.
+    #certificateScheme = 3;
+    certificateScheme = "acme-nginx";
+
+    # Enable IMAP and POP3
+    enableImap = true;
+    enablePop3 = true;
+    enableSubmission = true;
+
+    # as well with ssl
+    enableImapSsl = true;
+    enablePop3Ssl = true;
+    enableSubmissionSsl = true;
+
+    enableManageSieve = true;
+
+    virusScanning = false;
+
+  };
+}
diff --git a/nix/lib/default.nix b/nix/lib/default.nix
new file mode 100644
index 0000000..99a47fc
--- /dev/null
+++ b/nix/lib/default.nix
@@ -0,0 +1,22 @@
+# original:
+# https://git.clerie.de/clerie/nixfiles/src/branch/master/lib/default.nix
+
+inputs:
+
+let
+	callLibs = file: import file ({
+		inherit lib inputs;
+	} // inputs);
+
+	lib = {
+		flake-helper = callLibs ./flake-helper.nix;
+		inherit ("flake-helper")
+			generateSystem
+			mapToNixosConfigurations
+			mapToDarwinConfigurations
+			generateDeployRsHost
+			mapToDeployRsConfiguration
+			buildHosts;
+	};
+in
+	lib
diff --git a/nix/lib/flake-helper.nix b/nix/lib/flake-helper.nix
new file mode 100644
index 0000000..aa1048a
--- /dev/null
+++ b/nix/lib/flake-helper.nix
@@ -0,0 +1,158 @@
+{ self, agenix, nixpkgs, nixpkgs-unstable, deploy-rs, home-manager, darwin, ... }@inputs:
+
+rec {
+	generateSystem = name: {
+		hostname ? name,
+		username ? "emile",
+		system ? "x86_64-linux",
+		deployUser ? "root",
+		homeManagerEnable ? false,
+		group ? null,
+		modules ? [],
+		...
+	}:
+	let
+
+		# inputs.nixpkgs-${name}, if that doesn't exist, just use nixpkgs
+		localNixpkgs =
+			nixpkgs.lib.attrByPath
+				[ "nixpkgs-${name}" ] # path
+				nixpkgs # default
+				inputs; # base
+
+		# determine if our system type that is used further down
+		systemType =
+			if system == "x86_64-linux" then localNixpkgs.lib.nixosSystem
+			else
+				if system == "aarch64-darwin" then darwin.lib.darwinSystem
+				else null;
+		
+	in systemType { # this may fail if we aren't using x86_64-linux or aarch64-darwin
+		inherit system;
+
+		# ; nix repl
+		# nix-repl> :lf .
+		# nix-repl> nixosConfigurations.corrino._module.args.modules
+
+		modules = modules ++ [
+
+			# a module so that we can access the flake output from inside the
+			# flake (yes, I need this for fetching the system type while building the hosts for deploy-rs)
+			{ config._module.args = { flake = self; }; }
+
+			# overlays
+			({ ... }: {
+				nixpkgs.overlays = [
+					self.overlays.emile
+					(_: _: { inherit (agenix.packages."x86_64-linux") agenix; })
+					(_: _: {
+						unstable = import nixpkgs-unstable {
+							system = "x86_64-linux";
+							config.allowUnfree = true;
+						};
+					})
+				];
+			})
+
+			# general modules
+			agenix.nixosModules.default
+
+			# # the host config itself
+			(../hosts +
+				(if (system == "x86_64-linux")
+				 then "/${name}/configuration.nix"
+				 else
+					if (system == "aarch64-darwin")
+					then "/${name}/darwin-configuration.nix"
+					else ""))
+
+			# secrets (have to be added to git (crypted) #lessonslearned)
+			({ lib, ... }: let
+				secretsPath = (../hosts + "/${name}/secrets");
+			in {
+				age.secrets = lib.mapAttrs'
+					(filename: _:
+						lib.nameValuePair (lib.removeSuffix ".age" filename)
+						{ file = secretsPath + "/${filename}"; }
+					)
+					(lib.filterAttrs
+						(name: type:
+							(type == "regular") &&
+							(lib.hasSuffix ".age" name) )
+						(if builtins.pathExists secretsPath
+						 then builtins.readDir secretsPath
+						 else {} )
+					);
+			})
+		]
+		
+		++ (if (system == "aarch64-darwin")
+			then [ (home-manager.darwinModules.home-manager) ]
+			else [])
+			
+		++ (if (homeManagerEnable == true)
+			then [{
+				home-manager = {
+					useGlobalPkgs = true;
+					users."${username}" =
+						import (../hosts + "/${hostname}/home_${username}.nix");
+				};
+			}]
+			else []);
+	};
+
+	mapToNixosConfigurations = { system ? "x86_64-linux", ... }@hosts:
+		builtins.mapAttrs
+		(name: host: generateSystem name host)
+		(nixpkgs.lib.filterAttrs
+			(n: v: v.system or "" == "x86_64-linux") hosts);
+
+	mapToDarwinConfigurations = hosts:
+		builtins.mapAttrs
+		(name: host: generateSystem name host)
+		(nixpkgs.lib.filterAttrs
+			(n: v: v.system or "" == "aarch64-darwin") hosts);
+
+	generateDeployRsHost = name: {
+		hostname ? name,
+		ip ? "${name}.pinto-pike.ts.net",
+		sshUser ? "root",
+		system ? "x86_64-linux",
+		...
+	}: {
+		reboteBuild = true;
+		hostname = "${ip}";
+		fastConnection = true;
+		profiles.system = {
+			user = "root"; # user to install as
+			sshUser = sshUser; # user to ssh to as
+
+			# make sure people can use sudo 
+			# sshOpts = ["-A", "-t", "-S"];
+
+			# make sure to add the nix foo on the darwin hosts to ~/.zshenv
+			# as the ~/.zshrc doesn't get sourced when ssh-ing into the system
+
+			path = (if system == "x86_64-linux"
+				 then deploy-rs.lib.x86_64-linux.activate.nixos
+					self.nixosConfigurations."${name}"
+				 else
+					if system == "aarch64-darwin"
+					then deploy-rs.lib.aarch64-darwin.activate.darwin
+						self.darwinConfigurations."${name}"
+					else "");
+		};
+	};
+
+	mapToDeployRsConfiguration = hosts:
+		builtins.mapAttrs (name: host: generateDeployRsHost name host) hosts;
+	
+	buildHosts = hosts:
+		builtins.mapAttrs (name: host: host.config.system.build.toplevel)
+
+		# don't build hosts that start with an underscore
+		(nixpkgs.lib.filterAttrs
+			(name: host: (builtins.substring 0 1 name) != "_")
+			hosts
+		);
+}
diff --git a/nix/lib/sec.nix b/nix/lib/sec.nix
new file mode 100644
index 0000000..5469f3d
--- /dev/null
+++ b/nix/lib/sec.nix
@@ -0,0 +1,25 @@
+{ pkgs ? import <nixpkgs> {} }:
+
+let
+a = name:
+	let
+		secretsPath = ../hosts + "/${name}/secrets";
+	in {
+		age.secrets = pkgs.lib.mapAttrs'
+			(filename: _:
+				pkgs.lib.nameValuePair (pkgs.lib.removeSuffix ".age" filename)
+				{
+					file = secretsPath + "/${filename}";
+				}
+			)
+			(pkgs.lib.filterAttrs
+				(name: type:
+					(type == "regular") &&
+					(pkgs.lib.hasSuffix ".age" name) )
+				(if builtins.pathExists secretsPath
+				 then builtins.readDir secretsPath
+				 else {} )
+			);
+	};
+in
+{ b = a "corrino"; }
diff --git a/nix/pkgs/overlay.nix b/nix/pkgs/overlay.nix
new file mode 100644
index 0000000..11531f2
--- /dev/null
+++ b/nix/pkgs/overlay.nix
@@ -0,0 +1,4 @@
+final: prev: {
+	vokobe = final.callPackage ../../web/vokobe { inherit (final) naersk; };
+}
+
diff --git a/nix/templates/ctf/flake.nix b/nix/templates/ctf/flake.nix
new file mode 100644
index 0000000..de6e2c1
--- /dev/null
+++ b/nix/templates/ctf/flake.nix
@@ -0,0 +1,47 @@
+{
+  description = "ctf";
+  nixConfig.bash-prompt = "\[ctf\]; ";
+
+  inputs = {
+    nixpkgs.url = "github:NixOS/nixpkgs";
+  };
+
+  # Flake outputs
+  outputs = { self, nixpkgs }:
+    let
+      # Systems supported
+      allSystems = [
+        "x86_64-linux" # 64-bit Intel/AMD Linux
+        "aarch64-linux" # 64-bit ARM Linux
+        "x86_64-darwin" # 64-bit Intel macOS
+        "aarch64-darwin" # 64-bit ARM macOS
+      ];
+
+      # Helper to provide system-specific attributes
+      nameValuePair = name: value: { inherit name value; };
+      genAttrs = names: f: builtins.listToAttrs (map (n: nameValuePair n (f n)) names);
+      forAllSystems = f: genAttrs allSystems (system: f {
+        pkgs = import nixpkgs { inherit system; };
+      });
+    in
+    {
+      # Development environment output
+      devShells = forAllSystems ({ pkgs }: {
+        default =
+          let
+            python = pkgs.python311; # Use Python 3.11
+          in
+          pkgs.mkShell {
+            packages = with pkgs; [
+              qemu
+            ] ++ [
+              # Python plus helper tools
+              (python.withPackages (ps: with ps; [
+                pwntools
+                pycryptodome
+              ]))
+            ];
+          };
+      });
+    };
+}
diff --git a/nix/users/emile/default.nix b/nix/users/emile/default.nix
new file mode 100644
index 0000000..e60d851
--- /dev/null
+++ b/nix/users/emile/default.nix
@@ -0,0 +1,13 @@
+{
+  # users.users.emile = {
+  #   isNormalUser = true;
+  #   extraGroups = [
+  #     "wheel"
+  #   ];
+  #   openssh.authorizedKeys.keys = [
+  #     (builtins.readFile ./ssh.pub)
+  #   ];
+  # };
+
+  sshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew";
+}
diff --git a/nix/users/emile/ssh.pub b/nix/users/emile/ssh.pub
new file mode 100644
index 0000000..e68dee1
--- /dev/null
+++ b/nix/users/emile/ssh.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew
diff --git a/secrets.nix b/secrets.nix
new file mode 100644
index 0000000..1a625d3
--- /dev/null
+++ b/secrets.nix
@@ -0,0 +1,59 @@
+# { pkgs ? import <nixpkgs> {} }:
+
+# taken from
+# https://git.clerie.de/clerie/nixfiles/src/branch/master/secrets.nix
+
+# nix eval --impure --expr 'import ./secrets.nix'
+
+let
+	pubkeysFor = directory:
+		let
+			instances = builtins.attrNames (builtins.readDir directory);
+			instancesWithPubkey = builtins.filter (i: builtins.pathExists (directory + "/${i}/ssh.pub")) instances; 
+		in
+			builtins.listToAttrs (
+				# map (i: { name = i; value = builtins.readFile (directory + "/${i}/ssh.pub"); }
+				map (i: {
+					name = i;
+					value = (import (directory + "/${i}/")).sshKey;
+				}
+			) instancesWithPubkey);
+
+	hosts = pubkeysFor ./nix/hosts;
+	users = pubkeysFor ./nix/users;
+
+	secretsForHost = hostname: let
+
+		secretFiles = builtins.attrNames
+			(builtins.readDir (./nix/hosts + "/${hostname}/secrets"));
+	
+		listOfSecrets = builtins.filter (i:
+			(builtins.stringLength i) > 4
+			&& builtins.substring ((builtins.stringLength i) - 4)
+				(builtins.stringLength i) i == ".age"
+		) secretFiles;
+
+	in
+		if
+			builtins.pathExists (./nix/hosts + "/${hostname}/secrets")
+			&& builtins.pathExists (./nix/hosts + "/${hostname}/ssh.pub")
+		then
+			map
+				(secret: {
+					name = "nix/hosts/${hostname}/secrets/${secret}";
+					value = {
+						publicKeys = [
+							users.emile
+							hosts."${hostname}"
+						];
+					};
+				})
+				(listOfSecrets ++ [ "new" ])
+		else
+			[];
+in
+	builtins.listToAttrs (
+		builtins.concatMap
+			(hostname: secretsForHost hostname)
+			(builtins.attrNames (builtins.readDir ./nix/hosts))
+	)
diff --git a/web/vokobe/.gitignore b/web/vokobe/.gitignore
new file mode 100644
index 0000000..b774d54
--- /dev/null
+++ b/web/vokobe/.gitignore
@@ -0,0 +1,7 @@
+# the cargo build artefacts
+debug/
+target/
+
+# the nix result symlink
+result
+
diff --git a/web/vokobe/Cargo.lock b/web/vokobe/Cargo.lock
new file mode 100644
index 0000000..cdf64e1
--- /dev/null
+++ b/web/vokobe/Cargo.lock
@@ -0,0 +1,270 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "structopt"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
+dependencies = [
+ "clap",
+ "lazy_static",
+ "structopt-derive",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "vokobe"
+version = "0.1.3"
+dependencies = [
+ "regex",
+ "structopt",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/web/vokobe/Cargo.toml b/web/vokobe/Cargo.toml
new file mode 100644
index 0000000..9c01d4e
--- /dev/null
+++ b/web/vokobe/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "vokobe"
+version = "0.1.3"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+regex = "1.10.3"
+structopt = "0.3"
diff --git a/web/vokobe/LICENSE b/web/vokobe/LICENSE
new file mode 100644
index 0000000..cb5d6ff
--- /dev/null
+++ b/web/vokobe/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Emile Hansmaennel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/web/vokobe/README.md b/web/vokobe/README.md
new file mode 100644
index 0000000..c2c54a9
--- /dev/null
+++ b/web/vokobe/README.md
@@ -0,0 +1,101 @@
+# Vokobe
+
+A minimal static site generator tailored to my needs.
+
+CI: [https://hydra.emile.space/project/vokobe](https://hydra.emile.space/project/vokobe)
+
+## Build
+
+```bash
+; cargo build --release
+```
+    
+## Usage/Examples
+
+```bash
+; ./target/release/vokobe --help
+vokobe 0.1.0
+A static site generator
+
+USAGE:
+    vokobe [FLAGS] <in-path> <out-path> <site-name>
+
+FLAGS:
+    -a, --analytics    Activate sending analytics to stats.emile.space
+    -h, --help         Prints help information
+    -V, --version      Prints version information
+
+ARGS:
+    <in-path>      Input path
+    <out-path>     Output path
+    <site-name>    Site name (e.g. emile.space)
+```
+
+
+## Deployment
+
+The following subsections contain some example for small shell scripts that might be useful for Deployment.
+
+### build.sh
+
+Remove the output dir, build it from scratch and update the perms.
+
+I'm actually considering rebuilding vokobe with incremental builds in mind, as it can take a bit to create some really large projects.
+
+```bash
+rm -rf out/
+vokobe -a ./in ./out emile.space
+chmod -R +r out/
+```
+
+### sync.sh
+
+Syncronize the generated output to the remote host for hosting it.
+
+```bash
+rsync -avz --delete <out-path>/* <user>@<host>:<path>
+```
+
+### publish.sh
+
+Build and Syncronize.
+
+```bash
+./build.sh
+./sync.sh
+```
+
+### host.sh
+
+Host the local version
+
+```bash
+python3 -m http.server 8081 -d <outpath>/ -b 0.0.0.0
+```
+
+### watchbuild.sh
+
+rebuild on changes
+
+```bash
+#! /usr/bin/env nix-shell
+#! nix-shell -i bash -p fd entr
+
+while sleep 0.5; do
+  fd . in | entr -d ./build.sh
+done
+```
+
+### local.sh
+
+run a script updating it on changes and one hosting the output.
+
+```bash
+sh ./watchbuild.sh &
+sh ./host.sh
+```
+
+
+## Contributing
+
+Send patches!
diff --git a/web/vokobe/default.nix b/web/vokobe/default.nix
new file mode 100644
index 0000000..7257962
--- /dev/null
+++ b/web/vokobe/default.nix
@@ -0,0 +1,16 @@
+{ pkgs, naersk, ... }:
+
+let
+	naersk' = pkgs.callPackage naersk {};
+in naersk'.buildPackage {
+	src = ./.;
+
+	meta = with pkgs.lib; {
+		description = "A minimal static site generator tailored to my needs.";
+		homepage    = "https://git.emile.space/hanemile/vokobe";
+		license     = licenses.mit;
+		platforms   = platforms.all;
+		maintainers = with maintainers; [ hanemile ];
+	};
+}
+
diff --git a/web/vokobe/flaaaaake.nix b/web/vokobe/flaaaaake.nix
new file mode 100644
index 0000000..7cf2f03
--- /dev/null
+++ b/web/vokobe/flaaaaake.nix
@@ -0,0 +1,44 @@
+{
+  inputs = {
+    flake-utils.url = "github:numtide/flake-utils";
+    naersk.url = "github:nix-community/naersk";
+    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+  };
+
+  outputs = { self, flake-utils, naersk, nixpkgs }:
+    let
+      pkgs = (import nixpkgs) {
+        system = "x86_64-linux";
+      };
+
+      naersk' = pkgs.callPackage naersk {};
+      
+    in rec {
+      packages."x86_64-linux".vokobe = naersk'.buildPackage {
+        src = ./.;
+
+        meta = with pkgs.lib; {
+          description = "A minimal static site generator tailored to my needs.";
+          homepage    = "https://git.emile.space/hanemile/vokobe";
+          license     = licenses.mit;
+          platforms   = platforms.all;
+          maintainers = with maintainers; [
+            hanemile
+          ];
+        };
+      };
+    
+      # For `nix build` & `nix run`:
+      defaultPackage = packages."x86_64-linux".vokobe;
+
+      # For `nix develop` (optional, can be skipped):
+      devShell = pkgs.mkShell {
+        nativeBuildInputs = with pkgs; [ rustc cargo ];
+      };
+
+      # hydraJobs."<attr>"."<system>" = derivation;
+      hydraJobs = {
+        build."x86_64-linux" = packages."x86_64-linux".vokobe;
+      };
+    };
+}
\ No newline at end of file
diff --git a/web/vokobe/src/main.rs b/web/vokobe/src/main.rs
new file mode 100644
index 0000000..ab26457
--- /dev/null
+++ b/web/vokobe/src/main.rs
@@ -0,0 +1,922 @@
+/*
+pull the std into scope and inline it so that we get documentation for it,
+even when running offline
+*/
+#[doc(inline)]
+pub use std;
+
+use std::path::{Path, PathBuf};
+use std::io::{self, Read, Write, BufRead, BufReader};
+use std::fs::{self, File};
+use std::time;
+use std::collections::HashMap;
+
+use structopt::StructOpt;
+use regex::Regex;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "vokobe", about = "A static site generator")]
+struct Opt {
+    /// Input path 
+    #[structopt(parse(from_os_str))]
+    input_path: PathBuf,
+
+    /// Output path
+    #[structopt(parse(from_os_str))]
+    output_path: PathBuf,
+
+    /// Site name (e.g. emile.space)
+    site_name: String,
+
+    /// Activate sending analytics to stats.emile.space
+    // -a and --analytics will be generated
+    // analytics are sent to stats.emile.space
+    #[structopt(short, long)]
+    analytics: bool,
+}
+
+fn main() -> std::io::Result<()> {
+
+    let mut internal_links: HashMap<String, Vec<String>> = HashMap::new();
+
+    let opt = Opt::from_args();
+
+    let in_path = opt.input_path;
+    let output_path = opt.output_path;
+
+    // read the style
+    let style_path = Path::new(&in_path).join("style.css");
+    let mut style_file = File::open(style_path)
+        .expect("could not open style file");
+    let mut style = String::new();
+    style_file.read_to_string(&mut style)
+        .expect("could not read style file to string");
+
+    // read all dirs in the input path
+    let pathes = recursive_read_dir(&in_path, false)?;
+
+    // pass 1: store the backlinks
+
+    for path in &pathes {
+        if path.ends_with("README.md") {
+            // open the file and read it as a string
+            let mut readme_file = File::open(path)?;
+            let mut readme = String::new();
+            readme_file.read_to_string(&mut readme)?;
+
+            let internal_links_in_file
+                = parse_internal_links(readme.as_str());
+
+            for link in internal_links_in_file {
+
+                internal_links.entry(link).or_insert_with(Vec::new).push(path.to_string_lossy().into_owned())
+            }
+        }
+    }
+
+
+    // for each markdown_file in markdown_files {
+    //     let internal_links_in_file = parse_internal_links(markdown_file);
+    //     internal_links.insert(markdown_file, internal_links_in_file);
+    // }
+
+    // pass 2: create the html
+
+    println!("Got {} files", pathes.len());
+    let mut readme_counter = 0;
+
+    for path in pathes {
+        let stripped_path = path.strip_prefix(&in_path)
+            .expect(format!(
+                "could not strip the in_path prefix: {:?}", in_path).as_str());
+
+        // copy images and other files to the output folder
+        if path.is_file() {
+
+            // define the source and destination
+            let src = Path::new(&in_path).join(stripped_path);
+            let dst = Path::new(&output_path).join(stripped_path);
+
+            // define the destination folder (the dst path without the file) and create it
+            let mut dst_folder = dst.clone();
+            dst_folder.pop(); // remove the file itself from the path
+            fs::create_dir_all(dst_folder)?;
+
+            // copy the file to the destination
+            std::fs::copy(src, dst.as_path())?;
+        }
+
+        if stripped_path.ends_with("README.md") {
+            readme_counter += 1;
+
+            // define the "raw" path (no infile prefix, no file)
+            let mut ancestors = stripped_path.ancestors();
+            ancestors.next();
+
+            let raw_path = ancestors.next()
+                .expect("could not extract next ancestor");
+
+            // out + rawpath
+            let index_path = output_path.join(raw_path);
+
+            // (out + rawpath) + "index.html"
+            let index_file = index_path.join("index.html");
+
+            // - create the dir for the index.html as well as the index.html
+            // itself
+            fs::create_dir_all(index_path)?;
+            let mut file = File::create(&index_file)?;
+
+            // this is the main block calling all other smaller functions. The
+            // whole output is compsed here
+            write_header(&mut file, &opt.site_name, &style)?;
+            write_body_start(&mut file, &opt.site_name)?;
+            write_nav(&mut file, in_path.as_path(), raw_path, opt.analytics)?;
+            write_same_level(&mut file, in_path.as_path(), raw_path)?;
+            write_readme_content(&mut file, in_path.as_path(), raw_path)?;
+            write_footer(&mut file, raw_path, &internal_links)?;
+
+            file.write_all("".as_bytes())?;
+        }
+    }
+
+    println!("Got {readme_counter} README.md files");
+
+    Ok(())
+}
+
+fn parse_internal_links(markdown_file: &str) -> Vec<String> {
+    // Define a regular expression to match markdown-style links
+    let link_regex = Regex::new(r"\[([^\]]+)\]\(([^)]+)\)").unwrap();
+
+    // Initialize a vector to store internal links found in the markdown file
+    let mut internal_links = Vec::new();
+
+    // Iterate over each match of the regular expression in the markdown content
+    for capture in link_regex.captures_iter(&markdown_file) {
+        // Extract the link text and URL from the capture groups
+        // let link_text = &capture[1];
+        let mut link_url = &capture[2];
+
+        // Check if the link is an internal link (e.g., relative URL)
+        // You can customize this condition based on your site's URL structure
+        if link_url.starts_with('/') || link_url.starts_with("../") {
+            if link_url.ends_with('/') {
+                link_url = link_url.trim_end_matches('/');
+            }
+            internal_links.push(link_url.to_string());
+        }
+    }
+
+    internal_links
+}
+
+/// Write the html header including the style file
+/// TODO: Don't add the style file into each compiled html output, as the
+/// style can be included allowing the user to cache the style file in their
+/// browser.
+fn write_header(file: &mut File, site_name: &String, style: &String) -> std::io::Result<()>{
+
+    // write the header including the style file
+    file.write_all(format!(r#"<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>{}</title>
+
+  <style>
+  {}
+  </style>
+</head>
+    "#, site_name, style).as_bytes())?;
+
+    Ok(())
+}
+
+/// write the start of the html body tag and the header linking back to the
+/// site itself.
+fn write_body_start(file: &mut File, site_name: &String) -> std::io::Result<()>{
+    file.write_all(format!(r#"
+<body>
+  <header>
+    <a href="/">{}</a>
+  </header>"#, site_name).as_bytes())?;
+
+    Ok(())
+}
+
+/// Write the navigation section to the given file
+fn write_nav(file: &mut File, in_path: &Path, raw_path: &Path, analytics: bool)
+    -> std::io::Result<()> {
+
+    if analytics == true {
+        /*
+        file.write_all(format!(r#"
+  <img src="https://stats.emile.space/count?p=/{}">
+  <nav>
+    <ul>"#, raw_path.to_str().unwrap()).as_bytes())?;
+        */
+        file.write_all(format!(r#"
+  <nav>
+    <ul>"#,).as_bytes())?;
+    } else {
+        file.write_all(format!(r#"
+  <nav>
+    <ul>"#).as_bytes())?;
+    }
+
+    // get the nav bar components
+    let components = raw_path.components().collect::<Vec<_>>();
+    
+    // for each list of components (["a"], ["a", "b"], ["a", "b", "c"]), create
+    // the path for the list, view all other dirs at that path and write the
+    // result to the file
+    let mut i = 0;
+    let slice = components.as_slice();
+
+    // for each navbar component
+    for component in slice {
+
+        // get the items belonging to that navbar item
+        // (["a"], ["a", "b"], ["a", "b", "c"])
+        let subpath_components = &slice[..i+1];
+        i += 1;
+
+        let mut subpath_path = PathBuf::new();
+
+        // push the inpath, so we've got a basis from where we can read the
+        // subpath items
+        // subpath_path = inpath + ???
+        subpath_path.push(in_path);
+
+        let mut nav_breadcrumb_link = PathBuf::new();
+
+        // for each item in the subpath, push it into the subpath_path so that
+        // in the end, we've got something like this:
+        // "inpath" + "a" + "b" + "c"
+        for subpath_component in subpath_components {
+            subpath_path.push(subpath_component);
+            nav_breadcrumb_link.push(subpath_component);
+        }
+
+        // make the nav_breadcrumb_link an absolute by prefixing it with a /
+        // (this is in scope of the web-page, so this is find) and make it a
+        // string
+        let nav_breadcrumb_link_absolute 
+            = Path::new("/")
+                .join(nav_breadcrumb_link);
+
+        let nav_breadcrumb_link
+            = nav_breadcrumb_link_absolute.to_str().unwrap();
+
+        // define the name of the breadcrumb
+        let nav_breadcrumb_name = component.as_os_str().to_str().unwrap();
+
+        ////////////////////////////////////////////////////////////////////////
+        file.write_all(format!(r#"
+        <li>
+            <a href="{}">{}</a>
+            <ul>"#, nav_breadcrumb_link, nav_breadcrumb_name).as_bytes())?;
+        ////////////////////////////////////////////////////////////////////////
+
+        // as we don't want to get the items for the individial entry, but on
+        // the same level, we push a ".."
+        // the subpath_path is now: inpath + subpath + ../
+        subpath_path.push("..");
+
+        // read all dirs in the subpath_path, add them to the dirs vector, so
+        // that we get a vector containing all the dirs we want
+        let mut dirs = Vec::new();
+        for entry in fs::read_dir(subpath_path)? {
+            let path = &entry?.path();
+            if path.is_dir() {
+                dirs.push(path.to_path_buf());
+            }
+        }
+
+        dirs.sort();
+
+        // DROPDOWN
+        // extract the link and name for each directory found
+        for dir in dirs {
+            let d = dir.canonicalize()?;
+            let abs_inpath = in_path.canonicalize()?;
+
+            let name = d.file_name().unwrap().to_str().unwrap();
+            let rel_link 
+                = d.strip_prefix(abs_inpath)
+                    .expect(format!(
+                        "could not strip the in_path prefix: {:?}",
+                        d).as_str());
+
+            let link = Path::new("/").join(rel_link);
+            let link = link.as_path().to_str().unwrap();
+
+            // don't add the current page to the dropdown, we're on it already!
+            if name == nav_breadcrumb_name {
+                continue
+            }
+
+            // don't add items starting with a dot to the dropdown, they're
+            // hidden!
+            if name.starts_with(".") {
+                continue
+            }
+
+            ////////////////////////////////////////////////////////////////////
+            file.write_all(format!(r#"
+                <li><a href="{}">{}/</a></li>"#, link, name).as_bytes())?;
+            ////////////////////////////////////////////////////////////////////
+        }
+
+        ////////////////////////////////////////////////////////////////////////
+        file.write_all(r#"
+            </ul>
+        </li>"#.as_bytes())?;
+        ////////////////////////////////////////////////////////////////////////
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    file.write_all(format!(r#"
+    </ul>
+    <ul style="float: right">
+        <li>{:?}</li>
+        <li>
+            <a href="README.md">.md</a>
+        </li>
+    </ul>
+  </nav>"#, in_path.metadata()?.modified()?.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs()).as_bytes())?;
+    ////////////////////////////////////////////////////////////////////////////
+
+    Ok(())
+}
+
+
+fn write_same_level(file: &mut File, in_path: &Path, raw_path: &Path)
+    -> std::io::Result<()> {
+
+    let search_path = Path::new(in_path).join(raw_path);
+
+    let mut dirs: Vec<PathBuf> = Vec::new();
+    let mut files: Vec<PathBuf> = Vec::new();
+
+    let mut vertical: bool = false;
+    let mut show_files: bool = false;
+
+    for entry in fs::read_dir(search_path)? {
+        let path = &entry?.path();
+
+        if path.is_dir() {
+            dirs.push(path.to_path_buf());
+        }
+        if path.is_file() {
+            files.push(path.to_path_buf());
+            if path.file_name().unwrap() == "vertical" {
+                vertical = true;
+            }
+            if path.file_name().unwrap() == "show_files" {
+                show_files = true;
+            }
+        }
+    }
+
+    dirs.sort();
+    files.sort();
+
+    let in_path = in_path.canonicalize()?;
+
+    if vertical == true {
+        file.write_all(format!(r#"
+  <ul class="vert">"#).as_bytes())?;
+    } else {
+        file.write_all(format!(r#"
+  <ul>"#).as_bytes())?;
+    }
+
+    for dir in dirs {
+        let dir = dir.canonicalize()?;
+        let dir = dir.strip_prefix(&in_path)
+            .expect("could not strip in_path prefix");
+
+        let link = Path::new("/").join(dir);
+        let link_str = link.as_path().to_str().unwrap();
+        let name = link.file_name().unwrap().to_str().unwrap();
+
+        if name.starts_with(".") {
+            continue
+        }
+
+        file.write_all(format!(r#"
+    <li><a href="{}">{}/</a></li>"#, link_str, name).as_bytes())?;
+    }
+
+    file.write_all(format!(r#"
+  </ul>"#).as_bytes())?;
+
+    if files.len() >= 1 && show_files == true {
+        file.write_all(format!(r#"<br>
+    <ul>"#).as_bytes())?;
+
+        for f in files {
+            let f = f.canonicalize()?;
+            let f = f.strip_prefix(&in_path)
+                .expect("could not strip in_path prefix");
+
+            let link = Path::new("/").join(f);
+            let link_str = link.as_path().to_str().unwrap();
+            let name = link.file_name().unwrap().to_str().unwrap();
+
+            if name == "README.md"
+                || name == "show_files"
+                || name.starts_with(".")
+                {
+                continue
+            };
+
+            file.write_all(format!(r#"
+        <li><a href="{}">{}</a></li>"#, link_str, name).as_bytes())?;
+        }
+
+        file.write_all(format!(r#"
+    </ul>"#).as_bytes())?;
+    }
+
+
+    Ok(())
+}
+
+fn write_readme_content(file: &mut File, in_path: &Path, raw_path: &Path) 
+    -> std::io::Result<()> {
+
+    // define the path of the README.md file
+    let readme_file_path 
+        = Path::new(in_path).join(raw_path).join("README.md");
+
+    // open the file and read it as a string
+    let mut readme_file = File::open(readme_file_path)?;
+    let mut readme = String::new();
+    readme_file.read_to_string(&mut readme)?;
+
+    // replace all "markdown" style links with HTML links
+    // let re = Regex::new(r"\[([^\[]+)\]\(([^\(]+)\)").unwrap();
+    let re = Regex::new(r"\[([^]]+)\]\(([^)]+)\)").unwrap();
+    let readme = re.replace_all(&readme, "<a href=\"$2\">$1</a>");
+
+    file.write_all(format!("<pre>").as_bytes())?;
+
+    // counting the occurrence of `---`
+    let mut hrule_count = 0;
+    let mut in_yaml_metadata_block= false;
+
+    let mut level_1_heading_num = 0;
+    let mut level_2_heading_num = 0;
+    let mut level_3_heading_num = 0;
+    let mut level_4_heading_num = 0;
+    let mut level_5_heading_num = 0;
+
+    // cheap markdown 2 html converter
+    for line in readme.split('\n') {
+
+        // 1 == 2, as I'm not sure how to comment out the file write 5 lines or so below
+        if in_yaml_metadata_block && 1 == 2 {
+            // if we find the end of the yaml metadata block, break this
+            if line.starts_with("---") {
+                in_yaml_metadata_block = false;
+                continue
+            } else {
+                file.write_all(format!(r##"yaml_line: {}
+"##, line).as_bytes())?;
+                continue
+            }
+        }
+
+        // if we've got a horizontal rule, it can be two things: the start and
+        // end of a yaml-metadata block or an actual horizontal rule.
+        //
+        // If it's yaml metadata, read it all, but don't print it, store it
+        // for later
+        // If it's a horizontal rule, print the horizontal rule
+        if line.starts_with("---") {
+
+            // store the yaml metadata
+            if hrule_count == 0 {
+                in_yaml_metadata_block = true;
+                continue
+            }                 
+            hrule_count += 1;
+
+            // print the horizontal rule
+            file.write_all(format!(r##"
+            <hr>"##).as_bytes())?;
+
+        } else if line.starts_with("#####") {
+            let heading = line.get(6..).unwrap();
+            let heading_sanitized = sanitize(heading.to_string());
+
+            level_5_heading_num += 1;
+
+            file.write_all(format!(r##"</pre>
+            <span id="{a}"></span>
+            <h5><a href="#{a}">{h1}.{h2}.{h3}.{h4}.{h5}. {b}</a></h3>
+            <pre>"##,
+                a = heading_sanitized,
+                b = heading,
+                h1 = level_1_heading_num,
+                h2 = level_2_heading_num,
+                h3 = level_3_heading_num,
+                h4 = level_4_heading_num,
+                h5 = level_5_heading_num,
+            ).as_bytes())?;
+
+        } else if line.starts_with("####") {
+            let heading = line.get(5..).unwrap();
+            let heading_sanitized = sanitize(heading.to_string());
+
+            level_4_heading_num += 1;
+            level_5_heading_num = 0;
+
+            file.write_all(format!(r##"</pre>
+            <span id="{a}"></span>
+            <h4><a href="#{a}">{h1}.{h2}.{h3}.{h4}. {b}</a></h3>
+            <pre>"##,
+                a = heading_sanitized,
+                b = heading,
+                h1 = level_1_heading_num,
+                h2 = level_2_heading_num,
+                h3 = level_3_heading_num,
+                h4 = level_4_heading_num,
+            ).as_bytes())?;
+
+        } else if line.starts_with("###") {
+            let heading = line.get(4..).unwrap();
+            let heading_sanitized = sanitize(heading.to_string());
+
+            level_3_heading_num += 1;
+            level_4_heading_num = 0;
+            level_5_heading_num = 0;
+
+            file.write_all(format!(r##"</pre>
+            <span id="{a}"></span>
+            <h3><a href="#{a}">{h1}.{h2}.{h3}. {b}</a></h3>
+            <pre>"##,
+                a = heading_sanitized,
+                b = heading,
+                h1 = level_1_heading_num,
+                h2 = level_2_heading_num,
+                h3 = level_3_heading_num,
+            ).as_bytes())?;
+
+        } else if line.starts_with("##") {
+            let heading = line.get(3..).unwrap();
+            let heading_sanitized = sanitize(heading.to_string());
+
+            level_2_heading_num += 1;
+            level_3_heading_num = 0;
+            level_4_heading_num = 0;
+            level_5_heading_num = 0;
+
+            file.write_all(format!(r##"</pre>
+            <span id="{a}"></span>
+            <h2><a href="#{a}">{h1}.{h2}. {b}</a></h2>
+            <pre>"##,
+                a = heading_sanitized,
+                b = heading,
+                h1 = level_1_heading_num,
+                h2 = level_2_heading_num,
+            ).as_bytes())?;
+
+        } else if line.starts_with("#") {
+            let heading = line.get(2..).unwrap();
+            let heading_sanitized = sanitize(heading.to_string());
+
+            level_1_heading_num += 1;
+            level_2_heading_num = 0;
+            level_3_heading_num = 0;
+            level_4_heading_num = 0;
+            level_5_heading_num = 0;
+
+            file.write_all(format!(r##"</pre>
+            <span id="{a}"></span>
+            <h1><a href="#{a}">{h1}. {b}</a></h1>
+            <pre>"##,
+                a = heading_sanitized,
+                b = heading,
+                h1 = level_1_heading_num
+            ).as_bytes())?;
+
+        } else if line.starts_with("> ") {
+            let line = line.replace("<", "&lt");
+            let line = line.get(2..).unwrap();
+            file.write_all(format!("</pre><pre class=\"code\">{}</pre><pre>\n", line).as_bytes())?;
+            
+        } else if line.starts_with(":::tree") {
+
+            // TODO: add some parameter controlling if the list is ascending or descending (reverse the list before writing)
+
+            // get all dirs in the current dir recursively
+            let tree_files_path = Path::new(in_path).join(raw_path);
+            let mut tree_files
+                = recursive_read_dir(&tree_files_path, true)?;
+
+            // sort them, otherwise we'll get complete chaos
+            tree_files.sort();
+
+            for path in tree_files {
+                
+                // strip the inpath prefix and raw_path prefix, as we don't need
+                // them
+                let path 
+                    = path.strip_prefix(in_path)
+                        .expect("could not strip in_file prefix")
+                        .strip_prefix(raw_path)
+                        .expect("could not strip raw_path prefix");
+
+                // convert the path to a string, check if it contains a hidden
+                // path by checking if it contains a `/.`, if so, skip this one
+                if String::from(path.to_str().unwrap()).contains("/.") {
+                    continue
+                }
+                if String::from(path.to_str().unwrap()).starts_with(".") {
+                    continue
+                }
+
+                // write the link and the entry name to the file
+                let link = Path::new(raw_path).join(path);
+                let name = path.file_name().unwrap().to_str().unwrap();
+
+                // count the amount of segments in the path and write spaces for
+                // each
+                let segments = path.iter().count();
+                for _ in 0..(segments-1) {
+                    file.write_all(r#"    "#.as_bytes())?;
+                }
+
+                file.write_all(
+                    format!("<a href=\"/{}\">{}</a>\n",
+                        link.display(), name, 
+                        ).as_bytes()
+                )?;
+            }
+
+        } else if line.starts_with(":::toc") {
+
+            // TODO: depth parameter for controlling the depth of the table of contents
+
+            let mut level_1_num = 0;
+            let mut level_2_num = 0;
+            let mut level_3_num = 0;
+            let mut level_4_num = 0;
+            let mut level_5_num = 0;
+
+            for line in readme.split('\n') {
+                if line.starts_with("#####") {
+                    let line = line.get(6..).unwrap();
+                    // trim the line to remove the trailing whitespace
+                    let line = line.trim();
+                    level_5_num += 1;
+                    file.write_all(
+                        format!(
+                            r##"           <a href="#{}">{}.{}.{}.{}.{}. {}</a>
+"##,
+                            sanitize(line.to_string()),
+                            level_1_num,
+                            level_2_num,
+                            level_3_num,
+                            level_4_num,
+                            level_5_num,
+                            line
+                        ).as_bytes()
+                    )?;
+                } else if line.starts_with("####") {
+                    let line = line.get(5..).unwrap();
+                    // trim the line to remove the trailing whitespace
+                    let line = line.trim();
+                    level_4_num += 1;
+                    level_5_num = 0;
+                    file.write_all(
+                        format!(
+                            r##"         <a href="#{}">{}.{}.{}.{}. {}</a>
+"##,
+                            sanitize(line.to_string()),
+                            level_1_num,
+                            level_2_num,
+                            level_3_num,
+                            level_4_num,
+                            line
+                        ).as_bytes()
+                    )?;
+                } else if line.starts_with("###") {
+                    let line = line.get(4..).unwrap();
+                    // trim the line to remove the trailing whitespace
+                    let line = line.trim();
+                    level_3_num += 1;
+                    level_4_num = 0;
+                    level_5_num = 0;
+                    file.write_all(
+                        format!(
+                            r##"       <a href="#{}">{}.{}.{}. {}</a>
+"##,
+                            sanitize(line.to_string()),
+                            level_1_num,
+                            level_2_num,
+                            level_3_num,
+                            line
+                        ).as_bytes()
+                    )?;
+                } else if line.starts_with("##") {
+                    let line = line.get(3..).unwrap();
+                    let line = line.trim();
+                    level_2_num += 1;
+                    level_3_num = 0;
+                    level_4_num = 0;
+                    level_5_num = 0;
+
+                    file.write_all(
+                        format!(
+                            //r##"    <a href="#{}">{}.{}. {}</a>
+                            r##"    <a href="#{}">{}.{}. {}</a>
+"##,
+                            sanitize(line.to_string()),
+                            level_1_num,
+                            level_2_num,
+                            line
+                        ).as_bytes()
+                    )?;
+                } else if line.starts_with("#") {
+                    let line = line.get(2..).unwrap();
+                    let line = line.trim();
+                    level_1_num += 1;
+                    level_2_num = 0;
+                    level_3_num = 0;
+                    level_4_num = 0;
+                    level_5_num = 0;
+
+                    file.write_all(
+                        format!(
+                            r##"<a href="#{}">{}. {}</a>
+"##,
+                            sanitize(line.to_string()),
+                            level_1_num,
+                            line
+                        ).as_bytes()
+                    )?;
+                }
+            }
+
+        } else {
+
+            // for the case that nothing of the above matches, just write the
+            // content into the html body as it is
+            file.write_all(format!("{}\n", line).as_bytes())?;
+        }
+    }
+
+    Ok(())
+}
+
+fn write_footer(file: &mut File, raw_path: &Path, internal_links: &HashMap<String, Vec<String>>) -> std::io::Result<()> {
+
+    // add some padding before the whole footer stuff
+    file.write_all(b"<br><br><br>")?;
+
+    // Backlinks
+
+    let search_path = Path::new("/").join(raw_path).into_os_string().into_string().unwrap();
+
+    match internal_links.get(&search_path) {
+        Some(values) => {
+
+            // only write "backlinks" if we've actually got some
+            file.write_all(b"backlinks:\n")?;
+
+            for link in values {
+
+                // strip the "in" prefix 
+                // strip the "README.md" suffix
+                // TODO: do all this magic by parsing it as a path and removing the unneeded parts, bonus by creating a function doing this and removing the horrible string mashing in this codebase
+                let a = link
+                    .strip_prefix("in")
+                    .expect("no prefix to strip")
+                    .strip_suffix("README.md")
+                    .expect("no README.md suffix to remove");
+
+                file.write_all(format!(r#"- <a href="{a}">{a}</a>
+"#).as_bytes())?;
+            }
+        }
+        None => (),
+    }
+
+    // The actual footer
+
+    file.write_all(format!(r#"
+    </pre>
+<a href="https://chaos.social/@hanemile.rss" target="_blank" rel="noopener" class="icon"><img class="webring" src="/rss.svg" alt="rss feed of @hanemile@chaos.social mastodon" height="32px"/></a>
+<a href="https://lieu.cblgh.org/" target="_blank" rel="noopener" class="icon"><img class="webring" src="/lieu.svg" alt="lieu webring search engine" height="32px"/></a>
+<a href="https://webring.xxiivv.com/#emile" target="_blank" rel="noopener" class="icon"><img class="webring" src="/webring.svg" alt="XXIIVV webring" height="32px"/></a>
+<a rel="me" href="https://chaos.social/@hanemile" target="_blank" class="icon"><img class="webring" src="/mastodon.svg" alt="mastodon" height="32px"/></a>
+    <pre>emile - {:?} - generated using <a href="https://github.com/hanemile/vokobe">vokobe {:?}</a><pre>
+</body>
+</html>
+"#,
+    time::SystemTime::now().duration_since(time::SystemTime::UNIX_EPOCH).unwrap(),
+    env!("CARGO_PKG_VERSION")
+    ).as_bytes())?;
+
+    Ok(())
+}
+
+/// sanitize the given string (to lower + space to hypen + keep only
+/// [a-zA-Z0-9])
+fn sanitize(input: String) -> String {
+    let input = input.replace(" ", "-");
+
+    input
+        .chars()
+        .filter(|c| c.is_ascii_alphanumeric() || c.eq(&'-'))
+        .collect::<String>()
+        .to_lowercase()
+}
+
+/// Return a list of all files in the directory, recursively.
+fn recursive_read_dir(dir: &PathBuf, dir_only: bool) -> io::Result<Vec<PathBuf>> {
+
+    // return an empty vec if the given path is not a directory
+    if dir.is_dir() == false {
+        return Ok(vec![]);
+    }
+
+    if dir.starts_with(".") {
+       return Ok(vec![]); 
+    }
+
+        // get all entries in the gitignore file, if it exists
+    let gitignore_entries: Vec<PathBuf> = gitignore_entries(&dir)?;
+
+    // store the child pathes
+    let mut entries: Vec<PathBuf> = Vec::new();
+    
+    // iterate over all items in the dir, pushing the dirs pathes to the dirs
+    // vector for returning it
+    'outer: for entry in fs::read_dir(dir)? {
+        let dir_entry = &entry?;
+        let path = dir_entry.path();
+
+        // skip hidden folders
+        if path.starts_with(".") {
+            //continue 'outer;
+            break 'outer;
+        }
+        if dir.starts_with(".") {
+            //continue 'outer;
+            break 'outer;
+        }
+
+        // check if the current entry is part of the gitignore, if so, skip it
+        for gitignore_entry in &gitignore_entries {
+            if gitignore_entry.to_str() == Some("") {
+                continue;
+            }
+            if path.ends_with(gitignore_entry) {
+                continue 'outer;
+            }
+        }
+
+        if dir_only == true {
+            if path.is_dir() {
+                entries.push(path.to_path_buf());
+            }
+        } else {
+            entries.push(path.to_path_buf());
+        }
+
+        // recursively push all dirs from all children to the dirs vector
+        let subdirs = recursive_read_dir(&path, dir_only)?;
+
+        for subdir in subdirs {
+            entries.push(subdir)
+        }
+    }
+
+    // return the dirs, the ones from this folder and the ones from all child folders
+    Ok(entries)
+}
+
+// try to open the gitignore file and read all entries from there.
+fn gitignore_entries(dir: &PathBuf) -> io::Result<Vec<PathBuf>> {
+    let gitignore_path = Path::new(&dir)
+        .join(Path::new(".gitignore"));
+
+    let mut entries: Vec<PathBuf> = Vec::new();
+    if let Ok(gitignore) = File::open(&gitignore_path) {
+        let reader = BufReader::new(gitignore);
+
+        for line in reader.lines() {
+            entries.push(PathBuf::from(line?));
+        }
+    }
+
+    Ok(entries)
+}