Two short updates:
I designed a minimalistic “Dark Theme” for this website. It can be selected in two ways. If your browser supports it, you can manually select the page layout. For example, in Firefox you can press Alt -> View -> Page style -> Dark theme. However, this makes the selection only for the current page. A slightly less transient option is to use the switch button in the navigation bar. I added some Javascript to save your preference for one session only in a little cookie, so that your preference sticks as you navigate to other pages of this website. The cookie becomes ineffective once you close your browser. The code I use for this is a bit crude currently, but I’ll improve on it over time.
Since I started this website, I have had “etc” in the title on the landing page. But only now have I actually made an “etc” section with a navigation header, under which I will collect miscellaneous pages. Check it out!
A friend challenged me to implement the option to leave comments on blog posts over the Christmas break, and rightfully so. I write these posts about things that currently interest me, not because I think I know it all, but to record a thought process or idea for later review by me or others. A blog is not the place for publishing well-polished papers, but instead ideally is a playground for developing and testing ideas in interaction with others. It is a big mistake when writing anything really, to not ask for feedback. That’s why I always share whatever I write with my friends, to see if something resonates. The conversations that this sometimes sparks up have been private so far, but I would like to at least give everyone (including all spambots out there) the opportunity to weigh in.
But there’s a challenge: my website is completely static, meaning that there is no backend server with which a user can interact to leave a comment on my blog. I wasn’t willing to give up the advantages of a static website just yet: it’s super fast, it’s secure, and I host it completely for free. There are some solutions for nevertheless still allowing users to leave a comment, but they all require the comments to be stored externally (e.g. on reddit), or involve some third-party widget to interact with an external database. So either the content is not visible directly on my website, or I have some ugly widget on my website messing with the slick look that I’m finally pretty happy with. But worst of all, in both those scenarios I do not have any control over the storing of the comments, as they are not part of my website!
But then I found a really cool option. This nice guy wrote a bot called staticman. The bot takes input from the form under this blog post, creates an appropriate .yml file for the comment, connects to its very own GitHub account, and then makes a merge request for adding the comment to the appropriate blog post. It is also possible to automatically push to my live master branch, but I preferred to have an extra check to guard against spam. This is awesome for static websites with auto-deployment, such as mine, which is hosted on Netlify. It literally solves all problems I had with other options: I have no external widgets on my website, yet all comments are visible on the same webpage as the blog. Most importantly, all comments are stored together with my website, and they are under my version control.
The writer of staticman created a public staticman GitHub account, but I found that it was horribly overloaded and being blocked by GitHub restrictions on the maximum amount of merge requests that are allowed per hour, as many people started using it. But the idea of staticman was too cool to give up immediately, so I decided to set up a raspberry pi that I borrowed from a friend with the GitHub bot.
It took me a few days to set everything up, there was plenty to do and I made enough mistakes on the way. On my website I needed to write Hugo code for sending comment information to the bot, and for showing existing comments under the right blog posts. I also added a captcha to limit spam. It took me a while to find out how to send blog posts to the right folders and render them on the right web pages. It was useful to look at this example website. But most importantly, to allow people to reply on comments of others, this blog was incredibly helpful. Other things you have to do for this setup is creating appropriate configuration files with options and instructions for the GitHub bot, and of course setting up the actual GitHub bot itself, by creating an account and generating a key on GitHub so that the bot can access the account. You also need to give it access to a branch of the repo that your website is stored on.
Then there’s setting up the staticman bot on the raspberry pi. I tried using docker since node.js didn’t work for me out of the box. But getting docker to work on the ARM architecture of the raspberry pi was much more of a pain than fixing the errors I had with the npm
command. Then… some port magic, some bug fixing for bugs that I created myself, testing, and voila!
This is the flow:
You fill in the form, and send the data to the Github bot running on the raspberry pi at my home.
The GitHub bot logs in to GitHub, creates a branch, and makes a merge request to apply a commentid.yml
file and put it in the right folder in the repo of my website.
I allow the merge request (optional: can also be automatic).
GitHub hooks into Netlify, and the website is automatically updated and deployed after the merge request.
We’ll see how it goes! Feel free to respond, leave ideas, suggestions etc. Be aware that your comment will not automatically show since I need to approve them first. I might change that if it turns out the spam is not too bad. A thing I’ll consider adding later is the option to receive emails when people reply on your comment, but for now I’ll test the current setup first. We’ll see how the raspberry pi holds up. It will be a bit slow, and the connection is not secured. If it’s broken please let me know over email!
If you write an interactive program that takes user commands as an input, you can select the appropriate action for a given command by going through a sequence of if... else if
statements.
But if you write in Python, there’s always a cooler way to do things. A method I like personally in this situation is defining a dictionary where the keys are the command strings, and the corresponding values are lambda expressions.
In the following definition, all commands are called with command[cmd](args)
. When we want to deal with faulty commands immediately in a single line we can write command.get(cmd, lambda x: (error(input), help_message()))(args)
.
By passing a command string to the dictionary as a key, the lambda expression corresponding to that command key is selected.
But in order to be fully applied, the lambda function still required an argument, which we can simply pass behind the call to the dictionary.
Although this method is maybe not more efficient… I would say it wins when scored on style.
def read_cmd(input):
inputs = input.split()
cmd = inputs[0]
args = inputs[1:]
commands = {
"help": lambda x: help_message(),
"poem": lambda x: say_poem_for(x[0]),
"say": lambda x: banner_say(" ".join(x)),
"exit": lambda x: banner_say("Bye cruel world!")
}
commands.get(cmd, lambda x: (error(input), help_message()))(args)
# Fabricate some fake user inputs for testing
user_inputs = ["Incorrect command", "say Welcome to the mean poem machine", "poem reader", "exit"]
for user_input in user_inputs:
read_cmd(user_input)
The get
function of a dictionary deals with wrong commands by returning a default value, which in our case also has to be a function, as we pass args
to it.
The one-liner command.get(cmd, lambda x: (error(input), help_message()))(args)
therefore does the same as:
try:
command[cmd](args)
except:
error(input)
help_message()
To run the code for yourself, you could use these silly functions. Run them in Python 3.
def help_message():
print("""
help see this menu, obviously
say say some text placed in a ascii banner
poem say a little poem for your muse
exit say bye!
""")
def banner_say(message):
print("""
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
%s
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
""" % message)
def say_poem_for(muse):
print("""
Dear %s,
Roses are not blue
Violets are not red
Whatever you do
It trumps being dead
""" % muse)
def error(incorrect_input):
print("""
'%s' was an example of an incorrect command
""" % incorrect_input)
Which together produces the following output:
'Incorrect command' was an example of an incorrect command
help see this menu, obviously
say say some text placed in a ascii banner
poem say a little poem for your muse
exit say bye!
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Welcome to the mean poem machine
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Dear reader,
Roses are not blue
Violets are not red
Whatever you do
It trumps being dead
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Bye cruel world!
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In NeoMutt, which I use for email, I wanted view email conversations (a “thread”) as a tree-like representation. This option can be enabled by sorting on threads. However, by default threads are sorted by their oldest message, which is the original email to which others started responding. This causes the unwanted behavior that new emails that belong to a thread to not show on top of my inbox, but instead are hidden somewhere deeper in my inbox, sorted at the date of the older original post. I did not quickly find people with similar wishes (and solution) on the internet, so I considered it potentially helpful to write down my solution here.
To solve this behavior, I defined a secondary sorting method.
The primary sorting method is on threads, but I want threads themselves to be sorted based on the date of the newest message.
Also, I still want to ensure that newest messages show up on top, and not on the bottom (this is the reverse of the default setting in NeoMutt).
I achieved this by setting these variables in my neomuttrc
:
set sort = threads
set sort_aux = reverse-last-date-received
This produces a tree-like representation as such:
Note that this is a very messy thread, where nobody cared to change the email title, and where multiple threads branch off from each other. Nevertheless, using NeoMutt with the thread sorting, I know exactly who responded to who.
I added my public PGP key to this website ( here ) so you can send me encrypted email if you would like to. PGP stands for “Pretty Good Privacy”. I used the free software GPG (Gnu Privacy Guard) program that implements the OpenPGP standard, and for which most mail programs have extensions or native support. Without delving into mathematical details (about RSA for example), this post quickly summarizes how to use GPG.
I assume here that you will be using GPG on the command line on Linux. If you are on another system, download the appropriate version for your system.
Asymmetric cryptography uses a public-private key pair.
So we first have to make GPG create this key pair for us.
If at any point you are lost, you can always run gpg --help
.
To create a new key pair, run gpg --full-generate-key
and follow the instructions.
You can accept the default for most settings.
One setting of interest is the expiration date.
Although it sounds undesirable, you do want your key to expire after a certain amount of time.
If you lose access to your own keys and you did not set an expiry date, then all the people that have your key have no way of knowing that the key is no longer valid. Whereas if you set an expiry date, everyone that has your key at least knows to no longer use it after the expiration date, somewhat mitigating the results of you losing access to your key.
If you did not lose your keys however, you can always extend the expiration date.
This means that you can have the added security measure of the expiration date without the inconvenience of making and distributing a new key every time.
A normal expiration time is two years. TLDR; let the key expire after 2yrs.
The generated keys are stored in a keyring, which is by default ~/.gnupg/pubring.kbx.
It depends on your system whether you have a nice GUI for managing all your keys.
GNOME-users for example have the seahorse
program.
If you want to check your own public keys and the keys of others you stored using GPG on the command line, simply run gpg -k
. You can also inspect your private key, but people handling a private key need to know what they are doing and can figure that out themselves.
Find the key associated with your email (the uid
) entry.
The pub
entry shows, amongst other things, the expiration date and the fingerprint of your public key.
Usually people share their key in so-called “ascii armored” form, like I did on this website.
To produce this ascii form of your key, copy your aforementioned public key fingerprint (a long string of random numbers and letters) and run gpg -a --export [yourfingerprintgoeshere]
.
If you want to directly write the command output to a file in the current directory, run gpg -a --export [yourfingerprintgoeshere] > my_pub_pgp_key.txt
.
So what can you do with this key? In this post I assume you will use GPG for sending mails. You can do two things, 1) encrypt your email to me, so that only I can read it, or 2) sign your email, so that I can verify that it was indeed you that sent the mail.
If you want to write me an email, you have to encrypt the mail with my public key. Then I can decipher it with my private key. If you want me to respond to you, you need to make your public key available to me, so I can do the same.
For signing the public and private key are used slightly different. If I sign an email, I sign it with my private key, which obviously only I should have. My signature can be verified by anyone having my public key.
You do not have to do this manually. Your mail program probably has an extension or native support for using PGP encryption (also with GPG).
If you use ThunderBird, for example, you can install the EnigMail extension.
I use the command line based program NeoMutt myself.
NeoMutt has native support for using PGP encryption. In order to tell it that I use GPG for that, I only need to allow NeoMutt to use gpgme
, which is GPG’s standard library for accessing GPG functionality from other programming languages. (This is assuming you have the latest GPG version installed).
So in my neomuttrc file I set:
set crypt_use_gpgme=yes
And if I want to sign my mails by default, I can simply set:
set crypt_autosign
When having written an email, you can press p
to open all encryption options.
The added security of PGP depends on the fact that the public key that I gave you, is really mine, and not of someone pretending to be me. Therefore you should review your trust into public keys. The most secure option is to manually verify with the person you are trying to reach that the public key is correct (e.g. by meeting or Skyping with them). But this is quite laborious and not feasible if you communicate with a lot of people. Therefore a common tactic is to build a web of trust. I can verify public keys of people I know by signing them with my own key. This then creates a situation where someone else trying to communicate with my friend sees: aha, Edwin trusts this person. Now the question is… does this person trust Edwin? But hey, that is lucky, a person I know and whose key I verified, turns out to trust Edwin. So I have now more confidence that the key of my friend is legit. Although not waterproof, through this type of interaction you can build a so-called “web of trust”.
This does however require people to not sign keys without thinking! So if you like the “GPG-philosophy”, that does give you some responsibility for maintaining it as well.
You can sign a key with gpg --sign-key [thekeyfingerprintgoeshere]
.
To import a key, use the gpg --import [keyfile]
command, or try get the key from a keyserver with the gpg --receive-keys
command.
Try to verify and sign mine!