Log in
Seblog.nl

Seblog.nl

Seb doet toetsenborden

Het begon allemaal bij een foto die ik zag op een Discord-server. Ik ben al jaren (tien, ongeveer) een trouw gebruiker van de ingebouwde MacBook toetsenborden en de Magic losse varianten die Apple daarvan verkoopt. Daarmee dus ook Qwerty, de rechtopstaande Enter, en de §/±-knop, die ideaal is voor je eigen sneltoetsen, omdat er geen enkele standaard toetsencombinatie is die die knop gebruikt.

Maar het begon dus bij die foto, met daarop de Moonlander van ZSA. (Had ik al gezegd? Klikken en/of deze serie lezen is op eigen risico.) Het was het begin van een lang konijnenhol naar beneden (?) waar ik, met tussenpauzes, steeds dieper in terecht ben gekomen. Ik ga er de komende week over bloggen, want het is gewoon te veel om in één blog te beschrijven.

Wat ik hier in elk geval alvast kan verklappen, is dat ik de Moonlander heb aangeschaft, in gebruik heb genomen, weggelegd heb, weer in gebruik heb genomen en op het punt sta om hem weer weg te leggen. Dit is hoe je zo’n konijnenhol induikt: in stapjes.

Meer info over deze reis dus komende week. Hier een overzicht van de delen:

Bij wijze van vanantie naar een onbekende Albert Heijn gegaan. Kan ik iedereen aanraden, alles wat je kent maar dan op een andere plek.

Je komt wel met meer zooi thuis die je anders nooit koopt.

Altijd alles kwijt zijn is vervelend, maar een idee hebben, opstaan, naar die ene onlogische plek lopen en daar inderdaad vinden wat je zocht vind ik stiekem ook best eng.

Advent of Code

Ik was dus bezig met Advent of Code maar ben uitgevallen na dag 12. Het is de eerste keer dat ik meedoe (collega was erg enthousiast) en ik had mezelf direct al een moeilijkheid gegeven van elke dag in een andere programmeertaal te werken. Maar na 12 dagen werd ik wel een beetje tureluurs van DuckDuckGoën hoe je een bestand moet inlezen in weer een andere taal. De laten waren nog niet op, ik had nog tot dag 18 aan talen klaar staan, met een nog veel langere lijst aan ideeën voor meer talen.

Dus vooralsnog ben ik gebleven bij de volgende 12 talen.

1. C

De eerste dag was makkelijk en ik herkeek colleges over C dus ik dacht, waarom niet. Beetje mezelf makkelijk van af gemaakt door niet zelf de file in te lezen maar hem in een #define te stoppen.

2. Elixir

Hier ben ik best bedreven in, dus deze was makkelijk. Hier hoefde ik ook nog niet op te zoeken hoe je een bestand opent want dat weet ik gewoon uit mijn hoofd. Het was ook een goede opdracht voor pattern-matching, dus Elixir leek me een prima keuze ervoor.

3. PHP

Dit is mijn 'moedertaal' als het om programmeertalen gaat, dus ook hier viel het nog allemaal best mee. Ik heb de oplossing ook ontzettend procedureel opgeschreven.

4. Ruby

Op een of andere manier kreeg ik heel erg Sandi Metz vibes toen ik deze opdracht las, dus daarom niet gewoon Ruby? Ruby is een taal die tegenwoordig een slechte naam heeft ('sloom', 'magic', 'onduidelijk'), maar ik vind het wel een heel vriendelijke taal.

5. Rust

Dit is een van de langere stukken code, gewoon omdat je veel meer zelf moet doen in Rust. (In C ook, maar zoals gezegd, de eerste dag was vrij simpel, en ik speelde vals.) Ik heb de uiteindelijke oplossing achteraf zelfs nog een stukje opgeruimd, qua code. Dit was niet de eerste keer dat ik me in Rust verdiepte, maar ik merk dat ik dankzij de colleges over C en assembly meer achtergrond had voor waarom bepaalde dingen werken zoals ze werken. Was erg interessant, eigenlijk wil ik meer van de problemen hierin doen.

6. Javascript

Dit was een maandag en ik had weer even een taal nodig die ik goed kende. Vooral in mijn oplossing voor deel 1 vond ik mezelf erg grappig (een Fish object met een anotherDayInTheLife() method die soms een kind returnt), maar helaas moest dat allemaal kapot voor deel 2.

7. Go

Ooit heb ik geprobeerd een go-bot te maken in Go, aan de hand van een boek dat het in Python doet. Ik had eigenlijk direct een segmentation fault omdat ik ergens een nil dereference, en dat was het einde van mijn avontuur in Go. Maar deze opdracht ging prima en segfaultloos.

8. Clojure

De colleges over C en assambly waren op dit punt aangekomen bij functioneel programmeren in Scheme. Hoewel ik waarschijnlijk wel ergens de Kawa-interpreter had kunnen vinden, besloot ik toch om naar de wat modernere en momenteel populairdere lisp Clojure te kijken. Het ging best aardig, maar in deel twee zat ik compleet vast en na vier uur vloeken besloot ik de oplossing voor PHP – die ik inmiddels prima in mijn hoofd had – uit te schrijven. Ook daar heb ik lekker fancy lopen doen met operators die ik normaal nooit kan gebruiken. Toen ik daarna het volgende college over Scheme aanzette, maakte ik kennis met let en het feit dat het niet erg is om dat te gebruiken (maar een beetje). Gewapend met dat bouwblok heb ik ook de versie in Clojure nog werkend gekregen die avond.

9. Python

De go-bot zoals beschreven in het boek had veel weg van de vorm van dit probleem: het zijn punten op de kaart en als die buren zijn worden ze samengevoegd. Daarnaast had ik gewoon ook even zin in een wat bekendere taal, na het Clojure-avontuur. Omdat het een kaartje is heb ik me bij deze ook uitgeleefd met de weergave van dat kaartje, met allemaal kleuren in de terminal.

10. Erlang

Tegen deze tijd waren eindelijk mijn dromen over haakjes sluiten verdwenen, komen ze met deze opdracht! Over Erlang: als je eenmaal Elixir kent valt Erlang best wel mee, maar de syntax is wel heel absurd als je het nog nooit gezien hebt. Maar ook hier kon ik erg goed pattern-matching gebruiken, vandaar de keuze.

11. Crystal

Ik moest weer even terug naar een gecompileerde taal, dus dat werd Crystal. Dit heb ik echt helemaal speciaal voor Advent of Code geïnstalleerd, maar ik ben dus best wel onder de indruk: het heeft de vriendelijkheid van Ruby, maar dan mét static type checking en het compiled dus naar iets best snels. Ik heb me ook hier helemaal uitgeleefd met terminal-kleuren en er zelfs een animatie van gemaakt.

12. Typescript

Het is bijna valsspelen, want Typescript en Javascript zijn echt niet zo heel anders, maar ik gebruikte Deno, de Typescript-versie van Node, en daarvoor moest ik toch echt weer even opzoeken hoe ik een file moest inlezen.


Dit rammelende stukje werd mede mogelijk gemaakt door mijn gevoel dat ik meer moet bloggen. Misschien ga ik nog wel verder aan Advent of Code, maar ik wil eigenlijk ook weer dingen gaan doen aan dit weblog. Vandaar.

Stronk

Een tijdje terug was er een landelijk nieuwsbericht over rellen in het Goffertpark, op dezelfde dag dat er ook een tribune sneuvelde onder het gewicht van een grote groep hossende Vitesse-supporters. De rellen waren flink uit de hand gelopen, er werd met van alles gegooid, ‘zelfs met een boomstam’, stond er.

Nog veel langer geleden fietste ik op een dag naar werk. Mijn route ging destijds door het Goffertpark. En het was niet zomaar een dag: het stormde. Net na het stadion stond een boom die al heftig ver was doorgezakt en flink stond te schudden. Ik heb toen naar de boom geknikt en ben afgeslagen, richting de Weg door Jonkerbos, om er niet onder te komen te liggen.

Toen ik terugfietste naar huis bleek de boom inderdaad te zijn omgevallen. Later is hij weggezaagd, bleef alleen de stronk over. Mijn route is sindsdien veranderd, maar telkens als ik er langs fiets groet ik die stronk nog even.

Vandaag kwam ik weer langs de stronk, en zoals ik inmiddels al vermoedde was het bovenste stuk, dat al een tijdje wat losser hing, weg. Gek, dacht ik, ik had op die bewuste dag niet verwacht die boom jaren later nog schade zou veroorzaken en het tot het landelijk nieuws zou schoppen.

I noticed that in my new login-setup, sessions last much longer. It used to be an hour or 3, but now I have been away from my computer for a full day and I am still logged in.

I like it, because it means that I will see my site more often in a logged-in state (as will others), but I will add to the ever-growing todo-list that I need some 'I trust this computer' checkbox.

Next up must be Webmentions, I feel. I just posted this article with a lot of backlinks to old posts, but since I send all Webmentions in a synchronous way on first-post-visit, it took ages, and I actually got a 502 Bad Gateway out of my site. But I also need weekend, so we will see.

IndieAuth without IndieAuth.com

In February 2017, I added the ability for other people to log in to my site, both via IndieAuth and Twitter. Since June 2019 this feature is more useful, as I moved all my checkins to being ‘private’, meaning there is actually something to see (albeit sparsely posted).

For this, I used the now outdated and deprecated service IndieAuth.com, which back then was also used to log in to the IndieWeb wiki. Due to the confusion between IndieAuth.com (the service) and IndieAuth (the protocol), a new project called IndieLogin was started and the IndieWeb wiki moved to that one. Since it didn’t offer that login service to others, as IndieAuth.com did, I never switched.

Since this week I have a new IndieAuth endpoint for myself. (Meaning: a place I can use to prove my own identity to others.) Unfortunately, it is not compatible with the outdated IndieAuth.com, so I could not use it to prove my identity to my own site. Because this felt wrong (and because I had the IndieAuth flow in my head again now), I decided that my site needed to do that part of IndieAuth on it’s own as well.

Since I was at it, I also have Twitter login working via the new flow. That was easier, because the library support was more plug-and-play.

Juggling with cookies

Last week I wrote about my new setup where I run two applications, the old PHP and the new Elixir, behind an unusual NGINX configuration. For the aforementioned feature of authentication, I had to create even a bigger beast.

All the posts, and all the logic for showing or hiding private posts, are still in PHP. The new authentication is in Elixir. Both ends are storing the information about the current logged-in user in session cookies. How do I merge those two into one user experience?

Luckily, since both applications run under the same domain, the browser is actually unaware of the fact that there are two apps. It just sends all cookies back.

So, in PHP I ask for all headers, look for a cookie-one that is named _yak_key, and if it is there, it makes a direct localhost call to Yak, the Elixir server, with that cookie. Yak exposes one route that just returns the currently logged-in user as a JSON. If that call returns a user, PHP can set a cookie of its own.

Then for the logout, PHP used to redirect to the homepage, but now it redirects to the page that logs you out of Yak, which redirects to the homepage.

At some point this setup will change again, but for now it seems to me as a good way to make one further step in the features I want to support in the new version of my site, while keeping the old one running.


Oh yeah, you may hunt down that localhost-call that checks the login status. It is actually exposed to the outside world too. But if you can provide it with a cookie for a person that is not you, that person and I have a problem anyway, regardless of you knowing the endpoint.

Showing incorrect IndieAuth redirect_uri to the user

Last Thursday I started using my new IndieAuth endpoint, which I can use authorize apps build by others (like the Quill Micropub client), to do stuff with my site (like posting this blogpost for example). In the following weekend I added a lot more validations than just my password, making it a safer endpoint.

One of these validations is the redirect_uri. My previous endpoint already showed this to me on the login-page, so I could manually inspect it, which is a good practice. The spec, however, describes that one should fetch the client_id (which is a URL) and look for a link with the rel-value of `rel="redirect_uri", which can be either in the HTML or in the HTTP Header.

So this is what it (currently) normally looks like:

Image showing the redirect_uri in grey.

And this is what it looks like when the redirect_uri differs from scheme, domain and/or port, and is also not present in at the client_id.

Image showing the redirect_uri in red and with explanation, plus the discovered redirect_uri.

Note that it is okay for Quill not to advertise another redirect_uri, for it is redirecting to a URL with the same scheme, domain and port. It only needs to add the link if it wants a URL where one of these are different. It is now clearer that someone who is not Quill is trying to steal a token.

Yak, strangling PHP with Phoenix and NGINX

At the time of writing this blogpost, my site is written in PHP, and most of the code is many years old. One could call it a legacy application: I was not very good at organising code back when I started it, so it is a bit of a mess.

Like any legacy application, rewriting it would be costly. There are many features I don’t want to lose, but at the same time I don’t want to lock myself up and rewrite them all before I can show and use any work. This kind of ‘Big Bang release’ would eat up all my free time for the next months and my motivation probably won’t make it all the way through.

A Yak to shave

Yesterday I attended the first IndieWebCamp since ‘March 2020’ and I didn’t want to come empty handed. I forced myself to start a new project called Yak.

This is actually the fifth project called ‘Yak’ on my system, but it’s the first one I actually deployed. All the Yak-projects focus on a different part of the app, since there is always something you think you should do first before the rest can start.

The newest and deployed Yak consists of a new IndieAuth endpoint. I was able to shave off this part since IndieAuth tries to be decoupled from the rest of the site: its endpoint might even be on a different domain.

I didn’t go that route though: I decided to strangle.

The simple strangle

With ‘strangle’ I refer to the ‘Strangler Pattern’, after the now more friendly(?) Named term ’Strangler Fig Application’ by Martin Fowler. This plant grows around another tree, slowly taking over its shape and killing it in the process, so that eventually, all that remains is the fig.

In terms of Seblog.nl, I would like Yak to take over more and more features from the PHP-version, until finally Yak is my new CMS. I say ‘PHP-version’, be because yes, Yak is written in Elixir now. How would I go about taking over features then? My first attempt was this:

index index.html index.php;

location /auth {
    # ... proxy headers
    proxy_pass http://localhost:4001;
}

location / {
    try_files $uri $uri/ index.php$is_args$query_string;
}

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    fastcgi_index /var/www/seblog.nl/index.php;
    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param PATH_INFO $fastcgi_script_name;
    include fastcgi_params;
}

That’s a pretty normal PHP setup, but with one path as a proxy. Yak is listening to port 4001 and NGINX sends all traffic for /auth and sub-folders to it. (A firewall keeps visitors for 4001 from outside away, don’t worry.) This way I can have my IndieAuth endpoint be in a different application while still maintaining one domain to the outside.

But since this is my hobby project, I can get niftier.

Proxy all the things

The IndieAuth route is fairly well scoped and easily separated from the rest of the app. For creating posts via Micropub, I can also separate a route and then store the created posts in the same file folder (or database) since both apps run on the same machine. But not all features are that easily separated.

To make it myself easier to add new routes to Yak, I created the following monster:

index index.html;

location @yak {
    # ... proxy headers
    proxy_pass http://localhost:4001;
    proxy_intercept_errors on;
    error_page 404 = /index.php$is_args$query_string;
    error_page 502 = /index.php$is_args$query_string;
}

location / {
    try_files $uri @yak;
}

location ~ \.php$ {
    # same as previous PHP
}

Since Yak is pretty fast (the Phoenix framework logs its response times in microseconds instead of milliseconds), I decided to just route all traffic through Yak. If Yak has the answer, that answer will be the final answer. But all routes that get a 404 Not Found error from Yak, are given to PHP afterwards.

This way the old site still functions: PHP is not even aware that someone has looked at the request before it. I also added the 502 Bad Gateway just in case Yak is down, which I expect only to happen for brief moments during big configuration changing deployments.

A few details to note: it all starts by adding @yak as a final location to try_files. Only the last location can be a virtual one, otherwise I would have put PHP directly in that list. I did remove the $uri/ and removed index.php from the index directive at the top: without this the homepage was not delivered to Yak.

The other crucial bit is the proxy_intercept_errors on, because that traps the errors in the proxy and enables you to add cases for certain errors. With error_page 404 = /index.php$is_args$query_string I send them to PHP. Other errors from Yak, like 401 and 403, are still just rendered as they came from Yak.

Fixing my POST to Micropub

While the above does work for most of my site, it broke my Webmentions and Micropub. (Yes, I tried posting this a few hours ago.) The previously described method makes all requests in PHP come in as GET, which makes sense for rendering an error page.

After a lot of trial and error, reading NGINX documentation and even trying to get Yak to proxy, I decided to localise the few POST requests I needed to be PHP and added those explicit in NGINX.

location /webmention {
    try_files $uri $uri/ /index.php$is_args$args;
}
location /micropub {
    try_files $uri $uri/ /index.php$is_args$args;
}

Once I moved these endpoints to Yak, I can remove them from my NGINX.

Meer laden