Compare commits
10 commits
c028e21211
...
596af4a731
Author | SHA1 | Date | |
---|---|---|---|
![]() |
596af4a731 | ||
![]() |
a836ac0343 | ||
![]() |
36fa8afd57 | ||
![]() |
d3b4134824 | ||
![]() |
3f8c323750 | ||
![]() |
255615b838 | ||
![]() |
0f665b0fa8 | ||
![]() |
5c6df6621c | ||
![]() |
a181c6d748 | ||
![]() |
6db0ac0470 |
12 changed files with 355 additions and 1 deletions
|
@ -1,5 +1,5 @@
|
||||||
# The URL the site will be built for
|
# 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
|
# Whether to automatically compile all Sass files in the sass directory
|
||||||
compile_sass = true
|
compile_sass = true
|
||||||
|
|
15
content/daily/2025-06-07.md
Normal file
15
content/daily/2025-06-07.md
Normal 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.
|
87
content/daily/2025-06-08.md
Normal file
87
content/daily/2025-06-08.md
Normal 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
123
content/daily/2025-06-09.md
Normal 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
6
content/daily/_index.md
Normal 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
26
flake.lock
generated
Normal 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
24
flake.nix
Normal 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
30
package.nix
Normal 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
14
templates/base.html
Normal 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
12
templates/blog.html
Normal 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
9
templates/blog_page.html
Normal 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
8
templates/index.html
Normal 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 %}
|
Loading…
Add table
Add a link
Reference in a new issue