Compare commits

..

10 commits

Author SHA1 Message Date
mtgmonkey
596af4a731 daily - added 'for tomorrow' 2025-06-09 14:38:19 -04:00
mtgmonkey
a836ac0343 daily 2025-06-09 14:36:57 -04:00
mtgmonkey
36fa8afd57 fix clerical errors 2025-06-08 15:18:56 +00:00
mtgmonkey
d3b4134824 fix broken link 2025-06-08 15:14:00 +00:00
mtgmonkey
3f8c323750 daily 2025-06-08 15:09:10 +00:00
mtgmonkey
255615b838 fixed the address in config.toml 2025-06-08 10:09:52 +00:00
mtgmonkey
0f665b0fa8 fix the flake nixpkgs 2025-06-08 01:34:03 +00:00
mtgmonkey
5c6df6621c rework 2025-06-07 20:44:49 -04:00
mtgmonkey
a181c6d748 remove ./public result folder 2025-06-07 19:43:17 -04:00
mtgmonkey
6db0ac0470 nixify 2025-06-07 19:41:35 -04:00
12 changed files with 355 additions and 1 deletions

View file

@ -1,5 +1,5 @@
# The URL the site will be built for
base_url = "https://mtgmonkey.net"
base_url = "https://blog.mtgmonkey.net"
# Whether to automatically compile all Sass files in the sass directory
compile_sass = true

View file

@ -0,0 +1,15 @@
+++
title = "init post"
date = 2025-06-07
+++
This is my first daily message, posted in the afternoon rather than the morning. In the future, all fo these will be posted when I wake up.
Let's do some about me.
I use nixos with limine, ly, xmonad, qutebrowser, and nvf on the day-to-day. I program in haskell, elm, nix, and rust. Functional programming for the win! My vps is also nixos, and it's so convenient to share my home-manager config between the two!
### Links
All sites are built %100 declatatively with Nix.
- [My rgit server](https://git.mtgmonkey.net), which hosts all my repos, including that for this site and for my pet site.
- [My blog](https://blog.mtgmonkey.net), which you are reading now.
- [My pet site](https://mtgmonkey.net), Elmskell, which has a Haskell Scotty backend with an Elm frontend.

View file

@ -0,0 +1,87 @@
+++
title = "TLS Troubles and Embarrassing Secrets"
date = 2025-06-08
+++
Last night, this website saw hours of downtime because of a combination of silly mistakes. Let me explain.
### TLS Troubles
This site runs with the [Ferron](https://www.ferronweb.org/) webserver. When you have a website, you need to have TLS certificates so users can use HTTPS to access it. Ferron has these really great configuration options to do this automatically.
> [Nix](https://git.mtgmonkey.net/server-configuration.git/tree/services/ferron.nix), used to define Ferron's config.yaml
>
> ```nix
> -- ferron-conf.nix
> {
> global = {
> enableAutomaticTLS = true;
> useAutomaticTLSHTTPChallenge = true;
> };
> }
> ```
These automatic TLS certificates are issued by [Let's Encrypt](https://letsencrypt.org), a fantastic nonprofit you should definitely go check out. They do, however, have pretty strict [rate limits](https://letsencrypt.org/docs/rate-limits/). Luckily, they offer a [staging feature](https://letsencrypt.org/docs/staging-environment) with much higher rate limits.
I made a number of mistakes. The first was testing in production rather than on a local server.
The second was not taking advantage of Let's Encrypt's staging features. Ferron even has an option `automaticTLSLetsEncryptProduction` that, when `false`, lets one use Let's Encrypt's staging features. Even though there is a [whole page](https://www.ferronweb.org/docs/automatic-tls), and despite knowing not to experiment in a production environment, I thought I would be fine and forged ahead.
It was the seventh `systemctl restart ferron` which broke my sites. Suddenly, all I got was `ERR_SSL_PROTOCOL_ERR` from visiting *any* of my sites. There it was: Lesson learned. I will do my experiments locally going forward.
A problem still remained, however. This blog is updated daily, and is built with a [flake](https://git.mtgmonkey.net/blog.git/tree/flake.nix). This makes it fully declarative; a boon! Ferron, however, was configured to point to `${blog.packages.x86_64-linux.default}/wwwroot`, which would be outdated until I `systeml restart`ed the server. Ferron requests a new certificate from Let's Encrypt, though, meaning I would hit the rate limit if I restarted even a couple of times too many. To fix this, my Ferron https server points to a second http server, which hosts the blog.
> My much-overcomplicated ferron setup
>
> ```nix
> # ferron-conf.nix
> {
> global = {
> # enable automatic tls for https
> secure = true;
> enableAutomaticTLS = true;
> useAutomaticTLSHTTPChallenge = true;
> # let an https connection travel to the blog-ferron http server without error
> disableProxyCertificateVerification = true;
> # enable proxying to local servers (reverse proxying)
> loadModules = ["rproxy"];
> };
> hosts = [
> {
> # route requests for the blog to port 8181
> domain = "blog.mtgmonkey.net";
> proxyTo = "http://localhost:8181/";
> }
> ];
> }
> ```
>
> ```nix
> # blog-ferron-conf.nix
> # take the blog flake as an input
> { blog, ... }: {
> global = {
> # expose server to port 8181, so the main ferron server will proxy to it
> port = 8181;
> # the default flake output includes the static site at wwwroot
> wwwroot = "${blog.packages.x86_64-linux.default}/wwwroot";
> };
> }
> ```
This means I only need to `systemctl restart blog-ferron`, rather than ...`ferron`, meaning I don't trigger a new Let's Encrypt cert request every time. I can update my blog *however frequently I want* and *still* ensure the reproducibility of Nix.
All code above is, clearly, just simplified snippets; the actual file are linked below
- [ferron.nix](https://git.mtgmonkey.net/server-configuration.git/tree/services/ferron.nix), where ferron-conf.nix is in the let binding
- [blog.nix](https://git.mtgmonkey.net/server-configuration.git/tree/services/blog.nix), where blog-ferron-conf.nix is in the let binding
- [flake.nix](https://git.mtgmonkey.net/server-configuration.git/tree/flake.nix),the flake to which the above modules are imported
### Embarrassing Secrets
I spent all morning trying to configure different secrets management programs, from [agenix](https://github.com/ryantm/agenix), to [spos-nix](https://github.com/Mic92/sops-nix), to even a simple `.gitignore`. After finally getting agenix configured properly, it occured to me that my only 'secrets' were my ssh *public keys*, which are harmless to share! I don't need *any* secrets management, much less something as complex as agenix.
### By Tomorrow
- [ ] Polish up [the blog](https://blog.mtgmonkey.net) a little bit
- [ ] Get a [translation api](https://github.com/LibreTranslate/LibreTranslate) set up

123
content/daily/2025-06-09.md Normal file
View file

@ -0,0 +1,123 @@
+++
title = "Spacebar and a Translation Service"
date = 2025-06-09
+++
### Since Yesterday
- [ ] Polish up [the blog](https://blog.mtgmonkey.net) a little bit
- [X] Get a [translation api](https://translate.mtgmonkey.net) set up
- [X] Get a [chat client](https://chat.mtgmonkey.net) set up
### Spacebar Server
The Spacebar server is super easy to run on Nix - its flake just works! Below I write the relevant portions of my Nix config.
> ```nix
> # flake.nix
> {
> # import server from the flake.nix on gh
> inputs.spacebar-server.url = "github:spacebarchat/server";
> outputs = {
> spacebar-server,
> ...
> }: {
> nixosConfigurations."server" = nixpkgs.lib.nixosSystem {
> system = "x86_64-linux";
> specialArgs = {
> # pass spacebar-server to any module that wants it
> inherit spacebar-server;
> };
> modules = [
> # add spacebar to configuration
> ./services/spacebar.nix
> ];
> };
> };
> }
> ```
>
> ```nix
> # services/spacebar.nix
> {
> spacebar-server,
> lib,
> pkgs,
> ...
> }: {
> systemd.services.spacebar-server = {
> serviceConfig = {
> Type = "simple";
> ExecStart = "${lib.getExe
> spacebar-server.packages.x86_64-linux.default}";
> # Ensure the server is run by a non-priveleged user for security
> RemainAfterExit = true;
> User = "spacebar";
> Group = "spacebar";
> };
> environment = {
> # Specify location of uploaded files and the db
> DATABASE = "/var/lib/spacebar-server/database.db";
> STORAGE_LOCATION = "/var/lib/spacebar-server/files/";
> };
> };
> # Create user that runs the server
> users.users.spacebar = {
> isSystemUser = true;
> group = "spacebar";
> # Create home, where database.db and files/ are located
> home = "/var/lib/spacebar-server";
> createHome = true;
> # utility packages while SSHing into the user
> packages = [
> pkgs.git
> # package to test drive the server with the unpriveleged user
> spacebar-server.packages.x86_64-linux.default
> # edit the database
> pkgs.sqlite
> ];
> # I have noshell in my flake, so there won't be a login shell unless it's specified
> shell = pkgs.bash;
> };
> # Groups need to be 'initialized' on nixos
> users.groups.spacebar = {};
> }
> ```
This configuration runs great! Well, given that you configure the database correctly - sqlite is going to be the death of me! `api_endpointPublic`, `cdn_endpointPublic`, and `gateway_endpointPublic` *all need to be set* before the server's connected up properly to take new users. That means running the below series of commands from the `sqlite3` repl - an interface without backspace~
> ```bash
> # as spacebar user
> sqlite3 ~/database.db
> ```
>> ```sql
>> update config
>> set value='"https://spacebar-api.mtgmonkey.net/api/v9"'
>> where key='api_endpointPublic';
>> update config
>> set value='"https://spacebar-api.mtgmonkey.net"'
>> where key='cdn_endpointPublic';
>> update config
>> set value='"wss://spacebar-api.mtgmonkey.net"'
>> where key='gateway_endpointPublic';
>> .exit
>> ```
Beleive it or not, the server was only the *easy* part! I next had to configure the client.
There are generally 3 web clients to choose from when it comes to Spacebar.
- [The official client](https://github.com/spacebarchat/client), written in React, is the most beautiful. It's not fully featured, however, missing the home page, friends, DMs, and more.
- [The legacy client](https://github.com/spacebarchat/client/tree/legacy-v2), written in Typescript, is known to work. Unfortunately, it is quite outdated and, no matter how much I tried, I could not get it to build on Nix.
- [JankClient](https://github.com/MathMan05/JankClient), an unofficial client written in TypeScript, is, as its name suggests, quite janky. It requires frequent browser refreshes to fix visual glitches and is poorly optimised on mobile.
JankClient, despite its glitchiness, is the one I finally decided on. Though the official client has a flake, it's currently non-functional, and has been for a while. Neither of the other two have flakes, unfortunately, but it's easy enough to run JankClient with Docker. I first ran `nix-shell -p compose2nix` before renaming `compose.yaml` to `docker-compose.yaml` (as compose2nix requires). I then ran compose2nix and used the output as the basis for [services/spacebar.nix](https://git.mtgmonkey.net/server-configuration.git/tree/services/spacebar.nix). I made a couple of major modifications: Firstly, I bound it to a different port as a matter of personal preference. I also changed all instances of `podman` with `docker`, as my rgit instance runs on docker and nix can only have 1 declaration of `virtualisation.oci-containers.backend`. Secondly, the generated `docker-build-spaceclient-jank.service` wouldn't run properly, so I had to build the image manually. Finally, I added an [anubis](https://github.com/TecharoHQ/anubis) PoW captcha and a reverse client entry in my [ferron](https://www.ferronweb.org) webserver, as appropriate.
All code above is, clearly, just simplified snippets; the actual file are linked below
- [flake.nix](https://git.mtgmonkey.net/server-configuration.git/tree/flake.nix).
- [services/spacebar.nix](https://git.mtgmonkey.net/server-configuration.git/tree/services/spacebar.nix).
- [services/ferron.nix](https://git.mtgmonkey.net/server-configuration.git/tree/services/ferron.nix).
- [services/translate.nix](https://git.mtgmonkey.net/server-configuration.git/tree/services/ferron.nix).
### For Tomorrow
- [ ] Polish up [the blog](https://blog.mtgmonkey.net) a little bit

6
content/daily/_index.md Normal file
View file

@ -0,0 +1,6 @@
+++
title = "Daily Posts"
sort_by = "date"
template = "blog.html"
page_template = "blog_page.html"
+++

26
flake.lock generated Normal file
View file

@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1749143949,
"narHash": "sha256-QuUtALJpVrPnPeozlUG/y+oIMSLdptHxb3GK6cpSVhA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d3d2d80a2191a73d1e86456a751b83aa13085d7d",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

24
flake.nix Normal file
View file

@ -0,0 +1,24 @@
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable";
};
outputs = {nixpkgs, ...}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system} = {
default = pkgs.callPackage ./package.nix {};
};
devShells.${system} = {
default = pkgs.mkShell {
nativeBuildInputs = [
pkgs.zola
pkgs.tokei
];
shellHook = ''
${pkgs.cowsay}/bin/cowsay Welcome to the devshell
'';
};
};
};
}

30
package.nix Normal file
View file

@ -0,0 +1,30 @@
{
zola,
lib,
stdenv,
...
}:
stdenv.mkDerivation {
pname = "elmskell-blog";
version = "0.1.0";
src = ./.;
nativeBuildInputs = [
zola
];
configurePhase = ''
# zola check
'';
buildPhase = ''
zola build
'';
installPhase = ''
mkdir -p $out/wwwroot
cp ./public/* $out/wwwroot/ -r
'';
meta = {
description = "Andromeda's blog, statically served via Zola";
longDescription = "Andromeda's blog, statically served via Zola";
homepage = "https://mtgmonkey.net";
license = lib.licenses.wtfpl;
};
}

14
templates/base.html Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Andromeda's blog</title>
</head>
<body>
<section class="section">
<div class="container">
{% block content %} {% endblock content %}
</div>
</section>
</body>
</html>

12
templates/blog.html Normal file
View file

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">
{{ section.title }}
</h1>
<ul>
{% for page in section.pages %}
<li><a href="{{ page.permalink | safe }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
{% endblock content %}

9
templates/blog_page.html Normal file
View file

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">
{{ page.title }}
</h1>
<p class="subtitle"><strong>{{ page.date }}</strong></p>
{{ page.content | safe }}
{% endblock content %}

8
templates/index.html Normal file
View file

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">
This is Andromeda's blog.
</h1>
<p><a href="{{ get_url(path='@/daily/_index.md') }}">Daily Posts</a></p>
{% endblock content %}