commit f0ed2e9eb6e68e7bc5ddd65bcc23e87e9555c9d7
Author: tri <tri@thac.loan>
Date:   Sun Oct 12 22:54:27 2025 +0700

    first post: spite

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..370a952
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+watch:
+	find . -not -path './.git*' | entr -c loa
+
+serve:
+	python -m http.server -b 127.0.0.1
diff --git a/_loa/script.js b/_loa/script.js
new file mode 100644
index 0000000..c998953
--- /dev/null
+++ b/_loa/script.js
@@ -0,0 +1,64 @@
+window.addEventListener("load", () => {
+  enhanceTimeElements();
+});
+
+// Replace ISO datetime strings with momentjs-style relative text e.g. "20
+// minutes ago", "2 weeks ago"...
+function enhanceTimeElements() {
+  const millisecondsPerSecond = 1000;
+  const secondsPerMinute = 60;
+  const minutesPerHour = 60;
+  const hoursPerDay = 24;
+  const daysPerWeek = 7;
+  const daysPerMonth = 31;
+  const daysPerYear = 365; // getting imprecise here, but no big deal
+  const intervals = {
+    year:
+      millisecondsPerSecond *
+      secondsPerMinute *
+      minutesPerHour *
+      hoursPerDay *
+      daysPerYear,
+    month:
+      millisecondsPerSecond *
+      secondsPerMinute *
+      minutesPerHour *
+      hoursPerDay *
+      daysPerMonth,
+    week:
+      millisecondsPerSecond *
+      secondsPerMinute *
+      minutesPerHour *
+      hoursPerDay *
+      daysPerWeek,
+    day:
+      millisecondsPerSecond * secondsPerMinute * minutesPerHour * hoursPerDay,
+    hour: millisecondsPerSecond * secondsPerMinute * minutesPerHour,
+    minute: millisecondsPerSecond * secondsPerMinute,
+    second: millisecondsPerSecond,
+  };
+  const relativeDateFormat = new Intl.RelativeTimeFormat("en", {
+    style: "long",
+  });
+
+  // https://stackoverflow.com/a/78704662
+  function formatRelativeTime(isoStr) {
+    const diff = new Date(isoStr) - new Date();
+    for (const interval in intervals) {
+      if (intervals[interval] <= Math.abs(diff)) {
+        return relativeDateFormat.format(
+          Math.trunc(diff / intervals[interval]),
+          interval,
+        );
+      }
+    }
+    return relativeDateFormat.format(diff / 1000, "second");
+  }
+
+  document.querySelectorAll("time.relative").forEach((element) => {
+    const timeStr = element.getAttribute("datetime");
+    if (!timeStr) return;
+    const relativeTimeStr = formatRelativeTime(timeStr);
+    element.innerHTML = relativeTimeStr;
+  });
+}
diff --git a/_loa/style.css b/_loa/style.css
new file mode 100644
index 0000000..acd8f8b
--- /dev/null
+++ b/_loa/style.css
@@ -0,0 +1,98 @@
+* {
+  box-sizing: border-box;
+}
+
+:root {
+  --font-family: "Noto Serif", serif;
+  --font-weight: 400;
+  --font-family-mono: "Noto Sans Mono", monospace;
+  --bg: white;
+  --fg: black;
+  --pre-bg: #f2f2f2;
+  --code-fg: darkred;
+  --base-padding: 0.7rem 1rem;
+
+  font-family: var(--font-family);
+  font-size: 100%;
+  font-weight: var(--font-weight);
+}
+
+body {
+  max-width: 50rem;
+  margin: auto;
+  padding: var(--base-padding);
+}
+
+body {
+  background-color: var(--bg);
+  color: var(--fg);
+}
+
+pre,
+code {
+  font-family: var(--font-family-mono);
+  font-size: 0.95rem;
+}
+
+pre {
+  padding: var(--base-padding);
+  background-color: var(--pre-bg);
+  max-width: 100%;
+  overflow-x: auto;
+}
+
+p code,
+ul code {
+  color: var(--code-fg);
+}
+
+p,
+ul {
+  line-height: 1.5;
+  margin: 1.5rem 0;
+}
+
+p + ul {
+  margin-top: -1rem;
+}
+
+@media (prefers-color-scheme: dark) {
+  :root {
+    --font-family: "Noto Sans", sans-serif;
+    --font-weight: 400;
+    --bg: black;
+    --fg: cornsilk;
+    --pre-bg: #333;
+    --code-fg: burlywood;
+  }
+
+  a {
+    color: cornflowerblue;
+  }
+  a:visited {
+    color: violet;
+  }
+}
+
+@media screen and (max-width: 600px) {
+  :root {
+    --base-padding: 0.5rem 0.7rem;
+  }
+}
+
+time.relative {
+  white-space: nowrap;
+}
+
+.home--post-published-at {
+  opacity: 0.5;
+  font-variant-caps: small-caps;
+}
+
+.post--published-at {
+  float: right;
+}
+
+.post--title {
+  line-height: 1.2;
+}
diff --git a/_loa_meta.json b/_loa_meta.json
new file mode 100644
index 0000000..c4e9938
--- /dev/null
+++ b/_loa_meta.json
@@ -0,0 +1,4 @@
+{
+  "site_name": "loa",
+  "tagline": "shouting into the internets"
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..c2f713f
--- /dev/null
+++ b/index.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <title>loa</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
+    <meta property="og:site_name" content="loa" />
+    <meta property="og:title" content="loa" />
+    <meta property="og:description" content="shouting into the void" />
+
+    <link rel="stylesheet" href="/_loa/style.css">
+    <script src="/_loa/script.js"></script>
+  </head>
+  <body>
+    <main>
+<h1 style="margin-bottom:0">
+loa</h1>
+<div class="tagline">shouting into the internets</div>
+<ul class="home--post-list">
+<li>
+  <a href="/spite/">Spite-driven development, or how I ended up writing my own git server software</a>
+  <time class="home--post-published-at relative" datetime="2025-10-12T15:13:51+07:00" title="2025-10-12T15:13:51+07:00">2025-10-12T15:13:51+07:00</time>
+</li>
+</ul>
+    </main>
+  </body>
+</html>
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..f7dd472
--- /dev/null
+++ b/readme.md
@@ -0,0 +1 @@
+Static blog thing. Powered by [loa](https://khoe.thac.loan/_/loa/).
diff --git a/spite/index.dj b/spite/index.dj
new file mode 100644
index 0000000..9f41fb1
--- /dev/null
+++ b/spite/index.dj
@@ -0,0 +1,34 @@
+```
+title: Spite-driven development, or how I ended up writing my own git server software
+published_at: 2025-10-12T15:13:51+07:00
+```
+
+I hate how most pieces of software I use every day keeps getting worse and worse.
+
+Youtube used to keep buffering the video to completion even when paused, which was especially helpful when my internet connection was less stable: just open the video, pause, do whatever else then come back later to watch a fully buffered video. Nuh-uh, can't have that anymore. How about watching an endless stream of tiktok-style clips where controls are either non-existent or near-impossible to use without misclicks?
+
+Google-branded phones used to be reasonably priced and came with stable Android builds. Nowadays my overpriced Pixel phone silently drops calls, which I didn't find out until enough of my friends complained that I never picked up my phone.
+
+My bank's Android app used to just work. It wasn't _great_, mind you, but the features that were there, worked - QR code scanning, transfers, OTP, savings deposit, boring, useful stuff. Then they got some sort of feature envy and began stuffing every conceivable mobile wallet super-app bullshit in there, which, honestly, who cares? But then they forced me to care by shoving pop-ups in my face every time I try to do the aforementioned boring stuff. _Hey we know you're trying to make a quick transfer, but have you heard about our latest {-frequent traveller bitch miles-} promotion program in collaboration with Grab? No? Sure, let us crash the whole app. Oh you're back? Have you heard about our-_ . *Fuck. You.*
+
+GitHub used to just work, then an acquisition and a few intern seasons later, their UI slows down to a crawl and only decides to work some of the time now. Never mind the fact that they now use my open source code to feed those fancy Markov chains that have already started to make code reviews at my day job extra miserable.
+
+We use Microsoft cloud stuff at work, because traditional corporation. Teams used to-- lol no, corporate Microsoftware has always been crap. At least they're consistent.
+
+But all of that is, ultimately, fine. I get paid to use Microsoft's garbage software. For other shitty services that I'm not forced to touch, I just naturally touch them less and less, leaving me more free time for more rewarding hobbies, like writing and sharing more free software. If Github gets worse, I'll just host my code elsewhere. After all, sourcehut and gitea/codeberg exist, right?
+
+... right?
+
+Gitea, itself being a fork of Gogs, got forked yet again into Forgejo because apparently they tried to pull a GitLab and start paywalling certain features? All of these forkings smells awfully like the constant webdev sidegrade treadmill that I've had the misfortune to experience in my day job. I ain't gonna subject myself to _that_ on my own time, thank you very much.
+
+Sourcehut, while barebones, works fine. But then Drew - the owner - started to prioritize his online holy crusades over building useful software. That, combined with the graphql migration that seemed to take forever, makes me think sourcehut will stay barebones forever at best, or implode spectacularly, Matt-from-WordPress style, at worst.
+
+There's been a weird fixation on bringing politics into pretty much every software-adjacent public space for some reason. Witch hunts galore. Namecalling so rampant that words started losing meaning. _This and that company are literally fascists! No, trannies and Jews are ruining everything they touch!_ So on and so forth. Who. Fucking. Cares? There's no shortage of political forums, on or off the internet, why do you have to bring that to a software forum?
+
+FLOSS is being given a great opportunity to be actually relevant, simply because so-call big tech corporate software keeps getting more and more blatantly user-hostile. Meanwhile over here we FLOSS people keep bickering amongst ourselves, weirding people out and impeding actual work on the actual software. Case in point: Twitter epically sabotaged itself, just when the Fediverse was actually becoming usable. Did everyone embrace the first new useful decentralized protocol since email? Nope, the Mastodon half started blocking the Pleroma half on sight. I like Pleroma themes, but I haven't found a single Pleroma (or fork) instance where I can follow all of the people I want without running into a block. It's all so tiresome.
+
+But I digress.
+
+Point is, Github/Microsoft pissed me off enough to consider moving out, but none of the existing alternatives seems to spark joy for me, for various reasons ranging from objective and technical to subjective and petty, so I wrote my own out of both necessity and spite: <https://khoe.thac.loan/>. It lets people browse my code before deciding if it's worth pulling. It's static so I don't have to worry that much about security holes. It does what I want and nothing else so it's snappy. I like it when my software's snappy and does what I want.
+
+Now back to programming.
diff --git a/spite/index.html b/spite/index.html
new file mode 100644
index 0000000..a52a50d
--- /dev/null
+++ b/spite/index.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <title>Spite-driven development, or how I ended up writing my own git server software | loa</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
+    <meta property="og:site_name" content="loa" />
+    <meta property="og:title" content="Spite-driven development, or how I ended up writing my own git server software" />
+    <meta property="og:description" content="I hate how most pieces of software I use every day keeps getting worse and worse.
+
+Youtube used to keep buffering the video to completion even when paused, which was especially helpful when my internet connection was less stable: just open the video, pause" />
+
+    <link rel="stylesheet" href="/_loa/style.css">
+    <script src="/_loa/script.js"></script>
+  </head>
+  <body>
+    <main>
+<a href="/">« back to home</a>
+<span class="post--published-at">
+  published
+  <time class="relative" datetime="2025-10-12T15:13:51+07:00" title="2025-10-12T15:13:51+07:00">2025-10-12T15:13:51+07:00</time>
+</span>
+<h1 class="post--title">Spite-driven development, or how I ended up writing my own git server software</h1><p>I hate how most pieces of software I use every day keeps getting worse and worse.</p>
+<p>Youtube used to keep buffering the video to completion even when paused, which was especially helpful when my internet connection was less stable: just open the video, pause, do whatever else then come back later to watch a fully buffered video. Nuh-uh, can&rsquo;t have that anymore. How about watching an endless stream of tiktok-style clips where controls are either non-existent or near-impossible to use without misclicks?</p>
+<p>Google-branded phones used to be reasonably priced and came with stable Android builds. Nowadays my overpriced Pixel phone silently drops calls, which I didn&rsquo;t find out until enough of my friends complained that I never picked up my phone.</p>
+<p>My bank&rsquo;s Android app used to just work. It wasn&rsquo;t <em>great</em>, mind you, but the features that were there, worked - QR code scanning, transfers, OTP, savings deposit, boring, useful stuff. Then they got some sort of feature envy and began stuffing every conceivable mobile wallet super-app bullshit in there, which, honestly, who cares? But then they forced me to care by shoving pop-ups in my face every time I try to do the aforementioned boring stuff. <em>Hey we know you&rsquo;re trying to make a quick transfer, but have you heard about our latest <del>frequent traveller bitch miles</del> promotion program in collaboration with Grab? No? Sure, let us crash the whole app. Oh you&rsquo;re back? Have you heard about our-</em> . <strong>Fuck. You.</strong></p>
+<p>GitHub used to just work, then an acquisition and a few intern seasons later, their UI slows down to a crawl and only decides to work some of the time now. Never mind the fact that they now use my open source code to feed those fancy Markov chains that have already started to make code reviews at my day job extra miserable.</p>
+<p>We use Microsoft cloud stuff at work, because traditional corporation. Teams used to&ndash; lol no, corporate Microsoftware has always been crap. At least they&rsquo;re consistent.</p>
+<p>But all of that is, ultimately, fine. I get paid to use Microsoft&rsquo;s garbage software. For other shitty services that I&rsquo;m not forced to touch, I just naturally touch them less and less, leaving me more free time for more rewarding hobbies, like writing and sharing more free software. If Github gets worse, I&rsquo;ll just host my code elsewhere. After all, sourcehut and gitea/codeberg exist, right?</p>
+<p>&hellip; right?</p>
+<p>Gitea, itself being a fork of Gogs, got forked yet again into Forgejo because apparently they tried to pull a GitLab and start paywalling certain features? All of these forkings smells awfully like the constant webdev sidegrade treadmill that I&rsquo;ve had the misfortune to experience in my day job. I ain&rsquo;t gonna subject myself to <em>that</em> on my own time, thank you very much.</p>
+<p>Sourcehut, while barebones, works fine. But then Drew - the owner - started to prioritize his online holy crusades over building useful software. That, combined with the graphql migration that seemed to take forever, makes me think sourcehut will stay barebones forever at best, or implode spectacularly, Matt-from-WordPress style, at worst.</p>
+<p>There&rsquo;s been a weird fixation on bringing politics into pretty much every software-adjacent public space for some reason. Witch hunts galore. Namecalling so rampant that words started losing meaning. <em>This and that company are literally fascists! No, trannies and Jews are ruining everything they touch!</em> So on and so forth. Who. Fucking. Cares? There&rsquo;s no shortage of political forums, on or off the internet, why do you have to bring that to a software forum?</p>
+<p>FLOSS is being given a great opportunity to be actually relevant, simply because so-call big tech corporate software keeps getting more and more blatantly user-hostile. Meanwhile over here we FLOSS people keep bickering amongst ourselves, weirding people out and impeding actual work on the actual software. Case in point: Twitter epically sabotaged itself, just when the Fediverse was actually becoming usable. Did everyone embrace the first new useful decentralized protocol since email? Nope, the Mastodon half started blocking the Pleroma half on sight. I like Pleroma themes, but I haven&rsquo;t found a single Pleroma (or fork) instance where I can follow all of the people I want without running into a block. It&rsquo;s all so tiresome.</p>
+<p>But I digress.</p>
+<p>Point is, Github/Microsoft pissed me off enough to consider moving out, but none of the existing alternatives seems to spark joy for me, for various reasons ranging from objective and technical to subjective and petty, so I wrote my own out of both necessity and spite: <a href="https://khoe.thac.loan/">https://khoe.thac.loan/</a>. It lets people browse my code before deciding if it&rsquo;s worth pulling. It&rsquo;s static so I don&rsquo;t have to worry that much about security holes. It does what I want and nothing else so it&rsquo;s snappy. I like it when my software&rsquo;s snappy and does what I want.</p>
+<p>Now back to programming.</p>
+    </main>
+  </body>
+</html>