summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorKleidi Bujari <mail@4kb.net>2025-09-12 20:57:59 -0700
committerKleidi Bujari <mail@4kb.net>2025-09-12 21:00:27 -0700
commitfe96211bb8dc81fde57bf6900b840e69f2b218fc (patch)
treeff1c63e7962c6373199b94b8fd236b15e3441244 /packages
parentab58c69b6db2902688841a4008221f72bbbb8692 (diff)
downloaddepot-fe96211bb8dc81fde57bf6900b840e69f2b218fc.tar.gz
depot-fe96211bb8dc81fde57bf6900b840e69f2b218fc.tar.bz2
depot-fe96211bb8dc81fde57bf6900b840e69f2b218fc.zip
move packages to overlay and expose certain packages
Diffstat (limited to 'packages')
-rw-r--r--packages/cses/default.nix13
-rw-r--r--packages/cses/problems/1068.cpp17
-rw-r--r--packages/cses/problems/1069.cpp23
-rw-r--r--packages/cses/problems/1070.cpp15
-rw-r--r--packages/cses/problems/1071.cpp19
-rw-r--r--packages/cses/problems/1083.cpp20
-rw-r--r--packages/cses/problems/1094.cpp27
-rw-r--r--packages/cv/default.nix4
-rw-r--r--packages/misc/cv/cv.typ (renamed from packages/cv/cv.typ)49
-rw-r--r--packages/misc/cv/package.nix3
-rw-r--r--packages/partitioner.nix82
-rw-r--r--packages/perf-flamegraph.nix22
-rw-r--r--packages/web/resistors/default.nix (renamed from packages/resistors/default.nix)4
-rw-r--r--packages/web/resistors/index.html (renamed from packages/resistors/index.html)0
-rw-r--r--packages/web/resistors/styles.css (renamed from packages/resistors/styles.css)0
-rw-r--r--packages/web/site/.gitignore1
-rw-r--r--packages/web/site/config.toml8
-rw-r--r--packages/web/site/content/about/index.md38
-rw-r--r--packages/web/site/content/paper-reviews/_index.md7
-rw-r--r--packages/web/site/content/posts/_index.md7
-rw-r--r--packages/web/site/content/posts/deterministic-hostnames.md80
-rw-r--r--packages/web/site/content/posts/nohup.md26
-rw-r--r--packages/web/site/content/posts/overusing-emacs-modes.md39
-rw-r--r--packages/web/site/content/posts/stateless-compute-networks.md41
-rw-r--r--packages/web/site/content/posts/vim-compilers.md20
-rw-r--r--packages/web/site/package.nix36
-rw-r--r--packages/web/site/static/atom.svg7
-rw-r--r--packages/web/site/static/theme.css71
-rw-r--r--packages/web/site/templates/404.html9
-rw-r--r--packages/web/site/templates/base.html31
-rw-r--r--packages/web/site/templates/index.html28
-rw-r--r--packages/web/site/templates/page.html17
-rw-r--r--packages/web/site/templates/section.html28
33 files changed, 530 insertions, 262 deletions
diff --git a/packages/cses/default.nix b/packages/cses/default.nix
deleted file mode 100644
index 6538eb8..0000000
--- a/packages/cses/default.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-{ pkgs ? import <nixpkgs> { }, ... }:
-
-pkgs.mkShell {
- packages = with pkgs; [
- clang-tools
- (writeShellScriptBin "cpprun" ''
- TEMP=".tmp.cpp"
- g++ -std=c++20 -O3 ./problems/"$1.cpp" -o $TEMP
- ./$TEMP
- rm $TEMP
- '')
- ];
-}
diff --git a/packages/cses/problems/1068.cpp b/packages/cses/problems/1068.cpp
deleted file mode 100644
index 8d05936..0000000
--- a/packages/cses/problems/1068.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <iostream>
-
-int main(int argc, char *argv[]) {
- unsigned long long n;
- std::cin >> n;
-
- while (n != 1) {
- std::cout << n << " ";
-
- if (n % 2 == 0)
- n /= 2;
- else
- n = (n * 3) + 1;
- }
-
- std::cout << 1;
-}
diff --git a/packages/cses/problems/1069.cpp b/packages/cses/problems/1069.cpp
deleted file mode 100644
index 232d2f7..0000000
--- a/packages/cses/problems/1069.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <iostream>
-
-int main() {
- std::string input;
- std::cin >> input;
-
- auto max = 0;
- auto curr = '-';
- auto count = 0;
-
- for (auto const ch : input) {
- if (curr != ch) {
- curr = ch;
- count = 0;
- }
-
- count += 1;
- max = std::max(max, count);
- }
-
- std::cout << max;
- return 0;
-}
diff --git a/packages/cses/problems/1070.cpp b/packages/cses/problems/1070.cpp
deleted file mode 100644
index 131a644..0000000
--- a/packages/cses/problems/1070.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <iostream>
-
-int main() {
- int n;
- std::cin >> n;
-
- if (n == 1)
- std::cout << 1;
- else if (n < 4)
- std::cout << "NO SOLUTION";
- else {
- for (auto i = 2; i <= n; i += 2) std::cout << i << " ";
- for (auto i = 1; i <= n; i += 2) std::cout << i << " ";
- }
-}
diff --git a/packages/cses/problems/1071.cpp b/packages/cses/problems/1071.cpp
deleted file mode 100644
index 778cc86..0000000
--- a/packages/cses/problems/1071.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <iostream>
-#include <tuple>
-#include <vector>
-
-using ull = unsigned long long;
-
-int main() {
- int n;
- std::cin >> n;
-
- while (n--) {
- ull x, y;
- std::cin >> y >> x;
-
- auto area = y * y;
- auto perimeter = y + y + 1;
- auto max = area + perimeter;
- }
-}
diff --git a/packages/cses/problems/1083.cpp b/packages/cses/problems/1083.cpp
deleted file mode 100644
index a3cd253..0000000
--- a/packages/cses/problems/1083.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include <iostream>
-#include <set>
-
-using ull = unsigned long long;
-
-int main(int argc, char *argv[]) {
- ull n;
- std::cin >> n;
-
- auto set = std::set<ull>{};
-
- std::string numstr;
- while (std::getline(std::cin, numstr, ' ')) set.insert(std::stoi(numstr));
-
- for (auto i = 1; i <= n; i++) {
- if (set.contains(i)) continue;
- std::cout << i;
- break;
- }
-}
diff --git a/packages/cses/problems/1094.cpp b/packages/cses/problems/1094.cpp
deleted file mode 100644
index 56f3886..0000000
--- a/packages/cses/problems/1094.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <iostream>
-#include <vector>
-
-using ull = unsigned long long;
-
-int main() {
- ull n;
- auto arr = std::vector<ull>{};
-
- std::cin >> n;
-
- std::string type;
- while (std::cin >> type) arr.push_back(std::stoi(type));
-
- ull count = 0;
- auto last = arr[0];
-
- for (auto const num : arr) {
- if (num > last) {
- last = num;
- continue;
- }
- count += last - num;
- }
-
- std::cout << count;
-}
diff --git a/packages/cv/default.nix b/packages/cv/default.nix
deleted file mode 100644
index 01485a6..0000000
--- a/packages/cv/default.nix
+++ /dev/null
@@ -1,4 +0,0 @@
-{ pkgs, ... }:
-
-pkgs.runCommand "cv" { }
- "${pkgs.typst}/bin/typst compile --format pdf ${./cv.typ} $out"
diff --git a/packages/cv/cv.typ b/packages/misc/cv/cv.typ
index 8e839b2..bad635d 100644
--- a/packages/cv/cv.typ
+++ b/packages/misc/cv/cv.typ
@@ -15,15 +15,11 @@
#line(length: 100%, stroke: 1pt)
]
-#let generic-two-by-two(
- top-left: "",
- top-right: "",
- bottom-left: "",
- bottom-right: "",
-) = [
- #strong(top-left) #h(1fr) #top-right \
- #bottom-left #h(1fr) #bottom-right
-]
+#let generic-headline(
+ left: "",
+ right: "",
+ description: "",
+) = [#strong(left) #h(1fr) #description `::` #right]
#let dates-helper(from: "", to: "") = from + " " + $dash.em$ + " " + to
@@ -31,24 +27,20 @@
institution: "",
dates: "",
degree: "",
- location: "",
-) = generic-two-by-two(
- top-left: institution,
- top-right: dates,
- bottom-left: degree,
- bottom-right: location,
+) = generic-headline(
+ left: institution,
+ description: degree,
+ right: dates,
)
#let work(
- title: "",
- dates: "",
- company: "",
- location: "",
-) = generic-two-by-two(
- top-left: title,
- top-right: dates,
- bottom-left: company,
- bottom-right: location,
+ title: "",
+ dates: "",
+ company: "",
+) = generic-headline(
+ left: company,
+ description: title,
+ right: dates,
)
= Kleidi Bujari
@@ -65,16 +57,14 @@
company: "Meta",
title: "Production Engineer",
dates: dates-helper(from: "Aug 2025", to: "Present"),
- location: "Menlo Park",
)
-#v(5em)
+- Probably working on really cool things.
#work(
company: "Toronto Metropolitan University",
title: "Graduate Research Assistant",
dates: dates-helper(from: "May 2024", to: "Sep 2024"),
- location: "Toronto",
)
- Organized webhosting and analytics for a large film release,
@@ -88,7 +78,6 @@
company: "Canadian Broadcasting Corporation",
title: "Network Engineering Intern",
dates: dates-helper(from: "May 2023", to: "Apr 2024"),
- location: "Toronto",
)
- Designed custom PXE based provisioning for hundreds of devices on existing network,
@@ -102,11 +91,8 @@
company: "WSP Canada",
title: "Student Engineer",
dates: dates-helper(from: "May", to: "Aug") + ", 2021, 2022",
- location: "Toronto",
)
-- Modernized subway control systems with modern C++,
- replacing legacy code with newer STL functions and safer standards.
- Validated large-scale electrical designs for power consumption,
cost efficiency, and viability with existing systems.
- Extended internal distributed databases with compression and deduplication,
@@ -148,7 +134,6 @@ Heavily outperforms default Python implementation.
#edu(
institution: "Toronto Metropolitan University",
dates: dates-helper(from: "Sep 2020", to: "Apr 2025"),
- location: "Canada",
degree: "Bachelor's of Engineering, Computer Engineering",
)
diff --git a/packages/misc/cv/package.nix b/packages/misc/cv/package.nix
new file mode 100644
index 0000000..fe51328
--- /dev/null
+++ b/packages/misc/cv/package.nix
@@ -0,0 +1,3 @@
+{ runCommandNoCC, typst, ... }:
+
+runCommandNoCC "cv" { } "${typst}/bin/typst compile --format pdf ${./cv.typ} $out"
diff --git a/packages/partitioner.nix b/packages/partitioner.nix
deleted file mode 100644
index df9f37d..0000000
--- a/packages/partitioner.nix
+++ /dev/null
@@ -1,82 +0,0 @@
-# Apply standard partition layout to a disk device.
-{ pkgs, ... }:
-
-pkgs.writeShellApplication {
- name = "partitioner";
-
- runtimeInputs = with pkgs; [
- gptfdisk
- dosfstools
- zfs
- coreutils
- util-linux
- ];
-
- text = ''
- set -euo pipefail
-
- if [ $# -ne 1 ]; then
- echo "Usage: $0 <device>"
- echo "Example: $0 /dev/sda or /dev/nvme0n1"
- exit 1
- fi
-
- DISK="$1"
-
- if [ ! -b "$DISK" ]; then
- echo "Error: $DISK is not a valid block device"
- exit 1
- fi
-
- echo "creating partition table on $DISK"
- # create gpt partition table
- sgdisk --zap-all "$DISK"
-
- echo "creating efi partition"
- sgdisk --new=1:0:+1G --typecode=1:EF00 --change-name=1:ESP "$DISK"
-
- echo "creating zfs partition with remaining space"
- sgdisk --new=2:0:0 --typecode=2:BF00 --change-name=2:ZROOT "$DISK"
-
- if [[ "$DISK" == *"nvme"* ]]; then
- EFI_PART="''${DISK}p1"
- ZFS_PART="''${DISK}p2"
- else
- EFI_PART="''${DISK}1"
- ZFS_PART="''${DISK}2"
- fi
-
- sleep 1
-
- echo "formatting rfi partition ($EFI_PART)"
- mkfs.fat -F32 -n ESP "$EFI_PART"
-
- echo "creating pool on $ZFS_PART"
- zpool create -f \
- -o ashift=12 \
- -o autotrim=on \
- -O acltype=posixacl \
- -O canmount=off \
- -O compression=on \
- -O mountpoint=none \
- -O normalization=formD \
- -O relatime=off \
- -O xattr=sa \
- zroot "$ZFS_PART"
-
- # create ZFS datasets
- echo "Creating datasets"
-
- zfs create \
- -o refreservation=10G \
- -o mountpoint=none \
- zroot/reserved
-
- zfs create -o mountpoint=legacy zroot/nix
- zfs create -o mountpoint=legacy zroot/home
- zfs create -o mountpoint=legacy zroot/persist
-
- echo "---"
- echo "done"
- '';
-}
diff --git a/packages/perf-flamegraph.nix b/packages/perf-flamegraph.nix
index b472b74..780a280 100644
--- a/packages/perf-flamegraph.nix
+++ b/packages/perf-flamegraph.nix
@@ -1,12 +1,18 @@
-# Script that collects perf timing for the execution of a command and writes a
-# flamegraph to stdout
-{ pkgs, ... }:
+# Script that collects perf timing for the execution of a command and
+# writes a flamegraph to stdout
-pkgs.writeShellScriptBin "perf-flamegraph" ''
+{
+ writeShellScriptBin,
+ linuxPackages,
+ flamegraph,
+ ...
+}:
+
+writeShellScriptBin "perf-flamegraph" ''
set -euo pipefail
- ${pkgs.linuxPackages.perf}/bin/perf record -g --call-graph dwarf -F max "$@"
- ${pkgs.linuxPackages.perf}/bin/perf script \
- | ${pkgs.flamegraph}/bin/stackcollapse-perf.pl \
- | ${pkgs.flamegraph}/bin/flamegraph.pl
+ ${linuxPackages.perf}/bin/perf record -g --call-graph dwarf -F max "$@"
+ ${linuxPackages.perf}/bin/perf script \
+ | ${flamegraph}/bin/stackcollapse-perf.pl \
+ | ${flamegraph}/bin/flamegraph.pl
''
diff --git a/packages/resistors/default.nix b/packages/web/resistors/default.nix
index 6a038ea..cb32c2f 100644
--- a/packages/resistors/default.nix
+++ b/packages/web/resistors/default.nix
@@ -1,6 +1,6 @@
-{ pkgs, ... }:
+{ runCommandNoCC, ... }:
-pkgs.runCommandNoCC "resisto-rs" { } ''
+runCommandNoCC "resisto-rs" { } ''
mkdir -p $out
cp ${./index.html} $out/index.html
cp ${./styles.css} $out/styles.css
diff --git a/packages/resistors/index.html b/packages/web/resistors/index.html
index 0683f49..0683f49 100644
--- a/packages/resistors/index.html
+++ b/packages/web/resistors/index.html
diff --git a/packages/resistors/styles.css b/packages/web/resistors/styles.css
index e2823e4..e2823e4 100644
--- a/packages/resistors/styles.css
+++ b/packages/web/resistors/styles.css
diff --git a/packages/web/site/.gitignore b/packages/web/site/.gitignore
new file mode 100644
index 0000000..d298be1
--- /dev/null
+++ b/packages/web/site/.gitignore
@@ -0,0 +1 @@
+public/ \ No newline at end of file
diff --git a/packages/web/site/config.toml b/packages/web/site/config.toml
new file mode 100644
index 0000000..6762a97
--- /dev/null
+++ b/packages/web/site/config.toml
@@ -0,0 +1,8 @@
+base_url = "https://kleidi.ca"
+build_search_index = false
+generate_feeds = true
+minify_html = true
+author = "Kleidi Bujari"
+
+[markdown]
+highlight_code = false
diff --git a/packages/web/site/content/about/index.md b/packages/web/site/content/about/index.md
new file mode 100644
index 0000000..65c4556
--- /dev/null
+++ b/packages/web/site/content/about/index.md
@@ -0,0 +1,38 @@
+---
+title: ""
+---
+
+I'm a programmer working on Linux stuff,
+distributed systems,
+and compilers/runtimes.
+
+## Uses
+
+I run NixOS on all my computers and servers,
+and prefer to use simple unixy workflows.
+
+### Editors
+
+I use Emacs as my text editor,
+although its slowly taking over my workflow as I grow proficient.
+Rather than use a premade distribution,
+I decided early on to go vanilla and build a config myself.
+Part of the reasoning was to get a better understanding of the core emacs philosophy before adding plugins,
+the other part being that I didn't know what most plugins even did.
+
+In the past I enjoyed using neovim but left to try out the emacs hype.
+I didn't think they were too different until I tried using neovim again after ~6 months,
+and immediately missed all the functionality from emacs,
+maybe I'll elaborate in a post one day.
+Sure, neovim is a lot more minimal as an editor,
+but emacs aims to be a computing platorm with a text editor bolted on.
+They are ultimately trying to do different things,
+and lisp is just better than lua.
+
+I've played around with Helix and Kakoune but haven't dedicated serious time yet.
+
+## Meta
+
+This site is built mostly using [zola](http://getzola.org),
+with other projects built with nix sprinkled in afterwards.
+The entire site is then generated as a nix derivation and served from there.
diff --git a/packages/web/site/content/paper-reviews/_index.md b/packages/web/site/content/paper-reviews/_index.md
new file mode 100644
index 0000000..33c9820
--- /dev/null
+++ b/packages/web/site/content/paper-reviews/_index.md
@@ -0,0 +1,7 @@
++++
+title = "Paper Reviews"
+sort_by = "date"
+generate_feeds = true
++++
+
+Once in a while I take some notes. \ No newline at end of file
diff --git a/packages/web/site/content/posts/_index.md b/packages/web/site/content/posts/_index.md
new file mode 100644
index 0000000..c7c63df
--- /dev/null
+++ b/packages/web/site/content/posts/_index.md
@@ -0,0 +1,7 @@
++++
+title = "Posts"
+sort_by = "date"
+generate_feeds = true
++++
+
+Writings and musings. \ No newline at end of file
diff --git a/packages/web/site/content/posts/deterministic-hostnames.md b/packages/web/site/content/posts/deterministic-hostnames.md
new file mode 100644
index 0000000..43dc7d8
--- /dev/null
+++ b/packages/web/site/content/posts/deterministic-hostnames.md
@@ -0,0 +1,80 @@
+---
+title: "Deterministic and unique network hostnames"
+date: "2024-11-10"
+---
+
+As part of building out a Kubernetes cluster, I wanted to build and distribute a
+single OS image to create stateless worker nodes. Using network booting, and
+some clever tricks to differentiate nodes, we can create a scaleable and
+efficient farm of workers for a cluster that don't even need disks.
+
+The idea came from a plan to build a cluster using the
+[compute blade](https://computeblade.com/), and a few Raspberry Pi SBCs I
+already own. Running the cluster from an SD card is not recommended due to the
+not-so-great reliability of the flash used by most manufacturers, so I wanted to
+try PXE booting each Pi to save money rather than purchasing an SSD for each
+one. The compute blades do support an NVMe disk, but I plan to use those for a
+storage cluster later, so they need to remain empty.
+
+## Base image
+
+Alpine Linux has been my preferred server OS for a long time. It provides a very
+lightweight base system, and bundles an excellent bootstrapping system,
+[apkovl](https://wiki.alpinelinux.org/wiki/Alpine_local_backup), that allows the
+user to save a set of customisations to an system as an overlay to a stock
+Alpine live image. In other words, we can create our image once, save the
+changes as an `apkovl.tar.gz` file, and apply the same changes to a base system
+on boot. This file can even be provided as a
+[kernel parameter](https://wiki.alpinelinux.org/wiki/PXE_boot#Guide_to_options)
+and will be fetched from a remote webserver automatically!
+
+Since the image and configuration will be shipped to the node via the network,
+an added benefit of using Alpine is its tiny space consumption. I'm not using
+enough nodes for this to really matter, but it's a cool optimization regardless.
+
+## Differentiating the nodes
+
+One of the main goals of this project is that there should be no persistent
+storage required outside the boot image itself. Since every node will download
+and generate the same root file-system on startup, the first problem that arises
+is how the nodes will identify themselves both on the network and the cluster,
+given that it's not possible to name them ahead of time. In other words, any
+given node has to generate a unique hostname that won't collide with other
+workers, and that will be the same each time that node boots.
+
+Since these nodes will not have a predefined name, we have to rely on
+characteristics of the hardware to differentiate each one. The hardware MAC
+address is perfect for this, since it's unique to to each node and will not be
+wiped away after the node reboots. On a system like Linux that exposes its
+hardware through a _sysfs_, we can find a file containing the address at
+`/sys/class/net/eth0/address`. I don't really like the idea of attaching the
+literal MAC address of the node to its network hostname, since it's a security
+risk, and a bit too verbose. Instead, we can transform it into something safer
+using a `sha1sum`, which is already present on our Alpine base system:
+
+```console
+sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}'
+```
+
+### Applying the new name
+
+Ideally, the node should apply its generated hostname before reaching out for an
+address over DHCP or joining the cluster. We can make sure it happens before any
+traffic is sent out by adding a `pre-up` command to the right interface in
+`/etc/network/interfaces`:
+
+```
+
+...
+
+auto eth0
+iface eth0 inet dhcp
+ pre-up sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}' > /etc/hostname
+
+...
+```
+
+The VM I tested with looks outputs `worker-e2fae8`. Pretty clean result, and if
+you want to know the physical node that maps to each hostname, you can take note
+of the MAC address beforehand and generate the same hash on another computer to
+match them up.
diff --git a/packages/web/site/content/posts/nohup.md b/packages/web/site/content/posts/nohup.md
new file mode 100644
index 0000000..64f7983
--- /dev/null
+++ b/packages/web/site/content/posts/nohup.md
@@ -0,0 +1,26 @@
+---
+title: "Spawning background processes"
+date: "2024-11-17"
+---
+
+Working in a terminal,
+I often pair my editor with a background process watching files.
+Before reaching for terminal multiplexers,
+see if you can get away with simple tty job control.
+Spawn the background process,
+still attached to the terminal instance:
+
+```
+program args &
+```
+
+Also redirect its output to a file,
+for when the process writes to the tty from the background:
+
+```
+nohup program args &
+```
+
+Extra reading:
+
+- <https://jvns.ca/blog/2024/07/03/reasons-to-use-job-control/>
diff --git a/packages/web/site/content/posts/overusing-emacs-modes.md b/packages/web/site/content/posts/overusing-emacs-modes.md
new file mode 100644
index 0000000..1ea1654
--- /dev/null
+++ b/packages/web/site/content/posts/overusing-emacs-modes.md
@@ -0,0 +1,39 @@
+---
+title: On editors and plugins
+date: 2025-08-14
+draft: true
+---
+
+The healthy package ecosystems that extensible editors have is a standout feature.
+
+After setting up my first neovim package manager,
+I never hesitated testdriving cool plugins from the community.
+I installed a terminal plugin that without really having problems with `:term`,
+or using a file manager without ever trying `:netrw`.
+After a while, I found neovim overloaded with more features than I understood,
+and frequently hit problems that encouraged yet another plugin to fix.
+The issue wasn't that I had too many plugins per se,
+it was that I didn't really understand what they did.
+I promptly declared config bankruptcy.
+
+Going forward I strictly used plugins that were worth their weight.
+That is, how they affect performance as well as any cognitive slowdown.
+I quickly found that when a plugin strays too far from core neovim,
+like not hooking into builtin functionality or not reusing normal keybinds,
+it wouldn't make the cut.
+It is simply not worth learning if it doesn't enrich the editor as a whole.
+
+Using this philosophy for a few years,
+the only external plugins that survived were
+LSP completion, a proper code formatter, and fzf for search.
+Some plugins ended up in core neovim,
+like language aware commenting and better compile commands.
+Most were deleted.
+
+This meant my editor was really good at editing text and code projects,
+and not much more.
+It was fine that it didn't have a UI for viewing GitHub stars or whatever.
+I could live without
+
+## Enter emacs
+Modes reshape emacs to better fit certain work.
diff --git a/packages/web/site/content/posts/stateless-compute-networks.md b/packages/web/site/content/posts/stateless-compute-networks.md
new file mode 100644
index 0000000..bac9e5d
--- /dev/null
+++ b/packages/web/site/content/posts/stateless-compute-networks.md
@@ -0,0 +1,41 @@
+---
+title: "On stateless compute networks"
+date: "2024-11-10"
+draft: true
+---
+
+On the topic of distributed systems and clustering,
+I am quite invested in the idea of compute nodes that rely entirely on the network for configuration.
+Arbitrary nodes can join a pre-existing cluster,
+offering their CPU time and memory for computation without relying on any pre-existing configuration on the node itself.
+In other words, any computer could pick up work,
+only needing power and a network connection to the cluster.
+
+Perhaps this eventually leads into a "self-healing" cluster where only one node is manually bootstrapped,
+which then serves a _configuration endpoint_ for other stateless nodes to reach out to for their instructions,
+which they will then also serve once they are themselves ready.
+
+Early revisions of these notes mention Kubernetes,
+but I am also trying to achieve similar results with NixOS on a custom project.
+In any case, these are my ever-updating notes towards a general implementation of a stateless distributed systems architecture.
+
+## Self healing cluster
+
+Assuming control of an external DHCP server,
+a self healing Kubernetes cluster would be feasible,
+with the PXE boot artifacts supplied by the cluster itself.
+That is, as long as one node is running the pod hosting the artifacts on a given endpoint,
+other nodes can boot those artifacts and join the cluster,
+thereby being able to host the artifacts as well.
+
+## Configuration endpoint
+
+The nodes shouldn't require a disk installed to be able to join the network.
+Rather, the lofty goal of zero-configuration compute nodes passes the job of node initialization to the supporting network.
+This is accomplished with PXE boot instructions supplied over DHCP.
+
+I delegate the following tasks to a single node in the subnet:
+
+- Gateway: Optional outbound connections if required
+- DHCP server: Cluster IPAM
+- TFTP and HTTP server: Serves iPXE firmware and kernel/initrd artifacts
diff --git a/packages/web/site/content/posts/vim-compilers.md b/packages/web/site/content/posts/vim-compilers.md
new file mode 100644
index 0000000..f735228
--- /dev/null
+++ b/packages/web/site/content/posts/vim-compilers.md
@@ -0,0 +1,20 @@
+---
+title: "Neovim's built-in compilers"
+date: "2025-01-17"
+---
+
+Today I learned that neovim's `:make` comes with many
+[backends](https://neovim.io/doc/user/quickfix.html#_6.-selecting-a-compiler)
+already configured.
+This works for checking c/cpp and python files, among other, but I was
+most interested to see [Typst] and [Pandoc] listed as well.
+
+I was looking to add this functionality with a plugin or implementing it
+manually,
+where a document can be compiled on the fly from the editor.
+After setting `:compiler pandoc`,
+generating a pdf is done with `:make pdf`,
+with other pandoc options just appended afterwards if needed.
+
+[typst]: https://typst.app
+[pandoc]: https://github.com/jgm/pandoc
diff --git a/packages/web/site/package.nix b/packages/web/site/package.nix
new file mode 100644
index 0000000..aed7567
--- /dev/null
+++ b/packages/web/site/package.nix
@@ -0,0 +1,36 @@
+{
+ lib,
+ zola,
+ depot,
+ stdenvNoCC,
+ ...
+}:
+
+let
+ inherit (lib) fileset;
+in
+stdenvNoCC.mkDerivation {
+ name = "megasite";
+ src = fileset.toSource {
+ root = ./.;
+ fileset = fileset.unions [
+ ./config.toml
+ ./content
+ ./static
+ ./templates
+ ];
+ };
+
+ nativeBuildInputs = [ zola ];
+ buildPhase = "zola build";
+
+ installPhase = ''
+ mkdir -p $out/share
+
+ # output site
+ cp -rv public/* $out/
+
+ # copy cv
+ cp -v ${depot.misc.cv} $out/share/cv.pdf
+ '';
+}
diff --git a/packages/web/site/static/atom.svg b/packages/web/site/static/atom.svg
new file mode 100644
index 0000000..22a8b23
--- /dev/null
+++ b/packages/web/site/static/atom.svg
@@ -0,0 +1,7 @@
+<svg width="80" height="80" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg">
+ <circle cx="40" cy="40" r="4" fill="#c5c9c5" stroke="#c5c9c5" stroke-width="2"/>
+
+ <ellipse cx="40" cy="40" rx="25" ry="12" fill="none" stroke="#c5c9c5" stroke-width="2"/>
+ <ellipse cx="40" cy="40" rx="25" ry="12" fill="none" stroke="#c5c9c5" stroke-width="2" transform="rotate(60 40 40)"/>
+ <ellipse cx="40" cy="40" rx="25" ry="12" fill="none" stroke="#c5c9c5" stroke-width="2" transform="rotate(120 40 40)"/>
+</svg>
diff --git a/packages/web/site/static/theme.css b/packages/web/site/static/theme.css
new file mode 100644
index 0000000..873a6ca
--- /dev/null
+++ b/packages/web/site/static/theme.css
@@ -0,0 +1,71 @@
+/* Global Reset */
+
+* {
+ margin: 0;
+ padding: 0;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-size: 110%;
+ margin: 0.50em 0;
+}
+
+h1 { font-size: 120%; }
+
+:root {
+ --bg: #181616;
+ --fg: #c5c9c5;
+ --link: #76946a;
+ --linkhover: #98bb6c;
+}
+
+html {
+ scroll-behavior: smooth;
+ color-scheme: dark;
+}
+
+body {
+ font-size: 1em;
+ font-family: system-ui, sans-serif;
+ padding: 0 0.75em;
+ max-width: 42em;
+ margin: auto;
+ background: var(--bg);
+ color: var(--fg);
+}
+
+p {
+ line-height: 1.3em;
+ margin: 1em 0;
+}
+
+th, td {
+ padding: 0.2em 0.4em;
+ border: thin solid;
+}
+
+table {
+ border: thin solid;
+ border-collapse: collapse;
+}
+
+a, a:link {
+ color: var(--link);
+}
+
+a:hover {
+ color: var(--linkhover);
+}
+
+article img {
+ display: block;
+ margin: 0 auto;
+ max-width: 80%;
+ height: auto;
+ object-fit: contain;
+}
+
+pre {
+ padding: 1em;
+ overflow-x: scroll;
+}
diff --git a/packages/web/site/templates/404.html b/packages/web/site/templates/404.html
new file mode 100644
index 0000000..a4669df
--- /dev/null
+++ b/packages/web/site/templates/404.html
@@ -0,0 +1,9 @@
+{% extends "base.html" %}
+
+{% block content %}
+<head>
+ <title>Error 404!</title>
+</head>
+
+404!
+{% endblock content %}
diff --git a/packages/web/site/templates/base.html b/packages/web/site/templates/base.html
new file mode 100644
index 0000000..8f49bb2
--- /dev/null
+++ b/packages/web/site/templates/base.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>{% block title %}kleidi.ca{% endblock title %}</title>
+ <link href="/theme.css" rel="stylesheet" />
+ {% block head%}{% endblock head %}
+ </head>
+ <body>
+ <header
+ style="
+ margin: 3em 0 3em 0;
+ display: flex;
+ gap: 8px;
+ flex-direction: column;
+ font-family: monospace;
+ "
+ >
+ <span>
+ Kleidi Bujari &lt;<a href="mailto:mail@4kb.net">mail@4kb.net</a>&gt;
+ </span>
+ <nav style="display: flex; gap: 8px">
+ <span><a href="/">kleidi.ca</a> |</span>
+ <a href="/about">about</a>
+ <a target="_blank" href="https://github.com/kbujari">github</a>
+ </nav>
+ </header>
+ <main>{% block content %} {% endblock %}</main>
+ </body>
+</html>
diff --git a/packages/web/site/templates/index.html b/packages/web/site/templates/index.html
new file mode 100644
index 0000000..bc2994a
--- /dev/null
+++ b/packages/web/site/templates/index.html
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% block content %}
+
+{% for name in ["posts", "paper-reviews"] %}
+{% set section = get_section(path=name ~ "/_index.md") %}
+{%- if section.pages | length > 0 %}
+<div style="margin-bottom: 3em">
+ <h1>{{ section.title }}</h1>
+ <ul style="list-style: none; padding-left: 0">
+ {% for page in section.pages | slice(end=3) %}
+ <li style="margin-bottom: 1em">
+ <a href="{{ page.path }}">{{ page.title }}</a>
+ <br />
+ <time datetime="{{ page.date }}">
+ {{ page.date | date(format="%B %d, %Y") }}
+ </time>
+ </li>
+ {% endfor %}
+ </ul>
+
+ {%- if section.pages | length > 2 %}
+ <a href="{{ section.path }}">see more ↗</a>
+ {% endif %}
+</div>
+{% endif %}
+{% endfor %}
+
+{% endblock content %}
diff --git a/packages/web/site/templates/page.html b/packages/web/site/templates/page.html
new file mode 100644
index 0000000..8189fe6
--- /dev/null
+++ b/packages/web/site/templates/page.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% if page.title %}
+{% block title%}
+{{ page.title }} :: {{ super() }}
+{% endblock title %}
+{% endif %}
+
+{% block content %}
+
+<article>
+ <h1>{{ page.title }}</h1>
+
+ {{ page.content | safe}}
+</article>
+
+{% endblock content %}
diff --git a/packages/web/site/templates/section.html b/packages/web/site/templates/section.html
new file mode 100644
index 0000000..9bd8956
--- /dev/null
+++ b/packages/web/site/templates/section.html
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{ section.title | lower }} :: {{ super() }} {% endblock title %}
+{% block content %}
+
+{% block head %}
+<link
+ rel="alternate"
+ type="application/atom+xml"
+ title="ATOM"
+ href="{{ section.permalink }}atom.xml">
+{% endblock head%}
+
+<h1>{{ section.title }}</h1>
+<p>{{ section.content | safe }}</p>
+
+<hr style="margin-bottom: 1em" />
+<ul style="list-style: none; padding-left: 0">
+ {% for page in section.pages %}
+ <li style="margin-bottom: 1em">
+ <a href="{{ page.path }}">{{ page.title }}</a>
+ <br />
+ <span>{{ page.date | date(format="%B %d, %Y") }}</span>
+ </li>
+ {% endfor %}
+</ul>
+{% endblock content %}