<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Blog on Tyil</title>
    <link>https://www.tyil.nl/posts/</link>
    <description>Recent content in Blog on Tyil</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 29 Aug 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://www.tyil.nl/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Releasing Raku modules with fez</title>
      <link>https://www.tyil.nl/post/2023/08/29/releasing-raku-modules-with-fez/</link>
      <pubDate>Tue, 29 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/08/29/releasing-raku-modules-with-fez/</guid>
      <description>&lt;p&gt;Last week I got a message on Matrix, asking me to update one of my
&lt;a href=&#34;https://raku.org/&#34;&gt;Raku&lt;/a&gt; modules,
&lt;a href=&#34;https://git.tyil.nl/raku/config-parser-toml/&#34;&gt;&lt;code&gt;Config::Parser::TOML&lt;/code&gt;&lt;/a&gt;. One of
the dependencies had been updated, and the old one is no longer available
through the module installer &lt;code&gt;zef&lt;/code&gt;. Its not that big a change, and there are
tests available, so its a reasonably small fix on itself.&lt;/p&gt;
&lt;p&gt;Recently I&amp;rsquo;ve set up &lt;a href=&#34;https://argoproj.github.io/workflows/&#34;&gt;Argo Workflows&lt;/a&gt; for
my CI/CD desires, and I found this a good and simple Raku project to try and
incorporate into a workflow. Since I had some additional quality checks ready to
use in my workflow, this has resulted in &lt;a href=&#34;https://reuse.software/&#34;&gt;REUSE&lt;/a&gt;
compliance for this Raku module, in addition to the regular &lt;code&gt;prove&lt;/code&gt; tests
already available in the project.  Additionally, the de facto default module
authoring tool &lt;code&gt;fez&lt;/code&gt; also brings a few new checks that have been incorporated.&lt;/p&gt;
&lt;p&gt;While all that is good, there were some annoyances I encountered while
configuring this. Notably, I&amp;rsquo;ve found &lt;code&gt;fez&lt;/code&gt; to be a chore to work with when it
comes to non-interactive use. All CI/CD jobs run in their own Kubernetes pods,
and &lt;em&gt;should&lt;/em&gt; not require any interaction from myself during these runs. I am
writing this blog post mainly to write down the annoyances I encountered, hoping
that &lt;code&gt;fez&lt;/code&gt; can be improved in the future.&lt;/p&gt;
&lt;p&gt;Lets start with the first issue I encountered while setting up the workflow:
&lt;code&gt;zef install fez&lt;/code&gt; fails by default. &lt;code&gt;zef&lt;/code&gt; gives the advice to &lt;code&gt;--exclude&lt;/code&gt; one of
the dependencies, and going by the issues reported on their Github repository,
this seems to be accepted workaround. However, I&amp;rsquo;d argue that this workaround
should not be needed to begin with. Especially seeing as &lt;code&gt;fez&lt;/code&gt; works fine and I
have absolutely no clue what this &lt;code&gt;z&lt;/code&gt; is or how I can supply it. Either drop
this dependency, or document its use and upstream so people can package it.&lt;/p&gt;
&lt;p&gt;The second issue I encountered was with the &lt;code&gt;login&lt;/code&gt; functionality of &lt;code&gt;fez&lt;/code&gt;.
There seems to be no way to handle this non-interactively. The way around this
for me has become to use &lt;code&gt;expect&lt;/code&gt; scripts, but this is obviously not very pretty
and will break whenever the interactive interface of &lt;code&gt;fez&lt;/code&gt; changes. A good means
of non-interactive authentication would be great to have. I&amp;rsquo;ve considered to
just mount &lt;code&gt;fez&lt;/code&gt;&amp;rsquo;s config/cache into the containers, but the documentation warns
that tokens aren&amp;rsquo;t permanent to begin with.&lt;/p&gt;
&lt;p&gt;Next up there&amp;rsquo;s the actual &lt;code&gt;upload&lt;/code&gt; command. I&amp;rsquo;m running it twice in my
workflow, once with &lt;code&gt;--dry-run&lt;/code&gt; and once with &lt;code&gt;--force&lt;/code&gt;. The first one is done
as a preliminary quality check to see if there&amp;rsquo;s any obvious issues that ought
to be fixed beforehand. I noticed on a subsequent run (the one with &lt;code&gt;--force&lt;/code&gt;)
that the &lt;em&gt;dry&lt;/em&gt; run isn&amp;rsquo;t all that dry. It leaves an &lt;code&gt;sdist&lt;/code&gt; directory, which in
turn will get included in the next step. There&amp;rsquo;s a flag to create this &lt;code&gt;sdist&lt;/code&gt;
directory, but no flag to do the inverse. My solution is to end this step with
&lt;code&gt;rm -fr -- sdist&lt;/code&gt; to clean it up again.&lt;/p&gt;
&lt;p&gt;And lastly, when all quality assurance checks have passed, the &lt;code&gt;fez upload --force&lt;/code&gt; command is ran on the working directory. I&amp;rsquo;d rather not force anything
here, but the alternative is that another interactive question pops up and the
job hangs forever. I don&amp;rsquo;t know all the possible prompts &lt;code&gt;fez&lt;/code&gt; can generate, and
for this one I didn&amp;rsquo;t even bother to try and look that up. Rather than a
&lt;code&gt;--force&lt;/code&gt; to practically say &amp;ldquo;yes&amp;rdquo; to everything, I&amp;rsquo;d prefer an option to say
&amp;ldquo;no&amp;rdquo; to everything, failing the pipeline immediately.&lt;/p&gt;
&lt;p&gt;Another pet-peeve of mine is that &lt;code&gt;fez&lt;/code&gt; seemingly doesn&amp;rsquo;t use exit codes. No
matter what happens, even something quite important such as &lt;code&gt;login&lt;/code&gt; with
incorrect credentials, it &lt;em&gt;always&lt;/em&gt; returns &lt;code&gt;0&lt;/code&gt; as exit code. This should
obviously be fixed sooner rather than later, as it is quite simple and it is the
basis for &lt;em&gt;many&lt;/em&gt; systems to check the exit code to deduce something is wrong.&lt;/p&gt;
&lt;p&gt;Uploads of module updates are currently working, which is good, but I feel like
a lot of workaround code I had to write should not be necessary. If &lt;code&gt;fez&lt;/code&gt; can
fix these issues, it will be much more of a breeze to use, which in turn
hopefully encourages more automated testing and distributing of Raku modules.
This can be a great boon for the module ecosystem and overall community.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>My New Server Rack: Nouki</title>
      <link>https://www.tyil.nl/post/2023/08/05/my-new-server-rack-nouki/</link>
      <pubDate>Sat, 05 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/08/05/my-new-server-rack-nouki/</guid>
      <description>&lt;p&gt;After setting up &lt;a href=&#34;https://www.tyil.nl/post/2023/07/23/my-new-server-rack-mieshu/&#34;&gt;mieshu&lt;/a&gt;, nouki is
the next server to work on in my home rack. Nouki is intended to live as my main
database server, mainly for PostgreSQL, but perhaps later on in life MySQL if I
ever want a service that doesn&amp;rsquo;t support superiour databases.&lt;/p&gt;
&lt;p&gt;The setup for nouki is much simpler in that regard, the base system is almost
identical. This server has ZFS with 2 NVMe disks running in a mirror
configuration. It is also a Gentoo based system, and again with systemd rather
than openrc. The experience of systemd with mieshu was much less painful than I
anticipated. It would seem that it has had time to mature, though I still
dislike how it kills diversity in init/service managers on GNU+Linux.&lt;/p&gt;
&lt;p&gt;Both PostgreSQL and ZFS have received some tweaking to run more smoothly. I&amp;rsquo;m no
DBA, so if you see anything silly in here, do let me know so I can improve my
life.&lt;/p&gt;
&lt;p&gt;For ZFS, tweaking was rather minimal. I&amp;rsquo;ve made a seperate dataset for
PostgreSQL to use, with &lt;code&gt;recordsize=8K&lt;/code&gt; as option. For PostgreSQL, I&amp;rsquo;ve altered
a bit more. First and foremost, the &lt;code&gt;pg_hba.conf&lt;/code&gt; to allow access from machines
in my tinc-based VPN.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;host&lt;/span&gt;    &lt;span class=&#34;nv&#34;&gt;all&lt;/span&gt;             &lt;span class=&#34;nv&#34;&gt;all&lt;/span&gt;             &lt;span class=&#34;mf&#34;&gt;10.57.0.0/16&lt;/span&gt;            &lt;span class=&#34;nv&#34;&gt;scram-sha-256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;postgresql.conf&lt;/code&gt; file received the following treatment, based solely on the
guidance provided by &lt;a href=&#34;https://pgtune.leopard.in.ua/&#34;&gt;PGTune&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;listen_address&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;10.57.101.20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;max_connections&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;shared_buffers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;8GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;effective_cache_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;24GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;maintenance_work_mem&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;2GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;checkpoint_completion_target&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;0.9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;wal_buffers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;16MB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;default_statistics_target&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;random_page_cost&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;effective_io_concurrency&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;work_mem&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;5242kB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;min_wal_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;1GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;max_wal_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;4GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;max_worker_processes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;max_parallel_workers_per_gather&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;max_parallel_workers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;max_parallel_maintenance_workers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this, PostgreSQL seems to perform very well on this machine, applications
using it are noticably faster. Sadly I have no timings from when it all ran on
my desktop, so I cannot make an exact statement on how much faster everything
has become.&lt;/p&gt;
&lt;p&gt;Additionally, I wanted to start gathering metrics of my machines and services,
so I can start thinking about dashboards and alerts. I&amp;rsquo;ve chosen to use the
current industry standard of Prometheus for this. Since I consider Prometheus to
be a database for metrics, it has been deployed on my database server as well.&lt;/p&gt;
&lt;p&gt;Prometheus is currently set to scrape metrics from the &lt;code&gt;node_exporter&lt;/code&gt; and
&lt;code&gt;postgresql_exporter&lt;/code&gt;, and seems to work fine. I expect I may need to tweak it
in the future to configure how long I want metrics to be available, since I&amp;rsquo;ve
seen it use quite a large amount of memory when storing a large amount of
metrics for a very long time.&lt;/p&gt;
&lt;p&gt;To actually see the metrics and have alerts, I currently intend to go with
Grafana. I already have ntfy running, and it appears relatively simple to mold
Grafana alerts into ntfy notifications. To do this properly, I will require some
machines to handle regular workloads. Most likely these will be Intel NUCs, or
similar machines, as they draw very little power for reasonable performance.
Raspberry Pi units would be cheaper, but also seem vastly less powerful, and I&amp;rsquo;d
need to ensure all my intended workloads can run on ARM which could become a
nuisance very quickly.&lt;/p&gt;
&lt;p&gt;As I already have an Intel NUC to play with, that&amp;rsquo;s what I&amp;rsquo;ll be doing for the
coming few days to see if this can work for my desires. Perhaps I can try out a
highly available cluster setup of K3s in the near future!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>My New Server Rack: Mieshu</title>
      <link>https://www.tyil.nl/post/2023/07/23/my-new-server-rack-mieshu/</link>
      <pubDate>Sun, 23 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/07/23/my-new-server-rack-mieshu/</guid>
      <description>&lt;p&gt;After saving up for a long while and thinking about what I want in my new home,
I have finally taken the leap and gotten myself a server rack for home use. Its
has a 15U capacity, which should be plenty to get started, but this same brand
has larger racks too, in case I do want to upgrade it and keep the same style.&lt;/p&gt;
&lt;p&gt;That said, for now there&amp;rsquo;s only two 4U units in them, one for (file) storage,
and one for database purposes. I sadly don&amp;rsquo;t have anything dedicated for
workloads yet, so for now, both of these servers are intended to also run some
light workloads. I haven&amp;rsquo;t made my mind up yet on how to solve the workload
issues. Now that I have a rack, I obviously want something rack-mountable, and
I probably want it to run a Kubernetes cluster too.&lt;/p&gt;
&lt;p&gt;In this regard, I &lt;em&gt;could&lt;/em&gt; go for a set of &lt;a href=&#34;https://www.raspberrypi.com/&#34;&gt;Raspberry Pi&lt;/a&gt;
units, there&amp;rsquo;s &lt;a href=&#34;https://www.uctronics.com/uctronics-19-inch-3u-rack-mount-for-raspberry-pi-4-with-8-mounting-plates.html&#34;&gt;3U mounts that can hold up to 12 Raspberry Pi machines&lt;/a&gt;,
which would be a nice amount. However, I am not yet completely sold on full-ARM
workloads, and I&amp;rsquo;m not entirely convinced of the power of Raspberry Pi units in
general. I&amp;rsquo;d much rather standardize on another brand, &lt;a href=&#34;https://www.hardkernel.com/&#34;&gt;Odroid&lt;/a&gt;,
as they have more types of units available, and are not limited to just ARM. But
since they&amp;rsquo;re not the popular kid in class, there&amp;rsquo;s very few off-the-shelf
rack mounting equipment for it. I&amp;rsquo;ll be thinking about this for just a bit more
before making a decision.&lt;/p&gt;
&lt;p&gt;For now, though, I wanted to talk about the setup of the first server, Mieshu,
who will be used as a storage server. Mieshu currently runs 8 HDDs, and 2 NVMe
drives. One of the NVMe drives is used for the rootfs, and the other is used for
caching in certain applications. The HDDs themselves offer the data storage
capacity.&lt;/p&gt;
&lt;p&gt;The HDDs are currently comprised of four 16TB drives, and four 8TB drives. The
smaller disks come from my desktop, Edephas, which used to serve as data storage
until Mieshu took over. All disks are configured into pairs, which themselves
make mirrors. This means I have four sets of mirror pools, two times 16TB, and
two times 8TB, for a total of 48TB of storage. I&amp;rsquo;m currently using about half of
this, and it should give me plenty of time before needing to increase the size
again.&lt;/p&gt;
&lt;p&gt;I chose to use mirrors since it has a good chance of your data being recoverable
on disk failure, and it allows me to buy disks per two, rather than in larger
numbers. This hopefully keeps the cost of expansion within reasonable limits.
The mirrors themselves are currently &lt;a href=&#34;https://openzfs.org/wiki/Main_Page&#34;&gt;ZFS&lt;/a&gt;
pools, but I hope to be able to use &lt;a href=&#34;https://bcachefs.org/&#34;&gt;bcachefs&lt;/a&gt; very soon
as well.&lt;/p&gt;
&lt;p&gt;Just a bunch of mirrors is rather inconvenient, however, so I&amp;rsquo;m also leveraging
&lt;a href=&#34;https://github.com/trapexit/mergerfs&#34;&gt;MergerFS&lt;/a&gt; to combine all the mirrors into
a single usable pool. This slightly odd setup was chosen over RAID-0 or RAID-Z*
to lower the impact of disk failure. Even if two disks in the same mirror were
the die at the same time, I wouldn&amp;rsquo;t lose &lt;em&gt;all&lt;/em&gt; data, just the bits on that
particular mirror. It would be very annoying, but it wouldn&amp;rsquo;t be disastrous.&lt;/p&gt;
&lt;p&gt;Apart from generic mass storage, I also host S3 buckets for personal use. This
is where I upload CI artifacts to, and &lt;a href=&#34;https://fedi.tyil.nl/@tyil&#34;&gt;my MissKey instance&lt;/a&gt;
uses it for storing objects as well. Future services such as &lt;a href=&#34;https://grafana.com/oss/mimir/&#34;&gt;Mimir&lt;/a&gt;
will probably leverage S3 for storage as well. This is achieved through
&lt;a href=&#34;https://garagehq.deuxfleurs.fr/&#34;&gt;Garage&lt;/a&gt;. I&amp;rsquo;ve also tried &lt;a href=&#34;https://seaweedfs.github.io/&#34;&gt;SeaweedFS&lt;/a&gt;,
which is a very neat project on its own, but Garage is just simpler to
configure, and allows a replicated setup with only two servers, whereas SeaweedFS
demands an odd number of master servers.&lt;/p&gt;
&lt;p&gt;And lastly, Mieshu runs &lt;a href=&#34;https://k3s.io/&#34;&gt;K3s&lt;/a&gt; for its Kubernetes component. It
is currently not serving anything yet, as the other server is supposed to become
the database server, which is needed for most workloads. Once that is up and
running, Mieshu will start hosting things such as
&lt;a href=&#34;https://grafana.com/oss/grafana&#34;&gt;Grafana&lt;/a&gt; and &lt;a href=&#34;https://grafana.com/oss/loki/&#34;&gt;Loki&lt;/a&gt;,
monitoring stuff basically. Perhaps I&amp;rsquo;ll move &lt;a href=&#34;https://laminar.ohwg.net/&#34;&gt;Laminar&lt;/a&gt;
to this server as well, but I&amp;rsquo;m unsure if I will run that as a Kubernetes service.&lt;/p&gt;
&lt;p&gt;The server itself runs on Gentoo, as it still is the most stable experience I
can get out of any GNU+Linux distribution. I am, however, not using the default
of OpenRC as the init system and service manager. For the first time, I&amp;rsquo;m
running Gentoo with systemd. After several years, it appears to have become
stable enough to trust with serious workloads. With its increased use, however,
some things have become simpler by just using systemd. I hope to get a better
understanding of it, and learn to bend it to my will as needed, by simply using
it on my own systems.&lt;/p&gt;
&lt;p&gt;I hope to have time to work on the other server sooner rather than later, so
I can finish up the base of my new setup. Be on the lookout for the next post,
where I&amp;rsquo;ll go into detail on Nouki, the database server.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Getting Emoji to Work in KDE on Debian</title>
      <link>https://www.tyil.nl/post/2023/07/13/getting-emoji-to-work-in-kde-on-debian/</link>
      <pubDate>Thu, 13 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/07/13/getting-emoji-to-work-in-kde-on-debian/</guid>
      <description>&lt;p&gt;This is going to be a relatively short and uninteresting post for most, it&amp;rsquo;ll
just document how to get emoji to work in KDE.&lt;/p&gt;
&lt;p&gt;While it will work with most applications out of the box, this doesn&amp;rsquo;t appear to
work in Qt applications by default, including the notification panel. As I use
my notifications for messages I get from my work chat, and I dislike seeing the
squares, I set out to find the solution. I&amp;rsquo;ve had to string together a couple
sources of information to get to the correct setup, and this blog post intends
to show just the useful bits. So here goes!&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need an emoji font (in my case &lt;code&gt;fonts-noto-color-emoji&lt;/code&gt;), add two
configuration files for fontconfig, rebuild the fontconfig cache, and most
likely log out and back into KDE. Installing the emoji font is probably the easy
bit and won&amp;rsquo;t need any additional explanation I hope. So let&amp;rsquo;s get started on
the first configuration file, which will enable the Noto emoji font to be used,
and also force it to be used in favour of other emoji fonts if any application
was using that specifically. I have it saved as
&lt;code&gt;/etc/fonts/conf.d/75-noto-color-emoji.conf&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE fontconfig SYSTEM &amp;#34;fonts.dtd&amp;#34;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- Add generic family. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- This adds Noto Color Emoji as a final fallback font for the default font families. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;sans&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;append&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;serif&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;append&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;sans-serif&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;append&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;monospace&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;append&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- Block Symbola from the list of fallback fonts. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;selectfont&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;rejectfont&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nt&#34;&gt;&amp;lt;patelt&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Symbola&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nt&#34;&gt;&amp;lt;/patelt&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/rejectfont&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/selectfont&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- Use Noto Color Emoji when other popular fonts are being specifically requested. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Apple Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Segoe UI Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Segoe UI Symbol&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Android Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Twitter Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Twemoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Twemoji Mozilla&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;TwemojiMozilla&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;EmojiTwo&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Emoji Two&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;EmojiSymbols&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;test&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;qual=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Symbola&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;assign&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;binding=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;same&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second configuration file, saved as &lt;code&gt;/etc/fonts/conf.d/local.conf&lt;/code&gt;, simply
adds the Noto emoji font as a fallback. This enables the use of it when an emoji
is going to be rendered.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;?xml version=&amp;#39;1.0&amp;#39;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!DOCTYPE fontconfig SYSTEM &amp;#39;fonts.dtd&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;target=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;edit&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;mode=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;append&amp;#34;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Noto Color Emoji&lt;span class=&#34;nt&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;lt;/edit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;/match&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/fontconfig&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And after this, a relog of your (graphical) session should be all that is needed
in order to make it work. You can easily test it with &lt;code&gt;notify-send&lt;/code&gt;, or trying
to render some emoji in &lt;code&gt;konsole&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Bashtard v2.0.0</title>
      <link>https://www.tyil.nl/post/2023/05/23/bashtard-v2.0.0/</link>
      <pubDate>Tue, 23 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/05/23/bashtard-v2.0.0/</guid>
      <description>&lt;p&gt;A little over a year ago I started on a project to create my own configuration
management system. I&amp;rsquo;ve been disappointed with existing alternatives, such as
Ansible, on the grounds that they don&amp;rsquo;t work all that well if you have a mix of
different distros with different package managers, and sometimes even different
paths to store data in.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been having a lot of fun working on it, since the limitations I&amp;rsquo;ve put on
it result in having to solve some problems in different ways than I would in a
full-fledged programming language. These limitations also keep things pretty
simple, and ensure that most of the features I have worked on need little to no
additional effort to run on all the different systems I use for my computing
needs.&lt;/p&gt;
&lt;p&gt;And now, a year later, I feel confident enough about a new release. There&amp;rsquo;s some
small backwards incompatible changes, so a new major release version is the way
to go. &lt;a href=&#34;https://www.tyil.nl/projects/bashtard/releases/2.0.0/&#34;&gt;Bashtard v2.0.0&lt;/a&gt;
is now available. There are a few big things that I want to go into a little
bit, but you can also find a full list of changes in the changelog included on
the release page.&lt;/p&gt;
&lt;h1 id=&#34;templating&#34;&gt;Templating&lt;/h1&gt;
&lt;p&gt;After using the templating features I &lt;a href=&#34;&#34;&gt;wrote about&lt;/a&gt; last month, I&amp;rsquo;ve decided
to &lt;em&gt;not&lt;/em&gt; include it into Bashtard. I am not convinced after using it in practice
that it adds enough value to warrant the size of the added code, and hassling
with two licenses instead of one. I am still very much open to the idea of a
good base templating engine, but for now you can always install &lt;code&gt;jinja2&lt;/code&gt; or
something on the target machine, and call that manually. The new
&lt;code&gt;playbook_path()&lt;/code&gt; function should make it easy to generate the path to your
playbook&amp;rsquo;s files.&lt;/p&gt;
&lt;h1 id=&#34;additional-bashtard_-vars&#34;&gt;Additional &lt;code&gt;$BASHTARD_*&lt;/code&gt; vars&lt;/h1&gt;
&lt;p&gt;Apart from having a new key in &lt;code&gt;$BASHTARD_PLATFORM&lt;/code&gt; called &lt;code&gt;init&lt;/code&gt;, there&amp;rsquo;s a
completely new variable in this version: &lt;code&gt;$BASHTARD_PLAYBOOK_VARS&lt;/code&gt;. Currently,
it&amp;rsquo;s only used to set a given variable as required, but can be extended in the
future with other kinds of checks. This allows playbooks to define some data to
be required for it to run, and have it refuse to run if those are not supplied,
rather than having to manually check them when the playbook runs. This is mainly
intended for use with playbooks you intend to share, so that other people can
get reasonable feedback as to what they &lt;em&gt;need&lt;/em&gt; to configure, vs what they &lt;em&gt;can&lt;/em&gt;
configure.&lt;/p&gt;
&lt;h1 id=&#34;re-usable-playbooks&#34;&gt;Re-usable playbooks&lt;/h1&gt;
&lt;p&gt;So let&amp;rsquo;s talk about one of the more important updates to Bashtard. At least, in
my opinion. How playbooks are being used has been altered slightly, in order to
allow a little easier re-use of them. I consider this a very important feature
of any configuration management system, the ability to share your playbooks with
others easily, and being able to use other people&amp;rsquo;s playbooks with minimal
effort. This greatly reduces the barrier to get started, and encourages people
to show off what they&amp;rsquo;ve made.&lt;/p&gt;
&lt;p&gt;The current implementation is built upon git submodules, and the &lt;code&gt;bashtard pull&lt;/code&gt;
command will take them into account. Perhaps I&amp;rsquo;ll add an &lt;code&gt;import&lt;/code&gt; subcommand in
the future to abstract the git submodule effort away, as I know that many people
find it difficult to work with. However, since &lt;code&gt;git&lt;/code&gt; is already ingrained in
Bashtard, this addition keeps dependencies low, and allows me to keep the
complexity out of the Bash code.&lt;/p&gt;
&lt;h1 id=&#34;datad&#34;&gt;data.d&lt;/h1&gt;
&lt;p&gt;Having re-usable playbooks introduced the need to have a place for data that is
important to my setup, but completely useless to someone else&amp;rsquo;s setup. For this,
the &lt;code&gt;data.d&lt;/code&gt; directory was added. You can store information that should be
preserved across sync runs on your machines, but are not a good fit to keep in
the actual playbook itself. I personally use it for my
&lt;a href=&#34;https://git.tyil.nl/bashtard/vpn-tinc/&#34;&gt;&lt;code&gt;vpn-tinc&lt;/code&gt;&lt;/a&gt; playbook to keep the host
files in.&lt;/p&gt;
&lt;p&gt;Another use-case for this directory is without a playbook at all. You can put a
regular directory in it, and symlink to it from a host system to keep a given
directory in sync across all your machines. In my case, I have an &lt;code&gt;etc-nixos&lt;/code&gt;
directory in my &lt;code&gt;data.d&lt;/code&gt; directory. On my NixOS system I have a symlink from
&lt;code&gt;/etc/nixos&lt;/code&gt; to &lt;code&gt;/etc/bashtard/data.d/nixos&lt;/code&gt;. If I ever continue with NixOS, I
can have this on all systems, and share any &lt;code&gt;.nix&lt;/code&gt; files across all machines.&lt;/p&gt;
&lt;h1 id=&#34;binary-packages&#34;&gt;Binary packages!&lt;/h1&gt;
&lt;p&gt;Lastly, I&amp;rsquo;ve &lt;a href=&#34;https://www.tyil.nl/post/2023/03/08/using-laminar-for-self-hosted-ci/&#34;&gt;written
about&lt;/a&gt;
Laminar before. I&amp;rsquo;m still using it, and I&amp;rsquo;m still very happy with its
simplicity. Since setting it up I&amp;rsquo;ve added jobs to verify my Bashtard code with
&lt;code&gt;shellcheck&lt;/code&gt;, and if it passes, it&amp;rsquo;ll queue up additional jobs to create a
&lt;code&gt;.tar.gz&lt;/code&gt; distribution and a &lt;code&gt;.deb&lt;/code&gt; distribution. I hope to expand this to also
generate binaries for use with Alpine, FreeBSD, and Archlinux. I&amp;rsquo;ve recently set
up an S3-compatible object storage,&lt;/p&gt;
&lt;p&gt;Additionally, I&amp;rsquo;ve recently set up an S3-compatible object store, which Laminar
should push such artifacts to immediately. This will simplify new releases of
any software, and offload this kind of storage to an actual remote server,
rather than hosting &lt;code&gt;dist.tyil.nl&lt;/code&gt; directly from my desktop.&lt;/p&gt;
&lt;h1 id=&#34;wrapping-up&#34;&gt;Wrapping up&lt;/h1&gt;
&lt;p&gt;All in all, I&amp;rsquo;ve been very happy with Bashtard so far, and I&amp;rsquo;ve been having a
&lt;em&gt;lot&lt;/em&gt; of fun working on it. I hope to be able to continue working on it and
making it even better that it is in this release.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and perhaps even using Bashtard!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Finally, Templating in Bashtard!</title>
      <link>https://www.tyil.nl/post/2023/03/29/finally-templating-in-bashtard/</link>
      <pubDate>Wed, 29 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/03/29/finally-templating-in-bashtard/</guid>
      <description>&lt;p&gt;In the past year, I&amp;rsquo;ve written Bashtard, a simple configuration system written
in Bash to minimize the required dependencies, and to have a better system to
handle different distributions/OSs in your cluster. Especially the past two
months I&amp;rsquo;ve done quite a bit of work on it. I&amp;rsquo;ve worked out how to do reusable
playbooks, generate a usable Debian package from the Makefile, extend the
supported platforms, and more. And now, I&amp;rsquo;ve finally found a library to improve
templating functionality, &lt;a href=&#34;https://github.com/husixu1/bpt&#34;&gt;Bash Pure Template&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When I originally started Bashtard I had looked around for nice and simple
templating solutions that I could use. Sadly, pretty much all the available
results required me to add dependencies, or couldn&amp;rsquo;t really do more than what I
did using &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a long time, I had accepted that the kind of system that I wanted didn&amp;rsquo;t
exist, and I wasn&amp;rsquo;t interested in making it myself at the time. Last night,
however, I decided to just give it a quick search to see if anything had
changed, and BPT popped up somewhere in my results. Having a quick look through
the documentation made me very interested, it seemed to have all the features I
desired, while still sticking to utilities I&amp;rsquo;ve already accepted for Bashtard.&lt;/p&gt;
&lt;p&gt;With one small exception, &lt;code&gt;md5sum&lt;/code&gt;. This utility is not available on the FreeBSD
systems I maintain. On FreeBSD, this tool is called &lt;code&gt;md5&lt;/code&gt;, and has different
options it can use. On the bright side, both &lt;code&gt;md5sum&lt;/code&gt; and &lt;code&gt;md5&lt;/code&gt; accept the
content to be hashed on &lt;code&gt;STDIN&lt;/code&gt;, and will write the hash to &lt;code&gt;STDOUT&lt;/code&gt;.
Additionally, Bashtard already contains logic to deduce what kind of system it
is running on.&lt;/p&gt;
&lt;p&gt;And so I decided it&amp;rsquo;s worth a try. There&amp;rsquo;s only 5 references to &lt;code&gt;md5sum&lt;/code&gt;, and
the all happen in the same function, &lt;code&gt;bpt.fingerprint&lt;/code&gt;. I&amp;rsquo;ve added an extra
variable, &lt;code&gt;util&lt;/code&gt;, and a &lt;code&gt;case...esac&lt;/code&gt; to set this variable.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; util
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BASHTARD_PLATFORM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[key]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    freebsd&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;util&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;md5 &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    linux-*&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;util&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;md5sum &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    *&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        debug &lt;span class=&#34;s2&#34;&gt;&amp;#34;bpt/fingerprint&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Falling back to md5sum for hashing&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;util&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;md5sum
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After that, just replace all the &lt;code&gt;md5sum&lt;/code&gt; invocations with &lt;code&gt;&amp;quot;$util&amp;quot;&lt;/code&gt;. And a
quick test later, it seems to function just fine. Implementing BPT as a library
was incredibly straightforward too.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;. &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BASHTARD_LIBDIR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/vendor/bpt.bash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;file_template_bpt&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;shift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;eval&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$*&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; bpt.main ge \&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$file&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;eval&lt;/code&gt; is a bit icky, but it saves me from polluting the environment
variables through various &lt;code&gt;export&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;Another small adjustment I&amp;rsquo;ve made to BPT is the shebang. Upstream uses
&lt;code&gt;#!/bin/bash&lt;/code&gt;, but this is incorrect on some systems, including FreeBSD. It uses
&lt;code&gt;#!/usr/bin/env bash&lt;/code&gt; in the Bashtard version. Additionally, the upstream
repository uses &lt;code&gt;.sh&lt;/code&gt; as the file extension, which I&amp;rsquo;ve updated to be &lt;code&gt;.bash&lt;/code&gt; to
more accurately reflect which shell it is used with. Upstream also uses a
4-space indent, which I&amp;rsquo;ve left as-is for now, since indentation is more of a
personal choice, even if that choice is wrong. Finally, I added 3 &lt;code&gt;shellcheck disable&lt;/code&gt; rules to make shellcheck happy.&lt;/p&gt;
&lt;p&gt;After some playbook testing on my own systems, I can say that BPT works pretty
well so far, and I&amp;rsquo;m very glad the author made it available as free software.
Thanks!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using Laminar for Self-hosted CI</title>
      <link>https://www.tyil.nl/post/2023/03/08/using-laminar-for-self-hosted-ci/</link>
      <pubDate>Wed, 08 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/03/08/using-laminar-for-self-hosted-ci/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve hosted my &lt;a href=&#34;https://git.tyil.nl&#34;&gt;own git repositories&lt;/a&gt; for quite a while,
but I hadn&amp;rsquo;t found a simple self-hosted CI solution yet. I&amp;rsquo;ve tried several,
and found them to be a bit too cumbersome to setup and actually put to use. The
majority requires you to host a full &amp;ldquo;git forge&amp;rdquo;, such as GitLab or Gitea, in
order to use their webhook functionality in order to trigger a CI build. This
didn&amp;rsquo;t seem worth the effort to me, so I kept looking for an alternative that
worked well for me.&lt;/p&gt;
&lt;p&gt;I think I&amp;rsquo;ve finally found one in &lt;a href=&#34;https://laminar.ohwg.net/&#34;&gt;Laminar&lt;/a&gt;, after a
suggestion from a friend on the Fediverse. I do wonder how I could&amp;rsquo;ve spent so
much time searching without ever finding this solution!&lt;/p&gt;
&lt;p&gt;Laminar itself was easy to install from source, but another person chimed in to
let me know they already made an &lt;code&gt;ebuild&lt;/code&gt; for it, which is available in their
overlay, making it even easier for me to try out. A single &lt;code&gt;emerge laminar&lt;/code&gt;,
and a couple seconds of building it, and I was ready to start trying it out.&lt;/p&gt;
&lt;p&gt;Configuration of jobs is done through scripts in whichever language you prefer,
giving you quite a bit of power. The documentation seems to mostly use Bash,
and that seemed to be a logical choice for me too, so that&amp;rsquo;s what I&amp;rsquo;ve been
playing with as well.&lt;/p&gt;
&lt;p&gt;Running jobs itself is as easy as &lt;code&gt;laminarc queue &amp;lt;name&amp;gt;&lt;/code&gt;. It can&amp;rsquo;t be much
simpler, and this CLI interface makes it very easy to start a new job from a
git &lt;code&gt;post-receive&lt;/code&gt; hook. I wrote one which also shows the URL of the job&amp;rsquo;s logs
whenever I push new comments to &lt;a href=&#34;https://git.tyil.nl/bashtard/about/&#34;&gt;the Bashtard
repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt; old new ref
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        laminarc queue bashtard &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;                &lt;span class=&#34;s2&#34;&gt;&amp;#34;GIT_BRANCH=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$ref&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;                &lt;span class=&#34;s2&#34;&gt;&amp;#34;GIT_COMMIT=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$new&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk -F: &lt;span class=&#34;s1&#34;&gt;&amp;#39;{ print &amp;#34;https://ci.tyil.nl/jobs/&amp;#34;$1&amp;#34;/&amp;#34;$2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using this, I can verify a job started, and immediately go to the page that
shows the logs. I plan to use Laminar&amp;rsquo;s post-job script to leverage &lt;code&gt;ntfy&lt;/code&gt; to
send me a notification on failed builds.&lt;/p&gt;
&lt;p&gt;Since all the worthwhile configuration for Laminar is just plain text, it is
also very easy to manage in your preferred configuration management system,
which is also something I plan to do in the nearby future.&lt;/p&gt;
&lt;p&gt;One slight annoyance I have so far is that I can&amp;rsquo;t use (sub)directories for all
the job scripts. Since I don&amp;rsquo;t have many yet, this isn&amp;rsquo;t a real problem yet,
but it could pose a minor issue in the far future once I&amp;rsquo;ve written more job
scripts.&lt;/p&gt;
&lt;p&gt;Given that that&amp;rsquo;s the only &amp;ldquo;issue&amp;rdquo; I&amp;rsquo;ve found thus far, after a couple days of
playing with it, I&amp;rsquo;d highly recommend taking a look at it if you want to set up
a CI system for your self-hosted git repositories!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Woes of AWSVPNClient</title>
      <link>https://www.tyil.nl/post/2023/02/23/the-woes-of-awsvpnclient/</link>
      <pubDate>Thu, 23 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2023/02/23/the-woes-of-awsvpnclient/</guid>
      <description>&lt;p&gt;For my current &lt;code&gt;$dayjob&lt;/code&gt; I am required to start using the AWS VPN Client. This
is not a problem per se, however, this piece of software has given me some
particular headaches. In this post, I want to air some frustrations that it has
brought me in the past two days, trying to get this software working properly
on Debian.&lt;/p&gt;
&lt;h2 id=&#34;gnulinux-support&#34;&gt;GNU+Linux Support&lt;/h2&gt;
&lt;p&gt;The AWS VPN Client has gotten an official client for GNU+Linux users. Not all
of them, sadly, they specifically support Ubuntu 18.04. I find it important to
note that this is 2 LTS versions behind the current Ubuntu version 22.04. Apart
from that, only Ubuntu is rather limited. Amazon isn&amp;rsquo;t a small company, and
they should be able to support various distributions.&lt;/p&gt;
&lt;p&gt;In general I would recommend to support the upstream distribution, which in
this case would be Debian. This would ensure that it becomes available on
Ubuntu by virtue of it being Debian based.&lt;/p&gt;
&lt;p&gt;That said, only Ubuntu packages wouldn&amp;rsquo;t be a huge problem if not for the next
issue I have with this software&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;proprietary-software&#34;&gt;Proprietary Software&lt;/h2&gt;
&lt;p&gt;The code for this application is private, and Amazon has no intention to change
this. There&amp;rsquo;s nothing very special about the application, it&amp;rsquo;s just a
proprietary wrapper around OpenVPN, so in my mind I find it hard to believe
that they&amp;rsquo;re trying to &amp;ldquo;protect&amp;rdquo; anything sensitive. It feels like a simple
move to instill the idea that you&amp;rsquo;re highly dependent on them.&lt;/p&gt;
&lt;p&gt;If they &lt;em&gt;were&lt;/em&gt; to make this software free (as in freedom), packaging could be
done by package maintainers, or really just anyone who feels like doing it.
This would remove a burden on Amazon, and ensure better availability for all
potential users.&lt;/p&gt;
&lt;p&gt;Additionally, it would make debugging issues much easier. Because&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;logging&#34;&gt;Logging&lt;/h2&gt;
&lt;p&gt;The logging the application does is pathetic. There&amp;rsquo;s a lot of duplicated logs
that are spammed hundreds of times per second. Tailing your logs can also be
more annoying than it needs to be, since the client rotates which file it logs
to every 1048629 bytes.&lt;/p&gt;
&lt;p&gt;I currently have 30 log files, generated by two sessions. In these log files,
the line &lt;code&gt;[INF] Begin receive init again&lt;/code&gt; appears 509114 times. Over &lt;em&gt;half a
million&lt;/em&gt; times. The total number of log lines in all these log files is 510394,
meaning only 1280 lines are something different.&lt;/p&gt;
&lt;p&gt;Of those 1280 lines, the logs themselves aren&amp;rsquo;t much better. I apparently had
to install &lt;code&gt;systemd-resolved&lt;/code&gt; in order to fix the following error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.870 +01:00 [DBG] CM received: &amp;gt;LOG:1677142970,F,WARNING: Failed running command (--up/--down): external program exited with error status: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt;FATAL:WARNING: Failed running command (--up/--down): external program exited with error status: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.870 +01:00 [DBG] CM processsing: &amp;gt;LOG:1677142970,F,WARNING: Failed running command (--up/--down): external program exited with error status: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.870 +01:00 [DBG] CM processsing: &amp;gt;FATAL:WARNING: Failed running command (--up/--down): external program exited with error status: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.870 +01:00 [DBG] Fatal exception occured
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.870 +01:00 [DBG] Stopping openvpn process
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.870 +01:00 [DBG] Sending SIGTERM to gracefully shut down the OpenVPN process
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.871 +01:00 [DBG] Invoke Error
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2023-02-23 10:02:50.871 +01:00 [DBG] DeDupeProcessDiedSignals: OpenVPN process encountered a fatal error and died. Try connecting again.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It is not particularly clear this fails due to not having &lt;code&gt;systemd-resolved&lt;/code&gt;
installed and running. The &lt;code&gt;.deb&lt;/code&gt; provided by Amazon does not even depend on
&lt;code&gt;systemd-resolved&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Another gripe I&amp;rsquo;ve had with the logs is their location. It saves these in
&lt;code&gt;~/.config/AWSVPNClient/logs&lt;/code&gt;. It may seem weird since this path contains a
directory named &lt;code&gt;.config&lt;/code&gt;, and indeed, this is not a great place to store logs.
The &lt;a href=&#34;https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html&#34;&gt;XDG Base Directory
Specification&lt;/a&gt;
specifies &lt;code&gt;$XDG_STATE_HOME&lt;/code&gt;, with one explicit example for it being logs.
However, for this to make sense, the application needs to respect the &lt;code&gt;XDG_*&lt;/code&gt;
values to begin with, which it currently doesn&amp;rsquo;t.&lt;/p&gt;
&lt;h2 id=&#34;all-in-all&#34;&gt;All in all&lt;/h2&gt;
&lt;p&gt;This software is pretty bad, but if it were free software, at least the users
could improve it to suck less, and easily introduce support for various
additional platforms. Instead, we&amp;rsquo;re just stuck with a piece of bad software.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Installing Gentoo with encrypted ZFS rootfs and EFIstub kernel</title>
      <link>https://www.tyil.nl/post/2022/11/20/installing-gentoo-with-encrypted-zfs-rootfs-and-efistub-kernel/</link>
      <pubDate>Sun, 20 Nov 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2022/11/20/installing-gentoo-with-encrypted-zfs-rootfs-and-efistub-kernel/</guid>
      <description>&lt;p&gt;A little while ago, I got a new work laptop. As is customary, I installed my
preferred GNU+Linux environment onto it. Consequently, a few people have asked
me to detail my steps to get this system up and running, as they would like to
try out a similar setup as I did. It&amp;rsquo;s also been a while since I made another
blog post, so here&amp;rsquo;s killing two birds with one stone!&lt;/p&gt;
&lt;h2 id=&#34;preparing-disks&#34;&gt;Preparing disks&lt;/h2&gt;
&lt;p&gt;Make sure you get the right device name, or you&amp;rsquo;ll purge the data on some other
drive!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;parted -a optimal /dev/nvme1n1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mklabel gpt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkpart esp      &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;5130&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkpart rootfs &lt;span class=&#34;m&#34;&gt;5130&lt;/span&gt;   -1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; boot on
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;quit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;get-ids-of-partitions&#34;&gt;Get IDs of partitions&lt;/h3&gt;
&lt;p&gt;For partitioning I&amp;rsquo;ve lately come to love using disk IDs, rather than their
&lt;code&gt;/dev/sd*&lt;/code&gt; entries. They&amp;rsquo;re easy to look up, so copy them over to use them later
on.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ls -l /dev/disk/by-id
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nvme-eui.36483331545090280025385800000001-part1&lt;/code&gt; -&amp;gt; ESP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nvme-eui.36483331545090280025385800000001-part2&lt;/code&gt; -&amp;gt; ZFS&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;formatting&#34;&gt;Formatting&lt;/h3&gt;
&lt;h4 id=&#34;esp&#34;&gt;ESP&lt;/h4&gt;
&lt;p&gt;The ESP partition holds the kernel and initramfs, and &lt;em&gt;must&lt;/em&gt; be FAT32.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkfs.vfat -F32 /dev/disk/by-id/nvme-eui.36483331545090280025385800000001-part1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;zpool&#34;&gt;zpool&lt;/h4&gt;
&lt;p&gt;The zpool settings used here are the settings I used. You should verify these
settings also work optimally for your setup! I generally name my pools after the
device they&amp;rsquo;re running from, in this case &lt;code&gt;ivdea&lt;/code&gt;. Any name will work here, just
make sure to be consistent later down the guide!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -f /etc/hostid &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; zgenhostid
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zpool create -f &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;acltype&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;posixacl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;compression&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;lz4 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;dedup&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;off &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;encryption&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;aes-256-gcm &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;keyformat&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;passphrase &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;keylocation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;prompt &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;relatime&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;on &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -O &lt;span class=&#34;nv&#34;&gt;xattr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;sa &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -R /mnt/gentoo &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -m none &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -o &lt;span class=&#34;nv&#34;&gt;ashift&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;12&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    -o &lt;span class=&#34;nv&#34;&gt;cachefile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/zfs/zpool.cache &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    ivdea0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        /dev/disk/by-id/nvme-eui.36483331545090280025385800000001-part2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o &lt;span class=&#34;nv&#34;&gt;mountpoint&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;none       ivdea0/rootfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o &lt;span class=&#34;nv&#34;&gt;mountpoint&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/          ivdea0/rootfs/gentoo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o &lt;span class=&#34;nv&#34;&gt;mountpoint&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;none       ivdea0/rootfs/gentoo/usr
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o &lt;span class=&#34;nv&#34;&gt;mountpoint&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;none       ivdea0/rootfs/gentoo/var
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o &lt;span class=&#34;nv&#34;&gt;mountpoint&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;none       ivdea0/rootfs/gentoo/var/lib
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o &lt;span class=&#34;nv&#34;&gt;mountpoint&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;none       ivdea0/home
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o &lt;span class=&#34;nv&#34;&gt;mountpoint&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/tyil ivdea0/home/tyil
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zpool &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;bootfs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;ivdea0/rootfs/gentoo ivdea0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;preparing-chroot&#34;&gt;Preparing chroot&lt;/h2&gt;
&lt;p&gt;You will want to grab the latest Gentoo autobuild tarball for your architecture.
I&amp;rsquo;m &lt;em&gt;not&lt;/em&gt; using systemd, if you do desire this for some reason, you may need to
alter some steps.&lt;/p&gt;
&lt;h3 id=&#34;initial&#34;&gt;Initial&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /mnt/gentoo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir efi
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount /dev/disk/by-id/nvme-eui.36483331545090280025385800000001-part1 efi
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;wget &lt;span class=&#34;nv&#34;&gt;$STAGE3&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;# Use whichever URL for the stage3 tarball you need&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar xpf stage3*.tar.xz --xattrs-include&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;*.*&amp;#39;&lt;/span&gt; --numeric-owner
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;recovery&#34;&gt;Recovery&lt;/h3&gt;
&lt;p&gt;This section is labeled &amp;ldquo;Recovery&amp;rdquo; to easily find it later, in case you need to
go back into the chroot to fix up any issues that prevent you from booting it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p etc/zfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp /etc/zfs/zpool.cache etc/zfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp --dereference /etc/resolv.conf /mnt/gentoo/etc/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t proc /proc proc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount --rbind --make-rslave /sys sys
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount --rbind --make-rslave /dev dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount --rbind --make-rslave /run run
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chroot . /bin/bash -l
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configuring-the-system&#34;&gt;Configuring the system&lt;/h2&gt;
&lt;p&gt;The base system is now installed, and most of the following steps are for
configuring it to actually work properly.&lt;/p&gt;
&lt;h3 id=&#34;portage&#34;&gt;Portage&lt;/h3&gt;
&lt;p&gt;Run the initial Portage tree download. This will use &lt;code&gt;webrsync&lt;/code&gt;, you can
configure it to use &lt;code&gt;git&lt;/code&gt; at a later stage if desired.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /etc/portage/repos.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp /usr/share/portage/config/repos.conf /etc/portage/repos.conf/gentoo.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;emerge-webrsync
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;editor&#34;&gt;Editor&lt;/h3&gt;
&lt;p&gt;Ofcourse, you can stick to &lt;code&gt;nano&lt;/code&gt;, but I&amp;rsquo;ve been a vim guy for a very long time
now, and without it I feel sad. It is the first thing I install, to make the
rest of the configuration easier to do, by virtue of having the best editor
available.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;emerge vim
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once &lt;code&gt;vim&lt;/code&gt; (or whichever worse editor you prefer) is installed, you can go
around editing configuration files as needed.&lt;/p&gt;
&lt;h3 id=&#34;locale&#34;&gt;locale&lt;/h3&gt;
&lt;p&gt;Enable all the locales you desire in &lt;code&gt;/etc/locale.gen&lt;/code&gt;. Once all the desird
locales are uncommented, you can generate the locales with &lt;code&gt;locale-gen&lt;/code&gt;. You
will most likely also want to add the locales to the &lt;code&gt;L10N&lt;/code&gt; variable in your
&lt;code&gt;make.conf&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;timezone&#34;&gt;timezone&lt;/h3&gt;
&lt;p&gt;Set your timezone by making &lt;code&gt;/etc/localtime&lt;/code&gt; a symlink to the timezone you use.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ln -fs /usr/share/zoneinfo/Europe/Amsterdam /etc/localtime
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;hostname&#34;&gt;hostname&lt;/h3&gt;
&lt;p&gt;Set the machine&amp;rsquo;s short hostname in &lt;code&gt;/etc/conf.d/hostname&lt;/code&gt; first, then add your
hostname aliases to &lt;code&gt;/etc/hosts&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# /etc/conf.d/hostname
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;hostname=&amp;#34;ivdea&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# /etc/hosts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1  ivdea.tyil.net ivdea
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;::1        ivdea.tyil.net ivdea
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;kernel&#34;&gt;kernel&lt;/h3&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		This will build the initramfs twice, since emerging gentoo-kernel will build it
automagically. This can be &amp;ldquo;fixed&amp;rdquo; by removing a USE flag, but this is easier to
me.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;By the time you&amp;rsquo;re reading this, the kernel version used here is probably
outdated. You will want to update it to whichever kernel version you&amp;rsquo;re going to
use.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;emerge &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    busybox &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    dracut &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    efibootmgr &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    gentoo-kernel &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    intel-microcode &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    linux-firmware
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;emerge sys-fs/zfs-kmod sys-fs/zfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;emerge --config gentoo-kernel
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-update add zfs-import boot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-update add zfs-mount boot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-update add zfs-share default
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-update add zfs-zed default
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zgenhostid
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp /boot/vmlinuz-5.15.59-gentoo-dist /efi/efi/gentoo/vmlinuz-5.15.59-gentoo-dist.efi
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp /boot/initramfs-5.15.59-gentoo-dist /efi/efi/gentoo/initramfs-5.15.59-gentoo-dist.img
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;efibootmgr &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --disk /dev/disk/by-id/nvme-eui.36483331545090280025385800000001 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --part &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --create &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --label &lt;span class=&#34;s2&#34;&gt;&amp;#34;Gentoo ZFS 5.15.59&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --loader &lt;span class=&#34;s1&#34;&gt;&amp;#39;efi\gentoo\vmlinuz-5.15.59-gentoo-dist.efi&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    --unicode &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;dozfs root=ZFS=ivdea0/rootfs/gentoo ro initrd=\efi\gentoo\initramfs-5.15.59-gentoo-dist.img encrypted&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;root-password&#34;&gt;Root password&lt;/h3&gt;
&lt;p&gt;Set the root password using &lt;code&gt;passwd&lt;/code&gt;. This would also be a good time to add any
other users you want to use, and configure them with the correct permissions and
groups.&lt;/p&gt;
&lt;h2 id=&#34;misc&#34;&gt;Misc&lt;/h2&gt;
&lt;p&gt;If you have any other software requirements, such as wireless network management
or privilege escalation utilities, this is the most appropriate time to install
and configure them.&lt;/p&gt;
&lt;h2 id=&#34;reboot&#34;&gt;Reboot&lt;/h2&gt;
&lt;p&gt;Now you can reboot into the system, and be done with this guide. If anything
isn&amp;rsquo;t working properly, return to the &amp;ldquo;Recovery&amp;rdquo; step and fix any outstanding
issues.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Configuring my Machines with Bashtard</title>
      <link>https://www.tyil.nl/post/2022/05/07/configuring-my-machines-with-bashtard/</link>
      <pubDate>Sat, 07 May 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2022/05/07/configuring-my-machines-with-bashtard/</guid>
      <description>&lt;p&gt;Over the past couple weeks I&amp;rsquo;ve been spending some time here and there to work
on my own application for configuring my machines. Before this I&amp;rsquo;ve tried
Ansible, but found it to be very convoluted to use, and requires a lot of
conditionals if your machines aren&amp;rsquo;t all running the same base system.&lt;/p&gt;
&lt;p&gt;So I made something in Bash, with a few abstractions to make certain
interactions less annoying to do manually every time. This used to be called
&lt;code&gt;tyilnet&lt;/code&gt;, but I&amp;rsquo;ve discussed the setup with a few people on IRC, and decided it
would be a fun project to make it a bit more agnostic, so other people could
also easily start using it. This resulted in the creation of
&lt;a href=&#34;https://git.tyil.nl/bashtard/&#34;&gt;Bashtard&lt;/a&gt;, pronounced as &amp;ldquo;bash&amp;rdquo;, followed by
&amp;ldquo;tard&amp;rdquo; (as in &amp;ldquo;bastard&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;It works by simply writing Bash scripts to do the configuration, and provides
abstractions for using the system&amp;rsquo;s package manager, service manager, and some
utilities such as logging and dealing with configured values. Configuration
values can be set on a per-host or per-OS basis. Since I run a varied base of
OSs, including Gentoo, Debian, and FreeBSD, the per-OS configuration comes in
very handy to me.&lt;/p&gt;
&lt;p&gt;As for the reason to use Bash, I chose it because most of the systems I run
already have this installed, so it doesn&amp;rsquo;t add a dependency &lt;em&gt;most of the time&lt;/em&gt;.
I would&amp;rsquo;ve liked to do it in POSIX sh, but I feel that when you&amp;rsquo;re reaching a
certain level of complexity, Bash offers some very nice features which can make
your code cleaner, or less likely to contain bugs. Features such as &lt;code&gt;[[ ]]&lt;/code&gt;,
&lt;code&gt;local&lt;/code&gt;, and arrays come to mind.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been kindly asked to guide potential new users to writing their first
Bashtard script, known as a &lt;em&gt;playbook&lt;/em&gt;, so if you want to know about how it
works in practice, keep on reading. If you&amp;rsquo;re satisfied with your current
configuration management system, this might not be quite as interesting to you,
so be warned.&lt;/p&gt;
&lt;p&gt;The first steps for a new user would obviously to install Bashtard, as it&amp;rsquo;s not
in any OSs package repositories yet. A &lt;code&gt;Makefile&lt;/code&gt; is supplied in the repository,
which should make this easy enough.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://git.tyil.nl/bashtard
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cd bashtard
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo make install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;hash -r
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once installed, it needs some initialization.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bashtard init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create the basic structure in &lt;code&gt;/etc/bashtard&lt;/code&gt;, including a
&lt;code&gt;playbooks.d&lt;/code&gt;. Inside this &lt;code&gt;playbooks.d&lt;/code&gt; directory, any directory is considered
to be a playbook, which requires a &lt;code&gt;description.txt&lt;/code&gt; and a &lt;code&gt;playbook.bash&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cd /etc/bashtard/playbooks.d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir ssh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cd ssh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;echo &amp;#34;OpenSSH configuration&amp;#34; &amp;gt; description.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$EDITOR playbook.bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;playbook.bash&lt;/code&gt; needs to contain 3 functions which are used by &lt;code&gt;bashtard&lt;/code&gt;, a
&lt;code&gt;playbook_add()&lt;/code&gt;, &lt;code&gt;playbook_sync()&lt;/code&gt;, and &lt;code&gt;playbook_del()&lt;/code&gt;. These will be called
by the &lt;code&gt;bashtard&lt;/code&gt; subcommand &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;sync&lt;/code&gt;, and &lt;code&gt;del&lt;/code&gt; respectively.&lt;/p&gt;
&lt;p&gt;I generally start with the &lt;code&gt;playbook_sync()&lt;/code&gt; function first, since this is the
function that&amp;rsquo;ll ensure all the configurations are kept in sync with my desires.
I want to have my own &lt;code&gt;sshd_config&lt;/code&gt;, which needs some templating for the
&lt;code&gt;Subsystem sftp&lt;/code&gt; line.  There&amp;rsquo;s a &lt;code&gt;file_template&lt;/code&gt; function provided by bashtard,
which does some very simple templating. I&amp;rsquo;ll pass it the &lt;code&gt;sftp&lt;/code&gt; variable to use.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;playbook_sync&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    file_template sshd_config &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;sftp=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;config &lt;span class=&#34;s2&#34;&gt;&amp;#34;ssh.sftp&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;        &amp;gt; /etc/ssh/sshd_config
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now to create the actual template. The &lt;code&gt;file_template&lt;/code&gt; function looks for
templates in the &lt;code&gt;share&lt;/code&gt; directory inside the playbook directory.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir share
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$EDITOR share/sshd_config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since I already know what I want my &lt;code&gt;sshd_config&lt;/code&gt; to look like from previous
installed systems, I&amp;rsquo;ll just use that, but with a variable for the &lt;code&gt;Subsystem sftp&lt;/code&gt; value.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cfg&#34; data-lang=&#34;cfg&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Connectivity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Port 22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;AddressFamily any&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ListenAddress 0.0.0.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ListenAddress ::&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Fluff&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;PrintMotd yes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# SFTP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Subsystem sftp ${sftp}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Authentication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;AuthorizedKeysFile /etc/ssh/authorized_keys .ssh/authorized_keys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;PermitRootLogin no&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;PasswordAuthentication no&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ChallengeResponseAuthentication no&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;PubkeyAuthentication no&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Allow tyil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Match User tyil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;PubkeyAuthentication yes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Allow public key authentication over VPN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;Match Address 10.57.0.0/16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;PubkeyAuthentication yes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;PermitRootLogin prohibit-password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;${sftp}&lt;/code&gt; placeholder will be filled with whatever value is returned by
&lt;code&gt;config &amp;quot;ssh.sftp&amp;quot;&lt;/code&gt;. And for this to work properly, we will need to define the
variable somewhere. These are written to the &lt;code&gt;etc&lt;/code&gt; directory inside a playbook.
You can specify defaults in a file called &lt;code&gt;defaults&lt;/code&gt;, and this can be
overwritten by OS-specific values, which in turn can be overwritten by
host-specific values.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir etc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$EDITOR etc/defaults
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The format for these files is a very simple &lt;code&gt;key=value&lt;/code&gt;. It splits on the first
&lt;code&gt;=&lt;/code&gt; to determine what the key and value are. This means you can use &lt;code&gt;=&lt;/code&gt; in your
values, but not your keys.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh.sftp=/usr/lib/openssh/sftp-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This value is correct for Debian and derivatives, but not for my Gentoo or
FreeBSD systems, so I&amp;rsquo;ve created OS-specific configuration files for these.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir etc/os.d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat etc/os.d/linux-gentoo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh.sftp=/usr/lib64/misc/sftp-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat etc/os.d/freebsd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh.sftp=/usr/lib64/misc/sftp-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My &lt;code&gt;sshd_config&lt;/code&gt; template also specifies the use of a &lt;code&gt;Motd&lt;/code&gt;, so that needs to
be created as well. This can again be done using the &lt;code&gt;template&lt;/code&gt; function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;file_template &lt;span class=&#34;s2&#34;&gt;&amp;#34;motd&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;fqdn=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BASHTARD_PLATFORM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[fqdn]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;time=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date -u &lt;span class=&#34;s2&#34;&gt;&amp;#34;+%FT%T&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;    &amp;gt; /etc/motd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;motd&lt;/code&gt; template gets saved at &lt;code&gt;share/motd&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          ████████╗██╗   ██╗██╗██╗        ███╗   ██╗███████╗████████╗
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          ╚══██╔══╝╚██╗ ██╔╝██║██║        ████╗  ██║██╔════╝╚══██╔══╝
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             ██║    ╚████╔╝ ██║██║        ██╔██╗ ██║█████╗     ██║
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             ██║     ╚██╔╝  ██║██║        ██║╚██╗██║██╔══╝     ██║
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             ██║      ██║   ██║███████╗██╗██║ ╚████║███████╗   ██║
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             ╚═╝      ╚═╝   ╚═╝╚══════╝╚═╝╚═╝  ╚═══╝╚══════╝   ╚═╝
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Welcome to ${fqdn}, last updated on ${time}.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lastly, we want to ensure the SSH daemon gets reloaded after every sync, so
let&amp;rsquo;s add that to the &lt;code&gt;playbook_sync()&lt;/code&gt; function as well.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;svc reload &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;svc&lt;/code&gt; utility looks for a configuration value that starts with &lt;code&gt;svc.&lt;/code&gt;,
followed by the service you&amp;rsquo;re trying to act upon, so in this case that would be
&lt;code&gt;svc.sshd&lt;/code&gt;. We can add this to our configuration files in &lt;code&gt;etc&lt;/code&gt;. Across all my
machines, &lt;code&gt;sshd&lt;/code&gt; seems to work as the value, so I only need to add one line to
&lt;code&gt;etc/defaults&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;svc.sshd=sshd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should take care of all the things I want automatically synced. The
&lt;code&gt;playbook_add()&lt;/code&gt; function is intended for all one-time setup required for any
playbook. In this case that means the SSH daemon&amp;rsquo;s service needs to be
activated, since it is not active by default on all my setups.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;playbook_add&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    svc &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    svc start &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, &lt;code&gt;add&lt;/code&gt; does not call a &lt;code&gt;sync&lt;/code&gt;, and I don&amp;rsquo;t want my SSH service to run
with default configuration until a &lt;code&gt;sync&lt;/code&gt; is initialized. So before enabling and
starting the service, I will call &lt;code&gt;sync&lt;/code&gt; manually, by running a &lt;code&gt;playbook_sync&lt;/code&gt;
first. This in turn, however, poses another problem, as &lt;code&gt;playbook_sync()&lt;/code&gt; wants
to reload the service, which it can&amp;rsquo;t do unless it is already running. To fix
this, I&amp;rsquo;ll add an &lt;code&gt;if&lt;/code&gt; statement to skip reloading if &lt;code&gt;bashtard&lt;/code&gt; is running the
&lt;code&gt;add&lt;/code&gt; command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;playbook_add&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    playbook_sync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    svc &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    svc start &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;playbook_sync&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;[[&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$BASHTARD_COMMAND&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;add&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    svc reload &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, &lt;code&gt;bashtard add sshd&lt;/code&gt; will run the &lt;code&gt;playbook_add()&lt;/code&gt; function, which calls the
&lt;code&gt;playbook_sync()&lt;/code&gt; function before enabling and starting the &lt;code&gt;sshd&lt;/code&gt; service. All
that is left is the &lt;code&gt;playbook_del()&lt;/code&gt; function, which only really needs to stop
and disable the service. The templated files can be removed here as well if
desired, of course.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;playbook_del&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    svc stop &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    svc disable &lt;span class=&#34;s2&#34;&gt;&amp;#34;sshd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lastly, I configured my &lt;code&gt;crond&lt;/code&gt; to run &lt;code&gt;bashtard sync&lt;/code&gt; every 20 minutes, so
whenever I update my configurations, it can take up to 20 minutes to propagate
to all my machines. Having an abstraction to deal with &lt;code&gt;cron&lt;/code&gt; (or SystemD timers
where applicable) in Bashtard is something I&amp;rsquo;d like to add, but I have no
concrete plans on how to do this, yet.&lt;/p&gt;
&lt;p&gt;You can find the full &lt;code&gt;playbook.bash&lt;/code&gt; source on
&lt;a href=&#34;https://git.tyil.nl/tyilnet/tree/playbooks.d/ssh/playbook.bash?id=319ab064370cb1e65be115ffddf5c0cd519af2dd&#34;&gt;git.tyil.nl&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A cookbook!</title>
      <link>https://www.tyil.nl/post/2022/04/23/a-cookbook/</link>
      <pubDate>Sat, 23 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2022/04/23/a-cookbook/</guid>
      <description>&lt;p&gt;Last week, I&amp;rsquo;ve decided to add a cookbook to my website. I&amp;rsquo;ve thought about
doing so for a while, but was rather hesitent since my website was mostly a
tech-minded blog. However, this time around I&amp;rsquo;ve decided to simply add a
cookbook, and have seperate RSS feeds for my &lt;a href=&#34;https://www.tyil.nl/posts/index.xml&#34;&gt;blog posts&lt;/a&gt; and
&lt;a href=&#34;https://www.tyil.nl/recipes/index.xml&#34;&gt;recipes&lt;/a&gt;, so that readers can decide what they want to
follow. Now I can easily share any recipes that visitors have asked me to share
over the years.&lt;/p&gt;
&lt;p&gt;The cookbook has an overview of all recipes, and each invidual recipe has a
layout greatly inspired by MartijnBraam&amp;rsquo;s &lt;a href=&#34;https://fathub.org&#34;&gt;FatHub&lt;/a&gt;. The
JavaScript is an exact copy, even.&lt;/p&gt;
&lt;p&gt;The format of the recipes has been altered slightly. I found the FatHub format
to be a little too verbose to make it simple enough to want to write down the
recipes. So I&amp;rsquo;ve trimmed down on some of that. You can check the sources that I
use on &lt;a href=&#34;https://git.tyil.nl/blog/&#34;&gt;git.tyil.nl&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since my website is generated with &lt;a href=&#34;https://gohugo.io&#34;&gt;Hugo&lt;/a&gt;, I had to do the
template myself as well. But these were fairly simple to port over, and I am
fairly happy with the result. I&amp;rsquo;ve made two changes compared to the layout used
by FatHub: Every step in the instructions is numbered, and the checkboxes for
the ingredients are at the left-hand side. The ingredients don&amp;rsquo;t contain a
preparation step in the tables, as I&amp;rsquo;ve made that part of the cooking
instructions.&lt;/p&gt;
&lt;p&gt;The entire thing was done in an afternoon during Easter, so it was pretty
straightforward to set up. I haven&amp;rsquo;t included many recipes yet, as I want to
double-check the right amounts of ingredients and instructions for many of them.
The downside of a recipe is that people generally follow it to the letter, and
I&amp;rsquo;m not a professional cook, I often cook just by tasting and adjusting as
desired.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s one thing I didn&amp;rsquo;t include, but which I might consider working on in the
future: &lt;a href=&#34;https://en.wikipedia.org/wiki/Baker_percentage&#34;&gt;baker&amp;rsquo;s percentages&lt;/a&gt;.
Some of my recipes have been written down in this notation, and for now I&amp;rsquo;ll be
converting them to regular quantities instead. There&amp;rsquo;s also some downsides to
baker&amp;rsquo;s percentages such as harder to calculate what a serving size is, or the
duration of certain steps, which has caused me to not make this a hard
requirement for the cookbook section of my site.&lt;/p&gt;
&lt;p&gt;I think the cookbook is more than enough to get started with, and to try sharing
some recipes in. If I&amp;rsquo;ve ever cooked something for you in person, and you would
like to know the recipe, don&amp;rsquo;t hesitate to ask me to include it in the cookbook.
Additionally, if you have some tips on how to improve an existing recipe, I&amp;rsquo;d be
very happy to hear about it!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Fixing ^w in tremc</title>
      <link>https://www.tyil.nl/post/2022/04/15/fixing-w-in-tremc/</link>
      <pubDate>Fri, 15 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2022/04/15/fixing-w-in-tremc/</guid>
      <description>&lt;p&gt;I like collecting GNU+Linux ISOs. I&amp;rsquo;d like to think I have a pretty serious
collection of these things. I have a pretty good and stable Internet connection,
so I collect them in my torrent client, and let them seed pretty much forever,
so other people can enjoy them too.&lt;/p&gt;
&lt;p&gt;For this hobby, I&amp;rsquo;m using &lt;a href=&#34;https://transmissionbt.com/&#34;&gt;Transmission&lt;/a&gt;, with
&lt;a href=&#34;https://github.com/tremc/tremc&#34;&gt;tremc&lt;/a&gt; as the frontend. I&amp;rsquo;ve been using it for
a while, but there&amp;rsquo;s always been a small thing bothering me. Whenever you get a
prompt to specify the name of the torrent, or the directory to which its
contents should be downloaded, &lt;code&gt;^w&lt;/code&gt; immediately kills the entire application.&lt;/p&gt;
&lt;p&gt;By regular shell standards, I&amp;rsquo;m used to &lt;code&gt;^w&lt;/code&gt; not killing the application, but
just removing from the cursor to the start of the previous word. I don&amp;rsquo;t often
change the names of the torrents, but when I do, I often use &lt;code&gt;^w&lt;/code&gt; to quickly
remove one or two words in the path. With tremc, this sadly means me killing the
application, being upset for a little while as I restart it, and hold down the
backspace for a while to get the intended effect.&lt;/p&gt;
&lt;p&gt;Until last night. I set out to read the documentation to fix this issue once and
for all. I dug into the manual, which specifies that a configuration file at
&lt;code&gt;~/.config/tremc/settings.cfg&lt;/code&gt; is read at startup. However, the manual
doesn&amp;rsquo;t specify anything about the format of this file. There&amp;rsquo;s also no other
manual pages included in the package.&lt;/p&gt;
&lt;p&gt;The Gentoo package specifies a homepage for this project on Github, so I open up
my least-hated browser and see if there&amp;rsquo;s anything there. Good news, the
repository contains a sample &lt;code&gt;settings.cfg&lt;/code&gt;, so I can read that and get an idea
on how to do keybinds. And the examples do show how to &lt;em&gt;set&lt;/em&gt; keybinds. But it
doesn&amp;rsquo;t seem to be able to &lt;em&gt;remove&lt;/em&gt; keybinds. This is kind of a bummer. Setting
a keybind to an empty value didn&amp;rsquo;t seem to do the trick either. This leaves only
one option, patching the defaults.&lt;/p&gt;
&lt;p&gt;This was actually pretty straightforward, just look for the list of default
keybinds, and remove a single line.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-patch&#34; data-lang=&#34;patch&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;@@ -222,7 +222,6 @@ class GConfig:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;             # First in list: 0=all 1=list 2=details 3=files 4=tracker 16=movement
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             # +256 for RPC&amp;gt;=14, +512 for RPC&amp;gt;=16
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             &amp;#39;list_key_bindings&amp;#39;: [0, [&amp;#39;F1&amp;#39;, &amp;#39;?&amp;#39;], &amp;#39;List key bindings&amp;#39;],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-            &amp;#39;quit_now&amp;#39;: [0, [&amp;#39;^w&amp;#39;], &amp;#39;Quit immediately&amp;#39;],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;             &amp;#39;quit&amp;#39;: [1, [&amp;#39;q&amp;#39;], &amp;#39;Quit&amp;#39;],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             &amp;#39;leave_details&amp;#39;: [2, [&amp;#39;BACKSPACE&amp;#39;, &amp;#39;q&amp;#39;], &amp;#39;Back to torrent list&amp;#39;],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             &amp;#39;go_back_or_unfocus&amp;#39;: [2, [&amp;#39;ESC&amp;#39;, &amp;#39;BREAK&amp;#39;], &amp;#39;Unfocus or back to torrent list&amp;#39;],
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since I&amp;rsquo;m just testing, and this program is a single Python file, I just edit
the file in-place, and delay making a proper patch out of for later. Restarting
tremc, going to the new torrent, pressing &lt;code&gt;m&lt;/code&gt; (for &amp;ldquo;move&amp;rdquo;), and hitting &lt;code&gt;^w&lt;/code&gt; to
try and remove a single word, hoping for a quick and easy fix, I was met with
tremc just quitting again. This was not what I wanted.&lt;/p&gt;
&lt;p&gt;So opening up the file again with everyone&amp;rsquo;s favourite editor, I search around
for the &lt;code&gt;quit_now&lt;/code&gt; function. Surely that&amp;rsquo;ll bring me closer? The &lt;code&gt;quit_now&lt;/code&gt;
string doesn&amp;rsquo;t seem to be used anywhere else, apart from the function that
defines what the keybind action should do, &lt;code&gt;action_quit_now&lt;/code&gt;. This seems to
simply defer to &lt;code&gt;exit_now&lt;/code&gt;, which leads me to a bit of code with a special case
to &lt;code&gt;exit_now&lt;/code&gt; if a certain character is detected. This character appears to be a
&lt;code&gt;^w&lt;/code&gt;, which is exactly what I&amp;rsquo;m trying to stop. So, let&amp;rsquo;s patch that out too.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-patch&#34; data-lang=&#34;patch&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;@@ -3760,10 +3759,7 @@ class Interface:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;                 self.update_torrent_list([win])
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     def wingetch(self, win):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-        c = win.getch()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-        if c == K.W_:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-            self.exit_now = True
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-        return c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+        return win.getch()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     def win_message(self, win, height, width, message, first=0):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         ypos = 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restart tremc and test again. Still exiting immediately upon getting a &lt;code&gt;^w&lt;/code&gt;.
This bit of code gives me some new insights, it appears &lt;code&gt;K.W_&lt;/code&gt; is related to the
keycode of a &lt;code&gt;^w&lt;/code&gt;. So I continue the search, this time looking for &lt;code&gt;K.W_&lt;/code&gt;. This
appears to be used later on to create a list of characters which should act as
&lt;code&gt;esc&lt;/code&gt; in certain contexts. Removing the &lt;code&gt;K.W_&lt;/code&gt; from this is simple enough.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-patch&#34; data-lang=&#34;patch&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;@@ -5039,7 +5047,7 @@ def parse_config_key(interface, config, gconfig, common_keys, details_keys, list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;     else:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         gconfig.esc_keys = (K.ESC, K.q, curses.KEY_BREAK)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     gconfig.esc_keys_no_ascii = tuple(x for x in gconfig.esc_keys if x not in range(32, 127))
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-    gconfig.esc_keys_w = gconfig.esc_keys + (K.W_,)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    gconfig.esc_keys_w = gconfig.esc_keys
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;     gconfig.esc_keys_w_enter = gconfig.esc_keys_w + (K.LF, K.CR, curses.KEY_ENTER)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     gconfig.esc_keys_w_no_ascii = tuple(x for x in gconfig.esc_keys_w if x not in range(32, 127))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restart, test, and&amp;hellip; Nothing. This is an improvement, the program isn&amp;rsquo;t exiting
immediately, it just does nothing. I know that &lt;code&gt;^u&lt;/code&gt; works as I expect, so
perhaps it needs some love to have &lt;code&gt;^w&lt;/code&gt; work properly as well. I search for
&lt;code&gt;K.U_&lt;/code&gt;, and indeed, there is code dedicated to this keypress, in a long &lt;code&gt;elif&lt;/code&gt;
construct. So I add some code to get &lt;code&gt;^w&lt;/code&gt; working as well. After a bit of
fiddling, and realizing I&amp;rsquo;ve spent way too much time on adding a torrent, I&amp;rsquo;ve
settled on this little bit of love.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-patch&#34; data-lang=&#34;patch&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;@@ -3957,6 +3953,18 @@ class Interface:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;                 # Delete from cursor until beginning of line
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 text = text[index:]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 index = 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+            elif c == K.W_:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                # Delete from cursor to beginning of previous word... mostly
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                text_match = re.search(&amp;#34;[\W\s]+&amp;#34;, text[::-1])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                if text_match.span()[0] == 0:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                    # This means the match was found immediately, I can&amp;#39;t be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                    # bothered to make this any nicer.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                    text = text[:-1]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                    index -= 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                else:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                    text = text[:-text_match.span()[0]]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;+                    index -= text_match.span()[0]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;             elif c in (curses.KEY_HOME, K.A_):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 index = 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;             elif c in (curses.KEY_END, K.E_):
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It looks for the first non-word character or a space, starting from the end of
the string (&lt;code&gt;[::-1]&lt;/code&gt; reverses a string in Python). The resulting &lt;code&gt;Match&lt;/code&gt; object
can tell me how many characters I need to delete in the &lt;code&gt;.span()[0]&lt;/code&gt; value. A
small exception is created if that value is &lt;code&gt;0&lt;/code&gt;, otherwise the logic below
doesn&amp;rsquo;t work well.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not perfect, but it gets the job done well enough, and I don&amp;rsquo;t like Python
enough to spend more time on it than I&amp;rsquo;ve already done. I am open for better
solutions that work better, though!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Deprecating ReiserFS</title>
      <link>https://www.tyil.nl/post/2022/03/05/deprecating-reiserfs/</link>
      <pubDate>Sat, 05 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2022/03/05/deprecating-reiserfs/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://lkml.org/lkml/2022/2/20/89&#34;&gt;ReiserFS is getting deprecated from Linux&lt;/a&gt;,
mostly due to it not being ready for the year 2038. This is a little sad, as I
still use it on some systems for storing the Gentoo Portage tree, and the Linux
kernel sources. It works well for this because it supports
&lt;a href=&#34;https://en.wikipedia.org/wiki/Block_suballocation#Tail_packing&#34;&gt;tail packing&lt;/a&gt;,
a form of block suballocation, which can save disk space.&lt;/p&gt;
&lt;p&gt;So, what alternatives are there for ReiserFS? After asking around and reading
some comments around the Internet, I&amp;rsquo;ve narrowed it down to 3 potential
candidates, bcachefs, btrfs, and zfs. Each comes with their own pros and cons,
as things tend to do.&lt;/p&gt;
&lt;h2 id=&#34;bcachefs&#34;&gt;bcachefs&lt;/h2&gt;
&lt;p&gt;There are several downsides for bcachefs for me. The first one I found was that
the documentation on their main site seems a bit lacking, followed shortly by
finding that there are no ebuilds for it in Gentoo.&lt;/p&gt;
&lt;p&gt;Since it was suggested several times on comments on a certain orange site, I
asked around if it at least supported block suballocation, which is the main
reason I would want to use it anyway. The answer came back as a &amp;ldquo;no&amp;rdquo;, so I could
safely ignore it for the rest of the journey.&lt;/p&gt;
&lt;h2 id=&#34;btrfs&#34;&gt;BTRFS&lt;/h2&gt;
&lt;p&gt;BTRFS seems like a more serious contender. It supports block suballocation, and
has good enough documentation. As an additional benefit, it is supported in the
mainline Linux kernel, making it easy to use on any modern setup. There are a
few issues, such as having to rebalance in certain situations, and this
rebalancing can itself cause issues. The files I&amp;rsquo;m storing are relatively easily
recreated with a single git clone, or downloading a tarball and unpacking that,
so that doesn&amp;rsquo;t have to be problematic to me.&lt;/p&gt;
&lt;h2 id=&#34;zfs&#34;&gt;ZFS&lt;/h2&gt;
&lt;p&gt;The final contestant, ZFS, supports block suballocation and has great
documentation. It is not part of the mainline Linux kernel, however, so this may
make things more complex on some systems. I run ZFS already on a few machines,
but not all, so where it is not used already, it is a drawback.&lt;/p&gt;
&lt;p&gt;Since my main concern is storing many small files, I created a few logical
volumes (and 1 ZFS subvol) and cloned the main reason for wanting a filesystem
with block suballocation, the &lt;a href=&#34;https://github.com/gentoo/portage&#34;&gt;Gentoo Portage
tree&lt;/a&gt;. The cloning itself was done with
&lt;code&gt;--depth=1&lt;/code&gt;.For reference, I also created an ext4 volume.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/dev/mapper/edephas0-test.btrfs     5.0G  559M  3.8G  13% /tmp/test/btrfs
/dev/mapper/edephas0-test.ext4      4.9G  756M  3.9G  17% /tmp/test/ext4
/dev/mapper/edephas0-test.reiserfs  5.0G  365M  4.7G   8% /tmp/test/reiserfs
tyilstore0/test                     5.0G  1.1G  4.0G  21% /tmp/test/zfs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Looking at the output from &lt;code&gt;df -h&lt;/code&gt;, ReiserFS seem to still be a clear winner
when it comes to storing many small files. Nothing is even close. What does
surprise me, however, is that ZFS is actually resulting in the largest space
requirement. I&amp;rsquo;m not sure why this is, as it should support block suballocation
just fine according to &lt;a href=&#34;https://en.wikipedia.org/wiki/Comparison_of_file_systems#Allocation_and_layout_policies&#34;&gt;the filesystem comparison chart on
Wikipedia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;BTRFS comes out as the next best option, after ReiserFS, so that&amp;rsquo;ll be what I am
going to use on my systems for storing large trees of small files.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Updating NginX TLS settings for 2022</title>
      <link>https://www.tyil.nl/post/2022/02/20/updating-nginx-tls-settings-for-2022/</link>
      <pubDate>Sun, 20 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2022/02/20/updating-nginx-tls-settings-for-2022/</guid>
      <description>&lt;p&gt;My blog (and pretty much every other site I host) is using Let&amp;rsquo;s Encrypt
certificates in order to be served over https. Using any certificate at all is
generally an upgrade when it comes to security, but your webserver&amp;rsquo;s
configuration needs to be up to standard as well. Unlike the Let&amp;rsquo;s Encrypt
certificates, keeping the configs up to date requires manual intervention, and
is therefore something I don&amp;rsquo;t do often.&lt;/p&gt;
&lt;p&gt;This week I decided I should check up on the state of my SSL configuration in
nginx. I usually check this through &lt;a href=&#34;https://www.ssllabs.com/ssltest/analyze.html&#34;&gt;SSL Lab&amp;rsquo;s SSL Server
Test&lt;/a&gt;. This is basically
&lt;a href=&#34;https://testssl.sh/&#34;&gt;testssl.sh&lt;/a&gt; in a shiny web frontend, and assigns you a
score. I aim to always have A or higher, but it seemed my old settings were
capped at a B. This was due to the cipher list allowing ciphers which are
nowadays considered insecure.&lt;/p&gt;
&lt;p&gt;While I could&amp;rsquo;ve just updated the cipher list, I decided to check up on all the
other settings as well. There&amp;rsquo;s a &lt;a href=&#34;https://gist.github.com/gavinhungry/7a67174c18085f4a23eb&#34;&gt;GitHub
gist&lt;/a&gt; which shows you
what settings to use with nginx to make it secure by modern standards, which
I&amp;rsquo;ve used to check if I should update some of my own settings.&lt;/p&gt;
&lt;p&gt;My old settings looked as follows. Please don&amp;rsquo;t be too scared of the giant list
in the &lt;code&gt;ssl_ciphers&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# DHparams
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ssl_dhparam&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/dhparam.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# SSL settings
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ssl_ciphers&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_protocols&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;TLSv1.2&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;TLSv1.3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_session_cache&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;shared:le_nginx_SSL:10m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_session_tickets&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_session_timeout&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1440m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Additional headers
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;max-age=63072000&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the new settings, I&amp;rsquo;ve added &lt;code&gt;ssl_buffer_size&lt;/code&gt; and &lt;code&gt;ssl_ecdh_curve&lt;/code&gt;. A
friend on IRC pointed out I should enable &lt;code&gt;ssl_prefer_server_ciphers&lt;/code&gt;, so this
has been enabled too.&lt;/p&gt;
&lt;p&gt;But most notably, the list of &lt;code&gt;ssl_ciphers&lt;/code&gt; has been dramatically reduced. I
still allow TLSv1.2 in order to allow slightly older clients to connect without
any issues, but the ciphers considered &lt;em&gt;WEAK&lt;/em&gt; have been disabled explicitly.
This leaves a total of 5 ciphers to use, all of them using ECDHE, so the
&lt;code&gt;ssl_dhparam&lt;/code&gt; could be dropped as well.&lt;/p&gt;
&lt;p&gt;Lastly, I&amp;rsquo;ve added a couple headers for security reasons, as recommended by
&lt;a href=&#34;https://securityheaders.com&#34;&gt;securityheaders.com&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# SSL settings
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ssl_protocols&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;TLSv1.3&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;TLSv1.2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_buffer_size&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;4K&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_ecdh_curve&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;secp521r1:secp384r1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_session_cache&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;shared:le_nginx_SSL:2m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_session_tickets&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_session_timeout&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1440m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;ssl_ciphers&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;EECDH+AESGCM:EECDH+AES256:!ECDHE-RSA-AES256-SHA384:!ECDHE-RSA-AES256-SHA&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Additional headers
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Content-Security-Policy&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;default-src&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#39;self&amp;#39;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Referrer-Policy&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;strict-origin-when-cross-origin&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;max-age=63072000&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;X-Content-Type-Options&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;nosniff&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;add_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;X-Frame-Options&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;SAMEORIGIN&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		I would still like the &lt;code&gt;ssl_ciphers&lt;/code&gt; to be formatted in a more clean way, which
I&amp;rsquo;ve tried to do with a variable through &lt;code&gt;set&lt;/code&gt;, but it appears variables are not
expanded within &lt;code&gt;ssl_ciphers&lt;/code&gt;. If you have any methods to format the list of
ciphers used in a cleaner way, I&amp;rsquo;d be very happy to know!
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;This configuration is saved as a small snippet which I &lt;code&gt;include&lt;/code&gt; in all other
site configurations, so it is updated everywhere at once as well. Now I should
be able to neglect this for another year or two again.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Trying out LibreWolf</title>
      <link>https://www.tyil.nl/post/2022/02/14/trying-out-librewolf/</link>
      <pubDate>Mon, 14 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2022/02/14/trying-out-librewolf/</guid>
      <description>&lt;p&gt;Over the past week, I&amp;rsquo;ve been trying out &lt;a href=&#34;https://librewolf.net/&#34;&gt;LibreWolf&lt;/a&gt; as
an alternative to mainline Firefox. I generally don&amp;rsquo;t hold a high opinion on any
&amp;ldquo;modern&amp;rdquo; browser to begin with, but Firefox has been the least bad for quite
some time. I used to actually like Firefox, but Mozilla has done their best to
alienate their user base in search for profits, and is eventually left with
neither. Their latest effort in digging their own grave is teaming up with Meta.&lt;/p&gt;
&lt;p&gt;As such, I have been searching for an alternative (modern) browser for a long
time. One major requirement that I&amp;rsquo;ve had is to have something like
&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/umatrix/&#34;&gt;uMatrix&lt;/a&gt;. And
obviously major features to block any and all advertisements, as these are a
major detriment to your own mental health, and to the resources your machine
uses. So, when someone recommended me LibreWolf, which is just a more
user-respecting fork of Firefox, I didn&amp;rsquo;t hesitate to try it out.&lt;/p&gt;
&lt;p&gt;The migration from Firefox to LibreWolf was remarkably simple. Since I use &lt;a href=&#34;https://git.tyil.nl/dotfiles/tree/.local/bin/firefox?id=1c8e9b136d9d00decc1d3570fe58072427107148&#34;&gt;a
small
wrapper&lt;/a&gt;
to launch Firefox with a specific profile directory, I just had to update that
to launch LibreWolf instead. It kept all my settings, installed add-ons, and even
open tabs. It seems that by default, however, it will use its own directory for
configuration. If you want to try out LibreWolf and have a similar experience,
you can just copy over your old Firefox configuration directory to a new
location for use with LibreWolf. In hindsight, that probably would&amp;rsquo;ve been the
safer route for me as well, but it already happened and it all went smooth, so
no losses.&lt;/p&gt;
&lt;p&gt;Now, while LibreWolf is more-or-less like Firefox, but less harmful to its own
users, some of the tweaks made by the LibreWolf team may or may not be desired.
I&amp;rsquo;ve taken note of any differences that could be conceived as issues. So far,
they&amp;rsquo;re not breaking for me, but these may be of interest to you if you&amp;rsquo;re
looking to try LibreWolf out as well.&lt;/p&gt;
&lt;h2 id=&#34;http&#34;&gt;HTTP&lt;/h2&gt;
&lt;p&gt;By default, LibreWolf will not let you visit sites over HTTP. This is generally
a very nice feature, but for some public hot-spots, this may cause issues. These
are generally completely unencrypted, and LibreWolf will refuse to connect. The
page presented instead will inform you that the page you&amp;rsquo;re trying to visit is
unencrypted, and allow you to make a temporary exception. Not a very big issue,
but it may be a little bit more annoying than you&amp;rsquo;re used to.&lt;/p&gt;
&lt;h2 id=&#34;add-ons&#34;&gt;Add-ons&lt;/h2&gt;
&lt;p&gt;While all my add-ons were retained, I did want to get another add-on to redirect
me away from YouTube, to use an Invidious instance. The page for installing
add-ons itself seems to work fine, but upon clicking the Install button, and
accepting the installation, LibreWolf throws an error that it simply failed to
install anything. The Install button is nothing more than a fancy anchor with a
link to the &lt;code&gt;xpi&lt;/code&gt; file, so you can manually download the file and install the
add-on manually through the &lt;a href=&#34;about:addons&#34;&gt;Add-ons Manager&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;element&#34;&gt;Element&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been using Matrix for a while, as an atechnical-friendly, open source
platform, for those unwilling to use IRC. Their recommended client,
&lt;a href=&#34;https://app.element.io/&#34;&gt;Element&lt;/a&gt;, is just another web page, because that&amp;rsquo;s
sadly how most software is made these days.  The chat itself works without a
hitch, but there are two minor inconveniences compared to my regular Firefox
setup.&lt;/p&gt;
&lt;p&gt;The first one is that LibreWolf does not share my local timezone with the
websites I visit. This causes timestamps to be off by one hour in the Element
client. A very minor issue that I can easily live with.&lt;/p&gt;
&lt;p&gt;The other is that the &amp;ldquo;default&amp;rdquo; icons, which is a capital letter with a colored
background, &lt;a href=&#34;https://dist.tyil.nl/blog/matrix-icons.png&#34;&gt;don&amp;rsquo;t look so well&lt;/a&gt;.
There&amp;rsquo;s some odd artifacts in the icons, which doesn&amp;rsquo;t seem to affect the letter
shown. Since I mostly use the
&lt;a href=&#34;https://github.com/poljar/weechat-matrix&#34;&gt;weechat-matrix&lt;/a&gt; plugin, it&amp;rsquo;s not
really an issue. And for the few times I do use Element, it doesn&amp;rsquo;t bother me
enough to consider it a real issue.&lt;/p&gt;
&lt;h2 id=&#34;jellyfin&#34;&gt;Jellyfin&lt;/h2&gt;
&lt;p&gt;For consuming all sorts of media, I have &lt;a href=&#34;https://jellyfin.org/&#34;&gt;Jellyfin&lt;/a&gt; set
up for personal use. This worked fine in my regular Firefox setup, but does not
seem to be willing to play any videos in LibreWolf. The console logs show some
issues with websockets, and I&amp;rsquo;ve not been able to find a good way to work around
this yet. For now, I&amp;rsquo;ll stick to using &lt;code&gt;mpv&lt;/code&gt; to watch any content to deal with
this issue.&lt;/p&gt;
&lt;p&gt;All in all, I think LibreWolf is a pretty solid browser, and unless I discover
something major to turn me off, I&amp;rsquo;ll keep using it for the foreseeable future.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Managing Docker Compose with OpenRC</title>
      <link>https://www.tyil.nl/post/2021/06/04/managing-docker-compose-with-openrc/</link>
      <pubDate>Fri, 04 Jun 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2021/06/04/managing-docker-compose-with-openrc/</guid>
      <description>&lt;p&gt;On one of my machines, I host a couple services using &lt;code&gt;docker-compose&lt;/code&gt;. I
wanted to start/restart/stop these using the default init/service manager used
on the machine, &lt;code&gt;openrc&lt;/code&gt;. This would allow them to start/stop automatically
with Docker (which coincides with the machine powering on or off,
respectively).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve set this up through a single &lt;code&gt;docker-compose&lt;/code&gt; meta-service. To add new
&lt;code&gt;docker-compose&lt;/code&gt; projects to be managed, all I need to do for &lt;code&gt;openrc&lt;/code&gt;
configuration is creating a symlink, and configure the path to the
&lt;code&gt;docker-compose.yaml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The meta-service lives at &lt;code&gt;/etc/init.d/docker-compose&lt;/code&gt;, just like all other
services managed by &lt;code&gt;openrc&lt;/code&gt;. This file is quite straightforward. To start off,
a number of variables are set and exported.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$RC_SVCNAME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;OpenRC script for managing the &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; docker-compose project&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Set default values&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DOCKER_COMPOSE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;DOCKER_COMPOSE&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;docker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;-compose&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE_ARGS&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;COMPOSE_PROJECT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COMPOSE_PROJECT_NAME&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Export all variables used by docker-compose CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PROJECT_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_FILE
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PROFILES
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_API_VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; DOCKER_HOST
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; DOCKER_TLS_VERIFY
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; DOCKER_CERT_PATH
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_HTTP_TIMEOUT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_TLS_VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_CONVERT_WINDOWS_PATHS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PATH_SEPARATOR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_FORCE_WINDOWS_HOST
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_IGNORE_ORPHANS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PARALLEL_LIMIT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_INTERACTIVE_NO_CLI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_DOCKER_CLI_BUILD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One of the services I use is also configured with its own &lt;code&gt;external&lt;/code&gt; network. I
want it to be created if it doesn&amp;rsquo;t exist, to ensure that the service can start
up properly. I do &lt;em&gt;not&lt;/em&gt; want it to be removed, so I left that out.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Set up (external) networks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; name in &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;DOCKER_NETWORKS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[@]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Create the network if needed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; ! docker network ls &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{ print $2 }&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -q &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                einfo &lt;span class=&#34;s2&#34;&gt;&amp;#34;Creating docker network &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                docker network create &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Expose some variables for the networks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;network_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;DOCKER_NETWORK_&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;_ID&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;declare&lt;/span&gt; -gx DOCKER_NETWORK_&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;_ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;docker network ls &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;$2 == &amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#34; { print $1 }&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;declare&lt;/span&gt; -gx DOCKER_NETWORK_&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;_GATEWAY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;docker network inspect &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!network_id&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; jq -r &lt;span class=&#34;s1&#34;&gt;&amp;#39;.[0].IPAM.Config[0].Gateway&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; network_id
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And lastly, there&amp;rsquo;s the four simple functions to declare dependencies,
configure how to &lt;code&gt;start&lt;/code&gt; or &lt;code&gt;stop&lt;/code&gt;, and how to get the &lt;code&gt;status&lt;/code&gt; of the service.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;depend&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        need docker
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;start&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE&lt;/span&gt; --project-directory &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; up -d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;status&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE&lt;/span&gt; --project-directory &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; ps
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;stop&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE&lt;/span&gt; --project-directory &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, to actually create a service file to manage a &lt;code&gt;docker-compose&lt;/code&gt; project, a
symlink must be made. I&amp;rsquo;ll take my
&lt;a href=&#34;https://github.com/azlux/botamusique&#34;&gt;&lt;code&gt;botamusique&lt;/code&gt;&lt;/a&gt; service as an example.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ln -s /etc/init.d/docker-compose /etc/init.d/botamusique
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This service can&amp;rsquo;t start just yet, as there&amp;rsquo;s no &lt;code&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/code&gt;
configured for it yet. For this, a similarly named file should be made in
&lt;code&gt;/etc/conf.d&lt;/code&gt;. In here, any variable used by the service can be configured.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$EDITOR /etc/conf.d/botamusique
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In my case, it only pertains the &lt;code&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;COMPOSE_PROJECT_DIRECTORY=&amp;#34;/var/docker-compose/botamusique&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that&amp;rsquo;s it. For additional &lt;code&gt;docker-compose&lt;/code&gt; projects I need to make only a
symlink and a configuration file. If I discover a bug or nuisance, only a
single file needs to be altered to get the benefit on all the &lt;code&gt;docker-compose&lt;/code&gt;
services.&lt;/p&gt;
&lt;p&gt;For reference, here&amp;rsquo;s the full &lt;code&gt;/etc/init.d/docker-compose&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/sbin/openrc-run
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# Copyright 2021 Gentoo Authors&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Distributed under the terms of the GNU General Public License v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$RC_SVCNAME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;OpenRC script for managing the &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; docker-compose project&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Set default values&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DOCKER_COMPOSE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;DOCKER_COMPOSE&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;docker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;-compose&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE_ARGS&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;COMPOSE_PROJECT_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;COMPOSE_PROJECT_NAME&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Export all variables used by docker-compose CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PROJECT_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_FILE
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PROFILES
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_API_VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; DOCKER_HOST
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; DOCKER_TLS_VERIFY
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; DOCKER_CERT_PATH
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_HTTP_TIMEOUT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_TLS_VERSION
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_CONVERT_WINDOWS_PATHS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PATH_SEPARATOR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_FORCE_WINDOWS_HOST
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_IGNORE_ORPHANS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_PARALLEL_LIMIT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_INTERACTIVE_NO_CLI
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; COMPOSE_DOCKER_CLI_BUILD
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Set up (external) networks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; name in &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;DOCKER_NETWORKS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[@]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Create the network if needed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; ! docker network ls &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{ print $2 }&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -q &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                einfo &lt;span class=&#34;s2&#34;&gt;&amp;#34;Creating docker network &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                docker network create &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Expose some variables for the networks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;network_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;DOCKER_NETWORK_&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;_ID&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;declare&lt;/span&gt; -gx DOCKER_NETWORK_&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;_ID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;docker network ls &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;$2 == &amp;#34;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#34; { print $1 }&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;declare&lt;/span&gt; -gx DOCKER_NETWORK_&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;_GATEWAY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;docker network inspect &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;!network_id&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; jq -r &lt;span class=&#34;s1&#34;&gt;&amp;#39;.[0].IPAM.Config[0].Gateway&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;unset&lt;/span&gt; network_id
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;depend&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        need docker
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;start&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE&lt;/span&gt; --project-directory &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; up -d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;status&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE&lt;/span&gt; --project-directory &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; ps
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;stop&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$DOCKER_COMPOSE&lt;/span&gt; --project-directory &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_PROJECT_DIRECTORY&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Raku is moving to Libera.chat</title>
      <link>https://www.tyil.nl/post/2021/05/22/raku-is-moving-to-libera.chat/</link>
      <pubDate>Sat, 22 May 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2021/05/22/raku-is-moving-to-libera.chat/</guid>
      <description>&lt;p&gt;Earlier this week, the staff at the Freenode IRC network have resigned en
masse, and created a new network, Libera. This was sparked by &lt;a href=&#34;https://kline.sh/&#34;&gt;new ownership of
Freenode&lt;/a&gt;. Due to concerns with the new ownership, the Raku
Steering Council has decided to also migrate our IRC channels to Libera.&lt;/p&gt;
&lt;p&gt;This requires us to take a couple steps. First and foremost, we need to
register the Raku project, to allow us a claim to the &lt;code&gt;#raku&lt;/code&gt; and related
channels. Approval for this happened within 24 hours, and as such, we can
continue on the more noticable steps.&lt;/p&gt;
&lt;p&gt;The IRC bots we&amp;rsquo;re using for various tasks will be moved next, and the Raku
documentation has to be updated to refer to Libera instead of Freenode. The
coming week we&amp;rsquo;ll be working on that, together with the people who provide
those bots.&lt;/p&gt;
&lt;p&gt;Once this is done, the last step involves the Matrix bridge. Libera and
Matrix.org staff are working on this, but there&amp;rsquo;s no definite timeline
available just yet. This may mean that Matrix users will temporarily not be
able to join the discussions happening at Libera. We will keep an eye on the
progress of this, and set up the bridge as soon as it has been made available.&lt;/p&gt;
&lt;p&gt;If you have any questions regarding the migration, feel free to reach out to us
via email (&lt;code&gt;rsc@raku.org&lt;/code&gt;) or on IRC (&lt;code&gt;#raku-steering-council&lt;/code&gt; on
irc.libera.chat).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A New IRC::Client</title>
      <link>https://www.tyil.nl/post/2021/05/13/a-new-ircclient/</link>
      <pubDate>Thu, 13 May 2021 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2021/05/13/a-new-ircclient/</guid>
      <description>&lt;p&gt;The Raku programming language has a popular module for creating IRC bots,
&lt;a href=&#34;https://github.com/raku-community-modules/IRC-Client&#34;&gt;&lt;code&gt;IRC::Client&lt;/code&gt;&lt;/a&gt;. However,
it&amp;rsquo;s been stale for quite a while, and one of the bots I host,
&lt;a href=&#34;https://github.com/Raku/geth&#34;&gt;Geth&lt;/a&gt;, is having troubles on a regular basis.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve looked at the source code, and found a lot of neat tricks, but when
maintaining a library, I generally want clean and straightforward code instead.
To that end, I decided to just write my own from scratch. Given that &lt;a href=&#34;https://tools.ietf.org/html/rfc2812&#34;&gt;the IRC
protocol is rather simple&lt;/a&gt;, this was the
easiest way to go about it.&lt;/p&gt;
&lt;p&gt;Sure enough, after a couple hours of playing around, I had something that
worked reasonably well. A few more hours a day afterwards brought me to an
&lt;code&gt;IRC::Client&lt;/code&gt; that is usable in mostly the same way as the current
&lt;code&gt;IRC::Client&lt;/code&gt;, to save me effort in getting my current bots to make use of it
for a few test runs.&lt;/p&gt;
&lt;p&gt;Geth was my main target, as I wanted it to stop from getting timed out so
often. For the past week, Geth has been running stable, without any time
out, so I think I&amp;rsquo;ve succeeded in the main goal.&lt;/p&gt;
&lt;p&gt;However, how to continue next? Since it is mostly compatible, but not
&lt;em&gt;completely&lt;/em&gt; compatible, if I were to adopt &lt;code&gt;IRC::Client&lt;/code&gt; from the Raku
ecosystem and push my version, many people&amp;rsquo;s IRC bots would break when people
update their dependencies. There is a solution for this built into the entire
ecosystem, which is using the correct &lt;code&gt;:ver&lt;/code&gt; and &lt;code&gt;:auth&lt;/code&gt; (and in some cases,
&lt;code&gt;:api&lt;/code&gt;) so you can ensure your project is always getting the &amp;ldquo;correct&amp;rdquo;
dependency. However, from personal experience, I know these additional
dependency restrictions are rarely used in practice.&lt;/p&gt;
&lt;p&gt;I hope that with this blog post, I can inform the community in time about the
changes that are coming to &lt;code&gt;IRC::Client&lt;/code&gt;, so people have ample time to set
their dependencies just right to keep their projects working. Of course, the
real solution for the long term would be to make the small changes required to
use the latest &lt;code&gt;IRC::Client&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;For convenience sake, I&amp;rsquo;ve added a small number of methods for backwards
compatibility as well, though these will generate &lt;a href=&#34;https://docs.raku.org/routine/is%20DEPRECATED&#34;&gt;a deprecation
warning&lt;/a&gt;, and will be removed in
a later &lt;code&gt;IRC::Client&lt;/code&gt; release.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s two major changes that are not backwards compatible, however. The first
one is the removal of the ability to have a single &lt;code&gt;IRC::Client&lt;/code&gt; connect to
multiple servers. This is also the easiest to remedy, by simply creating
multiple instances of &lt;code&gt;IRC::Client&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second major incompatible change is how events are dispatched to plugins.
This used to be handled by going through all the plugins sequentially, allowing
one plugin to stop the dispatch to another plugin. In my new version, events
are dispatched to all plugins in parallel. This allows for faster execution,
and for multiple plugins to handle an event without having to use &lt;code&gt;$*NEXT&lt;/code&gt;
(which has been removed). My main motivation is that a buggy plugin will no
longer interfere with the interactions provided by other plugins. The ordering
of your plugins will also stop being an issue to worry about.&lt;/p&gt;
&lt;p&gt;Geth&amp;rsquo;s updates to actually use my updated &lt;code&gt;IRC::Client&lt;/code&gt; module was introduced
in
&lt;a href=&#34;https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa&#34;&gt;&lt;code&gt;edc6b08&lt;/code&gt;&lt;/a&gt;,
and most if it was updates to the way it handled logging. The actual changes
needed to make Geth play nice were&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R14&#34;&gt;Adding the &lt;code&gt;IRC::Client::Plugin&lt;/code&gt; role to &lt;code&gt;Geth::Plugin::Info&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R24&#34;&gt;And to &lt;code&gt;Geth::Plugin::Uptime&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R34&#34;&gt;Using &lt;code&gt;nicks&lt;/code&gt; instead of &lt;code&gt;nick&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-104b5cdb61f2aa423eb941ce32a4412b4cb814014728cae968e5aeff7dc587d2R46&#34;&gt;Using &lt;code&gt;start&lt;/code&gt; instead of &lt;code&gt;run&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Raku/geth/commit/edc6b08036c8ede08afdea39fd1768655a2766aa#diff-c388af832470886cdd4304aeb17c4c2f406ac184d33afb956b0ef8a92b69855bR57&#34;&gt;Using &lt;code&gt;privmsg&lt;/code&gt; instead of &lt;code&gt;send&lt;/code&gt;&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last two changes aren&amp;rsquo;t strictly necessary, as there are backwards
compatibility methods made for these, but it&amp;rsquo;s a rather small change and
reduces the amount of noise in the logs.&lt;/p&gt;
&lt;p&gt;With this, I hope everyone using &lt;code&gt;IRC::Client&lt;/code&gt; is prepared for the coming
changes. If you have any comments or questions, do not hesitate to reach out to
me and share your thoughts!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Merging JSON in PostgreSQL</title>
      <link>https://www.tyil.nl/post/2020/12/15/merging-json-in-postgresql/</link>
      <pubDate>Tue, 15 Dec 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2020/12/15/merging-json-in-postgresql/</guid>
      <description>&lt;p&gt;At my &lt;code&gt;$day-job&lt;/code&gt; we have a lot of &lt;code&gt;jsonb&lt;/code&gt; in our database. From time to time, I
have to manually run a query to fix something in there. This week was one of
those times.&lt;/p&gt;
&lt;p&gt;While you can pretty much do everything you need with regards to JSON editing
with &lt;code&gt;jsonb_se&lt;/code&gt;t, I thought it might be nice if I were able to &lt;em&gt;merge&lt;/em&gt; a given
JSON object into an existing object. This might be cleaner in some situations,
but mostly it is fun to figure it out. And who doesn’t like spending time with
&lt;code&gt;plpgsql&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;The way I wanted to have it work is like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;properties&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb_merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;properties&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;notifications&amp;#34;: {&amp;#34;new_case&amp;#34;: false, &amp;#34;new_document&amp;#34;: true}}&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this is the eventual function I produced to do it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;REPLACE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FUNCTION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb_merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RETURNS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$$&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DECLARE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;json_object_agg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;COALESCE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CASE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;THEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;THEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb_typeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;object&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb_typeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;object&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;THEN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ELSE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb_merge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;END&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb_each&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jsonb_each&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_key&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RETURN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;END&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$$&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;LANGUAGE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;plpgsql&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Raku Modules in Gentoo Portage</title>
      <link>https://www.tyil.nl/post/2020/12/14/raku-modules-in-gentoo-portage/</link>
      <pubDate>Mon, 14 Dec 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2020/12/14/raku-modules-in-gentoo-portage/</guid>
      <description>&lt;p&gt;The last couple of days I&amp;rsquo;ve been taking another look at getting modules for
the Raku programming language into Gentoo&amp;rsquo;s Portage tree. Making new packages
available in Gentoo is incredibly easy with their overlay system.&lt;/p&gt;
&lt;p&gt;The more complex part was Raku&amp;rsquo;s side of things. While most languages just have
a certain directory to drop files in, Raku &lt;em&gt;should&lt;/em&gt; use a
&lt;code&gt;CompUnit::Repository&lt;/code&gt; object, which exposes the &lt;code&gt;.install&lt;/code&gt; method. This is
obviously slower than just copying the files around, but there are merits to
this method. For one, it allows installation of the same module with different
versions, or from different authors. It also handles all the Unicode complexity
for you.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		There &lt;em&gt;is&lt;/em&gt; a
&lt;a href=&#34;https://docs.raku.org/type/CompUnit::Repository::FileSystem&#34;&gt;CompUnit::Repository::FileSystem&lt;/a&gt;
which would allow me to just copy over files to the right directory, however, I
quite like the ability to have multiple versions of the same module installed.
This is actually something Portage is designed with, too!
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;After an email to the Raku users mailing list, I got some pointers over IRC. I
let these sink in for a couple days, considering how to approach the problem
properly. Then, one night, a solution came to mind, and I set out to test it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;It actually worked&lt;/em&gt;. And a similar method should be usable for other
distributions too, such as Debian, OpenSUSE or Archlinux, to create packages
out of Raku modules. This should greatly improve the ability to ship Raku
programs to end-users, without requiring them to learn how Raku&amp;rsquo;s ecosystem is
modeled, or which module manager it uses.&lt;/p&gt;
&lt;p&gt;The most important part of this approach is the
&lt;a href=&#34;https://git.sr.ht/~tyil/raku-overlay/tree/7494c81524ec1845c77dabfbb3303a34eb4b89f4/item/dev-lang/raku/files/module-installer.raku&#34;&gt;&lt;code&gt;module-installer.raku&lt;/code&gt;&lt;/a&gt;
program, which is part of &lt;code&gt;dev-lang/raku::raku-overlay&lt;/code&gt;. It accepts a path to
the module to install. It does not depend on any one module manager, so it can
be used to bootstrap a user-friendly module manager (such as
&lt;a href=&#34;https://github.com/ugexe/zef/&#34;&gt;&lt;code&gt;zef&lt;/code&gt;&lt;/a&gt;) for the user.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;Install a Raku module.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;The path to the Raku module sources.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;The repository to install it in. Options are &amp;#34;site&amp;#34; (ment for
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;user-installed modules), &amp;#34;vendor&amp;#34; (ment for distributions that want
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;to include more modules) and &amp;#34;core&amp;#34; (ment for modules distributed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;along with Raku itself).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kt&#34;&gt;Str:D&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$repo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;site&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;Force installation of the module.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kt&#34;&gt;Bool:D&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$force&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;CATCH&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;die&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;This script should be used by Portage only!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;vg&#34;&gt;%*ENV&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$prefix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;vg&#34;&gt;%*ENV&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;IO&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;usr/share/perl6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$repo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$repository&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;CompUnit::Repository::Installation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$meta-file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;META6.json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dist&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Distribution::Path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$meta-file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$repository&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;install&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$dist&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$force&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a fairly straightforward program. It checks for &lt;code&gt;$D&lt;/code&gt; to be set in the
environment, which is a variable Portage sets as the destination directory to
install new files in. This directory gets merged into the root filesystem to
finalize installation of any package.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;$D&lt;/code&gt; is set, I append the path used by Raku in Gentoo to it, followed by a
repo name. Next I create a &lt;code&gt;CompUnit::Repository&lt;/code&gt; using this path. This is a
trick to get the files to appear in the right directory for Portage, to
eventually merge them in the system-wide &lt;code&gt;site&lt;/code&gt; module repo used by Raku.
Additionally, I can use the &lt;code&gt;CompUnit::Repository&lt;/code&gt;&amp;rsquo;s &lt;code&gt;install&lt;/code&gt; method to handle
all the Raku specific parts that I don&amp;rsquo;t want to handle myself.&lt;/p&gt;
&lt;p&gt;This leaves one last issue. By creating this new repo, I also get a couple
files that already exist in the system wide &lt;code&gt;site&lt;/code&gt; repo. Portage will complain
about possible file collisions and refuse to install the package if these
remain. However, this can be solved rather easily by calling &lt;code&gt;rm&lt;/code&gt; on these files.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -- &amp;#34;${D}/usr/share/perl6/site/version&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -- &amp;#34;${D}/usr/share/perl6/site/repo.lock&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -- &amp;#34;${D}/usr/share/perl6/site/precomp/.lock&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And with this, my test module, &lt;code&gt;IO::Path::XDG&lt;/code&gt;, installs cleanly through the
power of Portage, and is usable by all users using the system-wide Raku
installation.&lt;/p&gt;
&lt;p&gt;To make this work for other distributions, the &lt;code&gt;module-installer.raku&lt;/code&gt; program
should be modified slightly. Most notably, the &lt;code&gt;$prefix&lt;/code&gt; must be altered to
point towards the right directory, so the files will be installed into whatever
directory will end up being packaged. Other than that, the standard means of
packaging can be followed.&lt;/p&gt;
&lt;p&gt;For the Gentoo users, this overlay is available at
&lt;a href=&#34;https://git.sr.ht/~tyil/raku-overlay&#34;&gt;SourceHut&lt;/a&gt;. It currently holds only
&lt;code&gt;IO::Path::XDG&lt;/code&gt; (&lt;code&gt;dev-raku/io-path-xdg&lt;/code&gt;), but you are invited to try it out and
report any issues you may encounter.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FreeBSD Email Server - Part 6: System Updates</title>
      <link>https://www.tyil.nl/post/2020/07/19/freebsd-email-server-part-6-system-updates/</link>
      <pubDate>Sun, 19 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2020/07/19/freebsd-email-server-part-6-system-updates/</guid>
      <description>&lt;p&gt;Four years have past, and my FreeBSD email server has keps on running without
any problems. However, some people on IRC have recently been nagging me to
support TLSv1.3 on my mailserver. Since the installation was done 4 years ago,
it didn&amp;rsquo;t do 1.3 yet, just 1.2. I set out to do a relatively simple system
update, which didn&amp;rsquo;t go as smooth as I had hoped. This tutorial post should
help you avoid the mistakes I made, so your updates &lt;em&gt;will&lt;/em&gt; go smooth.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		info
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		The rest of this tutorial assumes you&amp;rsquo;re running as the &lt;code&gt;root&lt;/code&gt; user.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;preparations&#34;&gt;Preparations&lt;/h2&gt;
&lt;p&gt;Before we do anything wild, let&amp;rsquo;s do the obvious first step: backups. Since
this is a FreeBSD server, it uses glorious
&lt;a href=&#34;https://en.wikipedia.org/wiki/ZFS&#34;&gt;ZFS&lt;/a&gt; as the filesystem, which allows us to
make use of
&lt;a href=&#34;https://docs.oracle.com/cd/E23824_01/html/821-1448/gbciq.html&#34;&gt;snapshots&lt;/a&gt;.
Which subvolumes to make snapshots off depends on your particular setup. In my
case, my actual email data is stored on &lt;code&gt;zroot/srv&lt;/code&gt;, and all the services and
their configurations are in &lt;code&gt;zroot/usr/local&lt;/code&gt;. My database&amp;rsquo;s data is stored on
&lt;code&gt;zroot/postgres/data96&lt;/code&gt;. Additionally, I want to make a snapshot of
&lt;code&gt;zroot/usr/ports&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/srv@`date +%Y%m%d%H%M%S`-11.0-final
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/usr/local@`date +%Y%m%d%H%M%S`-11.0-final
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/postgres@`date +%Y%m%d%H%M%S`-11.0-final
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/usr/ports@`date +%Y%m%d%H%M%S`-11.0-final
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will make a snapshot of each of these locations, for easy restoration in
case any problems arise. You can list all your snapshots with &lt;code&gt;zfs list -t snapshot&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Your server is most likely hosted at a provider, not in your home. This means
you won&amp;rsquo;t be able to just physically access it and retrieve the harddrive if
things go really bad. You might not be able to boot single-user mode either.
Because of this, you might not be able to restore the snapshots if things go
&lt;em&gt;really&lt;/em&gt; bad. In this case, you should also make a local copy of the important
data.&lt;/p&gt;
&lt;p&gt;The services and their configuration can be recreated, just follow the earlier
parts of this series again. The email data, however, cannot. This is the data
in &lt;code&gt;/srv/mail&lt;/code&gt;. You can make a local copy of all this data using &lt;code&gt;rsync&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rsync -av example.org:/srv/mail/ ~/mail-backup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s one more thing to do, which I learned the hard way. Set your login
shell to a simple one, provided by the base system. The obvious choice is
&lt;code&gt;/bin/sh&lt;/code&gt;, but some people may wrongly prefer &lt;code&gt;/bin/tcsh&lt;/code&gt; as well. During a
major version update, the ABI changes, which will temporarily break most of
the user-installed packages, including your shell.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chsh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		Be sure to change the shell for whatever user you&amp;rsquo;re using to SSH into this
machine too, if any!
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;updating-the-base-system&#34;&gt;Updating the Base System&lt;/h2&gt;
&lt;p&gt;With the preparations in place in case things get royally screwed up, the
actual updates can begin. FreeBSD has a dedicated program to handle updating
the base system, &lt;code&gt;freebsd-update&lt;/code&gt;. First off, fetch any updates, and make sure
all the updates for your current version are applied.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;freebsd-update fetch install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Afterwards, set the new system version you want to update to. In my case, this
is &lt;code&gt;12.1-RELEASE&lt;/code&gt;, but if you&amp;rsquo;re reading this in the future, you most certainly
want a newer version.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;freebsd-update -r 12.1-RELEASE upgrade
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will ask you to review the changes and confirm them as well. It
should generally be fine, but this is your last chance to make any backups or
perform other actions to secure your data! If you&amp;rsquo;re ready to continue, install
the updates to the machine.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;freebsd-update install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point, your kernel has been updated. Next you must reboot to start
using the new kernel.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;reboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the system is back online, you can continue installing the rest of the
updates.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;freebsd-update install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When this command finishes, the base system has been updated and should be
ready for use. Next up is updating all the software you installed manually.&lt;/p&gt;
&lt;h2 id=&#34;updating-user-installed-packages&#34;&gt;Updating User-Installed Packages&lt;/h2&gt;
&lt;p&gt;Unlike GNU+Linux distributions, FreeBSD has a clear distinction between the
&lt;em&gt;base system&lt;/em&gt; and &lt;em&gt;user installed software&lt;/em&gt;. The base system has now been
updated, but everything installed through &lt;code&gt;pkg&lt;/code&gt; or ports is still at the old
version. If you performed a major version upgrade (say, FreeBSD 11.x to 12.x),
the ABI has changed and few, if any, of the user-installed packages still work.&lt;/p&gt;
&lt;h3 id=&#34;binary-packages-using-pkg&#34;&gt;Binary Packages using &lt;code&gt;pkg&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Binary packages are the most common packages used. These are the packages
installed through &lt;code&gt;pkg&lt;/code&gt;. Currently, &lt;code&gt;pkg&lt;/code&gt; itself doesn&amp;rsquo;t even work. Luckily,
FreeBSD has &lt;code&gt;pkg-static&lt;/code&gt;, which is a statically compiled version of &lt;code&gt;pkg&lt;/code&gt;
intended to fix this very problem. Let&amp;rsquo;s fix up &lt;code&gt;pkg&lt;/code&gt; itself first.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pkg-static install -f pkg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That will make &lt;code&gt;pkg&lt;/code&gt; itself work again. Now you can use &lt;code&gt;pkg&lt;/code&gt; to update package
information, and upgrade all packages to a version that works under this
FreeBSD version.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pkg update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pkg upgrade
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;postgresql&#34;&gt;PostgreSQL&lt;/h4&gt;
&lt;p&gt;A particular package that was installed through &lt;code&gt;pkg&lt;/code&gt;, PostgreSQL, just got
updated to the latest version. On FreeBSD, the data directory used by
PostgreSQL is dependent on the version you&amp;rsquo;re running. If you try to list
databases now, you&amp;rsquo;ll notice that the &lt;code&gt;mail&lt;/code&gt; database used throughout the
tutorial is gone. The data directory is still there, so you &lt;em&gt;could&lt;/em&gt; downgrade
PostgreSQL again, restart the database, run a &lt;code&gt;pgdump&lt;/code&gt;, upgrade, restart and
import. However, I find it much cleaner to use FreeBSD jails to solve this
issue.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		info
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		My original installation used PostgreSQL 9.6, you may need to update some
version numbers accordingly!
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;I generally put my jails in a ZFS subvolume, so let&amp;rsquo;s create one of those
first.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create -o mountpoint=/usr/jails zroot/jails
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs create zroot/jails/postgres96
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create a new subvolume at &lt;code&gt;/usr/jails/postgres96&lt;/code&gt;. Using
&lt;code&gt;bsdinstall&lt;/code&gt;, a clean FreeBSD installation usable by the jail can be set up
here. This command will give you some popups you may remember from installing
FreeBSD initially. This time, you can uncheck &lt;em&gt;all&lt;/em&gt; boxes, to get the most
minimal system.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bsdinstall jail /usr/jails/postgres96
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When &lt;code&gt;bsdinstall&lt;/code&gt; finishes, you can configure the jail. This is done in
&lt;code&gt;/etc/jail.conf&lt;/code&gt;. If this file doesn&amp;rsquo;t exist, you can create it. Make sure the
following configuration block is written to the file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cfg&#34; data-lang=&#34;cfg&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;postgres96 {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Init information&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;exec.start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/bin/sh /etc/rc&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    exec.stop  = &amp;#34;/bin/sh /etc/rc.shutdown&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    exec.clean;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Set the root path of the jail&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/usr/jails/$name&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Mount /dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;mount.devfs;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Set network information&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;host.hostname&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;$name;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    ip4.addr = &amp;#34;lo0|127.1.1.1/32&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    ip6.addr = &amp;#34;lo0|fd00:1:1:1::1/64&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Required for PostgreSQL to function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;allow.raw_sockets;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;allow.sysvipc;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can start up the jail, so it can be used.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;service jail onestart postgres96
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using the host system&amp;rsquo;s &lt;code&gt;pkg&lt;/code&gt;, you can install PostgreSQL into the jail.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pkg -c /usr/jails/postgres96 install postgresql96-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you just need to make the data directory available to the jail, which you
can most easily do using
&lt;a href=&#34;https://www.freebsd.org/cgi/man.cgi?query=nullfs&amp;amp;sektion=&amp;amp;n=1&#34;&gt;&lt;code&gt;nullfs&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount -t nullfs /var/db/postgres/data96 /usr/jails/postgres96/var/db/postgres/data96
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now everything should be ready for use inside the jail. Let&amp;rsquo;s head on in using
&lt;code&gt;jexec&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;jexec postgres96
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once inside the jail, you can start the PostgreSQL service, and dump the &lt;code&gt;mail&lt;/code&gt;
database.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;service postgresql onestart
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;su - postgres
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pg_dump mail &amp;gt; ~/mail.sql
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will write the dump to &lt;code&gt;/usr/jails/postgres96/var/db/postgres/mail.sql&lt;/code&gt; on
the host system. You can leave the jail and close it down again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;exit
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;exit
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;service jail onestop postgres96
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This dump can be imported in your updated PostgreSQL on the host system.
Connect to the database first.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;su - postgres
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;psql
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, recreate the user, database and import the data from the dump.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;USER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;postfix&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PASSWORD&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;incredibly-secret!&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DATABASE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mail&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OWNER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;postfix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mail&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;usr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;jails&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;postgres96&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mail&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sql&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;mail&lt;/code&gt; database is now back, and ready for use!&lt;/p&gt;
&lt;h3 id=&#34;packages-from-ports&#34;&gt;Packages from Ports&lt;/h3&gt;
&lt;p&gt;With all the binary packages out of the way, it&amp;rsquo;s time to update packages from
ports. While it is very possible to just go to each port&amp;rsquo;s directory and
manually update each one individually, I opted to use &lt;code&gt;portupgrade&lt;/code&gt;. This will
need manual installation, but afterwards, we can rely on &lt;code&gt;portupgrade&lt;/code&gt; to do
the rest. Before doing anything with the ports collection, it should be
updated, which is done using &lt;code&gt;portsnap&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;portsnap fetch extract
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once this is done, you can go to the &lt;code&gt;portupgrade&lt;/code&gt; directory and install it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cd /usr/ports/ports-mgmt/portupgrade
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make install clean
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, to upgrade all other ports.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;portupgrade -a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Be sure to double-check the compilation options that you are prompted about! If
you&amp;rsquo;re missing a certain option, you may miss an important feature that is
required for your mailserver to work appropriately. This can be easily fixed by
recompiling, but a few seconds checking now can save you an hour figuring it
out later!&lt;/p&gt;
&lt;h2 id=&#34;tidying-up&#34;&gt;Tidying Up&lt;/h2&gt;
&lt;p&gt;Now that all user-installed software has been updated too, it&amp;rsquo;s time to
finalize the update by running &lt;code&gt;freebsd-update&lt;/code&gt; for a final time.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;freebsd-update install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can return to your favourite shell again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chsh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you can clean up the ports directories to get some wasted space back.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;portsclean -C
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I would suggest making a new snapshot as well, now that you&amp;rsquo;re on a relatively
clean and stable state.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/srv@`date +%Y%m%d%H%M%S`-12.1-clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/usr/local@`date +%Y%m%d%H%M%S`-12.1-clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/postgres@`date +%Y%m%d%H%M%S`-12.1-clean
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zfs snapshot -r zroot/usr/ports@`date +%Y%m%d%H%M%S`-12.1-clean
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that concludes your system update. Your mailserver is ready to be neglected
for years again!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Config 3.0</title>
      <link>https://www.tyil.nl/post/2020/07/15/config-3.0/</link>
      <pubDate>Wed, 15 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2020/07/15/config-3.0/</guid>
      <description>&lt;p&gt;For those who don&amp;rsquo;t know, the
&lt;a href=&#34;https://modules.raku.org/dist/Config:cpan:TYIL&#34;&gt;&lt;code&gt;Config&lt;/code&gt;&lt;/a&gt; module for the Raku
programming language is a generic class to hold&amp;hellip; well&amp;hellip; configuration data.
It supports
&lt;a href=&#34;https://modules.raku.org/search/?q=Config%3A%3AParser&#34;&gt;&lt;code&gt;Config::Parser&lt;/code&gt;&lt;/a&gt;
modules to handle different configuration file formats, such as &lt;code&gt;JSON&lt;/code&gt;, &lt;code&gt;YAML&lt;/code&gt;
and &lt;code&gt;TOML&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Up until now, the module didn&amp;rsquo;t do much for you other than provide an interface
that&amp;rsquo;s generally the same, so you won&amp;rsquo;t need to learn differing methods to
handle differing configuration file formats. It was my first Raku module, and
as such, the code wasn&amp;rsquo;t the cleanest. I&amp;rsquo;ve written many new modules since
then, and learned about a good number of (hopefully better) practices.&lt;/p&gt;
&lt;p&gt;For version 3.0, I specifically wanted to remove effort from using the &lt;code&gt;Config&lt;/code&gt;
module on the developer&amp;rsquo;s end. It should check default locations for
configuration files, so I don&amp;rsquo;t have to rewrite that code in every other module
all the time. Additionally, configuration using environment variables is quite
popular in the current day and age, especially for Dockerized applications. So,
I set out to make an automated way to read those too.&lt;/p&gt;
&lt;h2 id=&#34;the-old-way&#34;&gt;The Old Way&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s take a look at how it used to work. Generally, I&amp;rsquo;d create the
default configuration structure and values first.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;foo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;bar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;alpha&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s&#34;&gt;beta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;gamma&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;version&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And after that, check for potential configuration file locations, and read any
that exist.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vg&#34;&gt;$*HOME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;config/project.toml&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;absolute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;.absolute&lt;/code&gt; call was necessary because I wrote the initial &lt;code&gt;Config&lt;/code&gt; version
with the &lt;code&gt;.read&lt;/code&gt; method not supporting &lt;code&gt;IO::Path&lt;/code&gt; objects. A fix for this has
existed for a while, but wasn&amp;rsquo;t released, so couldn&amp;rsquo;t be relied on outside of
my general development machines.&lt;/p&gt;
&lt;p&gt;If you wanted to add additional environment variable lookups, you&amp;rsquo;d have to
check for those as well, and perhaps also cast them as well, since environment
variables are all strings by default.&lt;/p&gt;
&lt;h2 id=&#34;version-30&#34;&gt;Version 3.0&lt;/h2&gt;
&lt;p&gt;So, how does the new version improve this? For starters, the &lt;code&gt;.new&lt;/code&gt; method of
&lt;code&gt;Config&lt;/code&gt; now takes a &lt;code&gt;Hash&lt;/code&gt; as positional argument, in order to create the
structure, and optionally types &lt;em&gt;or&lt;/em&gt; default values of your configuration
object.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Config&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;foo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;alpha&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s&#34;&gt;beta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;gamma&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;version&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;project&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		&lt;code&gt;foo&lt;/code&gt; has been made into the &lt;code&gt;Str&lt;/code&gt; &lt;em&gt;type object&lt;/em&gt;, rather than a &lt;code&gt;Str&lt;/code&gt; &lt;em&gt;value&lt;/em&gt;.
This was technically allowed in previous &lt;code&gt;Config&lt;/code&gt; versions, but it comes with
actual significance in 3.0.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;Using &lt;code&gt;.new&lt;/code&gt; instead of &lt;code&gt;.read&lt;/code&gt; is a minor syntactic change, which saves 1 word
per program. This isn&amp;rsquo;t quite that big of a deal. However, the optional &lt;code&gt;name&lt;/code&gt;
argument will enable the new automagic features. The name you give to &lt;code&gt;.new&lt;/code&gt; is
arbitrary, but will be used to deduce which directories to check, and which
environment variables to read.&lt;/p&gt;
&lt;h3 id=&#34;automatic-configuration-file-handling&#34;&gt;Automatic Configuration File Handling&lt;/h3&gt;
&lt;p&gt;By setting &lt;code&gt;name&lt;/code&gt; to the value &lt;code&gt;project&lt;/code&gt;, &lt;code&gt;Config&lt;/code&gt; will consult the
configuration directories from the &lt;a href=&#34;https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html&#34;&gt;XDG Base Directory
Specification&lt;/a&gt;.
It uses one of my other modules,
&lt;a href=&#34;https://modules.raku.org/dist/IO::Path::XDG:cpan:TYIL&#34;&gt;&lt;code&gt;IO::Path::XDG&lt;/code&gt;&lt;/a&gt;, for
this, together with
&lt;a href=&#34;https://modules.raku.org/dist/IO::Glob:cpan:HANENKAMP&#34;&gt;&lt;code&gt;IO::Glob&lt;/code&gt;&lt;/a&gt;.
Specifically, it will check my &lt;code&gt;$XDG_CONFIG_DIRS&lt;/code&gt; and &lt;code&gt;$XDG_CONFIG_HOME&lt;/code&gt; (in
that order) for any files that match the globs &lt;code&gt;project.*&lt;/code&gt; or
&lt;code&gt;project/config.*&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If any files are found to match, they will be read as well, and the
configuration values contained therein, merged into &lt;code&gt;$config&lt;/code&gt;. It will load the
appropriate &lt;code&gt;Config::Parser&lt;/code&gt; implementation based on the file&amp;rsquo;s extension. I
intend to add a number of these to future Rakudo Star releases, to ensure most
default configuration file formats are supported out of the box.&lt;/p&gt;
&lt;h3 id=&#34;automatic-environment-variable-handling&#34;&gt;Automatic Environment Variable Handling&lt;/h3&gt;
&lt;p&gt;After this step, it will try out some environment variables for configuration
values. Which variables are checked depends on the structure (and &lt;code&gt;name&lt;/code&gt;) of
the &lt;code&gt;Config&lt;/code&gt; object. The entire structure is squashed into a 1-dimensional list
of fields. Each level is replaced by an &lt;code&gt;_&lt;/code&gt;. Additionally, each variable name
is prefixed with the &lt;code&gt;name&lt;/code&gt;. Lastly, all the variable names are uppercased.&lt;/p&gt;
&lt;p&gt;For the example &lt;code&gt;Config&lt;/code&gt; given above, this would result in the following
environment variables being checked.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$PROJECT_FOO&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$PROJECT_ALPHA_BETA&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$PROJECT_VERSION&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If any are found, they&amp;rsquo;re also cast to the appropriate type. Thus,
&lt;code&gt;$PROJECT_FOO&lt;/code&gt; would be cast to a &lt;code&gt;Str&lt;/code&gt;, and so would &lt;code&gt;$PROJECT_ALPHA_BETA&lt;/code&gt;. In
this case that doesn&amp;rsquo;t do much, since they&amp;rsquo;re already strings. But
&lt;code&gt;$PROJECT_VERSION&lt;/code&gt; would be cast to an &lt;code&gt;Int&lt;/code&gt;, since it&amp;rsquo;s default value is also
of the &lt;code&gt;Int&lt;/code&gt; type. This should ensure that your variables are always in the
type you expected them to be originally, no matter the user&amp;rsquo;s configuration
choices.&lt;/p&gt;
&lt;h2 id=&#34;debugging&#34;&gt;Debugging&lt;/h2&gt;
&lt;p&gt;In addition to these new features, &lt;code&gt;Config&lt;/code&gt; now also makes use of my
&lt;a href=&#34;https://modules.raku.org/dist/Log:cpan:TYIL&#34;&gt;&lt;code&gt;Log&lt;/code&gt;&lt;/a&gt; module. This module is
made around the idea that logging should be simple if module developers are to
use it, and the way logs are represented is up to the end-user. When running an
application in your local terminal, you may want more human-friendly logs,
whereas in production you may want &lt;code&gt;JSON&lt;/code&gt; formatted logs to make it fit better
into other tools.&lt;/p&gt;
&lt;p&gt;You can tune the amount of logging performed using the &lt;code&gt;$RAKU_LOG_LEVEL&lt;/code&gt;
environment variable, as per the &lt;code&gt;Log&lt;/code&gt; module&amp;rsquo;s interface. When set to &lt;code&gt;7&lt;/code&gt; (for
&amp;ldquo;debug&amp;rdquo;), it will print the configuration files that are being merged into your
&lt;code&gt;Config&lt;/code&gt; and which environment veriables are being used as well.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		A downside is that the application using &lt;code&gt;Config&lt;/code&gt; for its configuration must
also support &lt;code&gt;Log&lt;/code&gt; to actually make the new logging work. Luckily, this is
quite easy to set up, and there&amp;rsquo;s example code for this in &lt;code&gt;Log&lt;/code&gt;&amp;rsquo;s README.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;too-fancy-for-me&#34;&gt;Too Fancy For Me&lt;/h2&gt;
&lt;p&gt;It could very well be that you don&amp;rsquo;t want these features, and you want to stick
to the old ways as much as possible. No tricks, just plain and simple
configuration handling. This can be done by simply ommitting the &lt;code&gt;name&lt;/code&gt;
argument to &lt;code&gt;.new&lt;/code&gt;. The new features depend on this name to be set, and won&amp;rsquo;t
do anything without it.&lt;/p&gt;
&lt;p&gt;Alternatively, both the automatic configuration file handling and the
environment variable handling can be turned off individually using &lt;code&gt;:!from-xdg&lt;/code&gt;
and &lt;code&gt;:!from-env&lt;/code&gt; arguments respectively.&lt;/p&gt;
&lt;h2 id=&#34;in-conclusion&#34;&gt;In Conclusion&lt;/h2&gt;
&lt;p&gt;The new &lt;code&gt;Config&lt;/code&gt; module should result in cleaner code in modules using it, and
more convenience for the developer. If you find any bugs or have other ideas
for improving the module, feel free to send an email to
&lt;code&gt;https://lists.sr.ht/~tyil/raku-devel&lt;/code&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Lately in Raku</title>
      <link>https://www.tyil.nl/post/2020/06/21/lately-in-raku/</link>
      <pubDate>Sun, 21 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2020/06/21/lately-in-raku/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been working on some Raku projects, but each of them is &lt;em&gt;just&lt;/em&gt; too small
to make an individual blog post about. So, I decided to just pack them together
in a slightly larger blog post instead.&lt;/p&gt;
&lt;h2 id=&#34;binary-rakudo-star-builds-for-gnulinux-and-freebsd&#34;&gt;Binary Rakudo Star builds for GNU+Linux and FreeBSD&lt;/h2&gt;
&lt;p&gt;A friend on IRC asked if it was possible to get Rakudo Star precompiled for
ARM, since compiling it on his machine took forever. I took a look around for
potential build services, and settled for &lt;a href=&#34;https://builds.sr.ht/&#34;&gt;Sourcehut&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I added build instructions for amd64 FreeBSD, GNU+Linux, musl+Linux, and ARM
GNU+Linux. Tarballs with precompiled binaries get build whenever I push to the
Rakudo Star mirror on Sourcehut, and are uploaded to
&lt;a href=&#34;https://dist.tyil.nl/tmp/&#34;&gt;dist.tyil.nl/tmp&lt;/a&gt;. Currently, these are not
considered to be an official part of Rakudo Star, but if interest increases and
more people can test these packages, I can include them in official releases.&lt;/p&gt;
&lt;h2 id=&#34;ircclient-plugins&#34;&gt;&lt;code&gt;IRC::Client&lt;/code&gt; plugins&lt;/h2&gt;
&lt;p&gt;IRC bots are great fun, and the
&lt;a href=&#34;https://github.com/raku-community-modules/perl6-IRC-Client&#34;&gt;&lt;code&gt;IRC::Client&lt;/code&gt;&lt;/a&gt;
module allows for easy extension through &lt;em&gt;plugins&lt;/em&gt;. For my own IRC bot,
&lt;a href=&#34;https://git.sr.ht/~tyil/raku-local-musashi&#34;&gt;musashi&lt;/a&gt;, I&amp;rsquo;ve created two new
plugins, which are now available in the Raku ecosystem for anyone to use.&lt;/p&gt;
&lt;h3 id=&#34;ircclientplugindicerolls&#34;&gt;&lt;code&gt;IRC::Client::Plugin::Dicerolls&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The first plugin I&amp;rsquo;ve created can do dice rolls, D&amp;amp;D style. You can roll any
number of dice, with any number of sides, and add (or subtract) bonusses from
these.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;@tyil&amp;gt; .roll 1d20
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;+musashi&amp;gt; 1d20 = 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;@tyil&amp;gt; .roll 5d20
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;+musashi&amp;gt; 5d20 = 3 + 19 + 8 + 6 + 11 = 47
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;@tyil&amp;gt; .roll 1d8+2d6+10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt;+musashi&amp;gt; 1d8+2d6+10 = 4 + 6 + 4 + 10 = 24
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since this is ripe for abuse, the plugin allows to set limits, and sets some
defaults for the limits as well. This should help prevent your bot from getting
killed for spam.&lt;/p&gt;
&lt;h3 id=&#34;ircclientpluginreminders&#34;&gt;&lt;code&gt;IRC::Client::Plugin::Reminders&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Everyone forgets things, and there&amp;rsquo;s various tools helping people remember
things in various situations. For IRC based situations, I created a reminder
plugin for &lt;code&gt;IRC::Client&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;10:19 &amp;lt;@tyil&amp;gt; musashi: remind me to write a blog post in 10 minutes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;10:19 &amp;lt;+musashi&amp;gt; Reminding you to write a blog post on 2020-06-21T08:29:00Z (UTC)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;10:29 &amp;lt;+musashi&amp;gt; tyil: Reminder to write a blog post
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s not very sophisticated yet, working only with numbers and certain
identifiers (minutes, hours, days, weeks), but I may add more useful
identifiers later on such as &amp;ldquo;tomorrow&amp;rdquo;, or &amp;ldquo;next Sunday&amp;rdquo;. Contributions for
such extended functionality are obviously also very welcome!&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s &lt;a href=&#34;https://git.sr.ht/~tyil/raku-irc-client-plugin-reminders/tree/master/lib/IRC/Client/Plugin/Reminders.rakumod#L69&#34;&gt;a small
issue&lt;/a&gt;
with logging in a &lt;code&gt;start&lt;/code&gt; block. It seems the dynamic variable &lt;code&gt;$*LOG&lt;/code&gt; is no
longer defined within it. If anyone has an idea why, and how I could fix this,
please let me know!&lt;/p&gt;
&lt;h2 id=&#34;template-program-for-dd&#34;&gt;Template program for D&amp;amp;D&lt;/h2&gt;
&lt;p&gt;Another little utility I made for D&amp;amp;D purposes. My DM asked me how hard it&amp;rsquo;d be
to create a program to fill out a number of templates he made, so he could use
them in the game with another party. He was able to hand me a list of variables
in the form of a CSV, so I set out to use that. With some help from &lt;code&gt;Text::CSV&lt;/code&gt;
and &lt;code&gt;Template::Mustache&lt;/code&gt;, I had a working solution in a couple minutes, with
all the required things nicely fit into a single file.&lt;/p&gt;
&lt;p&gt;I had not used &lt;code&gt;$=pod&lt;/code&gt; before in Raku, and I&amp;rsquo;m quite happy with how easy it is
to use, though I would like a cleaner way to refer to a Pod block by name.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/env raku&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Template::Mustache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Text::CSV&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;Read a CSV input file to render contracts with.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Set the directory to write the contracts to.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$output-dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;vg&#34;&gt;$*PROGRAM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Make sure the output directory exists&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$output-dir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;mkdir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Load the template&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$template&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$=pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;grep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.^&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;can&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;first&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;contents&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;contents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n\n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Parse STDIN as CSV&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@records&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Text::CSV&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getline_all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vg&#34;&gt;$*IN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;skip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# Create a contract out of each record&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@records&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nv&#34;&gt;$output-dir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;contract-&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.txt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;spurt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;Template::Mustache&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;s&#34;&gt;contractor&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;s&#34;&gt;date&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;s&#34;&gt;description&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;s&#34;&gt;item&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;s&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;s&#34;&gt;price&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@record&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;=begin&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;As per our verbal agreement this contract will detail the rewards, rights, and
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;obligations of both parties involved.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;The contractor, to be known henceforth as {{ contractor }}.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;The contractee, to be known henceforth as the Association.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;{{ contractor }} requests the delivery of an object identified as the &amp;#34;{{ item }}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;to be delivered by the Association at the location specified for the agreed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;upon compensation. The Association shall deliver the object within two weeks of
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;the signing of this contract and receive compensation upon delivery.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;The location is to be known as &amp;#34;{{ location }}&amp;#34;, described as &amp;#34;{{ description }}&amp;#34;.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;The compensation agreed upon is {{ price }} pieces of Hondia standard
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;gold-coin currency, or an equivalent in precious gemstones.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;Written and signed on the {{ date }}.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;For the association, Lan Torrez
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;For the {{ contractor }}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;=end template&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Setting Up a PGP Webkey Directory</title>
      <link>https://www.tyil.nl/post/2020/05/30/setting-up-a-pgp-webkey-directory/</link>
      <pubDate>Sat, 30 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2020/05/30/setting-up-a-pgp-webkey-directory/</guid>
      <description>&lt;p&gt;A little while ago, a friend on IRC asked me how I set up a PGP webkey
directory on my website. For those that don&amp;rsquo;t know, a webkey directory is a
method to find keys through &lt;code&gt;gpg&lt;/code&gt;&amp;rsquo;s &lt;code&gt;--locate-key&lt;/code&gt; command. This allows people
to find my key using this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --locate-key p.spek@tyil.nl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a very user-friendly way for people to get your key, as compared to
using long IDs.&lt;/p&gt;
&lt;p&gt;This post will walk you through setting it up on your site, so you can make
your key more easily accessible to other people.&lt;/p&gt;
&lt;h2 id=&#34;set-up-the-infrastructure&#34;&gt;Set up the infrastructure&lt;/h2&gt;
&lt;p&gt;For a webkey directory to work, you simply need to have your key available at a
certain path on your website. The base path for this is
&lt;code&gt;.well-known/openpgpkey/&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p .well-known/openpgpkey
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The webkey protocol will check for a &lt;code&gt;policy&lt;/code&gt; file to exist, so you must create
this too. The file can be completely empty, and that&amp;rsquo;s exactly how I have it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;touch .well-known/openpgpkey/policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key(s) will be placed in the &lt;code&gt;hu&lt;/code&gt; directory, so create this one too.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir .well-known/openpgpkey/hu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;adding-your-pgp-key&#34;&gt;Adding your PGP key&lt;/h2&gt;
&lt;p&gt;The key itself is just a standard export of your key, without ASCII armouring.
However, the key does need to have its file &lt;strong&gt;name&lt;/strong&gt; in a specific format.
Luckily, you can just show this format with &lt;code&gt;gpg&lt;/code&gt;&amp;rsquo;s &lt;code&gt;--with-wkd-hash&lt;/code&gt; option.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --with-wkd-hash -k p.spek@tyil.nl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will yield output that may look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pub   rsa4096/0x7A6AC285E2D98827 2018-09-04 [SC]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      Key fingerprint = 1660 F6A2 DFA7 5347 322A  4DC0 7A6A C285 E2D9 8827
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uid                   [ultimate] Patrick Spek &amp;lt;p.spek@tyil.nl&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      i4fxxwcfae1o4d7wnb5bop89yfx399yf@tyil.nl
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sub   rsa2048/0x031D65902E840821 2018-09-04 [S]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sub   rsa2048/0x556812D46DABE60E 2018-09-04 [E]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sub   rsa2048/0x66CFE18D6D588BBF 2018-09-04 [A]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What we&amp;rsquo;re interested in is the &lt;code&gt;uid&lt;/code&gt; line with the hash in the local-part of
the email address, which would be &lt;code&gt;i4fxxwcfae1o4d7wnb5bop89yfx399yf@tyil.nl&lt;/code&gt;.
For the filename, we only care about the local-part itself, meaning the export
of the key must be saved in a file called &lt;code&gt;i4fxxwcfae1o4d7wnb5bop89yfx399yf&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --export 0x7A6AC285E2D98827 &amp;gt; .well-known/openpgpkey/hu/i4fxxwcfae1o4d7wnb5bop89yfx399yf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configuring-your-webserver&#34;&gt;Configuring your webserver&lt;/h2&gt;
&lt;p&gt;Lastly, your webserver may require some configuration to serve the files
correctly. For my blog, I&amp;rsquo;m using &lt;a href=&#34;https://www.lighttpd.net/&#34;&gt;&lt;code&gt;lighttpd&lt;/code&gt;&lt;/a&gt;, for
which the configuration block I&amp;rsquo;m using is as follows.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-lighttpd&#34; data-lang=&#34;lighttpd&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;$HTTP&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=~&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;^/.well-known/openpgpkey&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;setenv.add-response-header&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;s2&#34;&gt;&amp;#34;Access-Control-Allow-Origin&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It may be worthwhile to note that if you do any redirection on your domain,
such as adding &lt;code&gt;www.&lt;/code&gt; in front of it, the key lookup may fail. The error
message given by &lt;code&gt;gpg&lt;/code&gt; on WKD lookup failures is&amp;hellip; poor to say the least, so
if anything goes wrong, try some verbose &lt;code&gt;curl&lt;/code&gt; commands and ensure that the
key is accessible at the right path in a single HTTP request.&lt;/p&gt;
&lt;h2 id=&#34;wrapping-up&#34;&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s all there&amp;rsquo;s to it! Adding this to your site should be relatively
straightforward, but it may be a huge convenience to anyone looking for your
key. If you have any questions or feedback, feel free to reach out to me!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Running cgit on Gentoo</title>
      <link>https://www.tyil.nl/post/2020/01/08/running-cgit-on-gentoo/</link>
      <pubDate>Wed, 08 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2020/01/08/running-cgit-on-gentoo/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://git.zx2c4.com/cgit/about/&#34;&gt;cgit&lt;/a&gt;, a web interface for git
repositories, allows you to easily share your projects&amp;rsquo; source code over a web
interface. It&amp;rsquo;s running on my desktop right now, so you can &lt;a href=&#34;https://home.tyil.nl/git&#34;&gt;see for
yourself&lt;/a&gt; what it looks like. On
&lt;a href=&#34;https://www.gentoo.org/&#34;&gt;Gentoo&lt;/a&gt;, the ebuild for this software can be found as
&lt;code&gt;www-apps/cgit&lt;/code&gt;. However, after installation, a number of configuration steps
should be performed to make it accessible on &lt;code&gt;$HOSTNAME/git&lt;/code&gt;, and index your
repositories. This post will guide you through the steps I took.&lt;/p&gt;
&lt;h2 id=&#34;filesystem-layout&#34;&gt;Filesystem layout&lt;/h2&gt;
&lt;p&gt;In my setup, my (bare) git repositories reside in &lt;code&gt;$HOME/.local/git&lt;/code&gt;. But, some
of the repositories should not be public, such as the
&lt;a href=&#34;https://www.passwordstore.org/&#34;&gt;&lt;code&gt;pass&lt;/code&gt;&lt;/a&gt; store. So, a different directory
for cgit to look in exists, at &lt;code&gt;$HOME/.local/srv/cgit&lt;/code&gt;. This directory contains
symlinks to the actual repositories I want publicly available.&lt;/p&gt;
&lt;h2 id=&#34;installing-the-required-software&#34;&gt;Installing the required software&lt;/h2&gt;
&lt;p&gt;For this to work, there is more than just cgit to install. There are a number
of ways to set this up, but I chose for Nginx as web server, and &lt;code&gt;uwsgi&lt;/code&gt; as the
handler for the fastcgi requests.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;emerge dev-python/pygments www-apps/cgit www-servers/nginx www-servers/uwsgi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configuring-all-elements&#34;&gt;Configuring all elements&lt;/h2&gt;
&lt;p&gt;After installation, each of these packages needs to be configured.&lt;/p&gt;
&lt;h3 id=&#34;cgit&#34;&gt;cgit&lt;/h3&gt;
&lt;p&gt;The configuration file for cgit resides in &lt;code&gt;/etc/cgitrc&lt;/code&gt;. After removing all
the comments, the contents of my &lt;code&gt;/etc/cgitrc&lt;/code&gt; can be found below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fixes for running cgit in a subdirectory
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;css=/git/cgit.css
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logo=/git/cgit.png
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;virtual-root=/git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;remove-suffix=1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Customization
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root-desc=All public repos from tyil
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enable-index-owner=0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cache-size=1000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;snapshots=tar.gz tar.bz2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;clone-prefix=https://home.tyil.nl/git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;robots=index, follow
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;readme=master:README.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;readme=master:README.pod6
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Add filters before repos (or filters won&amp;#39;t work)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;about-filter=/usr/lib64/cgit/filters/about-formatting.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;source-filter=/usr/lib64/cgit/filters/syntax-highlighting.py
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Scan paths for repos
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;scan-path=/home/tyil/.local/srv/cgit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should probably update the values of &lt;code&gt;root-desc&lt;/code&gt;, &lt;code&gt;clone-prefix&lt;/code&gt; and
&lt;code&gt;scan-path&lt;/code&gt;. The first describes the small line of text at the top of the web
interface. &lt;code&gt;clone-prefix&lt;/code&gt; is the prefix URL used for &lt;code&gt;git clone&lt;/code&gt; URLs. The
&lt;code&gt;scan-path&lt;/code&gt; is the directory &lt;code&gt;cgit&lt;/code&gt; will look for repositories in.&lt;/p&gt;
&lt;p&gt;Additionally, the &lt;code&gt;readme=master:README.pod6&lt;/code&gt; only positively affects
your setup if you also use my &lt;a href=&#34;https://raku.org/&#34;&gt;Raku&lt;/a&gt; customizations,
outlined in the next section.&lt;/p&gt;
&lt;p&gt;For more information on the available settings and their impact, consult &lt;code&gt;man cgitrc&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;raku-customizations&#34;&gt;Raku customizations&lt;/h4&gt;
&lt;p&gt;Since I love working with Raku, I made some changes and a couple modules to get
&lt;code&gt;README.pod6&lt;/code&gt; files rendered on the &lt;em&gt;about&lt;/em&gt; tab on projects. You should ensure
the &lt;code&gt;cgit&lt;/code&gt; user can run &lt;code&gt;raku&lt;/code&gt; and has the
&lt;a href=&#34;https://home.tyil.nl/git/raku/Pod::To::Anything/&#34;&gt;&lt;code&gt;Pod::To::Anything&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&#34;https://home.tyil.nl/git/raku/Pod::To::HTML::Section/&#34;&gt;&lt;code&gt;Pod::To::HTML::Section&lt;/code&gt;&lt;/a&gt;
modules installed (including any dependencies). How to achieve this depends on
how you installed Raku. Feel free to send me an email if you need help on this
part!&lt;/p&gt;
&lt;p&gt;Once this works, however, the remaining step is quite simple. The
&lt;code&gt;about-filter&lt;/code&gt; configuration item in &lt;code&gt;/etc/cgitrc&lt;/code&gt; points to a small shell
script that invokes the required program to convert a document to HTML. In my
case, this file is at &lt;code&gt;/usr/lib64/cgit/filters/about-formatting.sh&lt;/code&gt;. Open up
this file in your favorite &lt;code&gt;$EDITOR&lt;/code&gt; and add another entry to the &lt;code&gt;case&lt;/code&gt; for
&lt;a href=&#34;https://docs.raku.org/language/pod&#34;&gt;Pod6&lt;/a&gt; to call Raku.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;%s&amp;#39;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; tr &lt;span class=&#34;s1&#34;&gt;&amp;#39;[:upper:]&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[:lower:]&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   *.markdown&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;*.mdown&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;*.md&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;*.mkd&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; ./md2html&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   *.pod6&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; raku --doc&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;HTML::Section&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   *.rst&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; ./rst2html&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   *.&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1-9&lt;span class=&#34;o&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; ./man2html&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   *.htm&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;*.html&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; cat&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   *.txt&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;*&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exec&lt;/span&gt; ./txt2html&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;highlighting-style&#34;&gt;Highlighting style&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;syntax-highlighting.py&lt;/code&gt; filter carries the responsibility to get your code
highlighted. This uses the Python library &lt;a href=&#34;https://pygments.org/&#34;&gt;pygments&lt;/a&gt;,
which comes with a number of styles. cgit uses &lt;em&gt;Pastie&lt;/em&gt; by default. To change
this, open the Python script, and look for the &lt;code&gt;HtmlFormatter&lt;/code&gt;, which contains
a &lt;code&gt;style=&#39;Pastie&#39;&lt;/code&gt; bit. You can change &lt;code&gt;Pastie&lt;/code&gt; for any other style name. These
styles are available in my version (2.4.2):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;default&lt;/li&gt;
&lt;li&gt;emacs&lt;/li&gt;
&lt;li&gt;friendly&lt;/li&gt;
&lt;li&gt;colorful&lt;/li&gt;
&lt;li&gt;autumn&lt;/li&gt;
&lt;li&gt;murphy&lt;/li&gt;
&lt;li&gt;manni&lt;/li&gt;
&lt;li&gt;monokai&lt;/li&gt;
&lt;li&gt;perldoc&lt;/li&gt;
&lt;li&gt;pastie&lt;/li&gt;
&lt;li&gt;borland&lt;/li&gt;
&lt;li&gt;trac&lt;/li&gt;
&lt;li&gt;native&lt;/li&gt;
&lt;li&gt;fruity&lt;/li&gt;
&lt;li&gt;bw&lt;/li&gt;
&lt;li&gt;vim&lt;/li&gt;
&lt;li&gt;vs&lt;/li&gt;
&lt;li&gt;tango&lt;/li&gt;
&lt;li&gt;rrt&lt;/li&gt;
&lt;li&gt;xcode&lt;/li&gt;
&lt;li&gt;igor&lt;/li&gt;
&lt;li&gt;paraiso-light&lt;/li&gt;
&lt;li&gt;paraiso-dark&lt;/li&gt;
&lt;li&gt;lovelace&lt;/li&gt;
&lt;li&gt;algol&lt;/li&gt;
&lt;li&gt;algol_nu&lt;/li&gt;
&lt;li&gt;arduino&lt;/li&gt;
&lt;li&gt;rainbow_dash&lt;/li&gt;
&lt;li&gt;abap&lt;/li&gt;
&lt;li&gt;solarized-dark&lt;/li&gt;
&lt;li&gt;solarized-light&lt;/li&gt;
&lt;li&gt;sas&lt;/li&gt;
&lt;li&gt;stata&lt;/li&gt;
&lt;li&gt;stata-light&lt;/li&gt;
&lt;li&gt;stata-dark&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For those interested, I use the &lt;code&gt;emacs&lt;/code&gt; theme.&lt;/p&gt;
&lt;h3 id=&#34;uwsgi&#34;&gt;uwsgi&lt;/h3&gt;
&lt;p&gt;Next up, &lt;code&gt;uwsgi&lt;/code&gt;. This needs configuration, which on Gentoo exists in
&lt;code&gt;/etc/conf.d/uwsgi&lt;/code&gt;. However, this file itself shouldn&amp;rsquo;t be altered. Instead,
make a copy of it, and call it &lt;code&gt;/etc/conf.d/uwsgi.cgit&lt;/code&gt;. The standard file
exists solely as a base template. For brevity, I left out all the comments in
the contents below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_SOCKET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_THREADS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_PROGRAM&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_XML_CONFIG&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_PROCESSES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_LOG_FILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_CHROOT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_DIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/tyil
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_PIDPATH_MODE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0750&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_USER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_GROUP&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_EMPEROR_PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_EMPEROR_PIDPATH_MODE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0770&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_EMPEROR_GROUP&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;UWSGI_EXTRA_OPTIONS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;--ini /etc/uwsgi.d/cgit.ini&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That covers the service configuration file. When things don&amp;rsquo;t work the way you
expect, specify a path in &lt;code&gt;UWSGI_LOG_FILE&lt;/code&gt; to see its logs. Additionally, you
may want to alter the value of &lt;code&gt;UWSGI_DIR&lt;/code&gt;. This specifies the working
directory from which the process starts.&lt;/p&gt;
&lt;p&gt;Now comes the application configuration, which will be read from
&lt;code&gt;/etc/uwsgi.d/cgit.ini&lt;/code&gt;, according to &lt;code&gt;UWSGI_EXTRA_OPTIONS&lt;/code&gt;. Create that file
with the following contents.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[uwsgi]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;master&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;plugins&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;cgi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;socket&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;127.0.0.1:1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;uid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;cgit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;gid&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;cgit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;procname-master&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;uwsgi cgit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;processes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;threads&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;cgi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/usr/share/webapps/cgit/1.2.1/hostroot/cgi-bin/cgit.cgi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the &lt;code&gt;cgi&lt;/code&gt; value contains the version number of &lt;code&gt;www-apps/cgit&lt;/code&gt;. You
may need to come back after an upgrade and update it accordingly.&lt;/p&gt;
&lt;p&gt;As last step for &lt;code&gt;uwsgi&lt;/code&gt; configuration, a service script, to manage it with
&lt;code&gt;rc-service&lt;/code&gt;. These scripts all exist in &lt;code&gt;/etc/conf.d&lt;/code&gt;, and the package
installed a script called &lt;code&gt;uwsgi&lt;/code&gt; in there. Just like with the &lt;code&gt;conf.d&lt;/code&gt;
variant, its just a template. This time, however, don&amp;rsquo;t make a copy of it, but
a symlink. It does not need to be edited, but the name must be the same as the
&lt;code&gt;conf.d&lt;/code&gt; entry name. That would be &lt;code&gt;uwsgi.cgit&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cd /etc/conf.d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ln -s uwsgi uwsgi.cgit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can start the service with &lt;code&gt;rc-service uwsgi.cgit start&lt;/code&gt;. If a
consequent &lt;code&gt;status&lt;/code&gt; notes the state as &lt;em&gt;Started&lt;/em&gt;, you&amp;rsquo;re all good. If the state
says &lt;em&gt;Crashed&lt;/em&gt;, you should go back and double-check all configuration files.
When those are correct and you can&amp;rsquo;t figure out why, feel free to reach out to
me via email.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-service uwsgi.cgit start
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-service uwsgi.cgit service
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Start this after boot
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rc-update add uwsgi.cgit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;nginx&#34;&gt;nginx&lt;/h3&gt;
&lt;p&gt;The final element to make it accessible, the web server, &lt;code&gt;nginx&lt;/code&gt;. How you
organize the configuration files here is largely up to you. Explaining how to
set up nginx from scratch is beyond the scope of this post. Assuming you know
how to configure this, add the following &lt;code&gt;location&lt;/code&gt; blocks to the &lt;code&gt;server&lt;/code&gt;
definition for the vhost you want to make &lt;code&gt;cgit&lt;/code&gt; available on.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/git&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;alias&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/usr/share/webapps/cgit/1.2.1/htdocs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;try_files&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;@cgit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;@cgit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;uwsgi_params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;gzip&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;uwsgi_modifier1&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;uwsgi_pass&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;127.0.0.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1234&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;fastcgi_split_path_info&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;^(/git/?)(.+)&lt;/span&gt;$&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   &lt;span class=&#34;kn&#34;&gt;uwsgi_param&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;PATH_INFO&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$fastcgi_path_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once saved, you can reload &lt;code&gt;nginx&lt;/code&gt;, and the &lt;code&gt;$HOSTNAME/git&lt;/code&gt; endpoint can be
reached, and should display an cgit page, detailing there are no repositories.
That can be easily solved by making some available in &lt;code&gt;$HOME/.local/srv/cgit&lt;/code&gt;,
through the power of symlinks.&lt;/p&gt;
&lt;h2 id=&#34;symlinking-the-repositories&#34;&gt;Symlinking the repositories&lt;/h2&gt;
&lt;p&gt;Go nuts with making symlinks to the various repositories you have gathered over
the years. You don&amp;rsquo;t need to use bare repositories, &lt;code&gt;cgit&lt;/code&gt; will also handle
regular repositories that you actively work in. As with the &lt;code&gt;nginx&lt;/code&gt;
configuration, explaining how to make symlinks is out of scope. In dire
situations, consult &lt;code&gt;man ln&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;git-mkbare&#34;&gt;&lt;code&gt;git-mkbare&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;While making the symlinks is easy, I found that it sheepishly boring to do. I go
to &lt;code&gt;$HOME/.local/git&lt;/code&gt;, make a directory, &lt;code&gt;cd&lt;/code&gt; to it, and create a bare
repository. Then off to &lt;code&gt;$HOME/.local/srv/cgit&lt;/code&gt; to make a symlink back to the
newly created bare repository. I think you can see this will get tedious very
quickly.&lt;/p&gt;
&lt;p&gt;So, to combat this, I made a small shell script to do all of that for me. I
called it &lt;code&gt;git-mkbare&lt;/code&gt;, and put it somewhere in my &lt;code&gt;$PATH&lt;/code&gt;. This allows me to
call it as &lt;code&gt;git mkbare repo-name&lt;/code&gt;. It will ask for a small description as well,
so I that can also be skipped as a manual task. This script may be of use to
you if you want to more quickly start a new project.&lt;/p&gt;
&lt;p&gt;You can find this script &lt;a href=&#34;https://git.tyil.nl/dotfiles/tree/.local/bin/git-mkbare&#34;&gt;in my dotfiles
repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;wrapping-up&#34;&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Now you should have cgit available from your site, allowing you to share the
sources of all your projects easily with the world. No need to make use of a
(proprietary) third-party service!&lt;/p&gt;
&lt;p&gt;If you have questions or comments on my setup, or the post in general, please
contact me through email or irc.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Getting Things Done with App::GTD</title>
      <link>https://www.tyil.nl/post/2019/10/07/getting-things-done-with-appgtd/</link>
      <pubDate>Mon, 07 Oct 2019 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2019/10/07/getting-things-done-with-appgtd/</guid>
      <description>&lt;p&gt;A couple months ago, I was given a workshop at work about &amp;ldquo;getting things
done&amp;rdquo;. There I was told that there exists a concept called &amp;ldquo;&lt;a href=&#34;https://en.wikipedia.org/wiki/Getting_Things_Done&#34;&gt;Getting Things
Done&lt;/a&gt;&amp;rdquo;, or &amp;ldquo;GTD&amp;rdquo; for short,
to help you, well, get things done. A number of web-based tools were introduced
to assist us with following the rules laid out in the concept.&lt;/p&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;The tools that were introduced did their job, and looked reasonably shiny.
However, most required a constant Internet connection. I like my tools to be
available offline, and optionally synced together. There was one local
application and a couple cloud-synced applications that I found, so this
problem could&amp;rsquo;ve been resolved. However, my other problem with all these
programs was that they&amp;rsquo;re all proprietary. Those who&amp;rsquo;ve read more of my blog
may have realized by now that I strongly prefer free software whenever
possible.&lt;/p&gt;
&lt;p&gt;Being unable to find any free software programs to fulfill my needs, I took a
look at the features I would need, and tried to adapt other programs to fit
those particular needs. I quickly learned that it&amp;rsquo;s inconvenient at best to try
and mold generic task keeping programs into the specifics of GTD. But, it did
give me a reasonable idea of what features I needed for basic usage. It
occurred to me that it shouldn&amp;rsquo;t be terribly hard to just write something of my
own. So I did.&lt;/p&gt;
&lt;h2 id=&#34;the-solution-appgtd&#34;&gt;The solution, &lt;code&gt;App::GTD&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Introducing &lt;a href=&#34;https://gitlab.com/tyil/raku-app-gtd&#34;&gt;&lt;code&gt;App::GTD&lt;/code&gt;&lt;/a&gt;, a brand new
project written in the &lt;a href=&#34;https://raku.org/&#34;&gt;Raku programming language&lt;/a&gt;. While
still in its early phases, it seems to be usable on a day-to-day basis for me
and another colleague. In its bare basics, it&amp;rsquo;s just another to-do list, but
the commands it gives you incorporate the concepts of GTD. There&amp;rsquo;s an inbox
that you fill up through the day, a list of next items to work on, and projects
to structure larger tasks in.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		The Raku programming language used to be called the Perl 6 programming
language. They function the same, but the name was changed for various reasons
I will not get into here.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;This program can be installed using &lt;code&gt;zef&lt;/code&gt;, though I&amp;rsquo;m planning an &lt;code&gt;ebuild&lt;/code&gt; for
Gentoo (and derivatives) too. Once installed, you can use &lt;code&gt;gtd&lt;/code&gt; from your
shell. Doing so without arguments will show the usage information. The most
important will be &lt;code&gt;gtd add&lt;/code&gt;, &lt;code&gt;gtd next&lt;/code&gt; and &lt;code&gt;gtd done&lt;/code&gt;. Most of these commands
require an &lt;code&gt;id&lt;/code&gt; argument. The IDs required are displayed in front of the items
when listing them with commands like &lt;code&gt;inbox&lt;/code&gt; or &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;daily-life-with-gtd&#34;&gt;Daily life with &lt;code&gt;gtd&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Once you have &lt;code&gt;gtd&lt;/code&gt; installed, you don&amp;rsquo;t &lt;em&gt;need&lt;/em&gt; to do any configuration, as the
defaults should work fine for most people. This means you can start using it
immediately if you want to try it out!&lt;/p&gt;
&lt;p&gt;The most common invocation will be with the &lt;code&gt;add&lt;/code&gt; sub-command. Whenever
something pops up that needs doing, you add it to your inbox using it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gtd add Buy eggs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gtd add &amp;#34;update cpan-raku&amp;#39;s help command&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These items go to your inbox, and don&amp;rsquo;t need to be long, so long as &lt;em&gt;you&lt;/em&gt;
understand what you meant by it. You can see that you also don&amp;rsquo;t need to use
quotes around the item you want to add. All arguments after &lt;code&gt;add&lt;/code&gt; will be
joined together as a string again, but some shells may perform their magic on
certain things. This is why I quoted the second call, but not the first.&lt;/p&gt;
&lt;p&gt;All these things that you write down like this need to be sorted out at some
point. I do this every day in the morning, before I get to my regular tasks at
work. To get started, I want to see an overview of your inbox, for which the
&lt;code&gt;inbox&lt;/code&gt; sub-command is intended. Running it will give you a list of all the
items in your inbox, including their ID and the date they were added.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd inbox
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[1] Buy eggs                        (2019-10-17)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[2] update cpan-raku&amp;#39;s help command (2019-10-17)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now I can go through the list, and decide which actions I should undertake
specifically. These are called &amp;ldquo;next items&amp;rdquo;, and the sub-command is named
&lt;code&gt;next&lt;/code&gt;. Called without arguments it will give you an overview of your next
items, but when given an ID it will move an inbox item to your list of next
items. You can optionally also specify a new name for the item, to be more
clear about what needs doing.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd next
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;You&amp;#39;re all out of Next actions!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd next 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#34;Buy eggs&amp;#34; has been added as a Next item.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd next 2 &amp;#34;Add usage and repo info to cpan-raku, whenever it&amp;#39;s messaged with &amp;#39;help&amp;#39;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#34;Add usage and repo info to cpan-raku, whenever it&amp;#39;s messaged with &amp;#39;help&amp;#39;&amp;#34; has
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;been added as a Next item.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can now see that your inbox is empty when using &lt;code&gt;inbox&lt;/code&gt;, and see a list of
the next items you created with &lt;code&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd inbox
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your inbox is empty!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd next
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[1] Buy eggs                                                                 (2019-10-17)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[2] Add usage and repo info to cpan-raku, whenever it&amp;#39;s messaged with &amp;#39;help&amp;#39; (2019-10-17)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now all that&amp;rsquo;s left is to do all the things you&amp;rsquo;ve created next items for. When
done, you can remove the entry from your next items using &lt;code&gt;done&lt;/code&gt;. This command
also works on items in your inbox, so small tasks that require no next item(s)
can be marked as done immediately.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd done 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#34;Buy eggs&amp;#34; has been removed from your list.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd done 2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#34;Add usage and repo info to cpan-raku, whenever it&amp;#39;s messaged with &amp;#39;help&amp;#39;&amp;#34; has
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;been removed from your list.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ gtd next
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;You&amp;#39;re all out of Next actions!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;future-plans&#34;&gt;Future plans&lt;/h2&gt;
&lt;p&gt;The basics are here, but there are some things I&amp;rsquo;d very much like to add. First
and foremost, I want to be have a context to add to items, and a single context
the program operates in. This way, I can more clearly separate work and
personal tasks, which now just share one global context.&lt;/p&gt;
&lt;p&gt;Additionally, I&amp;rsquo;ve read about a new YouTube tutorial about using &lt;code&gt;ncurses&lt;/code&gt; in
Raku, which I hope can guide me through making an &lt;code&gt;ncurses&lt;/code&gt; application for
this as well. Perhaps I can find time to make a &lt;code&gt;GTK&lt;/code&gt; application out of it as
well.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve already mentioned wanting to create a Gentoo &lt;code&gt;ebuild&lt;/code&gt; for the application,
but this will require packaging all the module dependencies as well. This comes
with a number of hurdles that I&amp;rsquo;m trying to iron out before starting on this
endeavor. If you are on Gentoo (or a derivative) and want to assist in any way,
please contact me.&lt;/p&gt;
&lt;p&gt;Another thing I&amp;rsquo;ve taken into account when structuring the application is the
possibility for other data back-end. &lt;code&gt;gtd&lt;/code&gt; is currently storing it&amp;rsquo;s
information in &lt;code&gt;JSON&lt;/code&gt; files in a filesystem directory, which comes with various
drawbacks. It may be beneficial to also support databases such as SQLite or
PostgreSQL. However, this is not a high priority for me right now, as it would
slow down the speed at which I can make improvements to the general program.&lt;/p&gt;
&lt;p&gt;I hope that &lt;code&gt;App::GTD&lt;/code&gt; can help others to get things done as well. The program
is all but finished, but it should be usable for people besides me and my
colleague by now. If you have any suggestions or questions about the program,
do not hesitate to seek me out!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The SoC Controversy</title>
      <link>https://www.tyil.nl/post/2019/08/10/the-soc-controversy/</link>
      <pubDate>Sat, 10 Aug 2019 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2019/08/10/the-soc-controversy/</guid>
      <description>&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Disclaimer
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		Please keep in mind that the opinion shared in this blog post is mine and mine
alone. I do not speak for any other members of the PerlCon organization team.
Please do not address anyone but me for the positions held in this post.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;Those that know me are probably aware that I generally dislike to make
political posts on my personal blog. I&amp;rsquo;d rather stick to technological
arguments, as there&amp;rsquo;s less problems to be found with regards to personal
feelings and all that. However, as I&amp;rsquo;m growing older (and hopefully more
mature), I find it harder to keep politics out of my life as I interact with
online communities. This becomes especially true as I plan to assist with
organizing &lt;a href=&#34;https://wiki.perlcon.eu/doku.php/proposals/2020/amsterdam&#34;&gt;PerlCon
2020&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;PerlCon 2019 ended yesterday, and I had a lot of fun. I&amp;rsquo;d like to thank the
organizer, Andrew Shitov, once more for doing an amazing job. Especially so, as
he has been harassed for weeks, for trying to organize the conference. The
reason behind the harassment was partly due to his decision to not have an SoC,
or &amp;ldquo;Standards of Conduct&amp;rdquo;, for PerlCon 2019.&lt;/p&gt;
&lt;p&gt;During his final announcements at the end of the conference, he noted that this
is still happening, even in person at the conference itself. This toxic
behavior towards him has made him decide to no longer involve himself in
organizing a conference for the Perl community. I personally think this is a
loss for everyone involved in the community, and one that was completely
avoidable by having humane discussion instead of going for Twitter harassment.&lt;/p&gt;
&lt;p&gt;For what it&amp;rsquo;s worth, I think Twitter is also the worst possible place on the
Internet for any reasonable discussion, as it puts a very low limit on the
amount of characters you are allowed to spend on a single post. This makes it
downright impossible for any discussion, and seems to always lead to petty
name-calling. This is one of the reasons why &lt;a href=&#34;https://soc.fglt.nl/main/public&#34;&gt;I&amp;rsquo;m instead using a Pleroma
instance&lt;/a&gt; for my social media presence on the
Internet. If anyone is on the Internet with the intent of having interesting
discussion, I&amp;rsquo;d highly recommend to use some entrance into the Fediverse. The
instance I&amp;rsquo;m using is open for sign-ups!&lt;/p&gt;
&lt;p&gt;But I digress. The SoC controversy is what made me want to write this blog
post. I wonder why this even is a controversy. Why do people think it is
impossible to co-exist without some document describing explicitly what is and
is not allowed? I would hope that we&amp;rsquo;re all adults, and can respect one another
as such.&lt;/p&gt;
&lt;p&gt;I wonder, was there any certain event at PerlCon 2019 that would&amp;rsquo;ve been
avoided if there &lt;em&gt;was&lt;/em&gt; a SoC provided? I certainly did not, at any point, feel
that people were being harmful to one another, but maybe I&amp;rsquo;m just blind to it.
If anyone has concrete examples of events that happened during PerlCon 2019
that a SoC could&amp;rsquo;ve prevented, I would be genuinely interested in hearing about
them. If I am to assist in organizing PerlCon 2020, and I want to be able to
present a good argument on the SoC discussion, I&amp;rsquo;ll need concrete examples of
real problems that have occurred.&lt;/p&gt;
&lt;p&gt;Of course, I also consider the opposite of this discussion. Can the SoC be used
to &lt;em&gt;cause&lt;/em&gt; harm, in stead of deter it? For this, I actually have clear
evidence, and the answer is a resounding &lt;strong&gt;yes&lt;/strong&gt;. The harassment brought upon
Andrew was originally caused by an event that transpired at The Perl Conference
in Pittsburgh (2019). A video was removed, and a speaker harassed, for
dead-naming someone. Until that event, I wasn&amp;rsquo;t even aware of the term, but
apparently it&amp;rsquo;s grounds for removal of your presentation from the conference
archives.&lt;/p&gt;
&lt;p&gt;A similar event happened with The Perl Conference in Glasgow (2018), where a
talk was also removed from the archives for a supposedly offensive joke that
was made. This also sparked a heavy discussion on IRC back then, with people
from all sides pitching in with their opinion.&lt;/p&gt;
&lt;p&gt;From my perspective, the people shouting the loudest in these discussions
aren&amp;rsquo;t interested in making the world a better place where we can live in
harmony, but to punish the offender for their behavior. I don&amp;rsquo;t think we
should strive towards punishment, but towards understanding, if anything. Just
being angry, shouting at people (either in real life, or over the Internet)
isn&amp;rsquo;t going to solve any underlying problem. It is more likely to cause more
issues in the long run, where people will just be more divided, and will want
to get continuous revenge upon the other side.&lt;/p&gt;
&lt;p&gt;Additionally, I think that the existence of an SoC or likewise document is a
sign towards outsiders that your community can&amp;rsquo;t behave itself maturely. They
need special rules laid out to them, after all. Like most rules, they are
codified because issues have arisen in the past, and keep on arising. I don&amp;rsquo;t
think the Perl community is too immature to behave itself. I trust in the good
faith of people, and to me it feels like a SoC does the exact opposite.&lt;/p&gt;
&lt;p&gt;I hope this blog post does it&amp;rsquo;s job to invite you kindly to share your opinions
with me, either on &lt;a href=&#34;https://www.tyil.nl/#contact&#34;&gt;IRC, email or on the Fediverse&lt;/a&gt;. I&amp;rsquo;d
gladly start a discussion on the positive and negative effects the SoC has, and the problems
it solves and creates. I think a civil discussion is in order here, to best
prepare us for PerlCon 2020.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Power(ful Tooling) of Gentoo</title>
      <link>https://www.tyil.nl/post/2019/07/22/the-powerful-tooling-of-gentoo/</link>
      <pubDate>Mon, 22 Jul 2019 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2019/07/22/the-powerful-tooling-of-gentoo/</guid>
      <description>&lt;p&gt;People often ask me for my reasons to use &lt;a href=&#34;https://gentoo.org/&#34;&gt;Gentoo&lt;/a&gt;. Many
perceive it as a &amp;ldquo;hard&amp;rdquo; distro that takes a lot of time. While it does come
with a learning curve, I don&amp;rsquo;t perceive it as particularly &amp;ldquo;hard&amp;rdquo;, as the
documentation is very thorough and the community is very helpful. And the
tooling you get to maintain your system is far beyond what I&amp;rsquo;ve come across
with any other GNU+Linux distribution.&lt;/p&gt;
&lt;p&gt;This blog post will highlight some of the key features I love about Gentoo.
There are certainly many more perks that I don&amp;rsquo;t (yet) use, so please feel free
to inform me of other cool things that I missed.&lt;/p&gt;
&lt;h2 id=&#34;configurability&#34;&gt;Configurability&lt;/h2&gt;
&lt;p&gt;One of the main reasons for preferring Gentoo is due to the ease of configuring
it to work just the way you want.&lt;/p&gt;
&lt;p&gt;A great example for this would be with &lt;code&gt;init&lt;/code&gt; choices. Many distributions only
support &lt;a href=&#34;https://en.wikipedia.org/wiki/Systemd&#34;&gt;systemd&lt;/a&gt; these days. As I&amp;rsquo;m not
a big fan of this particular system, I want to change this. But even asking a
question about this will get you a lot of hatred in most distribution
communities. In Gentoo, however, changing init is supported and well
documented, allowing you to pick from a range of possible inits.&lt;/p&gt;
&lt;h3 id=&#34;use-flags&#34;&gt;&lt;code&gt;USE&lt;/code&gt; flags&lt;/h3&gt;
&lt;p&gt;One of the core concepts of Gentoo are the &lt;a href=&#34;https://wiki.gentoo.org/wiki/USE_flag&#34;&gt;&lt;code&gt;USE&lt;/code&gt;
flags&lt;/a&gt;. These allow you to easily alter
the software you&amp;rsquo;re compiling to use the features you want. They can also be
used to indicate which library you would like to use to make use of a certain
feature, if there are multiple implementations available.&lt;/p&gt;
&lt;h3 id=&#34;makeconf&#34;&gt;&lt;code&gt;make.conf&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Like most distros that work with self-compiled packages, Gentoo has a
&lt;code&gt;make.conf&lt;/code&gt; file available to specify some default arguments in to use while
compiling. Unlike most other distros, Gentoo&amp;rsquo;s &lt;code&gt;make.conf&lt;/code&gt; also allows for some
configuration of the &lt;code&gt;emerge&lt;/code&gt; utility.&lt;/p&gt;
&lt;p&gt;For instance, I use my &lt;code&gt;make.conf&lt;/code&gt; to ensure &lt;code&gt;emerge&lt;/code&gt; always asks for
confirmation before performing actions. I also ensure that the build system,
&lt;code&gt;portage&lt;/code&gt;, is heavily sandboxed when building packages.&lt;/p&gt;
&lt;p&gt;Additionally, like all configuration files in &lt;code&gt;/etc/portage&lt;/code&gt;, it can be made
into a directory. In this case, all files in the directory will be loaded in
alphabetical order. This allows for easier management using tools like
&lt;a href=&#34;https://www.ansible.com/&#34;&gt;Ansible&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;ease-of-patching&#34;&gt;Ease of patching&lt;/h3&gt;
&lt;p&gt;Another feature I find very useful of Gentoo, is the ease of applying my own
patches to software. If you have a custom patch for a package that you want to
be applied, all you have to do is drop it in a directory in
&lt;code&gt;/etc/portage/patches&lt;/code&gt;. The directory is should be in is the same as the
package&amp;rsquo;s name the patch is intended for. For instance, I have the following
patch in &lt;code&gt;/etc/portage/patches/www-client/firefox&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;diff --git a/browser/extensions/moz.build b/browser/extensions/moz.build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;index 6357998..c5272a2 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;--- a/browser/extensions/moz.build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+++ b/browser/extensions/moz.build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;@@ -5,15 +5,10 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt; # file, You can obtain one at http://mozilla.org/MPL/2.0/.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; DIRS += [
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-    &amp;#39;activity-stream&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;     &amp;#39;aushelper&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &amp;#39;followonsearch&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &amp;#39;formautofill&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &amp;#39;jaws-esr&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-    &amp;#39;onboarding&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-    &amp;#39;pdfjs&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-    &amp;#39;pocket&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;-    &amp;#39;screenshots&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;     &amp;#39;webcompat&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Whenever a new Firefox is released and built, this patch will be applied on it
to remove some of the features I dislike.&lt;/p&gt;
&lt;h2 id=&#34;ebuilds-and-overlays&#34;&gt;Ebuilds and overlays&lt;/h2&gt;
&lt;p&gt;In Gentoo vocabulary, &lt;code&gt;ebuild&lt;/code&gt; files are the files that describe how a package
is to be built, which &lt;code&gt;USE&lt;/code&gt; flags it supports and everything else relating to a
package. An overlay is a repository of ebuild files. Everyone can make their
own, and easily add 5 lines in their &lt;code&gt;repos.conf&lt;/code&gt; to use it. In most cases,
they&amp;rsquo;re just git repositories.&lt;/p&gt;
&lt;p&gt;The documentation on everything around ebuilds is superb, in my experience,
especially compared to other distros. It is incredibly easy to get started
with, since it&amp;rsquo;s made to be usable with very little effort. While being simple,
it&amp;rsquo;s also very flexible: All default behaviours can be overwritten if needed to
get a package to build.&lt;/p&gt;
&lt;h2 id=&#34;binary-packages&#34;&gt;Binary packages&lt;/h2&gt;
&lt;p&gt;Yes, you read that right. &lt;a href=&#34;https://wiki.gentoo.org/wiki/Binary_package_guide&#34;&gt;Binary
packages&lt;/a&gt;! Contrary to
popular belief, Gentoo &lt;em&gt;does&lt;/em&gt; support this. You can instruct &lt;code&gt;emerge&lt;/code&gt; to build
binary packages of all the packages it compiles, which can then be re-used on
other systems. It does need to be compiled in such a way that the other machine
can use it, of course. You can&amp;rsquo;t simply exchange the packages of an x64 machine
with and ARM machine, for instance. You can set up a &lt;a href=&#34;https://wiki.gentoo.org/wiki/Cross_build_environment&#34;&gt;cross build
environment&lt;/a&gt; to get that
particular usecase going, though.&lt;/p&gt;
&lt;p&gt;If you want to easily share the binary packages you build with one machine, you
can set up a
&lt;a href=&#34;https://wiki.gentoo.org/wiki/Binary_package_guide#Setting_up_a_binary_package_host&#34;&gt;binhost&lt;/a&gt;,
and have &lt;code&gt;emerge&lt;/code&gt; pull the binary packages on the other systems as needed using
&lt;code&gt;--usepkg&lt;/code&gt;. There actually is a &lt;a href=&#34;http://packages.gentooexperimental.org/&#34;&gt;binhost provided by Gentoo
itself&lt;/a&gt;, but it seems to only contain
important packages used to restore systems into a working state.&lt;/p&gt;
&lt;h2 id=&#34;tooling&#34;&gt;Tooling&lt;/h2&gt;
&lt;p&gt;Some of the core tooling available to any Gentoo user has already been talked
about. But there&amp;rsquo;s some additional tooling you can install to make your life
even better.&lt;/p&gt;
&lt;h3 id=&#34;genkernel&#34;&gt;&lt;code&gt;genkernel&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;One of the hardest tasks to newcomers to Gentoo is often to compile a kernel.
Of course, Gentoo has an answer for this, &lt;code&gt;genkernel&lt;/code&gt;. The defaults &lt;code&gt;genkernel&lt;/code&gt;
will give you are reasonably sane if you just want to have a kernel that works.
Of course, you can still edit the kernelconfig before compilation starts. It
will also build an &lt;code&gt;initramfs&lt;/code&gt; when requested, that goes along with the kernel.
When things have been made, the kernel and initramfs will be moved to &lt;code&gt;/boot&lt;/code&gt;,
and a copy of the working kernelconfig is saved to &lt;code&gt;/etc/kernels&lt;/code&gt;. All you need
to remember is to update your preferred bootloader&amp;rsquo;s configuration to include
your new kernel.&lt;/p&gt;
&lt;h3 id=&#34;eix&#34;&gt;&lt;code&gt;eix&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://wiki.gentoo.org/wiki/Eix&#34;&gt;&lt;code&gt;eix&lt;/code&gt;&lt;/a&gt; is a utility most Gentoo users use to
update the Portage repositories and search for available packages. The
interface is considered more convenient, and it&amp;rsquo;s a bit faster at getting your
results.&lt;/p&gt;
&lt;p&gt;To get a quick overview of which packages are in need of updates, you can run
&lt;code&gt;eix -uc&lt;/code&gt; (&lt;em&gt;u&lt;/em&gt;pdates, &lt;em&gt;c&lt;/em&gt;ompact). To sync the Portage tree and all overlays,
&lt;code&gt;eix-sync&lt;/code&gt; is the way to go. This will ensure the cache used by &lt;code&gt;eix&lt;/code&gt; also gets
updated.&lt;/p&gt;
&lt;p&gt;In addition to having a cleaner interface and being faster, it also comes with
additional tools for keeping your system sane. The most notable to me is
&lt;code&gt;eix-test-obsolete&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This utility will report any installed packages that are no longer provided by
any repository (orphaned packages). It will also report all configuration lines
that affect such packages. This is really valuable in keeping your
configuration maintainable.&lt;/p&gt;
&lt;h3 id=&#34;glsa-check&#34;&gt;&lt;code&gt;glsa-check&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;glsa-check&lt;/code&gt; utility is part of the &lt;code&gt;app-portage/gentoolkit&lt;/code&gt; package. When
ran, it will produce a list of all packages which have known vulnerabilities.
It will use the &lt;a href=&#34;https://security.gentoo.org/glsa&#34;&gt;GLSA database&lt;/a&gt; for the list
of known vulnerabilities. This can be much easier than subscribing to a mailing
list and having to check every mail to see if a vulnerability affects you.&lt;/p&gt;
&lt;h3 id=&#34;qlop&#34;&gt;&lt;code&gt;qlop&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;qlop&lt;/code&gt; is another utility that comes with &lt;code&gt;app-portage/gentoolkit&lt;/code&gt;. This
program parses the logs from &lt;code&gt;emerge&lt;/code&gt; to give provide you with some
information. I use this mostly to see compile times of certain packages using
&lt;code&gt;qlop -Htvg &amp;lt;package-name&amp;gt;&lt;/code&gt;. Using this, I can more easily deduce if I want my
desktop (with a stronger CPU) to compile a certain package, or if it&amp;rsquo;ll be
faster to just compile it on my laptop.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Perl 6 nightly Docker images</title>
      <link>https://www.tyil.nl/post/2019/04/11/perl-6-nightly-docker-images/</link>
      <pubDate>Thu, 11 Apr 2019 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2019/04/11/perl-6-nightly-docker-images/</guid>
      <description>&lt;p&gt;Due to the slow release of Rakudo Star (which actually did release a new
version last month), I had set out to make Docker images for personal use based
on the regular Perl 6 releases. But, as I discovered some &lt;a href=&#34;https://github.com/rakudo/rakudo/issues/1501&#34;&gt;memory related
issues&lt;/a&gt;, and &lt;a href=&#34;https://github.com/MoarVM/MoarVM/pull/1072&#34;&gt;another branch with
some possible fixes&lt;/a&gt;, I changed my
mind to make them nightlies based on the &lt;code&gt;master&lt;/code&gt; branches of all related
projects instead. This way I could get fixes faster, and help testing when
needed.&lt;/p&gt;
&lt;p&gt;These nightlies are now up and running, available on &lt;a href=&#34;https://hub.docker.com/r/tyil/perl6&#34;&gt;Docker
Hub&lt;/a&gt; for anyone to use! You can also find
&lt;a href=&#34;https://git.tyil.nl/docker/perl6&#34;&gt;the Dockerfiles I&amp;rsquo;m using on git.tyil.nl&lt;/a&gt;,
in case you&amp;rsquo;re interested or have suggestions to further improve the process.&lt;/p&gt;
&lt;p&gt;The timing of the (public) release of these images could have been better,
though. About two weeks ago, other nightlies were released as well, by Tony
O&amp;rsquo;Dell, as has been noted in the &lt;a href=&#34;https://p6weekly.wordpress.com/2019/03/25/2019-12-cool-truck/&#34;&gt;Perl 6 Weekly
post&lt;/a&gt;. While I
greatly appreciate his efforts, I was not going to just abandon all the work
I&amp;rsquo;ve put into my images. Instead I&amp;rsquo;ve tried to make smaller images, and provide
different bases than him. Maybe we can eventually learn from each other&amp;rsquo;s images
and improve Docker support for the entire community together.&lt;/p&gt;
&lt;p&gt;The easiest thing to work on was providing different bases. For now, this means
I have images with the following four base images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alpine&lt;/li&gt;
&lt;li&gt;Debian&lt;/li&gt;
&lt;li&gt;Ubuntu&lt;/li&gt;
&lt;li&gt;Voidlinux&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This way, people can have more options with regards to using the distribution
tooling that they&amp;rsquo;re more comfortable with. One could also opt to use a more
familiar or better supported base image for development and testing out their
module, and use a smaller image for production releases.&lt;/p&gt;
&lt;p&gt;As to the size of the images, Tony&amp;rsquo;s &lt;code&gt;tonyodell/rakudo-nightly:latest&lt;/code&gt; is about
1.42GB at the time of writing this post. My images range from 43.6MB
(&lt;code&gt;alpine-latest&lt;/code&gt;) to 165MB (&lt;code&gt;voidlinux-latest&lt;/code&gt;). Though this is not a
completely fair comparison, as my images have stripped out a lot of the tooling
used (and often required) to build some Perl 6 modules, making them unusable in
their default shape for many projects.&lt;/p&gt;
&lt;p&gt;To remedy this particular issue, I&amp;rsquo;ve also created &lt;em&gt;-dev&lt;/em&gt; images. These images
come with a number of additional packages installed to allow &lt;code&gt;zef&lt;/code&gt; to do its
work to get dependencies installed without requiring end-users to search for
those packages. This should reduce complexity when using the images for
end-users. If we take the dev images into account when comparing sizes, my
images range from 256MB (&lt;code&gt;alpine-dev-latest&lt;/code&gt;) to 1.27GB
(&lt;code&gt;voidlinux-dev-latest&lt;/code&gt;). That&amp;rsquo;s much closer to the &lt;code&gt;rakudo-nightly&lt;/code&gt; image.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in trying these images out, you may be interested in the
way I&amp;rsquo;m using these images myself as reference. Currently, my &lt;a href=&#34;https://git.tyil.nl/perl6/app-cpan-uploadannouncer-irc&#34;&gt;CPAN upload
notifier bot&lt;/a&gt; is using
these nightly images in its
&lt;a href=&#34;https://git.tyil.nl/perl6/app-cpan-uploadannouncer-irc/src/branch/master/Dockerfile&#34;&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Dockerfile&#34; data-lang=&#34;Dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; tyil/perl6:debian-dev-latest as install&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; apt update &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt install -y libssl-dev uuid-dev&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; META6.json META6.json&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; zef install --deps-only --/test .&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see from the &lt;code&gt;Dockerfile&lt;/code&gt;, I start out by using a &lt;code&gt;-dev&lt;/code&gt; image, and
name that stage &lt;code&gt;install&lt;/code&gt;. I&amp;rsquo;m still contemplating to include &lt;code&gt;libssl-dev&lt;/code&gt; into
the &lt;code&gt;-dev&lt;/code&gt; images, as it seems to pop up a lot, but for now, it&amp;rsquo;s not part of
the &lt;code&gt;-dev&lt;/code&gt; images, so I install it manually. Same goes for &lt;code&gt;uuid-dev&lt;/code&gt;. Then I
copy in the &lt;code&gt;META6.json&lt;/code&gt;, and instruct &lt;code&gt;zef&lt;/code&gt; to install all the dependencies
required.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Dockerfile&#34; data-lang=&#34;Dockerfile&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; tyil/perl6:debian-latest&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ENV&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PERL6LIB&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;lib
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;WORKDIR&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; /app&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; mkdir -p /usr/share/man/man1&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; mkdir -p /usr/share/man/man7&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; apt update &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt install -y libssl-dev postgresql-client&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; bin bin&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; lib lib&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt; --from&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;install /usr/local /usr/local&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; mkdir -p /var/docker/meta&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RUN&lt;/span&gt; date &lt;span class=&#34;s2&#34;&gt;&amp;#34;+%FT%TZ&amp;#34;&lt;/span&gt; &amp;gt; /var/docker/meta/build-date&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CMD&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;perl6&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;bin/bot&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I start a new stage. I set the &lt;code&gt;$PERL6LIB&lt;/code&gt; environment variable so I don&amp;rsquo;t
have to use &lt;code&gt;-Ilib&lt;/code&gt; at the end, and set a &lt;code&gt;WORKDIR&lt;/code&gt; to have a clean directory
to work in. Next, I set up the &lt;em&gt;runtime dependencies&lt;/em&gt; of the application.&lt;/p&gt;
&lt;p&gt;I then continue to copy in the &lt;code&gt;bin&lt;/code&gt; and &lt;code&gt;lib&lt;/code&gt; directories, containing the
application itself, and copy over &lt;code&gt;/usr/local&lt;/code&gt; from the &lt;code&gt;install&lt;/code&gt; stage.
&lt;code&gt;/usr/local&lt;/code&gt; is where Perl 6 is installed, and &lt;code&gt;zef&lt;/code&gt; installs all its
dependencies into. This way, the &lt;code&gt;-dev&lt;/code&gt; image can be used for building all the
dependencies as needed, and only the finished dependencies end up in the final
image that&amp;rsquo;s going to run in production.&lt;/p&gt;
&lt;p&gt;Lastly, I set the build date and time of the image in a file, so the
application can refer to it later on. It is displayed when the IRC bot replies
to a &lt;code&gt;.bots&lt;/code&gt; command, so I can verify that the running bot is the one I just
built. And finally, the &lt;code&gt;CMD&lt;/code&gt; instruction runs the application.&lt;/p&gt;
&lt;p&gt;I hope this displays how the images can be used for your applications, and the
reasoning as to why I made them the way they are. If you have any suggestions
or issues, feel free to contact me in whatever way suits you best. You can find
some contact details on the homepage of my blog.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to sign PGP keys</title>
      <link>https://www.tyil.nl/post/2019/02/03/how-to-sign-pgp-keys/</link>
      <pubDate>Sun, 03 Feb 2019 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2019/02/03/how-to-sign-pgp-keys/</guid>
      <description>&lt;p&gt;Having attended &lt;a href=&#34;https://fosdem.org/2019/&#34;&gt;FOSDEM&lt;/a&gt; last weekend, I have been
asked to help some people out with signing PGP keys. As it is an international
gathering of users and developers of all levels of expertise, it&amp;rsquo;s a great event
to get your key out in to the wild. While helping people out, I figured it might
be even easier next time around to just refer to a small tutorial on my blog
instead.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-pgp-key&#34;&gt;Creating a PGP key&lt;/h2&gt;
&lt;p&gt;The first step to sign keys, is to have a PGP key. If you already have one,
you&amp;rsquo;re good to go to the next part of this tutorial. If you don&amp;rsquo;t, you can check
out the &lt;code&gt;gpg&lt;/code&gt; manual on how to create a key, or read about key creation in my
[article on using PGP with a Yubikey][yubikey-pgp-article]. While I would
strongly suggest reading at least some material, &lt;code&gt;gpg&lt;/code&gt; does quite a good job of
guiding you through the process without prior knowledge, so you can just get
started with &lt;code&gt;gpg --generate-key&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;[yubikey-pgp-article]: {{ &amp;ldquo;/post/2018/09/04/setting-up-pgp-with-a-yubikey/#creating-pgp-keys&amp;rdquo; | prepend: site.baseurl | prepend: site.url }}&lt;/p&gt;
&lt;h2 id=&#34;create-key-slips&#34;&gt;Create key slips&lt;/h2&gt;
&lt;p&gt;A &lt;em&gt;key slip&lt;/em&gt; is a small piece of paper containing some basic information about
the PGP key. They&amp;rsquo;re exchanged when people meet, so they don&amp;rsquo;t have to
immediately sign the key, but can do it safely at home. When you&amp;rsquo;re signing in a
group, this may be faster to work with. Another benefit is that some people
don&amp;rsquo;t have their private keys with them. They can then just collect the key slips
from the people who&amp;rsquo;s key they want to sign, and sign it whenever they are in
possession of their private key again.&lt;/p&gt;
&lt;p&gt;A key slip doesn&amp;rsquo;t have to contain much. A key ID, fingerprint, email address and
a name is plenty. For reference, my key slips look as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Patrick Spek &amp;lt;p.spek@tyil.nl&amp;gt;   rsa4096/0x7A6AC285E2D98827
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    1660 F6A2 DFA7 5347 322A  4DC0 7A6A C285 E2D9 8827
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;verifying-the-owner&#34;&gt;Verifying the owner&lt;/h2&gt;
&lt;p&gt;Before you sign anyone&amp;rsquo;s public key, you should verify that the person is
actually who they say they are. You can easily do this by asking for government
issued identification, such as an ID card, driver&amp;rsquo;s license or passport. What
constitutes good proof is up to you, but in general people expect at least one
form of government issued identification.&lt;/p&gt;
&lt;p&gt;If the person can&amp;rsquo;t verify who they are, you should &lt;em&gt;not&lt;/em&gt; sign their key!&lt;/p&gt;
&lt;h2 id=&#34;retrieving-their-key&#34;&gt;Retrieving their key&lt;/h2&gt;
&lt;p&gt;Once you have verified the person is who they say they are, and you have
received their key slip containing their key ID, you can look up their key
online. You can let &lt;code&gt;gpg&lt;/code&gt; do all the work for you in searching and downloading
the key, using the &lt;code&gt;--search&lt;/code&gt; switch. For instance, to retrieve my key, do the
following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --search-keys 0x7A6AC285E2D98827
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If a result has been found, you are prompted to enter the numbers of the keys
you want to download. Make sure you download the right key, in case multiple
have been found!&lt;/p&gt;
&lt;p&gt;After retrieving the key, you can see it in the list of all the keys &lt;code&gt;gpg&lt;/code&gt; knows
about using &lt;code&gt;gpg --list-keys&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;signing-their-key&#34;&gt;Signing their key&lt;/h2&gt;
&lt;p&gt;To actually sign their key, and show that you trust that the key belongs to the
person&amp;rsquo;s name attached to it, you can use &lt;code&gt;gpg --sign-key&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --sign-key 0x7A6AC285E2D98827
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will be prompted whether you are sure you want to sign. You should answer
this with a single &lt;code&gt;y&lt;/code&gt; to continue.&lt;/p&gt;
&lt;p&gt;After signing it, you&amp;rsquo;ll have signed a PGP key! You can verify this by looking
at the signatures on a given key with &lt;code&gt;--list-sigs 0x7A6AC285E2D98827&lt;/code&gt;. This should
contain your name and key ID.&lt;/p&gt;
&lt;h2 id=&#34;exchanging-the-signed-key&#34;&gt;Exchanging the signed key&lt;/h2&gt;
&lt;p&gt;While you could publish the updated public key with your signature on it, you
should &lt;strong&gt;not&lt;/strong&gt; do this! You should encrypt the updated public key and send it to
the person that owns the private key, and they should upload it themselves. One
reason for this is that it allows you to safely verify that they do in fact
actually own the private key as well, without ever asking them explicitly to
show you their private key.&lt;/p&gt;
&lt;p&gt;To export the public key, use &lt;code&gt;--export&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --armor --export 0x7A6AC285E2D98827 &amp;gt; pubkey-tyil.asc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;--armor&lt;/code&gt; option is used to export the key as base64, instead of binary
data.&lt;/p&gt;
&lt;p&gt;You can attach this file to an email, and let your email client encrypt the
entire email and all attachments for they key ID. How you can do this depends on
your email client, so you should research how to do this properly in the
documentation for it.&lt;/p&gt;
&lt;p&gt;However, it&amp;rsquo;s also possible to encrypt the public key file before adding it as
an attachment, in case you don&amp;rsquo;t know how to let your email client do it (or if
you don&amp;rsquo;t trust your email client to do it right).&lt;/p&gt;
&lt;p&gt;You can use the &lt;code&gt;--encrypt&lt;/code&gt; option for this, and add a &lt;code&gt;--recipient&lt;/code&gt; to encrypt
it for a specific key.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --encrypt --recipient 0x7A6AC285E2D98827 &amp;lt; pubkey-tyil.asc &amp;gt; pubkey-tyil.pgp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can use this encrypted key file and share it with the owner of the key.
If the person you send it to really is the owner of the key, they can use the
private key to decrypt the file, import it with &lt;code&gt;gpg --import&lt;/code&gt; and then publish
it with &lt;code&gt;gpg --send-keys&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;winding-down&#34;&gt;Winding down&lt;/h2&gt;
&lt;p&gt;Once all this is done, other people should have sent you your signed pubkey as
well, and you should have published your updated key with the new signatures.
Now you can start using PGP signatures and encryption for your communication
with the world. People who have not signed your key can see that there&amp;rsquo;s other
people that do trust your key, and they can use that information to deduce that
whatever&amp;rsquo;s signed with your key really came from you, and that anything they
encrypt with your public key can only be read by you.&lt;/p&gt;
&lt;p&gt;With this &lt;a href=&#34;https://en.wikipedia.org/wiki/Web_of_trust&#34;&gt;trust&lt;/a&gt;, you can make
communication and data exchange in general more secure.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hackerrank Solutions: Python 3 and Perl 6 (part 1)</title>
      <link>https://www.tyil.nl/post/2018/09/13/hackerrank-solutions-python-3-and-perl-6-part-1/</link>
      <pubDate>Thu, 13 Sep 2018 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2018/09/13/hackerrank-solutions-python-3-and-perl-6-part-1/</guid>
      <description>&lt;p&gt;I recently started at a new company, for which I will have to write Python 3
code. To make sure I still know how to do basic stuff in Python, I started to
work on some &lt;a href=&#34;https://www.hackerrank.com/&#34;&gt;Hackerrank challenges&lt;/a&gt;. In this post,
I will show solutions to some challenges to show the differences. I hope that I
can show that Perl doesn&amp;rsquo;t have to be the &amp;ldquo;write only&amp;rdquo; language that many
people make it out to be.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		I am &lt;em&gt;much&lt;/em&gt; more proficient in the Perl 6 programming language than in Python
(2 or 3), so I might not always use the most optimal solutions in the Python
variants. Suggestions are welcome via email, though I most likely won&amp;rsquo;t update
this post with better solutions. I ofcourse also welcome feedback on the Perl 6
solutions!
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;challenges&#34;&gt;Challenges&lt;/h2&gt;
&lt;p&gt;The challenges covered in this post are the &lt;a href=&#34;https://www.hackerrank.com/domains/algorithms?filters%5Bsubdomains%5D%5B%5D=warmup&#34;&gt;warmup
challenges&lt;/a&gt;
you are recommended to solve when you make a new account. The code around the
function I&amp;rsquo;m expected to solve won&amp;rsquo;t be included, as this should be irrelevant
(for now). Additionally, I may rename the sub to conform to
&lt;a href=&#34;https://en.wikipedia.org/wiki/Letter_case#Special_case_styles&#34;&gt;kebab-case&lt;/a&gt;, as
this is more readable (in my opinion), and allowed in Perl 6.&lt;/p&gt;
&lt;h3 id=&#34;solve-me-first&#34;&gt;Solve Me First&lt;/h3&gt;
&lt;p&gt;This challenge is just a very simple example to introduce how the site works.
It required me to make a simple &lt;code&gt;a + b&lt;/code&gt; function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;solveMeFirst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Perl 6 variant isn&amp;rsquo;t going to very different here.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;solve-me-first&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For those not familiar with Perl 6, the &lt;code&gt;$&lt;/code&gt; in front of the variable names is
called a &lt;a href=&#34;https://docs.perl6.org/language/glossary#index-entry-Sigil&#34;&gt;Sigil&lt;/a&gt;,
and it signals that the variable contains only a single value.&lt;/p&gt;
&lt;p&gt;You may have noticed that there&amp;rsquo;s also no &lt;code&gt;return&lt;/code&gt; in the Perl 6 variant of
this example. In Perl 6, the last statement in a block is also the implicit
return value (just like in Perl 5 or Ruby).&lt;/p&gt;
&lt;h3 id=&#34;simple-array-sum&#34;&gt;Simple Array Sum&lt;/h3&gt;
&lt;p&gt;For this challenge I had to write a function that would return the sum of a
list of values. Naturally, I wanted to use a &lt;code&gt;reduce&lt;/code&gt; function, but Python 3
does not support these. So I wrote it with a &lt;code&gt;for&lt;/code&gt; loop instead.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;simpleArraySum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Perl 6 does have a &lt;code&gt;reduce&lt;/code&gt; function, so I would use that to solve the problem
here.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;simple-array-sum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;reduce&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$b&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here you can see a different sigil for &lt;code&gt;@ar&lt;/code&gt;. The &lt;code&gt;@&lt;/code&gt; sigil denotes a list of
scalars in Perl 6. In most other languages this would simply be an array.&lt;/p&gt;
&lt;p&gt;This code can be written even shorter, however. Perl 6 has &lt;a href=&#34;https://docs.perl6.org/language/operators#index-entry-%5B%2B%5D_%28reduction_metaoperators%29&#34;&gt;reduction
meta-operators&lt;/a&gt;.
This allows you to put an operator between brackets, like &lt;code&gt;[+]&lt;/code&gt;, to apply a
certain operator as a reduce function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;simple-array-sum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;[+]&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		After publishing this post I have learned that both Python 3 and Perl 6 have a
&lt;code&gt;.sum&lt;/code&gt; function that can also be called on the array, simplifying the code in
both languages.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h3 id=&#34;compare-the-triplets&#34;&gt;Compare the Triplets&lt;/h3&gt;
&lt;p&gt;This challenge provides you with 2 lists of 3 elements each. The lists should
be compared to one another, and a &amp;ldquo;score&amp;rdquo; should be kept. For each index, if
the first list contains a larger number, the first list&amp;rsquo;s score must be
incremented. Similarly, if the second list contains a larger number on that
index, the second list&amp;rsquo;s score must be incremented. If the values are equal, do
nothing.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;compareTriplets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;scores&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;scores&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;scores&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scores&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I learned that Python 3 has no &lt;code&gt;++&lt;/code&gt; operator to increment a value by 1, so I
had to use &lt;code&gt;+= 1&lt;/code&gt; instead.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;compare-triplets&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@scores&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;^&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;@scores&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]++&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;@scores&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]++&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@b&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In Perl 6, the &lt;code&gt;^3&lt;/code&gt; notation simply means a range from 0 to 3, non-inclusive,
so &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, meaning it will loop 3 times. The &lt;code&gt;$_&lt;/code&gt; is called the
&lt;em&gt;topic&lt;/em&gt;, and in a &lt;code&gt;for&lt;/code&gt; loop it is the current element of the iteration.&lt;/p&gt;
&lt;p&gt;Both of these loops could use a &lt;code&gt;continue&lt;/code&gt; (or &lt;code&gt;next&lt;/code&gt; in Perl 6) to skip the
second &lt;code&gt;if&lt;/code&gt; in case the first &lt;code&gt;if&lt;/code&gt; was true, but for readability I chose not
to.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		After publishing this post I learned that Python 3 also supports the inline if
syntax, just like Perl 6, so I could&amp;rsquo;ve used this in Python 3 as well.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h3 id=&#34;a-very-big-sum&#34;&gt;A Very Big Sum&lt;/h3&gt;
&lt;p&gt;In this challenge, you need to write the function body for &lt;code&gt;aVeryBigSum&lt;/code&gt;, which
gets an array of integers, and has to return the sum of this array. Both Python
3 and Perl 6 handle the large integers transparently for you, so I was able to
use the same code as I used for the simple array sum challenge.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;aVeryBigSum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And for Perl 6 using the &lt;code&gt;[+]&lt;/code&gt; reduce meta-operation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;a-very-big-sum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;[+]&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;plus-minus&#34;&gt;Plus Minus&lt;/h3&gt;
&lt;p&gt;The next challenge gives a list of numbers, and wants you to return the
fractions of its elements which are positive, negative or zero. The fractions
should be rounded down to 6 decimals. I made a counter just like in the
&lt;em&gt;Compare the Triplets&lt;/em&gt; challenge, and calculated the fractions and rounded them
at the end.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;plusMinus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;counters&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;counters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;counters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;counters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;counters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%.6f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the Perl 6 solution, I went for a &lt;code&gt;given/when&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; and the &lt;code&gt;fmt&lt;/code&gt;
function to format the fractions.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;plus-minus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@counters&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@arr&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;given&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@counters&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@counters&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@counters&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;@counters&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;%.6f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may notice a number of statements do not have a terminating &lt;code&gt;;&lt;/code&gt; at the end.
In Perl 6, this is not needed if it&amp;rsquo;s the last statement in a block (any code
surrounded by a &lt;code&gt;{&lt;/code&gt; and &lt;code&gt;}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;given/when&lt;/code&gt; construct is similar to a &lt;code&gt;switch/case&lt;/code&gt; found in other
languages (but not Python, sadly), but uses the &lt;a href=&#34;https://docs.perl6.org/language/operators#index-entry-smartmatch_operator&#34;&gt;Smartmatch
operator&lt;/a&gt;
implicitly to check if the statements given to &lt;code&gt;when&lt;/code&gt; are &lt;code&gt;True&lt;/code&gt;. The &lt;code&gt;*&lt;/code&gt; is the
&lt;a href=&#34;https://docs.perl6.org/type/Whatever&#34;&gt;Whatever operator&lt;/a&gt;, which in this case
will get the value of &lt;code&gt;$i&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Lastly, he &lt;code&gt;$_&lt;/code&gt; in the &lt;code&gt;map&lt;/code&gt; function is similar to inside a &lt;code&gt;for&lt;/code&gt; loop,
it&amp;rsquo;s the current element. Since the code given to &lt;code&gt;map&lt;/code&gt; is inside a block,
there&amp;rsquo;s no need for a &lt;code&gt;;&lt;/code&gt; after &lt;code&gt;say&lt;/code&gt; either.&lt;/p&gt;
&lt;h3 id=&#34;staircase&#34;&gt;Staircase&lt;/h3&gt;
&lt;p&gt;This challenge gives you an integer 𝓃, and you&amp;rsquo;re tasked with &amp;ldquo;drawing&amp;rdquo; a
staircase that is 𝓃 high, and 𝓃 wide at the base. The staircase must be made
using &lt;code&gt;#&lt;/code&gt; characters, and for the spacing you must use regular spaces.&lt;/p&gt;
&lt;p&gt;It seems that in Python, you &lt;em&gt;must&lt;/em&gt; specify the &lt;code&gt;i in&lt;/code&gt; part oft the &lt;code&gt;for i in range&lt;/code&gt;. Since I don&amp;rsquo;t really care for the value, I assigned it to &lt;code&gt;_&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;staircase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In Perl 6, there&amp;rsquo;s also a &lt;code&gt;print&lt;/code&gt; function, which is like &lt;code&gt;say&lt;/code&gt;, but does not
append a &lt;code&gt;\n&lt;/code&gt; at the end of the string. The &lt;code&gt;for&lt;/code&gt; loop in Perl 6 allows for
just a range to operate as expected. The &lt;code&gt;..&lt;/code&gt; operator creates a range from the
left-hand side up to the right hand side, inclusive.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;staircase&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;^&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;mini-maxi-sum&#34;&gt;Mini-Maxi Sum&lt;/h3&gt;
&lt;p&gt;Here you will be given 5 integers, and have to calculate the minimum and
maximum values that can be calculated using only 4 of them.&lt;/p&gt;
&lt;p&gt;I sort the array, and iterate over the first 4 values to calculate the sum and
print it. I then do the same but sort it in reverse for the sum of the 4
highest values.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;miniMaxSum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Perl 6 has immutable lists, so calling &lt;code&gt;sort&lt;/code&gt; on them will return a new list
which has been sorted. I can call &lt;code&gt;reverse&lt;/code&gt; on that list to get the highest
number at the top instead. &lt;code&gt;head&lt;/code&gt; allows me to get the first 4 elements in a
functional way. You&amp;rsquo;ve already seen the meta-reduce operator &lt;code&gt;[+]&lt;/code&gt;, which will
get me the sum of the 4 elements I got from &lt;code&gt;head&lt;/code&gt;. I wrap the calculation in
parenthesis so I can call &lt;code&gt;print&lt;/code&gt; on the result immediately.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mini-maxi-sum&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[+]&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@arr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[+]&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@arr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;birthday-cake-candles&#34;&gt;Birthday Cake Candles&lt;/h3&gt;
&lt;p&gt;In this challenge, you&amp;rsquo;re given a list of numbers. You must find the highest
number in the list, and return how often that number occurs in the list.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s fairly straightforward, I keep track of the current largest value as
&lt;code&gt;size&lt;/code&gt;, and a &lt;code&gt;count&lt;/code&gt; that I reset whenever I find a larger value than I
currently have.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;birthdayCakeCandles&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Perl 6 variant does not differ in how it solves the problem, apart from
having a very different syntax of course.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;birthday-cake-candles&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@ar&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nv&#34;&gt;$count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nv&#34;&gt;$count&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		On IRC, someone showed me a clean solution in Python 3: &lt;code&gt;return ar.count(max(ar))&lt;/code&gt;. This feels like a much cleaner solution than what I had
created.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h3 id=&#34;time-conversion&#34;&gt;Time Conversion&lt;/h3&gt;
&lt;p&gt;This is the final challenge of this section on Hackerrank, and also this post.
You&amp;rsquo;re given a timestamp in 12-hour AM/PM format, and have to convert it to a
24-hour format.&lt;/p&gt;
&lt;p&gt;I split the AM/PM identifier from the actual time by treating the string as a
list of characters and taking two slices, one of the last two characters, and
one of everything &lt;em&gt;but&lt;/em&gt; the last two characters. Then I split the time into
parts, and convert the first part (hours) to integers for calculations. Next I
set the hours to 0 if it&amp;rsquo;s set to 12, and add 12 hours if the timestamp was
post meridiem. Finally, I convert the hours back to a string with leading
zeroes, and join all the parts together to form a timestamp again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python3&#34; data-lang=&#34;python3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;timeConversion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;meridiem&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;rest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;meridiem&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lower&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pm&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%02d&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%s&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hours&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Perl 6 solution again doesn&amp;rsquo;t differ much from the Python solution in terms
of the logic it&amp;rsquo;s using to get the result. The biggest difference is that in
Perl 6, strings can&amp;rsquo;t be accessed as lists, so I use the &lt;code&gt;substr&lt;/code&gt; method to
extract the parts that I want. The first one starts at &lt;code&gt;*-2&lt;/code&gt;, which means 2
places before the end. The others get a
&lt;a href=&#34;https://docs.perl6.org/type/Range&#34;&gt;&lt;code&gt;Range&lt;/code&gt;&lt;/a&gt; as argument, and will get the
characters that exist in that range.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;time-conversion&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$meridiem&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;substr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;substr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$rest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;substr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..*-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$hours&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$meridiem&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;lc&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;pm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;sprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;%02d:%s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$hours&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$rest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;.Int&lt;/code&gt; method converts the &lt;code&gt;Str&lt;/code&gt; object into an &lt;code&gt;Int&lt;/code&gt; object, so we can
perform calculations on it. The &lt;code&gt;eq&lt;/code&gt; operator checks specifically for &lt;a href=&#34;https://docs.perl6.org/routine/eq&#34;&gt;&lt;em&gt;string
equality&lt;/em&gt;&lt;/a&gt;. Since Perl 6 is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Gradual_typing&#34;&gt;gradually
typed programming language&lt;/a&gt;,
there&amp;rsquo;s a dedicated operator to ensure that you&amp;rsquo;re checking string equality
correctly.&lt;/p&gt;
&lt;h2 id=&#34;wrap-up&#34;&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;These challenges were just the warm-up challenges I was given after creating a
new account and choosing Python as a language to use. I intend to write up more
posts like this, for the near future I&amp;rsquo;ll stick to Python 3 challenges since I
want to get better at that specific language for work.&lt;/p&gt;
&lt;p&gt;This is also the first post in which I have tried this format to show off two
languages side-by-side, and to highlight differences in how you can accomplish
certain (relatively simple) tasks with them. If you have suggestions to improve
this format, do not hesitate to contact me. I am always open for feedback,
preferably via email. You can find my contact details on the &lt;a href=&#34;https://www.tyil.nl/&#34;&gt;homepage&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting up PGP with a Yubikey</title>
      <link>https://www.tyil.nl/post/2018/09/04/setting-up-pgp-with-a-yubikey/</link>
      <pubDate>Tue, 04 Sep 2018 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2018/09/04/setting-up-pgp-with-a-yubikey/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently started a job where I am required to have above-average security
practices in place on my machine. I already had some standard security in
place, such as full disk encryption and PGP encrypted email, but I thought that
this would be a good time to up my game. To accomplish this, I purchased a
Yubikey to act as my physical security token. Additionally, I have a USB device
which is also encrypted to hold backups of the keys.&lt;/p&gt;
&lt;p&gt;In this blogpost, I will detail how I set up my security policies in the hopes
it will be able to help out other people looking to improve their security, and
to get feedback to improve my set up as well.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		I am using the Yubikey 4. If you&amp;rsquo;re using another version, some steps may
differ.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;installing-required-software&#34;&gt;Installing required software&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll need some software to set all of this up. Depending on your
distribution, some of it might already be installed. Everything not installed
yet should be installed with your distribution&amp;rsquo;s package manager.&lt;/p&gt;
&lt;p&gt;For encrypting the disk and the USB key, you will need &lt;code&gt;cryptsetup&lt;/code&gt;. To
generate and use the PGP keys, you will need &lt;code&gt;gpg&lt;/code&gt;, at least version 2.0.12. To
interface with the Yubikey itself, you&amp;rsquo;ll need &lt;code&gt;pcsc-lite&lt;/code&gt;, and start the
service as well. It may be necessary to restart the &lt;code&gt;gpg-agent&lt;/code&gt; after
installing &lt;code&gt;pcsc-lite&lt;/code&gt;, which you can do by simply killing the existing
&lt;code&gt;gpg-agent&lt;/code&gt; process. It restarts itself when needed.&lt;/p&gt;
&lt;p&gt;To securely remove the temporary data we need, you should make sure you have
&lt;code&gt;secure-delete&lt;/code&gt; available on your system as well.&lt;/p&gt;
&lt;h2 id=&#34;personalizing-the-yubikey&#34;&gt;Personalizing the Yubikey&lt;/h2&gt;
&lt;p&gt;The Yubikey can be personalized. Some of this personalization is completely
optional, such as setting personal information. However, setting new PIN codes
is strongly advised, as the default values are publicly known.&lt;/p&gt;
&lt;h3 id=&#34;pin-codes&#34;&gt;PIN codes&lt;/h3&gt;
&lt;p&gt;The PIN codes are short combinations of numbers, letters and symbols to grant
permission to write to or retrieve data from the Yubikey. The default value for
the user PIN is &lt;code&gt;123456&lt;/code&gt;. The admin PIN is &lt;code&gt;12345678&lt;/code&gt; by default. These should
be changed, as they&amp;rsquo;re publicly known and allow the usage of your private keys.
To change these, use the &lt;code&gt;gpg&lt;/code&gt; program and enter admin mode:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --card-edit
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg/card&amp;gt; admin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Admin commands are allowed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll notice it immediately says that admin commands are now allowed to be
used. The admin PIN (&lt;code&gt;12345678&lt;/code&gt;) will be asked whenever an admin command is
executed. It will then be stored for this session, so you won&amp;rsquo;t have to enter
it right away. To update the PIN values, run the following commands:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg/card&amp;gt; passwd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg/card&amp;gt; 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will change the admin PIN first. This PIN is required for managing the
keys and user PIN on the Yubikey. To set the user PIN, pick &lt;code&gt;1&lt;/code&gt; instead of &lt;code&gt;3&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg/card&amp;gt; 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once this is done, you can quit the &lt;code&gt;passwd&lt;/code&gt; submenu using &lt;code&gt;q&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg/card&amp;gt; q
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may have noticed we skipped the reset code. Resetting the device will wipe
existing keys, so it&amp;rsquo;s not a serious risk to keep this at the default. The
private keys will be backed up to an encrypted USB drive, so we can always
retrieve them and put them back on the Yubikey if ever needed.&lt;/p&gt;
&lt;h3 id=&#34;personal-information&#34;&gt;Personal information&lt;/h3&gt;
&lt;p&gt;The personal information is optional, but could be used by a friendly person to
find out who a found Yubikey belongs to. They can contact the owner, and send
the key back. You can set as many of the personally identifying fields as you
want. If you&amp;rsquo;re interested in setting this information, plug in your Yubikey
and edit the card information with &lt;code&gt;gpg&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --card-edit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you&amp;rsquo;re back in the GPG shell, you can update your personal information.
There are 5 attributes that you can set in this way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;, which is your real name;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lang&lt;/code&gt;, which is your preferred contact language;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sex&lt;/code&gt;, which is your real sex;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;url&lt;/code&gt;, which indicates a location to retrieve your public key from;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;login&lt;/code&gt;, which indicates your email address.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these attributes can be updated by running the command in the GPG
shell. For instance, to update your real name, run the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg/card&amp;gt; name
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You do not need to explicitly save once you&amp;rsquo;re done. You can run &lt;code&gt;quit&lt;/code&gt; to quit
the GPG shell and return to your regular shell.&lt;/p&gt;
&lt;h2 id=&#34;creating-pgp-keys&#34;&gt;Creating PGP keys&lt;/h2&gt;
&lt;p&gt;To create the PGP keys, we&amp;rsquo;ll create a temporary directory which will function
as our working directory to store the keys in. This way you can&amp;rsquo;t accidentally
break existing keys if you have them, and ensure that the private keys don&amp;rsquo;t
accidentally linger on in your filesystem.&lt;/p&gt;
&lt;h3 id=&#34;preparing-a-clean-environment&#34;&gt;Preparing a clean environment&lt;/h3&gt;
&lt;p&gt;To create such a temporary directory, we&amp;rsquo;ll use &lt;code&gt;mktemp&lt;/code&gt;, and store the result
in an environment variable so we can easily re-use it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;GNUPGHOME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;mktemp -d&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can switch to that directory using &lt;code&gt;cd &amp;quot;$GNUPGHOME&amp;quot;&lt;/code&gt;. Additionally,
&lt;code&gt;$GNUPGHOME&lt;/code&gt; is also the directory &lt;code&gt;gpg&lt;/code&gt; uses as its working directory, if it
is set. This means you can use a temporary custom configuration for &lt;code&gt;gpg&lt;/code&gt; as
well, without it affecting your normal setup. The following configuration is
recommended to set in &lt;code&gt;$GNUPGHOME/gpg.conf&lt;/code&gt; before starting:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;use-agent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;charset&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;utf-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;no-comments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;keyid-format&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;0xlong&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;list-options&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;show-uid-validity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;verify-options&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;show-uid-validity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;with-fingerprint&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have a &lt;code&gt;gpg-agent&lt;/code&gt; running, it is recommended to stop it before
continuing with &lt;code&gt;killall gpg-agent&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;creating-the-master-key&#34;&gt;Creating the master key&lt;/h3&gt;
&lt;p&gt;For our master key, we&amp;rsquo;ll go for a 4096 bytes RSA key. 2048 would be plenty as
well, if you want the generation to be a tad quicker. &lt;code&gt;gpg&lt;/code&gt; will ask you a
couple questions to establish your identity, which is required for a PGP key.
You can add more identities later, in case you&amp;rsquo;re using multiple email
addresses, for instance.&lt;/p&gt;
&lt;p&gt;Start the key generation process with &lt;code&gt;gpg&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --full-generate-key
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When asked what kind of key you want, choose &lt;code&gt;4&lt;/code&gt; (RSA (sign only)). Next is the
key size, which should be &lt;code&gt;4096&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The key&amp;rsquo;s expiration is optional, though highly recommended. It will be more
effort to maintain the keys, as you&amp;rsquo;ll occasionally need the private master
keys to extend the validity, but you can also guarantee that your keys won&amp;rsquo;t
stay valid in case you ever lose them. If you don&amp;rsquo;t want to bother with
refreshing your keys from time to time, just press enter here to continue.&lt;/p&gt;
&lt;p&gt;When prompted on whether the data is correct, doublecheck whether the data is
really correct, and then enter &lt;code&gt;y&lt;/code&gt; and press enter to accept the current
values. &lt;code&gt;gpg&lt;/code&gt; will continue with your identity information, which you should
fill out with your real information. The comment field can be left empty, this
is an optional field to add a comment to your identity, such as &amp;ldquo;School&amp;rdquo;, or
&amp;ldquo;Work keys&amp;rdquo;. &lt;code&gt;gpg&lt;/code&gt; will ask your confirmation one final time. Enter an &lt;code&gt;o&lt;/code&gt;
(it&amp;rsquo;s not case sensitive) and press enter again. The final step before it will
generate a key is to enter a passphrase. This is technically optional, but
highly recommended. If anyone ever gets their hands on your private master key,
they will need the passphrase in order to use it. Adding one is yet another
layer against malicious use of your key.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve chosen a passphrase, it will generate they key and output some
information about the key. Verify whether this information is correct one more
time, and if it is, you can continue to the next step. If it is not, redo the
whole PGP section of this post.&lt;/p&gt;
&lt;p&gt;Take note of the line starting with &lt;code&gt;pub&lt;/code&gt;. It shows that the key is an
&lt;code&gt;rsa4096&lt;/code&gt; key, followed by a &lt;code&gt;/&lt;/code&gt;, and then the key ID. You&amp;rsquo;ll need this key ID
throughout the rest of this post. For convenience, you can store this ID in
a variable, and just refer to the variable when you need it&amp;rsquo;s value again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;KEYID&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0x27F53A16486878C7
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This post will use the &lt;code&gt;$KEYID&lt;/code&gt; variable from now on, to make it easier to
follow.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-revocation-certificate&#34;&gt;Creating a revocation certificate&lt;/h3&gt;
&lt;p&gt;The revocation certificate can be used to invalidate your newly created key.
You should store it seperately from the private master key, preferably printed
on a sheet of paper. If you want to be able to easily read it back in, consider
printing it as a QR code.&lt;/p&gt;
&lt;p&gt;To create the certificate, run the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --gen-revoke $KEYID &amp;gt; $GNUPGHOME/revoke.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will prompt you to specify a reason, for which you&amp;rsquo;ll want to use &lt;code&gt;1&lt;/code&gt;.
This way you can easily revoke the key&amp;rsquo;s validity if you ever lose it. If you
want to revoke your keys in the future for any other reason, you can always
generate a new revocation certificate for that specific purpose. You don&amp;rsquo;t have
to supply an additional description, so just hit enter. A revocation
certificate will be written to &lt;code&gt;$GNUPGHOME/revoke.txt&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;creating-the-subkeys&#34;&gt;Creating the subkeys&lt;/h3&gt;
&lt;p&gt;Now that you have your master key and the ability to revoke it in case anything
goes wrong in the future, it&amp;rsquo;s time to create a couple of subkeys which can be
stored on the Yubikey, and used in your daily life. We&amp;rsquo;ll create seperate keys
for &lt;em&gt;encryption&lt;/em&gt;, &lt;em&gt;signing&lt;/em&gt; and &lt;em&gt;authentication&lt;/em&gt;, and store each of them in
their own dedicated slot on the Yubikey.&lt;/p&gt;
&lt;p&gt;To add subkeys to your master key, enter a GPG shell to edit your existing
key with &lt;code&gt;gpg --expert --edit-key $KEYID&lt;/code&gt;. The &lt;code&gt;--expert&lt;/code&gt; is required to show
all the options we&amp;rsquo;re going to need. Once the GPG shell has started, run
&lt;code&gt;addkey&lt;/code&gt; to add a new key.&lt;/p&gt;
&lt;p&gt;Just like with the master key, a number of questions will be asked. Expiration
for subkeys is generally not advised, as the subkeys will be considered invalid
whenever the master key has expired. The key sizes for the subkeys can be left
at 2048 as well, which is also the maximum size for keys for the older Yubikey
models. The key type is different for all 3 subkeys.&lt;/p&gt;
&lt;p&gt;You will want to select type &lt;code&gt;4&lt;/code&gt; (RSA (sign only)) for your signing key, type
&lt;code&gt;6&lt;/code&gt; (RSA (encrypt only)) for the encryption key, and type &lt;code&gt;8&lt;/code&gt; (RSA (set your
own capabilities)) for the authentication key. With the final key, it will ask
you what capabilities you want to enable. The only capability you want it to
have is &lt;em&gt;Authentication&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve created the subkeys, you can check &lt;code&gt;gpg --list-secret-keys&lt;/code&gt; to look
at your newly created keys. You should have 1 &lt;code&gt;sec&lt;/code&gt; key, which is the master
key, and 3 &lt;code&gt;ssb&lt;/code&gt; keys, which are the subkeys. One line should end with &lt;code&gt;[S]&lt;/code&gt;,
one with &lt;code&gt;[E]&lt;/code&gt; and one with &lt;code&gt;[A]&lt;/code&gt;. These denote the capabilities of the
subkeys, &lt;em&gt;Sign&lt;/em&gt;, &lt;em&gt;Encrypt&lt;/em&gt; and &lt;em&gt;Authenticate&lt;/em&gt;, respectively.&lt;/p&gt;
&lt;h3 id=&#34;export-the-keys&#34;&gt;Export the keys&lt;/h3&gt;
&lt;p&gt;Now that you have your keys generated, you should export them, allowing you to
easily import them in another environment in case you ever need to generate
more keys, invalidate some keys, or extend the validity of the keys in case you
set an expiry date. This can be done with the following commands:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --armor --export-secret-keys $KEYID &amp;gt; masterkey.asc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --armor --export-secret-subkeys $KEYID &amp;gt; subkeys.asc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;creating-a-backup-usb&#34;&gt;Creating a backup USB&lt;/h2&gt;
&lt;p&gt;For the backup of the private keys, I&amp;rsquo;m using an encrypted USB device. You can
also opt to print the keys to paper, and retype them if you ever need them. Or
print a QR code that you can scan. But for convenience sake, I went with a USB
device. I encrypted it, and stored it in a safe and sealed location, so it&amp;rsquo;s
easy to detect unwanted attempted access.&lt;/p&gt;
&lt;h3 id=&#34;encrypting-the-usb&#34;&gt;Encrypting the USB&lt;/h3&gt;
&lt;p&gt;For the encryption, I went with full device encryption using LUKS. You will
need the &lt;code&gt;cryptsetup&lt;/code&gt; utility to apply the encryption, and to unlock the drive.
You can find out the device name from &lt;code&gt;dmesg&lt;/code&gt; or &lt;code&gt;lsblk&lt;/code&gt;. Once you know it,
encrypt the drive with the &lt;code&gt;luksFormat&lt;/code&gt; subcommand.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		Using the wrong name for the device can irrecoverably destroy data from another
drive!
	&lt;/div&gt;
&lt;/section&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cryptsetup luksFormat /dev/sdb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It will prompt you whether you want to continue, and ask twice for a passphrase
to ensure it is correct. Make sure you don&amp;rsquo;t forget the passphrase, or you&amp;rsquo;ll
lose access to your backup keys.&lt;/p&gt;
&lt;p&gt;Once it has been encrypted, unlock the device.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cryptsetup luksOpen /dev/sdb crypt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will open the device as &lt;code&gt;/dev/mapper/crypt&lt;/code&gt;. Format it with your favourite
filesystem. I used &lt;code&gt;ext4&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkfs.ext4 /dev/mapper/crypt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once it has been formatted, you can mount it as a regular device.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mount /dev/mapper/crypt /mnt/usb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;copying-the-keys&#34;&gt;Copying the keys&lt;/h3&gt;
&lt;p&gt;Copying the keys is as straightforward as copying other files. You can use
&lt;code&gt;$GNUPGHOME&lt;/code&gt; to target the source directory.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp -arv &amp;#34;$GNUPGHOME&amp;#34;/* /mnt/usb/.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the files are copied, you can unmount the drive, lock it and unplug the
USB.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sync
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;umount /mnt/usb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cryptsetup luksClose crypt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Store the USB in a safe location, because these private keys can give someone
full control of your identity.&lt;/p&gt;
&lt;h2 id=&#34;storing-the-private-keys-on-the-yubikey&#34;&gt;Storing the private keys on the Yubikey&lt;/h2&gt;
&lt;p&gt;The Yubikey has key slots for encryption, signing and authentication.  These
need to be set individually, which can be done using &lt;code&gt;gpg&lt;/code&gt;. First, you need to
select a key using the &lt;code&gt;key&lt;/code&gt; command, then store it on the card using
&lt;code&gt;keytocard&lt;/code&gt; and select a slot to store it in, then finally deselect the key by
using the &lt;code&gt;key&lt;/code&gt; command again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --edit-key $KEYID
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; key 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; keytocard
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your selection? 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; key 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; key 2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; keytocard
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your selection? 2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; key 2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; key 3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; keytocard
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your selection? 3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg&amp;gt; save
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can verify whether the keys are available on the Yubikey now using &lt;code&gt;gpg --card-status&lt;/code&gt;. It will show the key fingerprints for the &lt;code&gt;Signature key&lt;/code&gt;,
&lt;code&gt;Encryption key&lt;/code&gt; and &lt;code&gt;Authentication key&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;sharing-your-public-key&#34;&gt;Sharing your public key&lt;/h3&gt;
&lt;p&gt;You can share your public keys in many ways. Mine is hosted &lt;a href=&#34;https://www.tyil.nl/pubkey.txt&#34;&gt;on my own
site&lt;/a&gt;, for instance. There are also &lt;a href=&#34;https://sks-keyservers.net/&#34;&gt;public
keyservers&lt;/a&gt; on which you can upload your keys.
&lt;code&gt;gpg&lt;/code&gt; has the &lt;code&gt;--send-keys&lt;/code&gt; and &lt;code&gt;--recv-keys&lt;/code&gt; switches to interact with these
public keyservers. For ease of use, I would recommend uploading them to a public
keyserver, so that other people can easily import it. For instance, my key can
be imported using &lt;code&gt;gpg&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gpg --recv-keys 0x7A6AC285E2D98827
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;clean-up&#34;&gt;Clean up&lt;/h2&gt;
&lt;p&gt;The keys are on the Yubikey, and you probably do not want to leave traces on
your local system of these new keys, so you should clean up the &lt;code&gt;$GNUPGHOME&lt;/code&gt;
directory. There&amp;rsquo;s a utility for securely removing a directory with all its
contents, called &lt;code&gt;secure-delete&lt;/code&gt;, which provides the &lt;code&gt;srm&lt;/code&gt; program. You can use
it just like the regular &lt;code&gt;rm&lt;/code&gt; on the temporary directory.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;srm -r &amp;#34;$GNUPGHOME&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also &lt;code&gt;unset&lt;/code&gt; the &lt;code&gt;$GNUPGHOME&lt;/code&gt; variable at this point, so &lt;code&gt;gpg&lt;/code&gt; will use
it&amp;rsquo;s default configuration again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;unset GNUPGHOME
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configure-gpg&#34;&gt;Configure GPG&lt;/h2&gt;
&lt;p&gt;Finally, you have your keys on the Yubikey and the traces that might have been
left on your device are wiped clean. Now you should configure &lt;code&gt;gpg&lt;/code&gt; for regular
use as well, however, this is completely optional. All this configuration does
is ensure you have good defaults for the current day and age.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;auto-key-locate&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;keyserver&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;keyserver&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;hkps&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;hkps.pool.sks-keyservers.net&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;keyserver-options&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;no-honor-keyserver-url&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;personal-cipher-preferences&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;AES256&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;AES192&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;AES&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAST5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;personal-digest-preferences&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA512&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA384&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA256&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA224&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;default-preference-list&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA512&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA384&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA256&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA224&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;AES256&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;AES192&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;AES&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAST5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;ZLIB&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;BZIP2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ZIP&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;Uncompressed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cert-digest-algo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA512&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;s2k-cipher-algo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;AES256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;s2k-digest-algo&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;SHA512&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;charset&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;utf-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fixed-list-mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;no-comments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;no-emit-version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;keyid-format&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;0xlong&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;list-options&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;show-uid-validity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;verify-options&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;show-uid-validity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;with-fingerprint&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;use-agent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;require-cross-certification&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You now have PGP keys available on your Yubikey. These keys are only available
to your system if the Yubikey is inserted, and the user PIN is given. You can
use these keys for authentication, signing and encrypting/decrypting messages.
In a future post, I&amp;rsquo;ll detail how to set up a number of services to use these
keys as well.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Perl Conference in Glasgow</title>
      <link>https://www.tyil.nl/post/2018/08/23/the-perl-conference-in-glasgow/</link>
      <pubDate>Thu, 23 Aug 2018 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2018/08/23/the-perl-conference-in-glasgow/</guid>
      <description>&lt;p&gt;This year the European Perl Conference was hosted in Glasgow, and of course
I&amp;rsquo;ve attended a number of presentations there. On some of these, I have some
feedback or comments. These talks, and the feedback I have for them, are
detailed in this blog post.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		The first talk I cover is not so much about Perl, but more about politics, as
the talk was mostly about the speaker&amp;rsquo;s ideology. If this does not interest you,
I&amp;rsquo;d suggest you skip the &lt;a href=&#34;#discourse-without-drama&#34;&gt;Discourse Without Drama&lt;/a&gt;
section, and head straight to the &lt;a href=&#34;#european-perl-mongers-organiser-s-forum-2018&#34;&gt;European Perl Mongers Organiser&amp;rsquo;s Forum
2018&lt;/a&gt;.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;discourse-without-drama&#34;&gt;Discourse Without Drama&lt;/h2&gt;
&lt;p&gt;This was the first talk, and the only talk available at this timeslot. I am
personally very much against the diversity ideology, and must admit I am
skeptical of such presentations from the get-go. Nonetheless, I did stay until
the end and tried to give it a fair shot.  However, I cannot sit idle while she
tries to force her ideology on this community I care very deeply about.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		I am not against the concept of diversity, I wholly support the idea of equal
opportunities. What I do not accept is the idea of equal outcome, or forced
diversity based on physical traits. This is what I refer to with &amp;ldquo;the diversity
ideology&amp;rdquo;. I also don&amp;rsquo;t think anyone has a right not to be offended, as this is
impossible to achieve in the real world.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;One of the things that stood out to me is that the speaker tells us not to use
logical fallacies to condemn her ideology. This on itself I can easily agree
with. However, this should go both ways: we should also not use logical
fallacies to promote her ideology. Most notably, she pointed out the
&lt;a href=&#34;https://en.wikipedia.org/wiki/Argumentum_ad_populum&#34;&gt;&lt;em&gt;argumentum ad populum&lt;/em&gt;&lt;/a&gt;.
This basically means that just because a lot of people do or say something,
doesn&amp;rsquo;t make it right. And this applies to the idea that we need to push the
diversity ideology in the Perl community as well. Try to bring facts and
statistics to show that this ideology will actually improve the community in
the long term. I&amp;rsquo;ve personally not seen any community improve with increasingly
harsh punishments for increasingly minor offenses.&lt;/p&gt;
&lt;p&gt;Another thing which slightly bothered me is the useless spin into radical
feminist ideology, which to me seems very off-topic for a Perl conference.
We&amp;rsquo;re not at a political rally, and these kinds of remarks have been very
divisive in all sorts of other environments already. I&amp;rsquo;d rather not bring this
kind of behaviour to a community which I have loved for being so incredibly
friendly without needing special rules and regulations for it.&lt;/p&gt;
&lt;p&gt;Next, a point is raised that people should &lt;em&gt;not&lt;/em&gt; grow a thicker skin. Instead,
people should get softer hearts. While I can get behind the latter part, I
have to disagree with the former. Reality shows that communications don&amp;rsquo;t
always go perfectly. This is even more true in a community that exists mostly
in the digital space. Context is often lost here, and that can lead to
situations where someone may feel attacked even if this was not the intention
at all. I can safely say I&amp;rsquo;ve been in many situations where my comments were
perceived as an attack when they were not ment to be.&lt;/p&gt;
&lt;p&gt;People need to be able to handle some criticism, and sometimes you&amp;rsquo;ll just have
to assume good faith from the other party. Telling people they should never
have to consider context and have a right not to be offended fosters an
environment in which people will be afraid to give genuine, valid feedback.&lt;/p&gt;
&lt;p&gt;She seemed very much in favour of an overly broad code of conduct as well, of
which I am also a strong opponent. There are various articles online, such as
&lt;a href=&#34;https://shiromarieke.github.io/coc.html&#34;&gt;this one&lt;/a&gt;, which show that just
slapping a generic, vague code of conduct to a community isn&amp;rsquo;t going to solve
the issue of trolls or harmful behaviour. There&amp;rsquo;s &lt;a href=&#34;http://quillette.com/2017/07/18/neurodiversity-case-free-speech/&#34;&gt;another great
article&lt;/a&gt; that
I was pointed towards that highlight how this attempt to censor people for the
sake of not offending anyone can effectively halt creativity and the exchange of
ideas. There was also an interesting quote written on one of the walls of the
venue:&lt;/p&gt;
&lt;div class=&#34;quoteblock&#34;&gt;
	&lt;blockquote&gt;
		&lt;div class=&#34;paragraph&#34;&gt;
			Aspire not to have more, but to be more&amp;hellip;
		&lt;/div&gt;
	&lt;/blockquote&gt;
	&lt;div class=&#34;attribution&#34;&gt;
		— Oscar Romero
	&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Don&amp;rsquo;t try to add meaningless documents such as a code of conduct, which more
often than not hurts a community instead of improving it. Try to be a better
person that tries to solve actual issues without harming the community at large.
Be the adult in the conversation that can take an insult, and still be kind.
&lt;a href=&#34;https://rakudo.party/post/On-Troll-Hugging-Hole-Digging-and-Improving-Open-Source-Communities#hug2:feedthehandthatbitesyou&#34;&gt;Remember to hug the
trolls&lt;/a&gt;,
and eventually they will hug you back.&lt;/p&gt;
&lt;h2 id=&#34;european-perl-mongers-organisers-forum-2018&#34;&gt;European Perl Mongers Organiser&amp;rsquo;s Forum 2018&lt;/h2&gt;
&lt;p&gt;The Perl community isn&amp;rsquo;t big nowadays, however, the Perl 6 language also offers
a lot of concepts which are very well suited for modern programming.  Sadly, if
no new users try out the language, it will be all for nothing. As such, we need
to bring new blood in to the community.&lt;/p&gt;
&lt;p&gt;One of the ways of doing this is by extending our promoting efforts outside of
the Perl community. Most people who like Perl are in a social bubble with other
people that are also familiar with the Perl programming language, be it 5 or 6.
But we need to reach new people as well, who will most likely be outside of
this social bubble. These people don&amp;rsquo;t have to be techies either, they might
just as well be marketeers or designers.&lt;/p&gt;
&lt;p&gt;I myself am part of the &amp;ldquo;techies&amp;rdquo;, so I&amp;rsquo;ll stick to this particular group for
now. And I know people like me can be found at meetups, so it would be
worthwhile to promote Perl at meetups which are not dedicated to Perl. Think of
more generic programming meetups, or GNU+Linux User Groups. We have to be
mindful not to be too pushy, though. Listen to other people, and try to
understand the problem they&amp;rsquo;re facing. Most of them will not be open to using a
different language immediately, especially not Perl (which sadly has a
particularly bad standing amongst people unfamiliar with it). Try to assist
them with their issues, and slowly introduce them to Perl (6) if it helps to
showcase what you mean. It might also be interesting to show people examples on
how to solve certain issues before telling them the language&amp;rsquo;s name, so they
don&amp;rsquo;t have a negative preconception solely from the name.&lt;/p&gt;
&lt;p&gt;Another thing to note is that Perl is more than just a programming language.
It&amp;rsquo;s a community, and a large library of modules, known as CPAN. And CPAN
offers some nifty tools, such as the CPAN testers, which help ensure module
developers that their code runs on a massive set of platforms and Perl
versions.&lt;/p&gt;
&lt;p&gt;This has led me to consider the creation of a new Perl 6 module:
&lt;code&gt;CPAN::Tester&lt;/code&gt;, to make it easy for people to contribute to a large-scale
testing environment for Perl 6. The idea is that one can run &lt;code&gt;CPAN::Tester&lt;/code&gt; on
their machine, which will keep track of new Perl 6 modules being uploaded to
CPAN. The results are to be sent to another server (or multiple servers), which
can aggregate the data and show a matrix of test results. This aggregating
server could also be built as a Perl 6 module, possibly named
&lt;code&gt;CPAN::Tester::ResultsServer&lt;/code&gt;. This would make setting up an environment
similar to CPAN testers for Perl 5 quite easy for Perl 6.&lt;/p&gt;
&lt;h2 id=&#34;perl-6-in-real-life-work&#34;&gt;Perl 6 in Real Life $Work&lt;/h2&gt;
&lt;p&gt;The speaker shows the perfect use case for
&lt;a href=&#34;https://docs.perl6.org/language/grammars&#34;&gt;Perl 6 grammars&lt;/a&gt;, advanced yet
readable parsing of text and performing actions with the results. It&amp;rsquo;s an
interesting talk, showcasing some nifty grammar constructs. The best part of
this is that it actually runs in production, where it parses over 700 files,
consisting over 100,000 lines of code, in about 22 seconds (on his laptop).
This goes to show that Perl 6 is no longer &amp;ldquo;too slow to use in production&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;It might be interesting to run this application of grammars on every Perl 6
release to gather more information on the speed improvements of Perl 6, much
like Tux&amp;rsquo;s &lt;code&gt;Text::CSV&lt;/code&gt; runs.&lt;/p&gt;
&lt;h2 id=&#34;releasing-a-perl-6-module&#34;&gt;Releasing a Perl 6 Module&lt;/h2&gt;
&lt;p&gt;The speaker starts off with detailing the platform which most Perl 6 modules
use to host their code repository, GitHub. He also touched upon automated
testing using Travis and AppVeyor. It was good to show how to make use of
these, as automated testing oftentimes stops unintended bugs from reaching end
users. But, I personally prefer GitLab over GitHub, as they have much better
testing functionality, and they actually release their own platform as an open
source package. I&amp;rsquo;d like more GitLab love from the community and speakers as
well if possible. This would also make the speaker&amp;rsquo;s CI configuration simpler,
for which he currently uses a &lt;code&gt;.travis.yml&lt;/code&gt; file.  This requires him to build
Perl 6 from source every test run, wasting quite a lot of time.&lt;/p&gt;
&lt;p&gt;It was also noted that there&amp;rsquo;s a module to help you set up this module
skeleton, &lt;code&gt;mi6&lt;/code&gt;. The speaker also noted that it doesn&amp;rsquo;t seem to add much once
you know how a Perl 6 module is organized, and I tend to agree with this.
Actually, I made a module precisely because I agree with him here,
&lt;code&gt;App::Assixt&lt;/code&gt;. This module intends to smoothen the entire course of module
development, not just the creation of a skeleton file. It will take care of
keeping your &lt;code&gt;META6.json&lt;/code&gt; up to date, and ease uploading your module to CPAN as
well.&lt;/p&gt;
&lt;p&gt;Lastly, the speaker says the &lt;code&gt;META6.json&lt;/code&gt; documentation can be found in S22.
While this is technically correct, S22 is &lt;em&gt;not&lt;/em&gt; the implementation&amp;rsquo;s
documentation, this lives in the official Perl 6 documentation instead. S22
offers many additional information to be stored in the &lt;code&gt;META6.json&lt;/code&gt;, but using
these fields will actually break installation of your module through &lt;code&gt;zef&lt;/code&gt;,
rendering it unusable by others. I would strongly recommend people not to use
S22 when trying to figure out what they can or cannot do with their
&lt;code&gt;META6.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;how-to-become-cpan-contributor&#34;&gt;How to become CPAN contributor?&lt;/h2&gt;
&lt;p&gt;Submitting a pull request (or more correctly named, merge request) to a
repository is possibly the most straightforward way to help out other projects.
However, sometimes it will take a long time to get a response. The speaker
notes this can actually be on the scale of years. I have authored a number of
modules myself, and have been in the situation where I had not realized I got a
merge request from another person (same goes for issue reports). I would
recommend people who are not getting timely responses to their contributions to
contact the maintainer via other channels which are more suited for
communications. Think of email or IRC, for instance. You&amp;rsquo;ll generally have a
much better chance of getting a timely response from the author, and then you
can work out your contribution and see if you can get it merged into the main
project.&lt;/p&gt;
&lt;p&gt;The speaker also lists a couple of ways to get started with contributing to
modules. One thing I missed in particular was the Squashathons&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; for Perl 6.
These generally offer a good entry point to help out with the language&amp;rsquo;s
development and the ecosystem&amp;rsquo;s maintainance.&lt;/p&gt;
&lt;p&gt;Near the end, it was pointed out that it is a good idea to have a thick skin.
Even when it&amp;rsquo;s not intended, people can come accross as rude. This is in
opposition to the talking point of the speaker yesterday (&lt;em&gt;Discourse Without
Drama&lt;/em&gt;), but he does raise a good point here. People oftentimes don&amp;rsquo;t mean to
insult you, but context is easily lost in written communications. Try to stay
mature and professional, you can simply ask for clarification. If you feel the
person remains hostile towards you, walk away. There&amp;rsquo;s plenty of other projects
that would love your contributions!&lt;/p&gt;
&lt;h2 id=&#34;conference-organizers--european-perl-mongers-organisers-forum-2018-bof&#34;&gt;Conference Organizers &amp;amp; European Perl Mongers Organiser&amp;rsquo;s Forum 2018 BoF&lt;/h2&gt;
&lt;p&gt;Well, that&amp;rsquo;s certainly a mouthful for a heading, and it even contains an
abbreviation! This event was not a presentation, but a platform to exchange
ideas together.&lt;/p&gt;
&lt;p&gt;One of the items that were up for discussion was &lt;em&gt;A Conference Toolkit&lt;/em&gt;, or ACT
for short.  This is the platform used to organize Perl events, such as this
conference and Perl workshops throughout the world. However, ACT is dated.
They enabled HTTPS a short while ago, but it&amp;rsquo;s still not the default because
people don&amp;rsquo;t want to risk breaking the platform. I think this is enough of
an indication that it might be time to make something new to replace it.&lt;/p&gt;
&lt;p&gt;And I&amp;rsquo;m not alone in that sentiment, it seems. However, ACT is big and contains
a lot of data we don&amp;rsquo;t want to lose. It&amp;rsquo;s a massive undertaking to make a new
tool that works at least as well, and allows us to make use of the old data as
well. There is a Trello board available that lists all the features that would
be required to implement, so that&amp;rsquo;s a good start already. I think now it needs
a dedicated product owner with people contributing code, so a start can be
made. This does seem like a touchy subject, since I&amp;rsquo;m far from the first person
to want this. Many before me have tried and failed already.&lt;/p&gt;
&lt;p&gt;As such, I&amp;rsquo;d propose not making it a Perl centric tool. Make it a modular,
generic event organizing tool. Get a good database design that we can import
our old data into, so nothing is lost, but things can be converted to be more
useful for our current needs. This way, we can work in small steps, and maybe
even reach contributors from outside the regular Perl circles. This might even
bring in new partnerships (or sponsors) towards the Perl community.&lt;/p&gt;
&lt;p&gt;Personally, I&amp;rsquo;d like to see something like this to be written in Perl 6. This
way, it could also be used as a showcase project for the Perl 6 programming
language.&lt;/p&gt;
&lt;h2 id=&#34;writing-a-perl-6-module&#34;&gt;Writing a Perl 6 Module&lt;/h2&gt;
&lt;p&gt;Perl 6 has this very neat feature called
&lt;a href=&#34;https://docs.perl6.org/language/typesystem#index-entry-subset-subset&#34;&gt;subsets&lt;/a&gt;.
These can be used to make your own types with very little effort, which can
help tremendously to keep your code clean and concise. There are two arguments
I have in favour of subsets that the speaker did not touch upon.&lt;/p&gt;
&lt;p&gt;First off, using a subset instead of a &lt;code&gt;where&lt;/code&gt; clause in a sub or method
signature will bring much better error messages. If you use a &lt;code&gt;where&lt;/code&gt; in your
signature, and the check fails, you&amp;rsquo;ll get an error that there was no signature
that matched &lt;code&gt;where { ... }&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Secondly, if you want to use abstract methods, you can&amp;rsquo;t really use a &lt;code&gt;where&lt;/code&gt;.
&lt;a href=&#34;https://stackoverflow.com/questions/51570655/how-to-use-abstract-multi-methods-containing-a-where&#34;&gt;I&amp;rsquo;ev asked a question about this on Stack
Overflow&lt;/a&gt;,
which has the details as to why this doesn&amp;rsquo;t work the way you might expect.&lt;/p&gt;
&lt;p&gt;Next, there&amp;rsquo;s some cool things about operators in Perl 6. There are many of
these available by default, and it&amp;rsquo;s &lt;em&gt;very&lt;/em&gt; easy to add new ones yourself as
well. In fact, the &lt;code&gt;Math::Matrix&lt;/code&gt; module used throughout the presentation makes
some available as well. Thanks to the ease of adding operators in Perl 6, if
you have a &lt;code&gt;Math::Matrix $m&lt;/code&gt; in Perl 6, you can get the norm by writing &lt;code&gt;|| $m ||&lt;/code&gt;. This is the mathematically correct way to write this, making it easy to
understand for everyone using matrixes in their daily lives. If you&amp;rsquo;re a
mathematician, small things like these are great to have.&lt;/p&gt;
&lt;p&gt;I have some comments on the &lt;code&gt;Math::Matrix&lt;/code&gt; module itself as well, based on
slides shown in the presentiation. The first thing I noticed is that there&amp;rsquo;s a
&lt;code&gt;norm&lt;/code&gt; method using a &lt;code&gt;where&lt;/code&gt; clause when it&amp;rsquo;s not needed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;norm&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$which&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;eq&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;row-sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This can be written instead as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;norm&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&amp;#39;&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;row-sum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#39;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is shorter and clearer, and you&amp;rsquo;ll get better feedback from the compiler as
well. I &lt;a href=&#34;https://github.com/pierre-vigier/Perl6-Math-Matrix/pull/49&#34;&gt;submitted a pull request on the GitHub
repository&lt;/a&gt; in an
attempt to improve this, which got merged! The speaker was not aware it could be
done in this manner, so I&amp;rsquo;m proud I got to teach him something right after he
did his presentation.&lt;/p&gt;
&lt;h2 id=&#34;winding-down&#34;&gt;Winding down&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve had a great time at the Perl conference, spoke to many people with whom
I&amp;rsquo;ve had some great discussions. I got to meet and personally thank a number of
people who&amp;rsquo;ve helped me out over the past year as well.&lt;/p&gt;
&lt;p&gt;A big thank you to all the people who made this conference possible, and I hope
to see you all again in Riga!&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;A Squashathon is like a hackathon, except everyone in the world is
invited, and you can help out over the Internet, staying in your own home. Of
course, you can still meet up with other developers and make it a social
gathering in the real world as well!&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Sparrowdo - Getting Started</title>
      <link>https://www.tyil.nl/post/2018/05/07/sparrowdo-getting-started/</link>
      <pubDate>Mon, 07 May 2018 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2018/05/07/sparrowdo-getting-started/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/melezhik/sparrowdo&#34;&gt;Sparrowdo&lt;/a&gt; is a Perl 6 project to
facilitate automatic configuration of systems. There&amp;rsquo;s a
&lt;a href=&#34;https://sparrowhub.org/&#34;&gt;repository of useful modules&lt;/a&gt; to make specific cases
easier to work with, but the
&lt;a href=&#34;https://github.com/melezhik/sparrowdo/blob/master/core-dsl.md&#34;&gt;Core DLS&lt;/a&gt; can
already take care of many tasks. In this tutorial, I&amp;rsquo;ll guide you through
setting up Sparrowdo, bootstrapping it onto your local system, writing a task
and running it.&lt;/p&gt;
&lt;h2 id=&#34;install-sparrowdo&#34;&gt;Install Sparrowdo&lt;/h2&gt;
&lt;p&gt;Sparrowdo is a [Perl 6]http://perl6.org/) project, so you&amp;rsquo;ll need to have Perl
6 installed. We&amp;rsquo;ll also use the Perl 6 package manager
&lt;a href=&#34;https://github.com/ugexe/zef/&#34;&gt;zef&lt;/a&gt; to install Sparrowdo itself. Luckily for
us, there&amp;rsquo;s a stable distribution of Perl 6 with everything we need added to it,
called &lt;a href=&#34;https://rakudo.org/files&#34;&gt;Rakudo Star&lt;/a&gt;. And to make it easier for
GNU+Linux users, I wrote a tool to fetch the latest Rakudo Star release, compile
it and install it, called &lt;a href=&#34;https://github.com/Tyil/lonestar&#34;&gt;LoneStar&lt;/a&gt;. Since
this tutorial will aim at GNU+Linux users, I&amp;rsquo;ll use that to install Perl 6.&lt;/p&gt;
&lt;h3 id=&#34;installing-perl-6-with-lonestar&#34;&gt;Installing Perl 6 with LoneStar&lt;/h3&gt;
&lt;p&gt;LoneStar is a Bash application to download, compile and set up Perl 6. It&amp;rsquo;s a
standalone application, meaning you don&amp;rsquo;t have to install it to your system. You
can just run it from the source directory. First, we&amp;rsquo;ll have to get the source
directory, which we&amp;rsquo;ll do using &lt;code&gt;git&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p ~/.local/src
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/tyil/lonestar.git ~/.local/src/lonestar
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cd !$
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you have the LoneStar sources available in &lt;code&gt;~/.local/src/lonestar&lt;/code&gt;. You can
run the application using &lt;code&gt;./bin/lonestar&lt;/code&gt;. Running it, you&amp;rsquo;ll get some help
output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ./bin/lonestar
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lonestar - Installation manager for Rakudo Star
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Usage: lonestar &amp;lt;action&amp;gt; [arguments...]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Actions:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  help      [action]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  init      [version=latest]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  install   [version=latest]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  path      [version=latest]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  reinstall [version=latest]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  upgrade
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll be needing the &lt;code&gt;install&lt;/code&gt; action to get Perl 6 installed, and the &lt;code&gt;init&lt;/code&gt;
action to configure the &lt;code&gt;$PATH&lt;/code&gt; environment variable. Depending on your
hardware, &lt;code&gt;install&lt;/code&gt; may take a couple minutes as it will compile Rakudo Perl 6
and install some base modules. You might want to grab a drink during this
period.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ./bin/lonestar install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ eval $(./bin/lonestar init)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ perl6 -v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;This is Rakudo Star version 2018.04.1 built on MoarVM version 2018.04.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;implementing Perl 6.c.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		If there&amp;rsquo;s a newer version available of Rakudo Star, the version numbers given
by &lt;code&gt;perl6 -v&lt;/code&gt; will differ for you.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h3 id=&#34;installing-sparrowdo-with-zef&#34;&gt;Installing Sparrowdo with zef&lt;/h3&gt;
&lt;p&gt;Now that you have Perl 6 available and installed, you can continue on using
&lt;code&gt;zef&lt;/code&gt; to install Sparrowdo. &lt;code&gt;zef&lt;/code&gt; is bundled with Rakudo Star, so you don&amp;rsquo;t have
to do anything to get it working.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zef install Sparrowdo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will instruct &lt;code&gt;zef&lt;/code&gt; to install Sparrowdo and all its dependencies. This can
take a couple minutes, again depending on the hardware of your machine.&lt;/p&gt;
&lt;h2 id=&#34;bootstrapping-your-system&#34;&gt;Bootstrapping your system&lt;/h2&gt;
&lt;p&gt;The first step to working with Sparrowdo is bootstrapping the system you wish to
use it with. In this case, that&amp;rsquo;ll be the local system. There&amp;rsquo;s a &lt;code&gt;--bootstrap&lt;/code&gt;
option to do this automatically.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sparrowdo --bootstrap
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		tip
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		If you wish to bootstrap a remote system, you can use the &lt;code&gt;--host&lt;/code&gt; option to
specify the system. For example: &lt;code&gt;sparrowdo --host=192.168.1.2 --bootstrap&lt;/code&gt;.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;Now your system is ready to be configured automatically using Sparrowdo!&lt;/p&gt;
&lt;h2 id=&#34;sparrowfiles&#34;&gt;Sparrowfiles&lt;/h2&gt;
&lt;p&gt;Sparrowfiles are the files that describe the tasks Sparrow should execute to
get you the configuration you want. They are valid Perl 6 code, and call the
subroutines (or &lt;em&gt;sparrowtasks&lt;/em&gt;) that will handle the actual actions. By default,
when running &lt;code&gt;sparrowdo&lt;/code&gt;, it will look for a file named &lt;code&gt;sparrowfile&lt;/code&gt; in the
current directory.&lt;/p&gt;
&lt;p&gt;To make our sample, we&amp;rsquo;ll create a new directory to work in, so we have clean
directory that can be shared easily. You can also keep this directory under
version control, so you can distribute the &lt;code&gt;sparrowfile&lt;/code&gt; with all its templates.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		tip
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		If you just want to create an empty directory to test things in, without
&amp;ldquo;polluting&amp;rdquo; the rest of your system, just call &lt;code&gt;cd -- &amp;quot;$(mktemp -d)&amp;quot;&lt;/code&gt;. This will
create a temporary directory and change the working directory to there.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;I&amp;rsquo;ll be using &lt;code&gt;~/.local/sparrowdo/local-dns&lt;/code&gt; to work in, as I&amp;rsquo;ll be setting up a
local dns cache with &lt;a href=&#34;http://www.thekelleys.org.uk/dnsmasq/doc.html&#34;&gt;dnsmasq&lt;/a&gt;
for the sample code.&lt;/p&gt;
&lt;h3 id=&#34;writing-a-sparrowfile&#34;&gt;Writing a &lt;code&gt;sparrowfile&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;As noted in the previous paragraph, for the sake of a demo I&amp;rsquo;ll guide you
through creating a &lt;code&gt;sparrowfile&lt;/code&gt; to install and configure &lt;code&gt;dnsmasq&lt;/code&gt; as a local
DNS cache. Using your favourite &lt;code&gt;$EDITOR&lt;/code&gt;, write the following to &lt;code&gt;sparrowfile&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;package-install&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;dnsmasq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;directory&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/etc/dnsmasq.d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;file-create&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/etc/dnsmasq.conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;content&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;slurp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;dnsmasq.conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;file-create&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/etc/dnsmasq.d/resolv.conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;content&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;slurp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;resolv.conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;service-start&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;dnsmasq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This &lt;code&gt;sparrowfile&lt;/code&gt; will set up the following configuration for &lt;code&gt;dnsmasq&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install the &lt;code&gt;dnsmasq&lt;/code&gt; package&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;/etc/dnsmasq.d&lt;/code&gt; directory in which we&amp;rsquo;ll store configuration files
for &lt;code&gt;dnsmasq&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create the configuration files &lt;code&gt;dnsmasq.conf&lt;/code&gt; at &lt;code&gt;/etc/dnsmasq.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;resolv.conf&lt;/code&gt; in the &lt;code&gt;dnsmasq.d&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Start the &lt;code&gt;dnsmasq&lt;/code&gt; service&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The configuration files will be created based on the configuration files in the
current directory. So for this to work, you&amp;rsquo;ll need to also create the
appropriate configuration files. Let&amp;rsquo;s start off with the main &lt;code&gt;dnsmasq&lt;/code&gt;
configuration in &lt;code&gt;dnsmasq.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;listen-address&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;no-dhcp-interface&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;resolv-file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;etc&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;dnsmasq.d&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;resolv.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will make &lt;code&gt;dnsmasq&lt;/code&gt; listen on the loopback interface, so it&amp;rsquo;ll only be able
to be used by the local machine. Furthermore, DHCP functionality will be
disabled, and the upstream resolvers are read from &lt;code&gt;/etc/dnsmasq.d/resolv.conf&lt;/code&gt;.
The contents of that file are as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;37.235.1.174&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;nameserver&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;37.235.1.177&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These nameservers are part of the &lt;a href=&#34;https://freedns.zone/en/&#34;&gt;FreeDNS&lt;/a&gt; project.
You can of course use whatever other DNS provider you want to use as your
upstream servers. Now, for &lt;code&gt;dnsmasq&lt;/code&gt; to be used, you will also need to set your
machine&amp;rsquo;s DNS resolvers to point to the &lt;code&gt;dnsmasq&lt;/code&gt; service. This is defined in
&lt;code&gt;/etc/resolv.conf&lt;/code&gt;, so lets append the following to our &lt;code&gt;sparrowfile&lt;/code&gt; to set
that up.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-conf&#34; data-lang=&#34;conf&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;bash&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;chattr -i /etc/resolv.conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;file-delete&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/etc/resolv.conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;file-create&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/etc/resolv.conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;%(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;content&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;nameserver 127.0.0.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;bash&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;chattr +i /etc/resolv.conf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will remove the &amp;ldquo;immutable&amp;rdquo; attribute from &lt;code&gt;/etc/resolv.conf&lt;/code&gt; if it&amp;rsquo;s set.
Next it will remove the current &lt;code&gt;/etc/resolv.conf&lt;/code&gt; and write out a new one which
only refers to the local machine as DNS resolver. This is to ensure an existing
&lt;code&gt;/etc/resolv.conf&lt;/code&gt; gets recreated with the configuration we want. Finally, it
adds back the immutable attribute to the file, so other processes won&amp;rsquo;t
overwrite it.&lt;/p&gt;
&lt;h3 id=&#34;running-the-sparrowfile&#34;&gt;Running the &lt;code&gt;sparrowfile&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To run the &lt;code&gt;sparrowfile&lt;/code&gt; and get the setup you desire, run the &lt;code&gt;sparrowdo&lt;/code&gt;
command with &lt;code&gt;--local_mode&lt;/code&gt; and wait.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sparrowdo --local_mode
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		If you want to run this on a remote machine to configure that one instead, you
can use &lt;code&gt;--host=&amp;lt;ip&amp;gt;&lt;/code&gt; instead of &lt;code&gt;--local_mode&lt;/code&gt;.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;You can check whether it actually worked by inspecting the files in
&lt;code&gt;/etc/dnsmasq.d&lt;/code&gt; and your &lt;code&gt;/etc/resolv.conf&lt;/code&gt;. The easiest way to check their
contents would be by using &lt;code&gt;cat&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/dnsmasq.d/dnsmasq.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/dnsmasq.d/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;closing-words&#34;&gt;Closing words&lt;/h2&gt;
&lt;p&gt;You should now have a working local DNS setup, configured programmatically
through Sparrowdo. This allows you easily get it working on other machines as
well, and updates can be done in a much simpler fashion for all of them
together.&lt;/p&gt;
&lt;p&gt;If you have more interest in automating configuration with Sparrowdo, go check
their website, &lt;a href=&#34;https://sparrowdo.wordpress.com/&#34;&gt;https://sparrowdo.wordpress.com/&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Perl 6 - Introduction to application programming</title>
      <link>https://www.tyil.nl/post/2018/03/20/perl-6-introduction-to-application-programming/</link>
      <pubDate>Tue, 20 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2018/03/20/perl-6-introduction-to-application-programming/</guid>
      <description>&lt;p&gt;In this tutorial, I&amp;rsquo;ll be guiding you through creating a simple application in
Perl 6. If you don&amp;rsquo;t have Perl 6 installed yet, get the &lt;a href=&#34;http://rakudo.org/how-to-get-rakudo/&#34;&gt;Rakudo
Star&lt;/a&gt; distribution for your OS.
Alternatively, you can use the &lt;a href=&#34;https://hub.docker.com/_/rakudo-star/&#34;&gt;Docker
image&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The application itself will be a simple dice-roller. You give it a number of
dice to roll, and the number of sides the die has. We&amp;rsquo;ll start off by creating
it as a console application, then work to make it a GUI as well with the
&lt;code&gt;GTK::Simple&lt;/code&gt; module.&lt;/p&gt;
&lt;h2 id=&#34;preparation&#34;&gt;Preparation&lt;/h2&gt;
&lt;p&gt;First, you&amp;rsquo;ll want to install the libgtk headers. How to get these depends on
your distro of choice. For Debian-based systems, which includes Ubuntu and
derivatives, this command would be the following &lt;code&gt;apt&lt;/code&gt; invocation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ apt install libgtk-3-dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For other distros, please consult your documentation.&lt;/p&gt;
&lt;p&gt;To ease up module/application building, I&amp;rsquo;ll use &lt;code&gt;App::Assixt&lt;/code&gt;. This module
eases up on common tasks required for building other modules or applications.
So we&amp;rsquo;ll start by installing this module through &lt;code&gt;zef&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ zef install App::Assixt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		You may need to rehash your &lt;code&gt;$PATH&lt;/code&gt; as well, which can be done using &lt;code&gt;hash -r&lt;/code&gt;
on &lt;code&gt;bash&lt;/code&gt;, or &lt;code&gt;rehash&lt;/code&gt; for &lt;code&gt;zsh&lt;/code&gt;. For other shells, consult your manual.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;Next up, we can use &lt;code&gt;assixt&lt;/code&gt; to create the new skeleton of our application,
with the &lt;code&gt;new&lt;/code&gt; subcommand. This will ask for some user input, which will be
recorded in the &lt;code&gt;META6.json&lt;/code&gt;, a json-formatted file to keep track of meta
information about the module.  &lt;code&gt;assixt&lt;/code&gt; should take care of this file for you,
so you never need to actually deal with it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ assixt new
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;assixt-input&#34;&gt;assixt input&lt;/h3&gt;
&lt;p&gt;Since the &lt;code&gt;assixt new&lt;/code&gt; command requires some input, I&amp;rsquo;ll walk through these
options and explain how these would affect your eventual application.&lt;/p&gt;
&lt;h4 id=&#34;name-of-the-module&#34;&gt;Name of the module&lt;/h4&gt;
&lt;p&gt;This is the name given to the module. This will be used for the directory name,
which by default in &lt;code&gt;assixt&lt;/code&gt; will be &lt;code&gt;perl6-&lt;/code&gt; prepended to a lower-case version
of the module name. If you ever wish to make a module that is to be shared in
the Perl 6 ecosystem, this should be unique across the entire ecosystem.  If
you&amp;rsquo;re interested in some guidelines, the &lt;a href=&#34;https://pause.perl.org/pause/query?ACTION=pause_namingmodules&#34;&gt;PAUSE
guidelines&lt;/a&gt; seem
to apply pretty well to Perl 6 as well.&lt;/p&gt;
&lt;p&gt;For this application, we&amp;rsquo;ll use &lt;code&gt;Local::App::Dicer&lt;/code&gt;, but you can use whatever
name you&amp;rsquo;d prefer here.&lt;/p&gt;
&lt;h4 id=&#34;your-name&#34;&gt;Your name&lt;/h4&gt;
&lt;p&gt;Your name. This will be used as the author&amp;rsquo;s name in the &lt;code&gt;META6.json&lt;/code&gt;. It is
used to find out who made it, in order to report issues (or words of praise,
of course).&lt;/p&gt;
&lt;h4 id=&#34;your-email-address&#34;&gt;Your email address&lt;/h4&gt;
&lt;p&gt;Your email address. Like your name, it will be used in case someone has to
contact you in regards off the module.&lt;/p&gt;
&lt;h4 id=&#34;perl-6-version&#34;&gt;Perl 6 version&lt;/h4&gt;
&lt;p&gt;This defaults to &lt;code&gt;c&lt;/code&gt; right now, and you can just hit enter to accept it. In the
future, there will be a Perl 6.d available as well, in which case you can use
this to indicate you want to use the newer features introduced in 6.d. This is
not the case yet, so you just want to go with the default &lt;code&gt;c&lt;/code&gt; value here.&lt;/p&gt;
&lt;h4 id=&#34;module-description&#34;&gt;Module description&lt;/h4&gt;
&lt;p&gt;A short description of your module, preferably a single sentence. This is
useful to people wondering what the module is for, and module managers can show
to the user.&lt;/p&gt;
&lt;h4 id=&#34;license-key&#34;&gt;License key&lt;/h4&gt;
&lt;p&gt;This indicates the license under which your module is distributed. This
defaults to &lt;code&gt;GPL-3.0&lt;/code&gt;, which I strongly recommend to use. The de-facto
default seems to be &lt;code&gt;Artistic-2.0&lt;/code&gt;, which is also used for Perl 6 itself.&lt;/p&gt;
&lt;p&gt;This identifier is based on the &lt;a href=&#34;https://spdx.org/licenses/&#34;&gt;SPDZ license list&lt;/a&gt;.
Anything not mentioned in this list is not acceptable. #TODO Clarify why&lt;/p&gt;
&lt;h2 id=&#34;writing-your-first-test&#34;&gt;Writing your first test&lt;/h2&gt;
&lt;p&gt;With the creation of the directory structure and metadata being taken care of
by &lt;code&gt;assixt&lt;/code&gt;, we can now start on writing things. Tests are not mandatory, but
are a great tool for quickly checking if everything works. If you make larger
applications, it really helps not having to manually test anything. Another
benefit is that you can quickly see if your changes, or those of someone else,
break anything.&lt;/p&gt;
&lt;p&gt;Creating the base template for tests, &lt;code&gt;assixt&lt;/code&gt; can help you out again: &lt;code&gt;assixt touch&lt;/code&gt; can create templates in the right location, so you don&amp;rsquo;t have to deal
with it. In this case we want to create a test, which we&amp;rsquo;ll call &amp;ldquo;basic&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ assixt touch test basic
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create the file &lt;code&gt;t/basic.t&lt;/code&gt; in your module directory. Its contents
will look as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#! /usr/bin/env perl6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;done-testing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# vim: ft=perl6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The only test it has right now is &lt;code&gt;ok True&lt;/code&gt;, which will always pass testing. We
will change that line into something more usable for this application:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Local::App::Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;subtest&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Legal rolls&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nb&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;≤&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;≤&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Rolls between 1 and &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;subtest&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Illegal rolls&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;throws-like&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Zero is not accepted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;throws-like&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Negative rolls are not accepted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;throws-like&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Can&amp;#39;t roll half sides&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		&lt;p&gt;Perl 6 allows mathematical characters to make your code more concise, as with
the ≤ in the above block. If you use &lt;a href=&#34;http://www.vim.org/&#34;&gt;vim&lt;/a&gt;, you can make use
of the &lt;a href=&#34;https://github.com/vim-perl/vim-perl6&#34;&gt;vim-perl6&lt;/a&gt; plugin, which has an
option to change the longer, ascii-based ops (in this case &lt;code&gt;\&amp;lt;=&lt;/code&gt;) into the
shorter unicode based ops (in this case &lt;code&gt;≤&lt;/code&gt;). This specific feature requires
&lt;code&gt;let g:perl6_unicode_abbrevs = 1&lt;/code&gt; in your &lt;code&gt;vimrc&lt;/code&gt; to be enabled with
&lt;code&gt;vim-perl6&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If that&amp;rsquo;s not an option, you can use a
&lt;a href=&#34;https://en.wikipedia.org/wiki/Compose_key&#34;&gt;compose key&lt;/a&gt;. If that is not viable
either, you can also stick to using the ascii-based ops. Perl 6 supports both
of them.&lt;/p&gt;

	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;This will run 53 tests, split up in two
&lt;a href=&#34;https://docs.perl6.org/language/testing#Grouping_tests&#34;&gt;subtests&lt;/a&gt;. Subtests are
used to logically group your tests. In this case, the calls that are correct are
in one subtest, the calls that should be rejected are in another.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;plan&lt;/code&gt; keywords indicate how many tests should be run. This will help spot
errors in case your expectations were not matched. For more information on
testing, check out &lt;a href=&#34;https://docs.perl6.org/language/testing&#34;&gt;the Perl 6 docs on
testing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re making use of two test routines, &lt;code&gt;ok&lt;/code&gt; and &lt;code&gt;throws-like&lt;/code&gt;. &lt;code&gt;ok&lt;/code&gt; is a
simple test: if the given statement is truthy, the test succeeds. The other
one, &lt;code&gt;throws-like&lt;/code&gt;, might require some more explanation. The first argument it
expects is a code block, hence the &lt;code&gt;{ }&lt;/code&gt;. Inside this block, you can run any
code you want. In this case, we run code that we know shouldn&amp;rsquo;t work. The
second argument is the exception it should throw. The test succeeds if the
right exception is thrown. Both &lt;code&gt;ok&lt;/code&gt; and &lt;code&gt;throws-like&lt;/code&gt; accept a descriptive
string as optional last argument.&lt;/p&gt;
&lt;h3 id=&#34;running-the-tests&#34;&gt;Running the tests&lt;/h3&gt;
&lt;p&gt;A test is useless if you can&amp;rsquo;t easily run it. For this, the &lt;code&gt;prove&lt;/code&gt; utility
exists. You can use &lt;code&gt;assixt test&lt;/code&gt; to run these tests properly as well, saving
you from having to manually type out the full &lt;code&gt;prove&lt;/code&gt; command with options.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ assixt test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might notice the tests are currently failing, which is correct. The
&lt;code&gt;Local::App::Dicer&lt;/code&gt; module doesn&amp;rsquo;t exist yet to test against. We&amp;rsquo;ll be working
on that next.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		For those interested, the command run by &lt;code&gt;assixt test&lt;/code&gt; is &lt;code&gt;prove -e &amp;quot;perl6 -Ilib&amp;quot; t&lt;/code&gt;. This will include the &lt;code&gt;lib&lt;/code&gt; directory into the &lt;code&gt;PERL6PATH&lt;/code&gt; to be
able to access the libraries we&amp;rsquo;ll be making. The &lt;code&gt;t&lt;/code&gt; argument specifies the
directory containing the tests.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;creating-the-library&#34;&gt;Creating the library&lt;/h2&gt;
&lt;p&gt;Again, let&amp;rsquo;s start with a &lt;code&gt;assixt&lt;/code&gt; command to create the base template. This
time, instead of &lt;code&gt;touch test&lt;/code&gt;, we&amp;rsquo;ll use &lt;code&gt;touch lib&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ assixt touch unit Local::App::Dicer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will generate a template file at &lt;code&gt;lib/Local/App/Dicer.pm6&lt;/code&gt; which some
defaults set. The file will look like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#! /usr/bin/env false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;unit&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Local::App::Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first line is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Shebang_(Unix)&#34;&gt;shebang&lt;/a&gt;. It
informs the shell what to do when you try to run the file as an executable
program. In this case, it will run &lt;code&gt;false&lt;/code&gt;, which immediately exits with a
non-success code. This file needs to be run as a Perl 6 module file, and
running it as a standalone file is an error.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;use v6.c&lt;/code&gt; line indicates what version of Perl 6 should be used, and is
taken from the &lt;code&gt;META6.json&lt;/code&gt;, which was generated with &lt;code&gt;assixt new&lt;/code&gt;.  The last
line informs the name of this module, which is &lt;code&gt;Local::App::Dicer&lt;/code&gt;. Beneath
this, we can add subroutines, which can be exported. These can then be accessed
from other Perl 6 files that &lt;code&gt;use&lt;/code&gt; this module.&lt;/p&gt;
&lt;h3 id=&#34;creating-the-roll-subroutine&#34;&gt;Creating the &lt;code&gt;roll&lt;/code&gt; subroutine&lt;/h3&gt;
&lt;p&gt;Since we want to be able to &lt;code&gt;roll&lt;/code&gt; a die, we&amp;rsquo;ll create a subroutine to do
exactly that. Let&amp;rsquo;s start with the signature, which tells the compiler the name
of the subroutine, which arguments it accepts, their types and what type the
subroutine will return.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		tip
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		Perl 6 is gradually typed, so all type information is optional. The subroutine
arguments are optional too, but you will rarely want a subroutine that doesn&amp;rsquo;t
have an argument list.
	&lt;/div&gt;
&lt;/section&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;export&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break this down.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sub&lt;/code&gt; informs the compiler we&amp;rsquo;re going to create a subroutine.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;roll&lt;/code&gt; is the name of the subroutine we&amp;rsquo;re going to create.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$sides&lt;/code&gt; defines an argument used by the subroutine.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is export&lt;/code&gt; tells the compiler that this subroutine is to be exported. This
allows access to the subroutine to another program that imports this module
through a &lt;code&gt;use&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ $sides }&lt;/code&gt; is the subroutine body. In Perl 6, the last statement is also
the return value in a code block, thus this returns the value of $sides. A
closing &lt;code&gt;;&lt;/code&gt; is also not required for the last statement in a block.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you run &lt;code&gt;assixt test&lt;/code&gt; now, you can see it only fails 1/2 subtests:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# TODO: Add output of failing tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Something is going right, but not all of it yet. The 3 tests to check for
illegal rolls are still failing, because there&amp;rsquo;s no constraints on the input of
the subroutine.&lt;/p&gt;
&lt;h3 id=&#34;adding-constraints&#34;&gt;Adding constraints&lt;/h3&gt;
&lt;p&gt;The first constraint we&amp;rsquo;ll add is to limit the value of &lt;code&gt;$sides&lt;/code&gt; to an &lt;code&gt;Int:D&lt;/code&gt;.
The first part of this constraint is common in many languages, the &lt;code&gt;Int&lt;/code&gt; part.
The &lt;code&gt;:D&lt;/code&gt; requires the argument to be &lt;strong&gt;defined&lt;/strong&gt;. This forces an actual
existing instance of &lt;code&gt;Int&lt;/code&gt;, not a &lt;code&gt;Nil&lt;/code&gt; or undefined value.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;export&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Fractional input is no longer allowed, since an &lt;code&gt;Int&lt;/code&gt; is always a round number.
But an &lt;code&gt;Int&lt;/code&gt; is still allowed to be 0 or negative, which isn&amp;rsquo;t possible in a
dice roll. Nearly every language will make you solve these two cases in the
subroutine body. But in Perl 6, you can add another constraint in the signature
that checks for exactly that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;export&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;where&lt;/code&gt; part specifies additional constraints, in this case &lt;code&gt;$sides &amp;gt; 0&lt;/code&gt;.
So now, only round numbers larger than 0 are allowed. If you run &lt;code&gt;assixt test&lt;/code&gt;
again, you should see all tests passing, indicating that all illegal rolls are
now correctly disallowed.&lt;/p&gt;
&lt;h3 id=&#34;returning-a-random-number&#34;&gt;Returning a random number&lt;/h3&gt;
&lt;p&gt;So now that we can be sure that the input is always correct, we can start on
making the output more random. In Perl 6, you can take a number and call
&lt;code&gt;.rand&lt;/code&gt; on it, to get a random number between 0 and the value of the number you
called it on. This in turn can be rounded up to get a number ranging from 1 to
the value of the number you called &lt;code&gt;.rand&lt;/code&gt; on. These two method calls can also
be changed to yield concise code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;export&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;rand&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;ceiling&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s all we need from the library itself. Now we can start on making a usable
program out of it.&lt;/p&gt;
&lt;h2 id=&#34;adding-a-console-interface&#34;&gt;Adding a console interface&lt;/h2&gt;
&lt;p&gt;First off, a console interface. &lt;code&gt;assixt&lt;/code&gt; can &lt;code&gt;touch&lt;/code&gt; a starting point for an
executable script as well, using &lt;code&gt;assixt touch bin&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ assixt touch bin dicer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create the file &lt;code&gt;bin/dicer&lt;/code&gt; in your repository, with the following
template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#! /usr/bin/env perl6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The program will run the &lt;code&gt;MAIN&lt;/code&gt; sub by default. We want to slightly change this
&lt;code&gt;MAIN&lt;/code&gt; signature though, since we want to accept user input. And it just so
happens that you can specify the command line parameters in the &lt;code&gt;MAIN&lt;/code&gt;
signature in Perl 6. This lets us add constraints to the parameters and give
them better names with next to no effort. We want to accept two numbers, one
for the number of dice, and one for the number of sides per die:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we see the &lt;code&gt;where&lt;/code&gt; applying constraints again. If you try running this
program in its current state, you&amp;rsquo;ll have to run the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ perl6 -Ilib bin/dicer
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Usage:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  bin/dicer &amp;lt;dice&amp;gt; &amp;lt;sides&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will return a list of all possible ways to invoke the program. There&amp;rsquo;s one
slight problem right now. The usage description does not inform the user that
both arguments need to be larger than 0. We&amp;rsquo;ll take care of that in a moment.
First we&amp;rsquo;ll make this part work the way we want.&lt;/p&gt;
&lt;p&gt;To do that, let&amp;rsquo;s add a &lt;code&gt;use&lt;/code&gt; statement to our &lt;code&gt;lib&lt;/code&gt; directory, and call the
&lt;code&gt;roll&lt;/code&gt; function we created earlier. The &lt;code&gt;bin/dicer&lt;/code&gt; file will come to look as
follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#! /usr/bin/env perl6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Local::App::Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;×&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		Just like the &lt;code&gt;≤&lt;/code&gt; character, Perl 6 allows to use the proper multiplication
character &lt;code&gt;×&lt;/code&gt; (this is not the letter &lt;code&gt;x&lt;/code&gt;!). You can use the more widely known
&lt;code&gt;*&lt;/code&gt; for multiplication as well.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;If you run the program with the arguments &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;20&lt;/code&gt; now, you&amp;rsquo;ll get a random
number between 2 and 40, just like we expect:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ perl6 -Ilib bin/dicer 2 20
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;18
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;the-usage-output&#34;&gt;The usage output&lt;/h3&gt;
&lt;p&gt;Now, we still have the trouble of illegal number input not clearly telling
what&amp;rsquo;s wrong. We can do a neat trick with &lt;a href=&#34;https://docs.perl6.org/language/functions#index-entry-USAGE&#34;&gt;the &lt;code&gt;USAGE&lt;/code&gt;
sub&lt;/a&gt; to achieve
this. Perl 6 allows a subroutine with the name &lt;code&gt;USAGE&lt;/code&gt; to be defined, overriding
the default behaviour.&lt;/p&gt;
&lt;p&gt;Using this, we can generate a friendlier message informing the user what they
need to supply more clearly. The &lt;code&gt;USAGE&lt;/code&gt; sub would look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;USAGE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Dicer requires two positive, round numbers as arguments.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you run the program with incorrect parameters now, it will show the text
from the &lt;code&gt;USAGE&lt;/code&gt; subroutine. If the parameters are correct, it will run the
&lt;code&gt;MAIN&lt;/code&gt; subroutine.&lt;/p&gt;
&lt;p&gt;You now have a working console application in Perl 6!&lt;/p&gt;
&lt;h2 id=&#34;a-simple-gui&#34;&gt;a simple GUI&lt;/h2&gt;
&lt;p&gt;But that&amp;rsquo;s not all. Perl 6 has a module to create GUIs with the
&lt;a href=&#34;https://www.gtk.org/&#34;&gt;GTK library&lt;/a&gt; as well. For this, we&amp;rsquo;ll use the
&lt;a href=&#34;https://github.com/perl6/gtk-simple&#34;&gt;&lt;code&gt;GTK::Simple&lt;/code&gt;&lt;/a&gt; module.&lt;/p&gt;
&lt;p&gt;You can add this module as a dependency to the &lt;code&gt;Local::App::Dicer&lt;/code&gt; repository
with &lt;code&gt;assixt&lt;/code&gt; as well, using the &lt;code&gt;depend&lt;/code&gt; command. By default, this will also
install the dependency locally so you can use it immediately.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ assixt depend GTK::Simple
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;multi-subs&#34;&gt;Multi subs&lt;/h3&gt;
&lt;p&gt;Next, we could create another executable file and call it &lt;code&gt;dicer-gtk&lt;/code&gt;. However,
I can also use this moment to introduce
&lt;a href=&#34;https://docs.perl6.org/language/glossary#index-entry-multi-method&#34;&gt;multi-methods&lt;/a&gt;.
These are subs with the same name, but differing signatures. If a call to such a
sub could potentially match multiple signatures, the most specific one will be
used. We will add another &lt;code&gt;MAIN&lt;/code&gt; sub, which will be called when &lt;code&gt;bin/dicer&lt;/code&gt; is
called with the &lt;code&gt;--gtk&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;We should also update the &lt;code&gt;USAGE&lt;/code&gt; sub accordingly, of course. And while we&amp;rsquo;re
at it, let&amp;rsquo;s also include the &lt;code&gt;GTK::Simple&lt;/code&gt; and &lt;code&gt;GTK::Simple::App&lt;/code&gt; modules. The
first pulls in all the different GTK elements we will use later on, while the
latter pulls in the class for the base GTK application window.  The updated
&lt;code&gt;MAIN&lt;/code&gt;, &lt;code&gt;USAGE&lt;/code&gt; and &lt;code&gt;use&lt;/code&gt; parts will now look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Local::App::Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;GTK::Simple&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;GTK::Simple::App&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;multi&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;×&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;multi&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Bool:D&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$gtk&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$gtk&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;# TODO: Create the GTK version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;USAGE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Launch Dicer as a GUI with --gtk, or supply two positive, round numbers as arguments.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s a new thing in a signature header here as well, &lt;code&gt;:$gtk&lt;/code&gt;. The &lt;code&gt;:&lt;/code&gt; in
front of it makes it a named argument, instead of a positional one. When used
in a &lt;code&gt;MAIN&lt;/code&gt;, this will allow it to be used like a long-opt, thus as &lt;code&gt;--gtk&lt;/code&gt;.
Its use in general subroutine signatures is explained in the next chapter.&lt;/p&gt;
&lt;p&gt;Running the application with &lt;code&gt;--gtk&lt;/code&gt; gives no output now, because the body only
contains a comment. Let&amp;rsquo;s fix that.&lt;/p&gt;
&lt;h3 id=&#34;creating-the-window&#34;&gt;Creating the window&lt;/h3&gt;
&lt;p&gt;First off, we require a &lt;code&gt;GTK::Simple::App&lt;/code&gt; instance. This is the main window,
in which we&amp;rsquo;ll be able to put elements such as buttons, labels, and input
fields. We can create the &lt;code&gt;GTK::Simple::App&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::App&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This one line brings in some new Perl 6 syntax, namely the &lt;code&gt;.=&lt;/code&gt; operator.
There&amp;rsquo;s also the use of a named argument in a regular subroutine.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.=&lt;/code&gt; operator performs a method on the variable on the left. In our case,
it will call the &lt;code&gt;new&lt;/code&gt; subroutine, which creates a new instance of the
&lt;code&gt;GTK::Simple::App&lt;/code&gt; class. This is commonly referred to as the &lt;strong&gt;constructor&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The named argument list (&lt;code&gt;title =&amp;gt; &amp;quot;Dicer&amp;quot;&lt;/code&gt;) is another commonly used feature
in Perl 6. Any method can be given a non-positional, named parameter. This is
done by appending a &lt;code&gt;:&lt;/code&gt; in front of the variable name in the sub signature.
This has already been used in our code, in &lt;code&gt;multi sub MAIN(Bool :$gtk where $gtk == True)&lt;/code&gt;. This has a couple of benefits, which are explained in the
&lt;a href=&#34;https://docs.perl6.org/type/Signature#index-entry-positional_argument_%28Signature%29_named_argument_%28Signature%29&#34;&gt;Perl 6 docs on signatures&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;creating-the-elements&#34;&gt;Creating the elements&lt;/h3&gt;
&lt;p&gt;Next up, we can create the elements we&amp;rsquo;d like to have visible in our
application window. We needed two inputs for the console version, so we&amp;rsquo;ll
probably need two for the GUI version as well. Since we have two inputs, we
want labels for them. The roll itself will be performed on a button press.
Lastly, we will want another label to display the outcome. This brings us to 6
elements in total:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3 labels&lt;/li&gt;
&lt;li&gt;2 entries&lt;/li&gt;
&lt;li&gt;1 button&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Label&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Amount of dice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Label&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Dice value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Label&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Entry&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Entry&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Button&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$button-roll&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;label&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Roll!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates all elements we want to show to the user.&lt;/p&gt;
&lt;h3 id=&#34;show-the-elements-in-the-application-window&#34;&gt;Show the elements in the application window&lt;/h3&gt;
&lt;p&gt;Now that we have our elements, let&amp;rsquo;s put them into the application window.
We&amp;rsquo;ll need to put them into a layout as well. For this, we&amp;rsquo;ll use a grid. The
&lt;code&gt;GTK::Simple::Grid&lt;/code&gt; constructor takes pairs, with the key being a tuple
containing 4 elements, and the value containing the element you want to show.
The tuple&amp;rsquo;s elements are the &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;w&lt;/code&gt; and &lt;code&gt;h&lt;/code&gt;, which are the x
coordinates, y coordinates, width and height respectively.&lt;/p&gt;
&lt;p&gt;This in turn takes us to the following statement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;set-content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;GTK::Simple::Grid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$button-roll&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Put a &lt;code&gt;$app.run&lt;/code&gt; beneath that, and try running &lt;code&gt;perl6 -Ilib bin/dicer --gtk&lt;/code&gt;.
That should provide you with a GTK window with all the elements visible in the
position we want. To make it a little more appealing, we can add a
&lt;code&gt;border-width&lt;/code&gt; to the &lt;code&gt;$app&lt;/code&gt;, which adds a margin between the border of the
application window, and the grid inside the window.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;border-width&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may notice that there&amp;rsquo;s no &lt;code&gt;()&lt;/code&gt; after the &lt;code&gt;run&lt;/code&gt; method call. In Perl 6,
these are optional if you&amp;rsquo;re not supplying any arguments any way.&lt;/p&gt;
&lt;h3 id=&#34;binding-an-action-to-the-button&#34;&gt;Binding an action to the button&lt;/h3&gt;
&lt;p&gt;Now that we have a visible window, it&amp;rsquo;s time to make the button perform an
action. The action we want to execute is to take the values from the two
inputs, roll the correct number of dice with the correct number of sides, and
present it to the user.&lt;/p&gt;
&lt;p&gt;The base code for binding an action to a button is to call &lt;code&gt;.clicked.tap&lt;/code&gt; on it,
and provide it with a code block. This code will be executed whenever the
button is clicked.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$button-roll&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;clicked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;tap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You see we can also invoke a method using &lt;code&gt;:&lt;/code&gt;, and then supplying its
arguments. This saves you the trouble of having to add additional &lt;code&gt;( )&lt;/code&gt; around
the call, and in this case it would be annoying to have to deal with yet
another set of parens.&lt;/p&gt;
&lt;p&gt;Next, we give the code block something to actually perform:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$button-roll&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;clicked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;tap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;CATCH&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Can&amp;#39;t roll with those numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;×&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$entry-sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s some new things in this block of code, so let&amp;rsquo;s go over these.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CATCH&lt;/code&gt; is the block in which we&amp;rsquo;ll end up if an exception is thrown in this
scope. &lt;code&gt;roll&lt;/code&gt; will throw an exception if the parameters are wrong, and this
allows us to cleanly deal with that.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X::TypeCheck::Binding::Parameter.new.throw&lt;/code&gt; throws a new exception of type
&lt;code&gt;X::TypeCheck::Binding::Parameter&lt;/code&gt;. This is the same exception type as thrown
by &lt;code&gt;roll&lt;/code&gt; if something is wrong. We need to check the number of dice manually
here, since &lt;code&gt;roll&lt;/code&gt; doesn&amp;rsquo;t take care of it, nor does any signature impose any
restrictions on the value of the entry box.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;if&lt;/code&gt; behind another statement. This is something Perl 6 allows, and in some
circumstances can result in cleaner code. It&amp;rsquo;s used here because it improves
the readability of the code, and to show that it&amp;rsquo;s possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-completed-product&#34;&gt;The completed product&lt;/h2&gt;
&lt;p&gt;And with that, you should have a dice roller in Perl 6, with both a console and
GTK interface. Below you can find the complete, finished sourcefiles which you
should have by now.&lt;/p&gt;
&lt;h3 id=&#34;tbasict&#34;&gt;t/basic.t&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#! /usr/bin/env perl6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Local::App::Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;subtest&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Legal rolls&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nb&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;≤&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;≤&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Rolls between 1 and &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;subtest&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Illegal rolls&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;plan&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;throws-like&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Zero is not accepted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;throws-like&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Negative rolls are not accepted&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;throws-like&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;1.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Can&amp;#39;t roll half sides&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;done-testing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# vim: ft=perl6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;liblocalappdicerpm6&#34;&gt;lib/Local/App/Dicer.pm6&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#! /usr/bin/env false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;unit&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Local::App::Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;export&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;rand&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;ceiling&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;bindicer&#34;&gt;bin/dicer&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#! /usr/bin/env perl6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;.c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;Local::App::Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;GTK::Simple&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;GTK::Simple::App&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;multi&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Int:D&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;×&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$sides&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;multi&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Bool:D&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$gtk&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$gtk&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::App&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Dicer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Label&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Number of dice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Label&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Number of sides per die&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Label&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Entry&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Entry&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-sides&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GTK::Simple::Button&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$button-roll&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;.=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;label&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Roll!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;set-content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;n&#34;&gt;GTK::Simple::Grid&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$button-roll&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$button-roll&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;clicked&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;tap&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;k&#34;&gt;CATCH&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;			&lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Can&amp;#39;t roll with those numbers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;ne&#34;&gt;X::TypeCheck::Binding::Parameter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nv&#34;&gt;$label-result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$entry-dice&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;×&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;roll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$entry-sides&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;border-width&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$app&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;USAGE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;say&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;Launch Dicer as a GUI with --gtk, or supply two positive, round numbers as arguments.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;installing-your-module&#34;&gt;Installing your module&lt;/h2&gt;
&lt;p&gt;Now that you have a finished application, you probably want to install it as
well, so you can run it by calling &lt;code&gt;dicer&lt;/code&gt; in your shell. For this, we&amp;rsquo;ll be
using &lt;code&gt;zef&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To install a local module, tell &lt;code&gt;zef&lt;/code&gt; to try and install the local directory
you&amp;rsquo;re in:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ zef install .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will resolve the dependencies of the local module, and then install it.
You should now be able to run &lt;code&gt;dicer&lt;/code&gt; from anywhere.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		With most shells, you have to &amp;ldquo;rehash&amp;rdquo; your &lt;code&gt;$PATH&lt;/code&gt; as well. On &lt;code&gt;bash&lt;/code&gt;, this is
done with &lt;code&gt;hash -r&lt;/code&gt;, on &lt;code&gt;zsh&lt;/code&gt; it&amp;rsquo;s &lt;code&gt;rehash&lt;/code&gt;. If you&amp;rsquo;re using any other shell,
please consult the manual.
	&lt;/div&gt;
&lt;/section&gt;

</description>
    </item>
    
    <item>
      <title>Why Perl 6?</title>
      <link>https://www.tyil.nl/post/2018/02/05/why-perl-6/</link>
      <pubDate>Mon, 05 Feb 2018 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2018/02/05/why-perl-6/</guid>
      <description>&lt;p&gt;For about a year now, I&amp;rsquo;ve been working in Perl 6. Telling this to other people
often brings about some confused faces. I&amp;rsquo;ve grown quite fond of Perl 6 the
more I learn about it, yet the general developer community still seems to think
Perl is a dirty word. In this article, I will detail some of the features that
make me like Perl 6, and why I try to use it wherever possible.&lt;/p&gt;
&lt;p&gt;== Hassle-free command line arguments
Whet creating an application, you usually want to be able to specify some
arguments at runtime. Most times this happens using command line arguments or
options. Perl 6 allows you to specify these in the
&lt;a href=&#34;https://docs.perl6.org/language/functions#index-entry-MAIN&#34;&gt;&lt;code&gt;MAIN&lt;/code&gt;&lt;/a&gt; subroutine
signature.&lt;/p&gt;
&lt;p&gt;For instance, if I want the application to accept two string arguments, I can
do it as easy as this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arg-one&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arg-two&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, if you wanted to add an option like &lt;code&gt;--output=/path/to/file&lt;/code&gt;, you can do
it just like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arg-one&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arg-two&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, if there&amp;rsquo;s a &lt;code&gt;MAIN&lt;/code&gt; available in your Perl 6 program, but the
arguments or options supplied by the user are incorrect, it will display the
right way to invoke the command, called the
&lt;a href=&#34;https://docs.perl6.org/language/functions#index-entry-USAGE&#34;&gt;&lt;code&gt;USAGE&lt;/code&gt;&lt;/a&gt;.
Ofcourse, this message can be changed if you wish, but the default is quite good
for most use-cases.&lt;/p&gt;
&lt;p&gt;However, sometimes you want to add a little explanation to what the argument or
option is intended for. Just for a liitle bit of additional user friendliness.&lt;/p&gt;
&lt;p&gt;Fear not, for this is also already covered by the defaults. In Perl, there was
POD to document your code. In Perl 6, we have
&lt;a href=&#34;https://docs.perl6.org/language/glossary#index-entry-POD&#34;&gt;POD&lt;/a&gt; as well. And
these comments can be inspected at runtime to provide the user some
information. And that&amp;rsquo;s exactly what the default &lt;code&gt;USAGE&lt;/code&gt; also does. So if you
want to add some helpful comments to the arguments or the program itself,
simply add the comments where you want them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;This is a sample program, just to showcase the awesome stuff available in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;#| &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;Perl 6.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sub&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;MAIN&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arg-one&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;#= &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;Just a random argument
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$arg-two&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;#= &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;Yet another argument used for showcasing
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;#= &lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;Last but not least, an option which allows for a value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;unicode&#34;&gt;Unicode&lt;/h2&gt;
&lt;p&gt;What if you could support all languages with a single implementation? That&amp;rsquo;s
where unicode comes in. And Perl 6 currently has the best support for Unicode
out of all programming languages available. Its only real competitor seems to
be Swift (at the time of writing this).&lt;/p&gt;
&lt;p&gt;But not just for handling strings, Perl 6 uses unicode as a core language
feature. This means you can use them in your source code as well. And that
opens up some nice possibilities. Using the right unicode characters allows you
to write cleaner and more concise code, reducing the cognitive load while
trying to understand the program.&lt;/p&gt;
&lt;p&gt;For instance, if you&amp;rsquo;re trying to do any kind of math, you can just use the
π character as a regular character. Or use the ² to get the square of a certain
number. This little piece is completely valid in Perl 6:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$r&lt;/span&gt;² &lt;span class=&#34;o&#34;&gt;÷&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;π&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, if you&amp;rsquo;re thinking &amp;ldquo;that looks neat, but how am I ever going to write
these?&amp;rdquo;, do not worry. Most operating systems and many editors have tools to let
you input these. For instance, using &lt;code&gt;vim&lt;/code&gt; with
&lt;a href=&#34;https://github.com/vim-perl/vim-perl6&#34;&gt;&lt;code&gt;vim-perl6&lt;/code&gt;&lt;/a&gt;, you can just write &amp;ldquo;pi&amp;rdquo;
and hit space (or type any non-alphabetical character).&lt;/p&gt;
&lt;p&gt;But not everyone is using an OS or an editor that makes it easy. And for those
people, Perl 6 simply supports using &lt;a href=&#34;https://docs.perl6.org/language/unicode_ascii&#34;&gt;ASCII based
operators&lt;/a&gt;. The previous block
could also be written as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;^&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;pi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As unicode becomes more accepted, input methods will hopefully improve to make
input easier for everyone in the long run. Those who can already input it
easily don&amp;rsquo;t have to wait for this future, Perl 6 already supports it.&lt;/p&gt;
&lt;h2 id=&#34;multithreading&#34;&gt;Multithreading&lt;/h2&gt;
&lt;p&gt;Multi-core processors are virtually everywhere these days. Yet many programming
languages still don&amp;rsquo;t support multithreaded application development natively,
if at all. In Perl 6, running something in a different thread is as easy as
wrapping it in a &lt;a href=&#34;https://docs.perl6.org/routine/start&#34;&gt;&lt;code&gt;start&lt;/code&gt;&lt;/a&gt; block:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nf&#34;&gt;do-something&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;start&lt;/code&gt; returns a &lt;a href=&#34;https://docs.perl6.org/type/Promise&#34;&gt;&lt;code&gt;Promise&lt;/code&gt;&lt;/a&gt;, which you can
store in a scalar variable just like any other object. You can check on whether
the &lt;code&gt;Promise&lt;/code&gt; has completed already and check whether it died, for instance.&lt;/p&gt;
&lt;p&gt;Other aspects which can often be spread over multiple threads are loops or
maps. For instance, consider the following
&lt;a href=&#34;https://docs.perl6.org/routine/map&#34;&gt;&lt;code&gt;map&lt;/code&gt;&lt;/a&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;@cats&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$^cat&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will pat each cat in turn, in the order they appear in the list. But you
can speed up the patting process by patting multiple cats at the same time. And
to get there, all you need to do is add a
&lt;a href=&#34;https://docs.perl6.org/routine/race&#34;&gt;&lt;code&gt;race&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;@cats&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;race&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$^cat&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will attempt to pat the cats over multiple threads, speeding up the
process to pat all the cats. If the result of the pattings needs to be in the
same order as the patting order, you use
&lt;a href=&#34;https://docs.perl6.org/routine/hyper&#34;&gt;&lt;code&gt;hyper&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;race&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;@cats&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;hyper&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nv&#34;&gt;$^cat&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;object-orientation&#34;&gt;Object orientation&lt;/h2&gt;
&lt;p&gt;Object oriented programming seems to be getting out of fashion with the new
generation of developers. But it&amp;rsquo;s still in wide use, being taught at most
universities, and is often easy to explain to new developers as well.&lt;/p&gt;
&lt;p&gt;And Perl 6 has &lt;a href=&#34;https://docs.perl6.org/language/classtut#index-entry-OOP&#34;&gt;OO&lt;/a&gt;
support built into its core:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Foo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;has&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$some-field&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;bar&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$some-arg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also have
&lt;a href=&#34;https://docs.perl6.org/language/glossary#index-entry-Multi-Dispatch&#34;&gt;multi-dispatch&lt;/a&gt;
methods on your classes, which are methods with the same names, but accepting
different arguments or argument types. For instance:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Foo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;multi&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;bar&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;kt&#34;&gt;Str&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$some-arg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;k&#34;&gt;multi&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;bar&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;kt&#34;&gt;Int&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$some-arg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which method is being used will be decided by the type of argument is being
passed in, in this case either a &lt;a href=&#34;https://docs.perl6.org/type/Str&#34;&gt;&lt;code&gt;Str&lt;/code&gt;&lt;/a&gt; or an
&lt;a href=&#34;https://docs.perl6.org/type/Int&#34;&gt;&lt;code&gt;Int&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;functional-programming&#34;&gt;Functional programming&lt;/h2&gt;
&lt;p&gt;Whilst OO is considered being old more and more, functional programming is
gaining ground. And this paradigm is fully supported in the core of Perl 6 as
well. You&amp;rsquo;ve seen the &lt;code&gt;map&lt;/code&gt; example already while patting cats earlier, for
instance.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s much more on the functional playing field, such as the
&lt;a href=&#34;https://docs.perl6.org/routine/==%3E&#34;&gt;&lt;code&gt;==&amp;gt;&lt;/code&gt;&lt;/a&gt; operator, known as the &lt;a href=&#34;https://docs.perl6.org/language/operators#infix_==%3E&#34;&gt;&lt;code&gt;feed operator&lt;/code&gt;&lt;/a&gt;. It simply
passed the output of a statement as the last argument to the next statement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-raku&#34; data-lang=&#34;raku&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;@grumpy-cats&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;==&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;feed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;==&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;pat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;==&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;snuggle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;o&#34;&gt;==&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;my&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;@happy-cats&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will take the &lt;code&gt;@grumpy-cats&lt;/code&gt;, feed them, pat them, snuggle them and put
the result into &lt;code&gt;@happy-cats&lt;/code&gt;. You could&amp;rsquo;ve chained the calls using a &lt;code&gt;.&lt;/code&gt;
instead, and Perl 6 allows you to do this too. But the &lt;code&gt;==&amp;gt;&lt;/code&gt; looks much more
readable to me, which is why I prefer using this instead.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still exploring the functional programming field myself, but these few
things have made me happy exploring it.&lt;/p&gt;
&lt;h2 id=&#34;community&#34;&gt;Community&lt;/h2&gt;
&lt;p&gt;(Almost) last, but certainly not least, the Perl 6 community is amazing. It&amp;rsquo;s
been the friendliest bunch I&amp;rsquo;ve been with, both on IRC, their mailing lists and
in real life. Everyone is welcoming, and they try to help you whenever they
can.&lt;/p&gt;
&lt;p&gt;Community is important to help you out whenever you get stuck for whatever
reason. A friendly community is the best you can get here to keep you a happy
developer yourself as well.&lt;/p&gt;
&lt;h2 id=&#34;other-little-aspects&#34;&gt;Other little aspects&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a few neat things I can do in Perl 6 that I can&amp;rsquo;t do in (most) other
languages, but aren&amp;rsquo;t important enough to warrant a large section to show them
off.&lt;/p&gt;
&lt;h3 id=&#34;dashes-in-names&#34;&gt;Dashes in names&lt;/h3&gt;
&lt;p&gt;You can use dashes in names: Things like &lt;code&gt;my $foo-bar&lt;/code&gt; is valid, just like
&lt;code&gt;method foo-bar&lt;/code&gt;. It&amp;rsquo;s nothing big on itself, but I&amp;rsquo;ve found it makes reading
code much more enjoyable than pascalCase, CamelCase or snake_case.&lt;/p&gt;
&lt;h3 id=&#34;gradual-typing&#34;&gt;Gradual typing&lt;/h3&gt;
&lt;p&gt;You don&amp;rsquo;t &lt;em&gt;need&lt;/em&gt; to use types in Perl 6. But when you want to use them (for
making use of multi-dispatch, for example), you can just start using them. If
types are added, the compiler will make sure the types are correct. If not, you
can always do them yourself (but why would you, when the compiler can do a
better job for free).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Funding Yourself As A Free Software Developer</title>
      <link>https://www.tyil.nl/post/2017/12/21/funding-yourself-as-a-free-software-developer/</link>
      <pubDate>Thu, 21 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2017/12/21/funding-yourself-as-a-free-software-developer/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been meaning to spend more time on developing free software, helping out
new users on IRC and writing more tutorials to get others started. All of these
cost time, and time is money - so I&amp;rsquo;ve set out to set up donation accounts.
In the hopes of helping other developers who struggle to fund their work, I&amp;rsquo;ve
written up this article to talk about my experience.  This is a living
document! As you explore this yourself, please send me your thoughts on each
platform and turn me on to interesting platforms I missed.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll be focussing on platforms allowing for recurring donations, as these are
more useful for procuring a stable income.&lt;/p&gt;
&lt;h2 id=&#34;platforms&#34;&gt;Platforms&lt;/h2&gt;
&lt;h3 id=&#34;bountysource&#34;&gt;BountySource&lt;/h3&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		&lt;ul&gt;
&lt;li&gt;Requires 3rd-party &lt;a href=&#34;https://www.tyil.nl/post/2017-12-17/on-cloudflare/&#34;&gt;Cloudflare&lt;/a&gt;-hosted
JavaScript sources to function.&lt;/li&gt;
&lt;/ul&gt;

	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;BountySource lets people donate money towards an issue on Github your projects.
Once an issue gets fixed, you can claim the &amp;ldquo;bounty&amp;rdquo; that was on this issue.
This can also help in making clear which issue you should aim for next, and
can increase interest in contributors for your project.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also BountySource Salt, which is a recurring donation platform.
Projects or teams can use this to gain monthly income to sustain the
development of their project(s).&lt;/p&gt;
&lt;p&gt;Support for this platform is offered through the IRC channel &lt;a href=&#34;https://kiwiirc.com/client/chat.freenode.net:+6697/#bountysource&#34;&gt;&lt;code&gt;#bountysource&lt;/code&gt; on
Freenode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The BountySource platform itself is also free software, and the source code
for it can be found &lt;a href=&#34;https://github.com/bountysource/core&#34;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can find BountySource at &lt;a href=&#34;https://www.bountysource.com/&#34;&gt;https://www.bountysource.com/&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;liberapay&#34;&gt;LiberaPay&lt;/h3&gt;
&lt;p&gt;This service seems to be completely free as in freedom. They even
&lt;a href=&#34;https://github.com/liberapay/liberapay.com&#34;&gt;publish their source on GitHub&lt;/a&gt;.
Their own funding comes through donations on their own platform, instead of
taking a cut of each donation like most other services.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to connect other accounts to your LiberaPay account. While this
feature in general is pretty common, they allow you to link to sites which are
interesting to show as developer, such as GitHub, GitLab, and BitBucket. They
also let you link to a Mastodon account, if you have one.&lt;/p&gt;
&lt;p&gt;To let people know you&amp;rsquo;re accepting donations through LiberaPay, you can use
one of the widgets they make available for you. This will show a donate button
which will link to you profile. Do note, this is not a regular HTML button or
cleverly implemented anchor tag, but a JavaScript-based button.&lt;/p&gt;
&lt;p&gt;Another thing LiberaPay lacks is a rewards system. Most other platforms allow
you to set reward tiers, which allow you to give certain benefits to donors.&lt;/p&gt;
&lt;p&gt;You can find Liberapay at &lt;a href=&#34;https://liberapay.com/&#34;&gt;https://liberapay.com/&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;makersupport&#34;&gt;MakerSupport&lt;/h3&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		&lt;ul&gt;
&lt;li&gt;The site requires a 3rd-party hosted jQuery.&lt;/li&gt;
&lt;li&gt;You have to solve a Google reCaptcha in order to register a new account.&lt;/li&gt;
&lt;/ul&gt;

	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;MakerSupport seems to be another option, aimed at content creators who might
need freedom of speech more than others. It seems to be less focused on
software development, as you cannot link to any of the major git hosting
platforms.&lt;/p&gt;
&lt;p&gt;There are options here to set up &amp;ldquo;tiers&amp;rdquo; for your donors; which is a convenient
way to provide them with perks for their support. For a free software
developer, this might be something like access to more direct support from the
developer.&lt;/p&gt;
&lt;p&gt;Sadly, registration wasn&amp;rsquo;t as smooth as most other platforms. My preferred
username, &amp;ldquo;tyil&amp;rdquo; is too short. There&amp;rsquo;s no indication of the requirements of any
of the fields, you just get a popup on submission of the form saying a field is
wrong.&lt;/p&gt;
&lt;p&gt;Additionally, the registration form requires some 3rd-party JavaScript to work,
and a Google reCaptcha to be solved in order to get the submit button to show
up. As I have set up uMatrix in my browser, this cost me some extra time to
finish registration.&lt;/p&gt;
&lt;p&gt;Setting a profile image proved to be a little harder. First off, I&amp;rsquo;m still
using uMatrix so I had to allow a 3rd-party (Amazon, in this case) XHR
requests. Secondly, their error when uploading a &amp;ldquo;wrong&amp;rdquo; format is also not
very user friendly, as it won&amp;rsquo;t give you any details on why it&amp;rsquo;s disallowed,
nor what images are allowed instead.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		It seems they check the extension of the uploaded image&amp;rsquo;s filename. As far as I
can tell, you&amp;rsquo;re allowed to upload files that end with &lt;code&gt;.jpg&lt;/code&gt; and &lt;code&gt;.png&lt;/code&gt;.
	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;You can find MakerSupport at &lt;a href=&#34;https://www.makersupport.com/&#34;&gt;https://www.makersupport.com/&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;patreon&#34;&gt;Patreon&lt;/h3&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		&lt;ul&gt;
&lt;li&gt;Requires 3rd-party &lt;a href=&#34;https://www.tyil.nl/post/2017/12/17/on-cloudflare/&#34;&gt;Cloudflare&lt;/a&gt;-hosted
JavaScript sources to function.&lt;/li&gt;
&lt;li&gt;You have to solve a Google reCaptcha in order to register a new account.&lt;/li&gt;
&lt;/ul&gt;

	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;Patreon is possibly the most famous donation-based funding platform available
right now. Its popularity is a good thing, since this means there&amp;rsquo;s probably
many donors already using this platform.&lt;/p&gt;
&lt;p&gt;At Patreon, you can set up so-called goals. Goals are the thing I haven&amp;rsquo;t found
with other funding platforms. It allows you to set a goal for an amount of
money, and add a reward to this. This way, you can inform your donors you will
be creating a certain kind of content once a one-time goal has been reached.
Basically, you can show your donors what you&amp;rsquo;re going to do with the money
they&amp;rsquo;re donating to you.&lt;/p&gt;
&lt;p&gt;Another interesting thing that I haven&amp;rsquo;t seen on other platforms is the option
to charge donors per creation, instead of per month. While this may seem less
fitting for software developers (unless you want to get paid per commit, I
guess), it&amp;rsquo;s an interesting feature that&amp;rsquo;s pretty unique. If you publish many
tutorials, guides or other posts, this might fit you very well.&lt;/p&gt;
&lt;p&gt;You can link your account to other services, similarly to other platforms, but
it seems to only allow you to be linked with proprietary social media
platforms.&lt;/p&gt;
&lt;p&gt;You can find Patreon at &lt;a href=&#34;https://www.patreon.com/home&#34;&gt;https://www.patreon.com/home&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;dishonorable-mentions&#34;&gt;(Dis)honorable mentions&lt;/h3&gt;
&lt;h4 id=&#34;hatreon&#34;&gt;Hatreon&lt;/h4&gt;
&lt;p&gt;I&amp;rsquo;ve included this because I found people talking about it on IRC. However, it
seems to be nothing more than a joke that&amp;rsquo;s gone too far. Its main reason for
existing seems to be to get away from the political correctness found with
earlier crowdfunding platforms, yet their site is invite-only, so those who are
actually interested can&amp;rsquo;t even use it. It seems that pledging is currently
disabled as well, and has been for at least 10 days.&lt;/p&gt;
&lt;h2 id=&#34;but-thats-not-all&#34;&gt;But that&amp;rsquo;s not all&lt;/h2&gt;
&lt;p&gt;Just setting up an account on a funding platform isn&amp;rsquo;t enough. There&amp;rsquo;s more to
keeping a healthy and happy supporter base.&lt;/p&gt;
&lt;h3 id=&#34;spread-awareness-of-your-work&#34;&gt;Spread awareness of your work&lt;/h3&gt;
&lt;p&gt;Whether you&amp;rsquo;re writing articles or publishing new releases of projects, tell
the world you&amp;rsquo;re doing whatever it is you&amp;rsquo;re doing. If nobody knows about your
project, they won&amp;rsquo;t be able to give any kind of appreciation for it. Use social
media outlets, public forums, mailing lists, anything! Tell them what you made,
why it&amp;rsquo;s useful and how they could use it to improve their digital life.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		Ofcourse, don&amp;rsquo;t spam it to unrelated communication channels. This will only
backfire.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h3 id=&#34;using-the-rewards-system&#34;&gt;Using the rewards system&lt;/h3&gt;
&lt;p&gt;On the platforms that support a rewards system, make use of it. There&amp;rsquo;s some
little things you can do that go a long way with your supporters. For instance,
you can offer things like stickers to donors that donate a certain amount of
money to you. These are reasonably cheap to produce and ship, and many people
like these.&lt;/p&gt;
&lt;p&gt;Another idea that seems to strike well with donors is having a way to talk with
the person they&amp;rsquo;re supporting directly. This can be done by giving them access
to an IRC channel for you and your donors. You can use another platform for
this, but most free software enthousiasts are already on IRC, and there&amp;rsquo;s few
real-time communication alternatives that they&amp;rsquo;re already using.&lt;/p&gt;
&lt;h3 id=&#34;dont-stick-to-a-single-platform&#34;&gt;Don&amp;rsquo;t stick to a single platform&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s multiple platforms out there, use them! Not all of them have the same
userbase, and you can reach more people by giving them more options to work
with.&lt;/p&gt;
&lt;h3 id=&#34;let-people-know-youre-accepting-donations&#34;&gt;Let people know you&amp;rsquo;re accepting donations&lt;/h3&gt;
&lt;p&gt;If people don&amp;rsquo;t know you&amp;rsquo;re even accepting donations, chances are pretty high
you won&amp;rsquo;t get any. Or if it&amp;rsquo;s too hard to figure out how to donate to you,
people will simply not take the effort. Make sure people can easily find out
that you&amp;rsquo;re accepting donations, and how to donate to you.&lt;/p&gt;
&lt;h3 id=&#34;show-what-youre-doing-with-donation-money&#34;&gt;Show what you&amp;rsquo;re doing with donation money&lt;/h3&gt;
&lt;p&gt;Have a page with information about what you&amp;rsquo;re using with the money. This can
be as simple as just saying you pay the rent and buy food with it. Most donors
don&amp;rsquo;t mind too much what you&amp;rsquo;re doing with the money they donate to you, but a
few do appreciate having this information available to them.&lt;/p&gt;
&lt;p&gt;It can be as simple as adding a &lt;code&gt;/donate&lt;/code&gt; link to your site where you explain
how to donate to you, and what you do with the donation money.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Warning
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		Don&amp;rsquo;t let it turn into an annoying advertisement though, this will surely have
an opposite effect.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;further-reading&#34;&gt;Further reading&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s more places to go for tips and tricks in getting funds to sustain your
free software development work. I&amp;rsquo;ve listed a couple of these here for those
interested.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://wiki.snowdrift.coop/market-research/other-crowdfunding&#34;&gt;snowdrift.coop wiki on crowdfunding/fundraising services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/nayafia/lemonade-stand&#34;&gt;A handy guide to financial support for open source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;rfc&#34;&gt;RFC&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;d love to receive feedback on this, as I think being able to get donations
easily for the work free software developers put in to their projects is
important.&lt;/p&gt;
&lt;p&gt;Getting to know more platforms and giving them a small write-up should help out
other developers like me looking for the best platform for their use case. I&amp;rsquo;d
also like to hear from developers already using a platform, to extend this
article with more useful information on how to successfully get donors for
their work.&lt;/p&gt;
&lt;p&gt;If you want to contact me, do take a look at the &lt;a href=&#34;https://www.tyil.nl/#contact&#34;&gt;Contact&lt;/a&gt; section,
and let me know about your experiences with funding.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Cloudflare</title>
      <link>https://www.tyil.nl/post/2017/12/17/on-cloudflare/</link>
      <pubDate>Sun, 17 Dec 2017 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2017/12/17/on-cloudflare/</guid>
      <description>&lt;h2 id=&#34;foreword&#34;&gt;Foreword&lt;/h2&gt;
&lt;p&gt;Cloudflare is a threat to online security and privacy. I am not the first on to
address this issue, and I probably will not be the last either. Sadly, people
still seem to be very uninformed as to what issues Cloudflare actually poses.
There also seems to be a big misconception about the benefits provided by using
Cloudflare. I would suggest reading the &lt;a href=&#34;http://cryto.net/~joepie91/blog/2016/07/14/cloudflare-we-have-a-problem/&#34;&gt;article on Cloudflare by
joepie91&lt;/a&gt;
for a more thorough look at Cloudflare.&lt;/p&gt;
&lt;p&gt;If anyone is using Cloudflare, please tell them to stop doing it. Link them to
this page or any of the articles referenced here. Cloudflare is harmful to your
visitors, and if you do not care about them, they will stop caring about you
too.&lt;/p&gt;
&lt;h2 id=&#34;a-literal-mitm-attack&#34;&gt;A literal MITM attack&lt;/h2&gt;
&lt;p&gt;Cloudflare poses a huge risk by completely breaking the TLS/SSL chain used by
browsers by setting itself up as a
&lt;a href=&#34;https://en.wikipedia.org/wiki/Man-in-the-middle_attack&#34;&gt;man in the middle&lt;/a&gt;.
Cloudflare doesn&amp;rsquo;t do actual DDoS protection, they just make the request to the
origin server for you. Once they have received the data, they decrypt it and
re-encrypts it with their own certificate.  This means that Cloudflare has
access to all requests in plain text and can optionally modify the data you
see. TLS/SSL is meant to prevent this very issue, but Cloudflare seems to care
very little.&lt;/p&gt;
&lt;p&gt;If we would consider Cloudflare to be a benevolent entity and surely never
modify any data ever, this is still an issue. Much data can be mined from the
plain text communications between you and the origin server. This data can be
used for all kinds of purposes. It is not uncommon for the USA government to
request a massive amount of surveillance information from companies without the
companies being able to speak up about it due to a gag order. This has become
clear once more by the &lt;a href=&#34;https://whispersystems.org/bigbrother/eastern-virginia-grand-jury/&#34;&gt;subpoena on
Signal&lt;/a&gt;. It
should be clear to anyone that end-to-end encryption has to be a standard and
implemented properly. Cloudflare goes out of its way to break this
implementation.&lt;/p&gt;
&lt;h3 id=&#34;cloudbleed&#34;&gt;Cloudbleed&lt;/h3&gt;
&lt;p&gt;The danger of their MITM style of operation was shown be the
&lt;a href=&#34;https://en.wikipedia.org/wiki/Cloudbleed&#34;&gt;Cloudbleed&lt;/a&gt; vulnerability. It also
shows that they make use of their MITM position to scan the data your site and
a visitor are exchanging. This includes private data, such as passwords.&lt;/p&gt;
&lt;p&gt;Even if you have an SSL connection to Cloudflare, they still decrypt it on
their end. They then serve the content under their own certificate. This makes
it look to the visitor like everything is secure, the browser says so after
all. But in reality, they don&amp;rsquo;t have a secure connection to your server. They
only have one up to Cloudflare, and when it reaches Cloudflare, they decrypt it
and re-encrypt it using your certificate again. If you use one, of course,
otherwise they&amp;rsquo;ll pass it on in plaintext back to your server, which is even
more dangerous. Whether or not you do, the content exists in plaintext on
Cloudflare&amp;rsquo;s servers, which is not what you want, if you truly care about
security.&lt;/p&gt;
&lt;h2 id=&#34;eliminating-your-privacy&#34;&gt;Eliminating your privacy&lt;/h2&gt;
&lt;p&gt;If Cloudflare were to fix their MITM behavior, the privacy problem would not
be solved all of a sudden. There are more questionable practices in use by
Cloudflare.&lt;/p&gt;
&lt;p&gt;People who are using a VPN or an anonimization service such as Tor are usually
greeted by a warning from Cloudflare. Let&amp;rsquo;s not talk about this warning being
incorrect about the reason behind the user receiving the warning, but instead
about the methodology used to &amp;ldquo;pass&amp;rdquo; this &amp;ldquo;warning&amp;rdquo;. Cloudflare presents you
with a page that requires you to solve a reCaptcha puzzle, which is hosted by a
well known third party that tries to harm your privacy as much as possible,
Google. If you do not wish to have Google tracking you all the time, you will
not be able to solve these puzzles, and in effect, unable to access the site
you were visiting. It is also interesting to note that this reCaptcha system is
sometimes broken if your browser does not identify itself as one of the regular
mainstream browsers such as Firefox or Chrome.&lt;/p&gt;
&lt;p&gt;Some site administrators disable this specific check. However, this still means
all your requests are logged by another third party, namely Cloudflare itself.
As noted in &lt;em&gt;A literal MITM attack&lt;/em&gt;, this data is still very interesting to
some parties. And do not fool yourself: meta data is still very worthwhile and
can tell a huge amount of information about a person.&lt;/p&gt;
&lt;h3 id=&#34;forcing-javascript&#34;&gt;Forcing JavaScript&lt;/h3&gt;
&lt;p&gt;This issue generally does not concern many people, as most people online
nowadays use a big mainstream browser with JavaScript enabled. However, there
are still people, services and applications that do not use JavaScript. This
makes sites unavailable when they are in the &amp;ldquo;under attack&amp;rdquo; mode by Cloudflare.
This will run a check sending Cloudflare your browser information before
deciding whether you are allowed to access the website. This is yet another
privacy issue, but at the same time, a usability issue. It makes your site
unavailable to people who simply do not wish to use JavaScript or people who
are currently limited to a browser with no JavaScript support.&lt;/p&gt;
&lt;p&gt;It is also common for Cloudflare to
&lt;a href=&#34;http://www.tedunangst.com/flak/post/cloudflare-and-rss&#34;&gt;break RSS readers&lt;/a&gt; by
presenting them with this check. This check is often presented to common user
agents used by services and programs. Since these do not include a big
JavaScript engine, there is no way for them to pass the test.&lt;/p&gt;
&lt;h2 id=&#34;false-advertising&#34;&gt;False advertising&lt;/h2&gt;
&lt;h3 id=&#34;ddos-protection&#34;&gt;DDoS protection&lt;/h3&gt;
&lt;p&gt;Cloudflare is hailed by many as a gratis DDoS protection service, and they
advertise themselves as such. However, Cloudflare does not offer DDoS
protection, they simply act as a pin cushion to soak the hit. Real DDoS
protection works by analyzing traffic, spotting unusual patterns and blocking
these requests. If they were to offer real DDoS protection like this, they
would be able to tunnel TLS/SSL traffic straight to the origin server, thereby
not breaking the TLS/SSL chain as they do right now.&lt;/p&gt;
&lt;p&gt;It should also be noted that this gratis &amp;ldquo;protection&amp;rdquo; truly gratis either. If
your site gets attacked for long enough, or for enough times in a short enough
time frame, you will be kicked off of the gratis plan and be moved onto the
&amp;ldquo;business&amp;rdquo; plan. This requires you to pay $200 per month for a service that does
not do what it is advertised to do. If you do not go to the business plan, you will
have about the same protection as you would have without it, but with the
addition of ruining the privacy and security of your visitors.&lt;/p&gt;
&lt;h3 id=&#34;faster-page-loads&#34;&gt;Faster page loads&lt;/h3&gt;
&lt;p&gt;This is very well explained on &lt;a href=&#34;http://cryto.net/~joepie91/blog/2016/07/14/cloudflare-we-have-a-problem/&#34;&gt;joepie91&amp;rsquo;s
article&lt;/a&gt;
under the heading &lt;em&gt;But The Speed! The Speed!&lt;/em&gt;. As such, I will refer to his
article instead of repeating him here.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting up a Raspberry Perl</title>
      <link>https://www.tyil.nl/post/2017/11/16/setting-up-a-raspberry-perl/</link>
      <pubDate>Thu, 16 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2017/11/16/setting-up-a-raspberry-perl/</guid>
      <description>&lt;p&gt;In this tutorial I&amp;rsquo;ll get you through setting up a Raspberry Pi with
&lt;a href=&#34;https://perl6.org/&#34;&gt;Perl 6&lt;/a&gt;. I am using a Raspberry Pi 3 myself, but other
versions should work fine too. However, older versions are slower, so it might
take a bit longer to install completely.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		&lt;p&gt;For those who have never had a Raspberry Pi before, you will need
the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Raspberry Pi board&lt;/li&gt;
&lt;li&gt;Power supply (5v 2A, micro USB)&lt;/li&gt;
&lt;li&gt;SD card of at least 4gb, but I would advise at least 8gb&lt;/li&gt;
&lt;li&gt;Monitor with HDMI cable&lt;/li&gt;
&lt;li&gt;Keyboard&lt;/li&gt;
&lt;/ul&gt;

	&lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;Perl 6 will be installed using
&lt;a href=&#34;https://github.com/tadzik/rakudobrew&#34;&gt;Rakudowbrew&lt;/a&gt;, which I&amp;rsquo;ll also be using to
get &lt;a href=&#34;https://github.com/ugexe/zef&#34;&gt;zef&lt;/a&gt; installed. Zef is the recommended module
manager for Perl 6.&lt;/p&gt;
&lt;h2 id=&#34;setting-up-raspbian&#34;&gt;Setting up Raspbian&lt;/h2&gt;
&lt;p&gt;The first step is getting the OS set up. To keep this tutorial simple, I will
stick to &lt;a href=&#34;https://www.raspbian.org/&#34;&gt;Raspbian&lt;/a&gt;, but if you feel confident in
your skills you can use any other distribution or OS. Perl 6 installs the same
on all UNIX(-like) operating systems.&lt;/p&gt;
&lt;h3 id=&#34;get-the-image&#34;&gt;Get the image&lt;/h3&gt;
&lt;p&gt;First, &lt;a href=&#34;https://www.Raspberrypi.org/downloads/raspbian/&#34;&gt;download the Raspbian image from the Raspberry Pi download
page&lt;/a&gt;. I chose the &lt;code&gt;LITE&lt;/code&gt;
version, but if you prefer having a graphical desktop you can go for the
&lt;code&gt;DESKTOP&lt;/code&gt; version instead.&lt;/p&gt;
&lt;p&gt;At the time of writing, this means I got the
&lt;code&gt;2017-09-07-raspbian-stretch-lite.zip&lt;/code&gt;. If you want to verify you got the
correct download and nothing went wrong saving it to your disk, you can verify
the checksum. The checksum for your download is noted below the download links.
To get the checksum of the file you downloaded, use &lt;code&gt;sha256sum&lt;/code&gt; as follows:&lt;/p&gt;
&lt;p&gt;NOTE: Lines prepended with a &lt;code&gt;$&lt;/code&gt; are to be ran as your normal user, whereas
lines with a &lt;code&gt;#&lt;/code&gt; are ment to be ran as &amp;ldquo;super user&amp;rdquo;. This can be done by using
a privilege escalation program, such as
&lt;a href=&#34;https://www.linux.com/blog/how-use-sudo-and-su-commands-linux-introduction&#34;&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sha256sum 2017-09-07-raspbian-stretch-lite.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the checksum matches the one noted below the download button you used, it
should be fine, and you can continue with extracting the image from the zip
using &lt;code&gt;unzip&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ unzip 2017-09-07-raspbian-stretch-lite.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will result in a similarly named file, but with a &lt;code&gt;.img&lt;/code&gt; extension instead
of &lt;code&gt;.zip&lt;/code&gt;. This is the image that you can write to the SD card.&lt;/p&gt;
&lt;h3 id=&#34;write-the-image-to-the-sd-card&#34;&gt;Write the image to the SD card&lt;/h3&gt;
&lt;p&gt;This step is pretty easy, but typos here can be disastrous for the system
you&amp;rsquo;re using to write to the SD card.&lt;/p&gt;
&lt;p&gt;Open a terminal and run &lt;code&gt;dmesg -w&lt;/code&gt; as super user (usually doable using &lt;code&gt;sudo dmesg -w&lt;/code&gt;). This will give immediate feedback when you insert your SD card, and
shows which device it is being assigned to. In my case, this was &lt;code&gt;sdb&lt;/code&gt;, which
means the device file resides at &lt;code&gt;/dev/sdb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, to actually write the image, I&amp;rsquo;ll use &lt;code&gt;dd&lt;/code&gt; since this is everyone&amp;rsquo;s
favourite tool, it seems. If you feel adventurous enough to try out something
different, feel free to read up on
&lt;a href=&#34;https://www.vidarholen.net/contents/blog/?p=479&#34;&gt;Useless Use of &lt;code&gt;dd&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Make sure to make the &lt;code&gt;if&lt;/code&gt; argument point to the correct path with your
extracted raspbian image, and &lt;code&gt;of&lt;/code&gt; to point to the correct device as identified
earlier. In order to be allowed to run this command, you must be root, which
can be achieved by using &lt;code&gt;sudo&lt;/code&gt; or &lt;code&gt;doas&lt;/code&gt; again.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# dd bs=4M status=progress if=/path/to/2017-09-07-raspbian-stretch-lite.img of=/dev/sdb
$ sync
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, plug it into your Raspberry Pi and attach all cables you might
need. Think of stuff like a keyboard, mouse, monitor, internet, power. Do power
last, as the Raspberry Pi will start immediatly once it receives power.&lt;/p&gt;
&lt;h3 id=&#34;first-boot&#34;&gt;First boot&lt;/h3&gt;
&lt;p&gt;The Raspberry Pi should start booting the moment you supply it with power. If
you attach the HDMI after the power, it&amp;rsquo;s possible you won&amp;rsquo;t have display
working, so make sure HDMI is attached before powering up.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see some text scrolling by, up to a point where it asks you for a
&lt;code&gt;login&lt;/code&gt;, and accepts keyboard input. The default username is &lt;code&gt;pi&lt;/code&gt;, and the
default password is &lt;code&gt;Raspberry&lt;/code&gt;. You are strongly advised to change the
password upon login, which can be done in the next step.&lt;/p&gt;
&lt;h3 id=&#34;configuration&#34;&gt;Configuration&lt;/h3&gt;
&lt;p&gt;The Raspberry Pi comes with its own configuration tool, &lt;code&gt;raspi-config&lt;/code&gt;. Run
this with &lt;code&gt;sudo&lt;/code&gt; prepended in front of it so you gain the right privileges. I
would advise you to at least change the user password from here. After this you
should go to &lt;code&gt;Advanced Options&lt;/code&gt; and expand the filesystem. This will grow the
filesystem to the entire SD card&amp;rsquo;s size.&lt;/p&gt;
&lt;p&gt;TIP: To get to the buttons on the bottom (&lt;code&gt;Select&lt;/code&gt;, &lt;code&gt;Finish&lt;/code&gt; and &lt;code&gt;Back&lt;/code&gt;), use
the arrow keys to go left or right.&lt;/p&gt;
&lt;p&gt;You can look around the tool for other interesting things to modify. Once you
are satisfied, go back to the main menu and choose &lt;code&gt;Finish&lt;/code&gt;. It will ask to
reboot, which you should accept. This will apply all the new configurations you
just made.&lt;/p&gt;
&lt;h3 id=&#34;updating-and-installing-additional-packages&#34;&gt;Updating and installing additional packages&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s rare for the system to be completely up to date after installing the image
on the SD card. Additionally, you also need some extra packages in order to get
rakudobrew, and to install Perl 6 itself. For this, we use the package manager
bundled with raspbian, &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# apt update
# apt upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will update the package lists, and then upgrade all outdated packages to
their newest versions. You should do this at least once a week to make sure
your system stays up to date.&lt;/p&gt;
&lt;p&gt;Once the upgrades are finished, you can install some new packages which are
needed later on in this tutorial:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# apt install git build-essential
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git&lt;/code&gt; is required to get the rakudobrew repository and is also used by
rakudobrew itself to get the sources needed to build Perl 6 and to install zef.
The &lt;code&gt;build-essential&lt;/code&gt; package comes with all sorts of tools to build software,
which is required to build Perl 6.&lt;/p&gt;
&lt;h2 id=&#34;installing-perl-6&#34;&gt;Installing Perl 6&lt;/h2&gt;
&lt;p&gt;Now, we&amp;rsquo;ve got a working Raspberry Pi installation. We can start doing things
with it, such as playing around with Perl 6.&lt;/p&gt;
&lt;h3 id=&#34;setting-up-rakudobrew&#34;&gt;Setting up Rakudobrew&lt;/h3&gt;
&lt;p&gt;Rakudobrew is a nice tool to manage Perl 6 installations on your system. It can
also install &lt;code&gt;zef&lt;/code&gt; for you, so you don&amp;rsquo;t have to deal with this manually. This
is all documented on the repository&amp;rsquo;s &lt;code&gt;README.md&lt;/code&gt; file as well, but I&amp;rsquo;ll
explain it here too. I do make a few small tweaks here and there to match my
preferred setup more closely.&lt;/p&gt;
&lt;p&gt;Clone the repository to your system, and add it to your &lt;code&gt;$PATH&lt;/code&gt; to be able to
use the scripts bundled with it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir -p ~/.local/var
$ git clone https://github.com/tadzik/rakudobrew.git ~/.local/var/rakudobrew
$ export PATH=${HOME}/.local/var/rakudobrew/bin:$PATH
$ hash -r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;hash -r&lt;/code&gt; call will rehash your PATH, so you can tab-complete &lt;code&gt;rakudobrew&lt;/code&gt;.
Next, initialize rakudobrew:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rakudobrew init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will give you a notification to automatically load rakudobrew next time.
It is advised you follow that message, so you won&amp;rsquo;t have to do it manually each
time you log in to the system.&lt;/p&gt;
&lt;h3 id=&#34;installing-perl-6-with-moarvm-backend&#34;&gt;Installing Perl 6 with MoarVM backend&lt;/h3&gt;
&lt;p&gt;Now that rakudobrew is installed and available to use, it&amp;rsquo;s time to make use of
it to install Perl 6.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rakudobrew build moar
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;installing-zef-the-module-manager&#34;&gt;Installing zef, the module manager&lt;/h3&gt;
&lt;p&gt;Getting zef to work isn&amp;rsquo;t much harder than installing Perl 6, but its a lot
faster. You can have rakudobrew take care of this too:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rakudobrew build zef
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;final-words&#34;&gt;Final words&lt;/h2&gt;
&lt;p&gt;And that should be it, you now have a working Perl 6 installation with the zef
module manager to take care of installing and upgrading modules. Now you just
need to come up with a nice project to work on to start using and learning the
wonders of Perl 6.&lt;/p&gt;
&lt;p&gt;If you need any help on getting started, try the &lt;code&gt;#perl6&lt;/code&gt; IRC channel on
Freenode, or check out some of the Perl 6 documentation and introduction sites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.perl6.org/&#34;&gt;https://docs.perl6.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://perl6intro.com/&#34;&gt;http://perl6intro.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For starting projects that are easy to start with and can bring quick results,
consider making an IRC bot using
&lt;a href=&#34;https://github.com/zoffixznet/perl6-IRC-Client&#34;&gt;&lt;code&gt;IRC::Client&lt;/code&gt;&lt;/a&gt;, or a small web
application using &lt;a href=&#34;https://github.com/Bailador/Bailador&#34;&gt;&lt;code&gt;Bailador&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hacktoberfest 2017</title>
      <link>https://www.tyil.nl/post/2017/11/01/hacktoberfest-2017/</link>
      <pubDate>Wed, 01 Nov 2017 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2017/11/01/hacktoberfest-2017/</guid>
      <description>&lt;p&gt;This year I actively participated in the Hacktoberfest event, which is &amp;ldquo;a
month-long celebration of open source software&amp;rdquo;. Ironic, given that the
companies organising it don&amp;rsquo;t have their own software stack open source.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve found some issues to solve in &lt;a href=&#34;https://perl6.org/&#34;&gt;Perl 6&lt;/a&gt; projects, and
that lead to trying to solve issues in some other projects, and eventually I
got more PRs out than there are days in the month. It did go at the cost of
some sleep, but in the end it seems worth it. In this article, I&amp;rsquo;ll give a
small overview of all those PRs, in no particular order.&lt;/p&gt;
&lt;h2 id=&#34;projects-contributed-to&#34;&gt;Projects contributed to&lt;/h2&gt;
&lt;h3 id=&#34;funtoo&#34;&gt;Funtoo&lt;/h3&gt;
&lt;h4 id=&#34;funtooboot-update&#34;&gt;funtoo/boot-update&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/funtoo/boot-update/pull/14&#34;&gt;https://github.com/funtoo/boot-update/pull/14&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When reinstalling my server to try out &lt;a href=&#34;https://docker.com&#34;&gt;Docker&lt;/a&gt;, I noticed
an error in the output of the &lt;code&gt;boot-update&lt;/code&gt; utility, a tool from
&lt;a href=&#34;https://www.funtoo.org/Welcome&#34;&gt;Funtoo&lt;/a&gt; to make installing and configuring the
bootloader easier. The error itself was a small type of a &lt;code&gt;-&lt;/code&gt; which had to be a
&lt;code&gt;_&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;scriptkittiesoverlay&#34;&gt;scriptkitties/overlay&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/overlay/pull/14&#34;&gt;https://github.com/scriptkitties/overlay/pull/14&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/overlay/pull/15&#34;&gt;https://github.com/scriptkitties/overlay/pull/15&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/overlay/pull/16&#34;&gt;https://github.com/scriptkitties/overlay/pull/16&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the overlay of the &lt;a href=&#34;https://scriptkitties.church&#34;&gt;Scriptkitties&lt;/a&gt;
community. It&amp;rsquo;s got some additional software released under a free license that
is not available in the main portage repository. Most of the packages in here
are of software made by the Scriptkitties community.&lt;/p&gt;
&lt;p&gt;This month I updated the readme to be in asciidoc, my new favourite format for
documentation. The Travis builds should also no longer throw errors, so those
can be used again to ensure the overlay is meeting quality standards. One
package has also been updated to be at it&amp;rsquo;s latest version again.&lt;/p&gt;
&lt;h3 id=&#34;perl-6&#34;&gt;Perl 6&lt;/h3&gt;
&lt;h4 id=&#34;moznionp6-html-escape&#34;&gt;moznion/p6-HTML-Escape&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/moznion/p6-HTML-Escape/pull/1&#34;&gt;https://github.com/moznion/p6-HTML-Escape/pull/1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On this repository, I added a subroutine to also handle unescaping HTML special
characters. Sadly, the owner of this repository has shown no sign of life, and
the PR remains open.&lt;/p&gt;
&lt;h4 id=&#34;rakudorakudo&#34;&gt;rakudo/rakudo&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/rakudo/rakudo/pull/1180&#34;&gt;https://github.com/rakudo/rakudo/pull/1180&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a rather small issue, but I noticed it when compiling Perl 6 with
&lt;a href=&#34;https://github.com/tadzik/rakudobrew&#34;&gt;Rakudobrew&lt;/a&gt; and it annoyed me.
&lt;a href=&#34;http://zoffix.com/&#34;&gt;Zoffix&lt;/a&gt; was a great help in getting me started on this one,
and in general with many other Perl related contributions as well.&lt;/p&gt;
&lt;h4 id=&#34;scriptkittiesperl6-irc-client-plugin-github&#34;&gt;scriptkitties/perl6-IRC-Client-Plugin-Github&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-IRC-Client-Plugin-Github/pull/2&#34;&gt;https://github.com/scriptkitties/perl6-IRC-Client-Plugin-Github/pull/2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A neat feature for the Github notification system, HMAC adds a header that can
be used to verify the body of the request, and can be used to verify the other
end of the connection knows the right &amp;ldquo;secret&amp;rdquo;. Inspired by a Perl 6 bot that
already did this, I made a PR to make this a proper
&lt;a href=&#34;https://github.com/zoffixznet/perl6-IRC-Client&#34;&gt;&lt;code&gt;IRC::Client&lt;/code&gt;&lt;/a&gt; plugin. It is still
being tested in &lt;a href=&#34;https://github.com/scriptkitties/musashi&#34;&gt;musashi&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;perl6roast&#34;&gt;perl6/roast&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/perl6/roast/pull/342&#34;&gt;https://github.com/perl6/roast/pull/342&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Roast is the test suite for Perl 6. There was an open issue for the IO::File
tests, which needed expansion. As my first contribution during a Perl 6
squashaton, I expanded these tests to fix the issue that was open for it.&lt;/p&gt;
&lt;h4 id=&#34;vim-perlvim-perl6&#34;&gt;vim-perl/vim-perl6&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/vim-perl/vim-perl6/pull/9&#34;&gt;https://github.com/vim-perl/vim-perl6/pull/9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/vim-perl/vim-perl6/pull/10&#34;&gt;https://github.com/vim-perl/vim-perl6/pull/10&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This first PR has become a bit of a drag, with the maintainers not responding
for two weeks, but suddenly very eager to respond when I mention I&amp;rsquo;m going to
fork off and update the reference on the Perl documentation to my fork.
Nonetheless, it&amp;rsquo;s sorted out, and the abbreviations for unicode operators
have been merged in!&lt;/p&gt;
&lt;h4 id=&#34;timojson_fast&#34;&gt;timo/json_fast&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/timo/json_fast/pull/32&#34;&gt;https://github.com/timo/json_fast/pull/32&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;JSON::Fast&lt;/code&gt; is the de-facto standard for dealing with JSON data in Perl 6 it
seems. For my work with &lt;code&gt;App::Cpan6&lt;/code&gt; I wanted the JSON data to be ordered, so I
added that as an option when calling &lt;code&gt;to-json&lt;/code&gt;. Having the JSON data ordered
makes it easier to compare diffs of two different versions of the data, making
git diffs a lot cleaner.&lt;/p&gt;
&lt;p&gt;Sadly, timo has not merged the PR yet, so I can&amp;rsquo;t properly depend on it in
&lt;code&gt;App::Cpan6&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;scriptkittiesperl6-semver&#34;&gt;scriptkitties/perl6-SemVer&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-SemVer/pull/1&#34;&gt;https://github.com/scriptkitties/perl6-SemVer/pull/1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is one of the new projects I started. It is intended to be used in
&lt;code&gt;App::Cpan6&lt;/code&gt;, since that uses &lt;a href=&#34;https://semver.org&#34;&gt;Semantic Versioning&lt;/a&gt; for all
modules it works with. This module defines a class that can interpret a SemVer
notation, and exposes methods to bump any part of the version.&lt;/p&gt;
&lt;h4 id=&#34;perl6doc&#34;&gt;perl6/doc&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/perl6/doc/pull/1614&#34;&gt;https://github.com/perl6/doc/pull/1614&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This has been one of the more annoying PRs to work on, as the current &lt;code&gt;zef&lt;/code&gt;
maintainer insists everything but his module is wrong, and seemed very
uninterested to improve the situation for users. After some discussion on IRC,
some more discussion on IRC, and then some discussion on the PR itself, I
decided to just word the paragraph differently.&lt;/p&gt;
&lt;p&gt;I am still interested in improving the documentation here and the ecosystem
itself, mainly the &lt;code&gt;META6.json&lt;/code&gt; specification, and getting &lt;code&gt;zef&lt;/code&gt; to play nice
with this spec. If anyone else is interested in helping me out on this, do
message me on IRC!&lt;/p&gt;
&lt;h4 id=&#34;perl6perl6org&#34;&gt;perl6/perl6.org&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/perl6/perl6.org/pull/86&#34;&gt;https://github.com/perl6/perl6.org/pull/86&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/perl6/perl6.org/pull/87&#34;&gt;https://github.com/perl6/perl6.org/pull/87&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There were some open issues for the &lt;a href=&#34;https://perl6.org&#34;&gt;perl6.org&lt;/a&gt; website, and
I decided to take a look at some and try to fix them. This resulted in NeoVim
being added to the list of recommended editors for Perl 6, and the list of IRC
bots being updated to include all bots in use right now.&lt;/p&gt;
&lt;h4 id=&#34;scriptkittiesp6-mpd-client&#34;&gt;scriptkitties/p6-MPD-Client&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/p6-MPD-Client/pull/1&#34;&gt;https://github.com/scriptkitties/p6-MPD-Client/pull/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/p6-MPD-Client/pull/2&#34;&gt;https://github.com/scriptkitties/p6-MPD-Client/pull/2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I was making &lt;code&gt;App::MPD::AutoQueue&lt;/code&gt; and &lt;code&gt;App::MPD::Notify&lt;/code&gt;, I found some
issues in &lt;code&gt;MPD::Client&lt;/code&gt;. I fixed those to get my two new projects working
nicely.&lt;/p&gt;
&lt;h4 id=&#34;melezhiksparrowdo&#34;&gt;melezhik/sparrowdo&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/melezhik/sparrowdo/pull/15&#34;&gt;https://github.com/melezhik/sparrowdo/pull/15&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/melezhik/sparrowdo/pull/18&#34;&gt;https://github.com/melezhik/sparrowdo/pull/18&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sparrowdo is a configuration management system, written in Perl 6. I learned
about it after a reference from the Perl 6 Weekly, and set out to try it. I ran
into some issues, which I reported and eventually fixed.&lt;/p&gt;
&lt;p&gt;In addition, I also rewrote the testing script for Travis, which enables
paralel builds of the tests. This has nearly halved the time required for
running the full test suite.&lt;/p&gt;
&lt;h4 id=&#34;perl6ecosystem&#34;&gt;perl6/ecosystem&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/perl6/ecosystem/pull/371&#34;&gt;https://github.com/perl6/ecosystem/pull/371&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/perl6/ecosystem/pull/372&#34;&gt;https://github.com/perl6/ecosystem/pull/372&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/perl6/ecosystem/pull/374&#34;&gt;https://github.com/perl6/ecosystem/pull/374&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These PRs added a module, and removed that one and more later on, since I got a
PAUSE ID and uploaded my modules to CPAN.&lt;/p&gt;
&lt;h4 id=&#34;scriptkittiesperl6-app-cpan6&#34;&gt;scriptkitties/perl6-App-Cpan6&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/1&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/2&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/3&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/4&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/12&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/12&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/13&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/13&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/14&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/14&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/scriptkitties/perl6-App-Cpan6/pull/15&#34;&gt;https://github.com/scriptkitties/perl6-App-Cpan6/pull/15&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;App::Cpan6&lt;/code&gt; is a tool I&amp;rsquo;ve started working on to assist me in creating new
Perl 6 modules. There&amp;rsquo;s been a couple of tasks that I do often in the process
of creating a module, and those tasks should become easier and faster using
this module.&lt;/p&gt;
&lt;p&gt;If everything works out and I learn enough of the module installation process,
I might consider letting this deal with the installation and updating of
modules as well.&lt;/p&gt;
&lt;h2 id=&#34;in-retrospect&#34;&gt;In retrospect&lt;/h2&gt;
&lt;p&gt;The Hacktoberfest has been an interesting month for me. I&amp;rsquo;ve gotten to
contribute to a project I have come to love a lot, Perl 6. I&amp;rsquo;ve also made some
new friends with similar goals. Sadly I can&amp;rsquo;t put in this much time every month
of the year, but I would if I could!&lt;/p&gt;
&lt;p&gt;I learned many interesting things for Perl 6, new operators, new functions, all
kinds of cool stuff to improve my Perl scripts with. I also got to learn about
parallelizing Travis builds with the Sparrowdo project, of which I will write
another tutorial post later.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve greatly enjoyed contributing to all the various projects, and would
recommend other people to check it out too. The people on the respective
project&amp;rsquo;s IRC channels have been a great help to me to get started, and I can
help out getting you started as well now.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Perl 6 - Creating a background service</title>
      <link>https://www.tyil.nl/post/2017/09/28/perl-6-creating-a-background-service/</link>
      <pubDate>Thu, 28 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2017/09/28/perl-6-creating-a-background-service/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently made some progress on
&lt;a href=&#34;https://github.com/scriptkitties/perl6-Shinrin&#34;&gt;Shinrin&lt;/a&gt; a centralized logging
system in Perl 6. This has to run as service, which means that for most service
managers it has to be able to run in the background.&lt;/p&gt;
&lt;section class=&#34;admonition&#34;&gt;
	&lt;div class=&#34;admonition-title&#34;&gt;
		Note
	&lt;/div&gt;
	&lt;div class=&#34;admonition-content&#34;&gt;
		If you just want to get to the solution and don&amp;rsquo;t care for the details, just
head straight to &lt;a href=&#34;#the-final-solution&#34;&gt;the full script&lt;/a&gt;.
	&lt;/div&gt;
&lt;/section&gt;

&lt;h2 id=&#34;its-not-possible&#34;&gt;It&amp;rsquo;s not possible!&lt;/h2&gt;
&lt;p&gt;After a lot of trying and talking with the folks at
&lt;a href=&#34;irc://chat.freenode.net:6697/#perl6&#34;&gt;#perl6&lt;/a&gt; I was told that it is not possible
to do this in pure Perl 6, explained by people with more knowledge than I have
on the internals:&lt;/p&gt;
&lt;div class=&#34;quoteblock&#34;&gt;
	&lt;blockquote&gt;
		&lt;div class=&#34;paragraph&#34;&gt;
			(jnthn suspects fork + multi-threaded VM = pain) Since fork only clones one
thread - the one that called it. So suddenly you&amp;rsquo;ve got an instance of the VM
missing most of its threads.
		&lt;/div&gt;
	&lt;/blockquote&gt;
	&lt;div class=&#34;attribution&#34;&gt;
		— jnthn
	&lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;quoteblock&#34;&gt;
	&lt;blockquote&gt;
		&lt;div class=&#34;paragraph&#34;&gt;
			The most common failure mode is that some thread is holding e.g. a mutex (or a
userspace lock) during the fork. The thread goes away but the lock is process
level and remains, with nothing around to know to unlock it. So then things
work until something else needs that lock and suddenly you deadlock.
		&lt;/div&gt;
	&lt;/blockquote&gt;
	&lt;div class=&#34;attribution&#34;&gt;
		— geekosaur
	&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Not much later, &lt;code&gt;jnthn&lt;/code&gt; &lt;a href=&#34;https://github.com/perl6/doc/commit/8f9443c3ac&#34;&gt;pushed a
commit&lt;/a&gt; to update the docs to
clarify that a &lt;code&gt;fork&lt;/code&gt; call through &lt;code&gt;NativeCall&lt;/code&gt; will probably not give the
result you were hoping for.&lt;/p&gt;
&lt;h2 id=&#34;or-is-it&#34;&gt;Or is it?&lt;/h2&gt;
&lt;p&gt;Luckily, the same people were able to think up of a work-around, which can be
made in POSIX sh, so it&amp;rsquo;s usable on any decent OS. The workaround is to let a
little shell script fork into the background, and let that run the Perl
application.&lt;/p&gt;
&lt;h3 id=&#34;a-first-example&#34;&gt;A first example&lt;/h3&gt;
&lt;p&gt;This is fairly simple to create, as in this example to launch &lt;code&gt;shinrind&lt;/code&gt; in the
background:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#! /usr/bin/env sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;main&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    perl6 -Ilib bin/shinrind &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;main &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works just fine if the working directory is correct. This means you need
to be in the parent directory to &lt;code&gt;lib&lt;/code&gt; and &lt;code&gt;bin&lt;/code&gt; of the program to make it
work.&lt;/p&gt;
&lt;h2 id=&#34;improving-the-forking-script&#34;&gt;Improving the forking script&lt;/h2&gt;
&lt;p&gt;While that short script works fine to show a proof of concept, in order to make
it viable for real-world scenarios, it can use some improvements. After all, it
would be annoying if you&amp;rsquo;d have to &lt;code&gt;cd&lt;/code&gt; to a specific directory any time you
want to start your application.&lt;/p&gt;
&lt;h3 id=&#34;ensure-you-are-in-the-directory-you-should-be-in&#34;&gt;Ensure you are in the directory you should be in&lt;/h3&gt;
&lt;p&gt;So for starters, let&amp;rsquo;s make sure that you can run it from anywhere on your
system.  For this, you should set the working directory for the script, so you
don&amp;rsquo;t have to do it manually. Because the script runs in its own subshell, the
shell you&amp;rsquo;re working from remains unaffected.&lt;/p&gt;
&lt;p&gt;A POSIX compliant way to get the directory the script is stored in is as
follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;DIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;CDPATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; -- &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;dirname -- &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will set &lt;code&gt;$DIR&lt;/code&gt; to the path of the directory the shell script is stored
in. You can simply &lt;code&gt;cd&lt;/code&gt; to that and be assured you&amp;rsquo;re in the right directory.&lt;/p&gt;
&lt;p&gt;In Perl 6, it is expected for executable files to live in the &lt;code&gt;bin&lt;/code&gt; directory
of your project repository. So you should actually be in the parent of the
directory holding your script. Furthermore, you should check the &lt;code&gt;cd&lt;/code&gt; command
executed correctly, just to be safe.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; -- &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;DIR&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/..&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;disable-stdout-and-stderr&#34;&gt;Disable &lt;code&gt;STDOUT&lt;/code&gt; and &lt;code&gt;STDERR&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A started service should not be polluting your interactive shell, so you should
disable (or otherwise redirect) &lt;code&gt;STDOUT&lt;/code&gt; and &lt;code&gt;STDERR&lt;/code&gt;. This is done in the
shell using a small bit of code behind whatever you want to redirect:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; /dev/null 2&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will set &lt;code&gt;STDOUT&lt;/code&gt; to &lt;code&gt;/dev/null&lt;/code&gt;, and set &lt;code&gt;STDERR&lt;/code&gt; to the same stream as
&lt;code&gt;STDOUT&lt;/code&gt;, which in effect will make all output go to &lt;code&gt;/dev/null&lt;/code&gt;.  If you want
to log everything to a single file, you can replace &lt;code&gt;/dev/null&lt;/code&gt; with another
file of your choice. If you don&amp;rsquo;t want logs to be overwritten on each start,
use a &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; instead of a single &lt;code&gt;&amp;gt;&lt;/code&gt; at the start.&lt;/p&gt;
&lt;p&gt;If you want to log errors and output in different files, you can use the
following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; /var/log/service.log 2&amp;gt; /var/log/service.err
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will put standard output in &lt;code&gt;/var/log/service.log&lt;/code&gt; and errors in
&lt;code&gt;/var/log/service.err&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;fork-just-the-perl-6-program&#34;&gt;Fork just the Perl 6 program&lt;/h3&gt;
&lt;p&gt;In the initial example, I put the &lt;code&gt;&amp;amp;&lt;/code&gt; behind the &lt;code&gt;main&lt;/code&gt; call, at the bottom of
the script. While this works just fine for most simple usage, if you want to do
additional chores, like creating a pidfile after starting the Perl 6 program,
you&amp;rsquo;re out of luck. If you were to only fork the Perl 6 application, you could
handle some other cases in the shell script.&lt;/p&gt;
&lt;h3 id=&#34;the-final-solution&#34;&gt;The final solution&lt;/h3&gt;
&lt;p&gt;For those eager to just get going with this, here is the complete example
script to just fork your Perl program into the background:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#! /usr/bin/env sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;DIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;CDPATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; -- &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;dirname -- &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$0&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;pwd&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;main&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; -- &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;DIR&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/..&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    perl6 -Ilib bin/shinrind &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt; /dev/null &amp;gt;2&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;main &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$@&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>How to: git</title>
      <link>https://www.tyil.nl/post/2017/09/14/how-to-git/</link>
      <pubDate>Thu, 14 Sep 2017 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2017/09/14/how-to-git/</guid>
      <description>&lt;p&gt;This guide will explain how to use &lt;code&gt;git&lt;/code&gt; more efficiently, and why you should
use it as such.&lt;/p&gt;
&lt;h2 id=&#34;forking&#34;&gt;Forking&lt;/h2&gt;
&lt;p&gt;When working in a team, there&amp;rsquo;s generally a remote server which is used to sync
your repositories. There are gratis services, such as &lt;a href=&#34;https://github.com&#34;&gt;GitHub&lt;/a&gt;,
&lt;a href=&#34;https://gitlab.com&#34;&gt;Gitlab&lt;/a&gt;, &lt;a href=&#34;https://gogs.io&#34;&gt;GOGS&lt;/a&gt;, and others. These services also allow you to
&lt;em&gt;fork&lt;/em&gt; a repository. This basically makes a copy of the entire repository for
your own use. In it, you have full control over the branches, tags, merge
process and everything else you want to do with it.&lt;/p&gt;
&lt;p&gt;One the main reasons to do this is so you do not have to clutter up the main
repository with a ton of branches (these are explained later in the post). If
there are two people working in the same branch, it can help reduce conflicts,
as each developer is working on the branch in his own fork.&lt;/p&gt;
&lt;p&gt;As such, &lt;strong&gt;always&lt;/strong&gt; use a fork. If the service does not have a fancy button for
you to click, you can still fork manually. Simply clone their repository as
usual, set a new remote and push it there:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git clone git@domain.tld:them/repo.git
cd repo
git remote rename origin upstream
git remote add origin git@domain.tld:you/repo.git
git push origin master
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The default naming convention uses &lt;code&gt;upstream&lt;/code&gt; for the base of your fork, and
&lt;code&gt;origin&lt;/code&gt; for your remote version of the repository. If a merge request is
accepted on the original repo, you can apply it to your fork using&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git pull upstream master
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;branching&#34;&gt;Branching&lt;/h2&gt;
&lt;p&gt;Branching is the art of using separate branches to introduce new code into your
&lt;code&gt;master&lt;/code&gt; branch. Every git repository starts with a &lt;code&gt;master&lt;/code&gt; branch by default.
This is the &lt;em&gt;main&lt;/em&gt; branch of your repository.&lt;/p&gt;
&lt;p&gt;Every time you want to add new code to your project, make a branch for the
feature or issue you are trying to solve. This way, you can commit freely
without having to worry about having untested or possibly broken code in the
&lt;code&gt;master&lt;/code&gt; branch. If something were to come up with a higher priority, such as a
critical bug, you can simply create a new branch off of &lt;code&gt;master&lt;/code&gt;, fix it and
merge that back into &lt;code&gt;master&lt;/code&gt;, without having to worry about that other feature
you were working on, which is not in a releasable state yet. Once the fix is
applied, you go back to your feature branch on continue working on the cool new
stuff you wanted to implement. Now, the bug is fixed, and no code has been
released that should not have been released. If that&amp;rsquo;s not convincing enough,
try some of the &lt;a href=&#34;https://softwareengineering.stackexchange.com/questions/335654/git-what-issues-arise-from-working-directly-on-master&#34;&gt;Stack Overflow posts&lt;/a&gt; on this very topic.&lt;/p&gt;
&lt;p&gt;Branches can be made at your leisure, with next to no overhead on your project.
Do not be scared to play around with your code in a new branch to test
something out. You can also delete branches as quickly as you made them if you
are not satisfied with the result.&lt;/p&gt;
&lt;p&gt;Creating branches is done using &lt;code&gt;git checkout -b new-branch&lt;/code&gt;. If you need to
switch to another existing branch to change something, use
&lt;code&gt;git checkout other-branch&lt;/code&gt;. Deleting a branch can be done using
&lt;code&gt;git branch -D old-branch&lt;/code&gt;. You can get a list of all branches in the
repository with &lt;code&gt;git branch&lt;/code&gt;. The current branch is marked with an *.&lt;/p&gt;
&lt;p&gt;If you start a new branch to implement a feature, be sure to always branch off
of &lt;code&gt;master&lt;/code&gt;, unless you have a very compelling reason not to do so. If you are
not sure what reasons would validate branching off of another branch, you
should just branch off of &lt;code&gt;master&lt;/code&gt;. If you branch off of another branch, you
will have the commit history of the other branch. This often includes commits
not accepted into master yet, which might result into commits getting into
master which should not be there (yet), or annoying merge conflicts later on.&lt;/p&gt;
&lt;h3 id=&#34;merging&#34;&gt;Merging&lt;/h3&gt;
&lt;p&gt;Using multiple branches brings along the concept of &lt;em&gt;merging&lt;/em&gt; branches
together. When working in a group, this is generally done by maintainers of the
upstream repository, via a &lt;em&gt;merge request&lt;/em&gt;. For some reason, certain services
have named this as a &lt;em&gt;pull request&lt;/em&gt; instead. The base idea of the process is as
follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pull the latest &lt;code&gt;upstream/master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create a new branch&lt;/li&gt;
&lt;li&gt;Apply the change you want&lt;/li&gt;
&lt;li&gt;Issue a merge request via the service you are using
&lt;ul&gt;
&lt;li&gt;Generally, you want your change to be merged into their &lt;code&gt;master&lt;/code&gt; branch&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add a title and a description of your change: What does it do, and why should it be accepted&lt;/li&gt;
&lt;li&gt;Optionally, discuss the changes with the upstream maintainers&lt;/li&gt;
&lt;li&gt;Optionally, make a couple of changes to your branch, and push it again&lt;/li&gt;
&lt;li&gt;Upstream maintainer accepts your change&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When everything worked out, the upstream repository now contains your changes.
If you pull their branch again, it will contain your code. Using the merge
request process, your code can be easily reviewed by others, and discussed if
needed.&lt;/p&gt;
&lt;h2 id=&#34;committing&#34;&gt;Committing&lt;/h2&gt;
&lt;p&gt;Whenever you have changed anything in the repository and you wish to share
these changes, you have to commit the changes. Committing in general is not
something people tend to have issues with. Simple add the changes you want to
commit using &lt;code&gt;git add&lt;/code&gt; (add the &lt;code&gt;-p&lt;/code&gt; switch if you want to commit only parts of
a changed file), then &lt;code&gt;git commit&lt;/code&gt; and enter a descriptive message. And that is
where most annoyances come from: the commit &lt;em&gt;message&lt;/em&gt;. There are no hard rules
on this forced by git itself. There are, however, some de-facto standards and
best practices which you should always follow. Even if you never intend to
share the repository with other people, having good commit messages can help
you identify a certain change when you look back into the history.&lt;/p&gt;
&lt;p&gt;A git commit message should be short, no more than 79 characters, on the first
line. It should be readable as &amp;ldquo;this commit message will &amp;hellip;&amp;rdquo;, where your
commit message will replace the &amp;ldquo;&amp;hellip;&amp;rdquo;. It is a de-facto standard to start your
commit message with a capital letter, and leave off a finishing period. You do
not &lt;em&gt;have&lt;/em&gt; to adhere to if you hate this, but be sure that all your commits are
consistent in how they are formatted.&lt;/p&gt;
&lt;p&gt;If you need to explain anything beyond that, such as a rationale for the
change, or things the reviewer should pay attention to in this particular
commit, you can leave an empty line and publish this message in the commit
body.&lt;/p&gt;
&lt;p&gt;When you are using a bug tracking system, you might also want to have a footer
with additional information. On services such as &lt;a href=&#34;https://gitlab.com&#34;&gt;Gitlab&lt;/a&gt; and
&lt;a href=&#34;https://github.com&#34;&gt;GitHub&lt;/a&gt;, you can close issues by adding &amp;ldquo;Closes: #1&amp;rdquo; in the commit
message footer. A full commit message with all these things might look as
follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Fix overflow issue in table rendering mechanism

An overflow issue was found in the table rendering mechanism, as explained in
CVE-0123-45678. Regression tests have been included as well.

Closes: #35
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order to achieve these kind of messages, you need to be sure that your
commits can fit in to this structure. This means you need to make small
commits. Having many smaller commits makes it easier to review the changes,
keep short, descriptive messages to describe each change, and revert a single
change in case it breaks something.&lt;/p&gt;
&lt;h3 id=&#34;signing-your-commits&#34;&gt;Signing your commits&lt;/h3&gt;
&lt;p&gt;You can set up git to cryptographically sign each commit you make. This will
ensure that the commit you made is proven to be from you, and not someone
impersonating you. People impersonating you might try to get harmful code into
a repo where you are a trusted contributor. Having all commits signed in a
repository can contribute in verifying the integrity of the project.&lt;/p&gt;
&lt;p&gt;Recently, &lt;a href=&#34;https://github.com&#34;&gt;Github&lt;/a&gt; has added the &lt;strong&gt;Verified&lt;/strong&gt; tag to commits if the
commit contains a correct signature.&lt;/p&gt;
&lt;p&gt;To enable signing of all commits, add the following configuration to your
&lt;code&gt;~/.gitconfig&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[commit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;na&#34;&gt;gpgsign&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;[user]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;na&#34;&gt;signingkey&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;9ACFE193FFBC1F50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ofcourse, you will have to update the value of the &lt;code&gt;signingkey&lt;/code&gt; to match
the key you want to sign your commits with.&lt;/p&gt;
&lt;h2 id=&#34;closing-words&#34;&gt;Closing words&lt;/h2&gt;
&lt;p&gt;I hope this post will help you in your adventures with git. It is a great tool
or working on projects together, but it gets much better when you stick to some
best practices. If you have any suggestions for this post, or any questions
after finishing it, contact me via any method listed on &lt;a href=&#34;https://tyil.work&#34;&gt;my home page&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FreeBSD email server - Part &#43;: Calendars and contacts</title>
      <link>https://www.tyil.nl/post/2016/11/24/freebsd-email-server-part--calendars-and-contacts/</link>
      <pubDate>Thu, 24 Nov 2016 08:26:09 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/11/24/freebsd-email-server-part--calendars-and-contacts/</guid>
      <description>&lt;p&gt;This guide is an addition to the &lt;a href=&#34;https://www.tyil.nl/post/2016/10/31/freebsd-mailserver-part-1-preparations/&#34;&gt;FreeBSD email server series&lt;/a&gt;.
It is not required for your email server to operate properly, but it is often
considered a very important feature for those who want to switch from a third
party email provider to their own solution. It does build upon the completed
series, so be sure to work through that before starting on this.&lt;/p&gt;
&lt;h2 id=&#34;install-required-packages&#34;&gt;Install required packages&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pkg install py27-radicale
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;configure-radicale&#34;&gt;Configure Radicale&lt;/h2&gt;
&lt;h3 id=&#34;usrlocaletcradicaleconfig&#34;&gt;/usr/local/etc/radicale/config&lt;/h3&gt;
&lt;p&gt;Open up the &lt;code&gt;/usr/local/etc/radicale/config&lt;/code&gt; file, and update each &lt;code&gt;[block]&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;server&#34;&gt;[server]&lt;/h4&gt;
&lt;p&gt;The server is binding to &lt;code&gt;localhost&lt;/code&gt; only. This way it is not accessible on
&lt;code&gt;:5232&lt;/code&gt; from outside the server. Outside access will be provided through an
nginx reverse proxy instead.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;hosts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;127.1:5232&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;daemon&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;dns_lookup&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;base_prefix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;can_skip_base_prefix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;realm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Radicale - Password required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;encoding&#34;&gt;[encoding]&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;request&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;utf-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;stock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;utf-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;auth&#34;&gt;[auth]&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;IMAP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;imap_hostname&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;localhost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;imap_port&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;143&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;imap_ssl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;storage&#34;&gt;[storage]&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;filesystem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;filesystem_folder&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/usr/local/share/radicale&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;logging&#34;&gt;[logging]&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/usr/local/etc/radicale/logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;usrlocaletcradicalelogging&#34;&gt;/usr/local/etc/radicale/logging&lt;/h3&gt;
&lt;p&gt;This file is fine on the defaults in FreeBSD 11. This saves you from
configuring a little bit.&lt;/p&gt;
&lt;h2 id=&#34;configure-dovecot&#34;&gt;Configure Dovecot&lt;/h2&gt;
&lt;h3 id=&#34;enable-imap&#34;&gt;Enable imap&lt;/h3&gt;
&lt;p&gt;This option was disabled in the &lt;a href=&#34;https://www.tyil.nl/post/2016/10/31/freebsd-mailserver-part-1-preparations/&#34;&gt;IMAP server tutorial&lt;/a&gt;,
however, if we want to auth using the same credentials as the mailserver, this
option is needed again. Bind it to &lt;code&gt;localhost&lt;/code&gt;, so it can only be used
internally. In &lt;code&gt;/usr/local/etc/dovecont/conf.d/10-master.conf&lt;/code&gt;, enable the
&lt;code&gt;imap&lt;/code&gt; port again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;service imap-login {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;inet_listener imap {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;address&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;127.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        port = 143
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configure-nginx&#34;&gt;Configure nginx&lt;/h2&gt;
&lt;p&gt;To make using the service easier, you can setup &lt;a href=&#34;https://www.nginx.com/&#34;&gt;nginx&lt;/a&gt; to act as a
reverse proxy. If you followed the &lt;a href=&#34;https://www.tyil.nl/post/2016/10/25/setup-nginx-with-lets-encrypt-ssl/&#34;&gt;webserver tutorial&lt;/a&gt;,
you already have the basics for this set up. I do recommend you check this out,
as I will only explain how to configure a virtual host to deal with the reverse
proxy here.&lt;/p&gt;
&lt;h3 id=&#34;setup-a-reverse-proxy&#34;&gt;Setup a reverse proxy&lt;/h3&gt;
&lt;p&gt;Assuming you have taken the crash-course in setting up the nginx webserver, you
can attain a reverse proxy using the following config block. Note that this block
only does HTTPS, as I use HTTP only to redirect to HTTPS.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# static HTTPS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# listeners
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt;       &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ssl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;radicale.domain.tld&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# enable HSTS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;max-age=31536000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;includeSubdomains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;preload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# keys
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate&lt;/span&gt;      &lt;span class=&#34;s&#34;&gt;/usr/local/etc/letsencrypt/live/domain.tld/fullchain.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate_key&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;/usr/local/etc/letsencrypt/live/domain.tld/privkey.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# / handler
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;Host&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;X-Real-IP&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$remote_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://127.1:5232&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;enable-the-service-at-startup&#34;&gt;Enable the service at startup&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;radicale_enable=&amp;#34;YES&amp;#34;&amp;#39; &amp;gt;&amp;gt; /etc/rc.conf.local
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;start-the-server&#34;&gt;Start the server&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;service radicale start
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>FreeBSD email server - Part 5: Filtering mail</title>
      <link>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-5-filtering-mail/</link>
      <pubDate>Mon, 31 Oct 2016 20:02:19 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-5-filtering-mail/</guid>
      <description>&lt;p&gt;Being able to send mail and not be flagged as spam is pretty awesome on itself.
But you also get hit by a lot of spam. The more you give out your email address
and domain name, the more spam you will receive over time. I welcome you to
another part of the FreeBSD email server series. In this part, we will set up
email filtering at the server side.&lt;/p&gt;
&lt;p&gt;We will accomplish this with a couple packages, &lt;a href=&#34;https://spamassassin.apache.org/&#34;&gt;SpamAssassin&lt;/a&gt;
and &lt;a href=&#34;http://pigeonhole.dovecot.org/&#34;&gt;Pigeonhole&lt;/a&gt;. The former deals with scanning the emails to
deduce whether it is spam or not. The latter filters messages. We will use this
filtering to drop emails marked as spam by SpamAssassin into the Junk folder,
instead of the inbox.&lt;/p&gt;
&lt;h2 id=&#34;installing-the-packages&#34;&gt;Installing the packages&lt;/h2&gt;
&lt;p&gt;Both packages are available through FreeBSD&amp;rsquo;s &lt;code&gt;pkg&lt;/code&gt; utility. Install them as
such.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pkg install dovecot-pigeonhole spamassassin
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;spamassassin&#34;&gt;SpamAssassin&lt;/h2&gt;
&lt;h3 id=&#34;enabling-the-service&#34;&gt;Enabling the service&lt;/h3&gt;
&lt;p&gt;Like most services, you have to enable them as well. Pigeonhole is an extension
to Dovecot, and Dovecot will handle this one. SpamAssassin requires you to
configure the service as well. You can enable it and set sane configuration to
it with the following two commands.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;spamd_enable=&amp;#34;YES&amp;#34;&amp;#39; &amp;gt;&amp;gt; /etc/rc.conf.local
echo &amp;#39;spamd_flags=&amp;#34;-u spamd -H /srv/mail&amp;#34;&amp;#39; &amp;gt;&amp;gt; /etc/rc.conf.local
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;acquiring-default-spam-rules&#34;&gt;Acquiring default spam rules&lt;/h3&gt;
&lt;p&gt;SpamAssassin has to &amp;ldquo;learn&amp;rdquo; what counts as &lt;em&gt;spam&lt;/em&gt; and what counts as &lt;em&gt;ham&lt;/em&gt;. To
fetch these rules, you should execute the updates for SpamAssassin with the
following command.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sa-update
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You most likely want to run this once every while, so it is advised to setup a
cronjob for this purpose.&lt;/p&gt;
&lt;h2 id=&#34;postfix&#34;&gt;Postfix&lt;/h2&gt;
&lt;p&gt;In order to have mails checked by SpamAssassin, Postfix must be instructed to
pass all email through to SpamAssassin, which will hand them back with a
&lt;code&gt;X-Spam-Flag&lt;/code&gt; header attached to them. This header can be used by other
applications to treat it as spam.&lt;/p&gt;
&lt;h3 id=&#34;mastercf&#34;&gt;master.cf&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s not much to include to the already existing Postfix configuration to
enable SpamAssassin to do its job. Just open &lt;code&gt;/usr/local/etc/postfix/master.cf&lt;/code&gt;
and append the block given below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;spamassassin  unix  -       n       n       -       -       pipe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;na&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;spamd argv=/usr/local/bin/spamc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;pigeonhole&#34;&gt;Pigeonhole&lt;/h2&gt;
&lt;p&gt;Pigeonhole is an implementation of Sieve for Dovecot. It deals with filtering
messages on the server side using a set of rules, defined in a file usually
named &lt;code&gt;sieve&lt;/code&gt;. This file is generally saved at
&lt;code&gt;/srv/mail/domain.tld/user/sieve&lt;/code&gt;. A default file to filter spam out is the
following example.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sieve&#34; data-lang=&#34;sieve&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;fileinto&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;mailbox&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;header&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;:contains&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;X-Spam-Flag&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;YES&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;fileinto&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;:create&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Junk&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;stop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This looks for the &lt;code&gt;X-Spam-Flag&lt;/code&gt; header, which is added by SpamAssassin. If it
is set to &lt;code&gt;YES&lt;/code&gt;, this indicates SpamAssassin thinks the message is spam. As
such, sieve is instructed to filter this message into the folder &lt;code&gt;Junk&lt;/code&gt;, and to
create this folder if it does not exist yet. The &lt;code&gt;stop;&lt;/code&gt; makes sieve stop
trying to process this message further based on later rules.&lt;/p&gt;
&lt;h2 id=&#34;dovecot&#34;&gt;Dovecot&lt;/h2&gt;
&lt;p&gt;Dovecot needs some additional configuration to work with Pigeonhole. Modify the
following files and add the contents described.&lt;/p&gt;
&lt;h3 id=&#34;confd20-lmtpconf&#34;&gt;conf.d/20-lmtp.conf&lt;/h3&gt;
&lt;p&gt;This will enable Pigeonhole in Dovecot.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;protocol lmtp {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;na&#34;&gt;mail_plugins&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;$mail_plugins sieve&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;confd90-pluginconf&#34;&gt;conf.d/90-plugin.conf&lt;/h3&gt;
&lt;p&gt;This configures Pigeonhole to look for a file named &lt;code&gt;sieve&lt;/code&gt; in the mailbox
homedir, and execute that when delivering mail.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;plugin {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;na&#34;&gt;sieve&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/srv/mail/%d/%n/sieve&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Spam is a pain, especially if you get a lot of it. The configuration added in
this part of the FreeBSD email server series should get rid of most of it. This
also concludes the series. If you have any questions or suggestions, please
contact me via any of the methods detailed on &lt;a href=&#34;https://www.tyil.nl/&#34;&gt;my home page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading along, and enjoy your very own email server!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FreeBSD email server - Part 4: Message authentication</title>
      <link>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-4-message-authentication/</link>
      <pubDate>Mon, 31 Oct 2016 20:00:38 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-4-message-authentication/</guid>
      <description>&lt;p&gt;Welcome to another part in the FreeBSD email server series. This time, we are
going to setup some mechanisms to deal with message authentication. This
practice will make other email providers accept your email messages and deliver
them properly in the inbox of the receiving user, instead of their spam box.&lt;/p&gt;
&lt;p&gt;We will do so using three of the most common practices: &lt;a href=&#34;https://en.wikipedia.org/wiki/Sender_Policy_Framework&#34;&gt;SPF&lt;/a&gt;,
&lt;a href=&#34;http://www.dkim.org/&#34;&gt;DKIM&lt;/a&gt; and &lt;a href=&#34;http://dmarc.org/&#34;&gt;DMARC&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;dkim&#34;&gt;DKIM&lt;/h2&gt;
&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;
&lt;p&gt;The tools for DKIM are easily installed using &lt;code&gt;pkg&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pkg install opendkim
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;configuration&#34;&gt;Configuration&lt;/h3&gt;
&lt;p&gt;Write the following configuration into &lt;code&gt;/usr/local/etc/mail/opendkim.conf&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-apache&#34; data-lang=&#34;apache&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Syslog&lt;/span&gt;  yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# permissions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;UserID&lt;/span&gt;  postfix
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;UMask&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;007&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# general settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;AutoRestart&lt;/span&gt;         yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Background&lt;/span&gt;          yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Canonicalization&lt;/span&gt;    relaxed/relaxed
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;DNSTimeout&lt;/span&gt;          &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Mode&lt;/span&gt;                sv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;SignatureAlgorithm&lt;/span&gt;  rsa-sha256
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;SubDomains&lt;/span&gt;          no
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;X-&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Header&lt;/span&gt;            yes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;OversignHeaders&lt;/span&gt;     From
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# tables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;KeyTable&lt;/span&gt;      &lt;span class=&#34;sx&#34;&gt;/usr/local/etc/opendkim/key.table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;SigningTable&lt;/span&gt;  &lt;span class=&#34;sx&#34;&gt;/usr/local/etc/opendkim/signing.table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# socket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Socket&lt;/span&gt;  inet:8891@localhost
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# domains&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Domain&lt;/span&gt;    domain.tld.privkey
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;KeyFile&lt;/span&gt;   &lt;span class=&#34;sx&#34;&gt;/usr/local/etc/opendkim/domain.tld&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Selector&lt;/span&gt;  mail
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;postfix&#34;&gt;Postfix&lt;/h4&gt;
&lt;p&gt;Postfix needs to be instructed to sign the messages with a DKIM header using
the opendkim service. You can do so by inserting the following configuration
block somewhere around the end of &lt;code&gt;/usr/local/etc/postfix/main.cf&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# milters&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;milter_protocol&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;milter_default_action&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;reject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;smtpd_milters&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    inet:localhost:8891&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;system-service&#34;&gt;System service&lt;/h4&gt;
&lt;p&gt;OpenDKIM runs as a system service. As such, you will have to enable this
service in rcinit. This is a simple step, achieved with the given command.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;milteropendkim_enable=&amp;#34;YES&amp;#34;&amp;#39; &amp;gt;&amp;gt; /etc/rc.conf.local
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do not forget to actually start the service when you are done with the
tutorial!&lt;/p&gt;
&lt;h3 id=&#34;creating-and-using-keys&#34;&gt;Creating and using keys&lt;/h3&gt;
&lt;p&gt;In order to use DKIM, you will need to generate some keys to sign the messages
with. You cannot use your Let&amp;rsquo;s Encrypt SSL keys for this. First, create a
directory to house your domain&amp;rsquo;s keys.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkdir -p /usr/local/etc/opendkim/keys/domain.tld
chown -R postfix:wheel $_
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next up, generate your first key.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;opendkim-genkey -D /usr/local/etc/opendkim/keys -b 4096 -r -s $(date +%Y%m%d) -d domain.tld
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I tend to use the current date for the key names so I can easily sort them by
the most recent one.&lt;/p&gt;
&lt;p&gt;Afterwards, you will have to add a line to two separate files to instruct DKIM
to use this key for a certain domain when signing mail. These are fairly
straightforward and can be done using a simple &lt;code&gt;echo&lt;/code&gt; as well.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;*@domain.tld  domain.tld&amp;#39; &amp;gt;&amp;gt; /usr/local/etc/opendkim/signing.table
echo &amp;#34;domain.tld  domain.tld:$(date +%Y%m%d):/usr/local/etc/opendkim/keys/domain.tld/$(date +%Y%m%d).private&amp;#34; \
  &amp;gt;&amp;gt; /usr/local/etc/opendkim/key.table
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;adding-the-dns-records&#34;&gt;Adding the DNS records&lt;/h3&gt;
&lt;p&gt;You may have already noticed that &lt;code&gt;opendkim-genkey&lt;/code&gt; also creates a &lt;code&gt;.txt&lt;/code&gt; file
in addition to the private key. This text file contains the DNS record value
you need to add for your domain&amp;rsquo;s DNS. Add the record to your DNS server, and
simply wait for it to propagate.&lt;/p&gt;
&lt;h2 id=&#34;spf&#34;&gt;SPF&lt;/h2&gt;
&lt;p&gt;SPF is simply a DNS record that shows which IPs are allowed to email for that
domain.&lt;/p&gt;
&lt;h3 id=&#34;adding-the-dns-records-1&#34;&gt;Adding the DNS records&lt;/h3&gt;
&lt;p&gt;A simple example for an SPF record is the following. It allows mail to be sent
in the domain&amp;rsquo;s name from any IP listed in the MX records.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;v=spf1 mx -all
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;dmarc&#34;&gt;DMARC&lt;/h2&gt;
&lt;p&gt;DMARC is, like SPF, a DNS record. It tells how to deal with messages coming
from the server and where to report abuse of your server. Some of the larger
email providers send out reports to the address given in the DMARC record so
you can figure out whether someone is spamming from your servers, for example.&lt;/p&gt;
&lt;h3 id=&#34;adding-the-dns-records-2&#34;&gt;Adding the DNS records&lt;/h3&gt;
&lt;p&gt;A simple DMARC policy to get started with is to quarantine all emails that fail
authentication. This means the emails will go into the receiving user&amp;rsquo;s spam
box. In addition, abuse reports will be sent to the address defined in the
&lt;code&gt;rua&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;v=DMARC1; p=quarantine; rua=mailto:abuse@domain.tld
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;These few simple measures will make receiving servers trust the authenticity of
the mails you send. In effect, your messages will be much less likely to be
marked as spam.  However, you are a target of spam as well. How you can deal
with that, will be available in the next part of this series.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FreeBSD email server - Part 1: Preparations</title>
      <link>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-1-preparations/</link>
      <pubDate>Mon, 31 Oct 2016 07:57:50 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-1-preparations/</guid>
      <description>&lt;p&gt;This tutorial is devised into multiple chapters to make it more manageable, and
to be able to better explain why certain parts are needed.&lt;/p&gt;
&lt;p&gt;The tutorial is created out of experience setting up my own email server. I have
read through quite a lot of documentation so you do not have to. Nonetheless, I
would recommend doing so. Email business is a tricky one, with a lot of moving
parts that have to fit into each other. Knowing how exactly each part works will
greatly help understanding why they are needed in a proper email server.
Besides that, it will make your life a lot more enjoyable if you want to tweak
some things after this tutorial.&lt;/p&gt;
&lt;p&gt;To kick off, some preparations should be done before you start on setting up
your own email server.&lt;/p&gt;
&lt;h2 id=&#34;dns-setup&#34;&gt;DNS setup&lt;/h2&gt;
&lt;p&gt;Some DNS setup is required for mail. Most importantly, the MX records of a
domain. Be sure you have a domain available, otherwise, get one. There are
plenty of registrars and the price is pretty low for most domains. If you want
to look hip, get a &lt;code&gt;.email&lt;/code&gt; TLD for your email server.&lt;/p&gt;
&lt;p&gt;For the DNS records themselves, make sure you have an &lt;code&gt;A&lt;/code&gt; record pointing to
the server IP you&amp;rsquo;re going to use.  If you have an IPv6 address, set up an
&lt;code&gt;AAAA&lt;/code&gt; record as well. Mail uses the &lt;code&gt;MX&lt;/code&gt; DNS records. Make one with the value
&lt;code&gt;10 @&lt;/code&gt;. If you have multiple servers, you can make MX records for these as
well, but replace the &lt;code&gt;10&lt;/code&gt; with a higher value each time (&lt;code&gt;20&lt;/code&gt;, &lt;code&gt;30&lt;/code&gt;, etc).
These will be used as fallback, in case the server with pointed to by the &lt;code&gt;10&lt;/code&gt;
record is unavailable.&lt;/p&gt;
&lt;h2 id=&#34;postgresql&#34;&gt;PostgreSQL&lt;/h2&gt;
&lt;p&gt;Next up you will have to install and configure &lt;a href=&#34;https://www.postgresql.org/&#34;&gt;PostgreSQL&lt;/a&gt;. Although
using a database is not required, this tutorial will make use of one. Using a
database makes administration easier and allows you to add a pretty basic web
interface for this task.&lt;/p&gt;
&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;
&lt;p&gt;Since the tutorial uses FreeBSD 11, you can install PostgreSQL easily by running&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pkg install postgresql96-server
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;starting-up&#34;&gt;Starting up&lt;/h3&gt;
&lt;p&gt;In order to start Postfix, you should enable the system service for it. This
way, &lt;code&gt;service&lt;/code&gt; can be used to easily manage it. In addition, it will start
automatically on boot.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;postgresql_enable=&amp;#34;YES&amp;#34;&amp;#39; &amp;gt;&amp;gt; /etc/rc.conf.local
service postgresql start
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;database-initialization&#34;&gt;Database initialization&lt;/h3&gt;
&lt;p&gt;Since PostgreSQL is a little different than the more popular &lt;a href=&#34;https://www.mysql.com/&#34;&gt;MySQL&lt;/a&gt;, I
will guide you through setting up the database as well. To begin, switch user
to &lt;code&gt;postgres&lt;/code&gt;, which is the default administrative user for PostgreSQL. Then
simply open up the PostgreSQL CLI.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;su postgres
psql
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once you are logged in to PostgreSQL, create a new user which will hold
ownership of the database and make a database for this user.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;USER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;postfix&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PASSWORD&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;incredibly-secret!&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DATABASE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mail&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;OWNER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;postfix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once this is done, create the tables which will hold some of our configuration
data.&lt;/p&gt;
&lt;h4 id=&#34;domains&#34;&gt;domains&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;domains&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;VARCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;users&#34;&gt;users&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;local&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;VARCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;VARCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;VARCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;128&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;local&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOREIGN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;REFERENCES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;domains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DELETE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CASCADE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;aliases&#34;&gt;aliases&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;aliases&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;VARCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;origin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;VARCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destination&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;VARCHAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;origin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destination&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOREIGN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;REFERENCES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;domains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DELETE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;CASCADE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;lets-encrypt&#34;&gt;Let&amp;rsquo;s Encrypt&lt;/h2&gt;
&lt;h3 id=&#34;installation-1&#34;&gt;Installation&lt;/h3&gt;
&lt;p&gt;Installing the &lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt; client is just as straightforward
as the PostgreSQL database, using &lt;code&gt;pkg&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pkg install py27-certbot
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;getting-a-certificate&#34;&gt;Getting a certificate&lt;/h3&gt;
&lt;p&gt;Requesting a certificate requires your DNS entries to properly resolve. If they
do not resolve yet, Let&amp;rsquo;s Encrypt will bother you with errors. If they do
resolve correctly, use &lt;code&gt;certbot&lt;/code&gt; to get your certificate.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;certbot certonly --standalone -d domain.tld
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This should be everything required to get started on setting up your own email
server. Continue to &lt;a href=&#34;https://www.tyil.nl/post/2016/10/31/freebsd-mailserver-part-2-mailing-with-postfix/&#34;&gt;part 2&lt;/a&gt; of this series to start setting up
Postfix.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FreeBSD email server - Part 3: Dovecot, IMAP and SASL</title>
      <link>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-3-dovecot-imap-and-sasl/</link>
      <pubDate>Mon, 31 Oct 2016 07:57:50 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-3-dovecot-imap-and-sasl/</guid>
      <description>&lt;p&gt;Welcome to the second part of my FreeBSD email server series. In this series, I
will guide you through setting up your own email service. Be sure to read the
previous parts before trying to continue on this part in case you have not done
so yet.&lt;/p&gt;
&lt;p&gt;This part will guide you through setting up &lt;a href=&#34;http://dovecot.org/&#34;&gt;Dovecot&lt;/a&gt;. This service
will deal with the SASL authentication to your email server and making your email
boxes accessible via IMAP. While this guide does not cover POP3 functionality,
Dovecot can handle this as well.&lt;/p&gt;
&lt;p&gt;Just like the Postfix setup, Dovecot has quite a few configuration options to
set before it will work as expected in this setup. If you have questions after
reading the full guide, please find me on IRC. You can find details on how to
do so on &lt;a href=&#34;https://www.tyil.nl/&#34;&gt;my homepage&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;installing-dovecot&#34;&gt;Installing Dovecot&lt;/h2&gt;
&lt;p&gt;Dovecot will also be installed from the ports tree from FreeBSD. As this guide
assumes you are working through them in order, explanation of acquiring the
ports tree will be omitted here.&lt;/p&gt;
&lt;p&gt;You can start the installation procedure with the following commands.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cd /usr/ports/mail/dovecot2
make configure install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, like with the Postfix installation, leave the default options on and add
the &lt;code&gt;PGSQL&lt;/code&gt; option so Dovecot can use PostgreSQL as the database back-end.&lt;/p&gt;
&lt;h2 id=&#34;enabling-dovecot&#34;&gt;Enabling Dovecot&lt;/h2&gt;
&lt;p&gt;Enable the Dovecot service for rcinit.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;dovecot_enable=&amp;#34;YES&amp;#34;&amp;#39; &amp;gt;&amp;gt; /etc/rc.conf.local
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;configuring-dovecot&#34;&gt;Configuring Dovecot&lt;/h2&gt;
&lt;p&gt;To start of with Dovecot configuration, copy over the sample files first.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cp -r /usr/local/etc/dovecot/example-config/* /usr/local/etc/dovecot/.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you can start editing a number of pesky files. The file names of the
headings all appear relative to &lt;code&gt;/usr/local/etc/dovecot&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;dovecotconf&#34;&gt;dovecot.conf&lt;/h3&gt;
&lt;p&gt;Here you only have to set which protocols you want to enable. Set them as
follows.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;protocols&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;imap lmtp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;confd10-mastercf&#34;&gt;conf.d/10-master.cf&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;master.cf&lt;/code&gt; configuration file indicates which sockets Dovecot should use
and provide and as which user its processes should be ran. Keep the defaults as
they are, with the exception of the following two blocks.&lt;/p&gt;
&lt;h4 id=&#34;service-imap-login&#34;&gt;service imap-login&lt;/h4&gt;
&lt;p&gt;This will enable imaps, IMAP over SSL, and disable plain IMAP.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;service-imap-login {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;inet_listener imap {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;port&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;inet_listener imaps {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;port&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;993
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        ssl = yes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;services&#34;&gt;services&lt;/h4&gt;
&lt;p&gt;This will instruct Dovecot to provide a service for authentication and &lt;code&gt;lmtp&lt;/code&gt;
the &lt;strong&gt;local mail transport protocol&lt;/strong&gt;. This is required to deliver the email
files into the correct email box location in the file system.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;service auth {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;unix_listener auth-userdb {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;mode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;0600
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        user = postfix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        group = postfix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;unix_listener /var/spool/postfix/private/auth {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;mode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;0666
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        user = postfix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        group = postfix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;dovecot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;service lmtp {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;unix_listener /var/spool/postfix/private/dovecot-lmtp {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;na&#34;&gt;mode&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;0600
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        user = postfix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        group = postfix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;service auth-worker {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;postfix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;confd10-sslconf&#34;&gt;conf.d/10-ssl.conf&lt;/h3&gt;
&lt;p&gt;Here you have to enable SSL and provide the correct paths to your SSL key in
order for Dovecot to work with them.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ssl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ssl_cert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;lt; /usr/local/etc/letsencrypt/live/domain.tld/fullchain.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;ssl_key&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;lt; /usr/local/etc/letsencrypt/live/domain.tld/privkey.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;confd10-mailconf&#34;&gt;conf.d/10-mail.conf&lt;/h3&gt;
&lt;p&gt;The mail.conf location instructs Dovecot which location to appoint for storing
the email files. &lt;code&gt;%d&lt;/code&gt; expands to the domain name, while &lt;code&gt;%n&lt;/code&gt; expands to the
local part of the email address.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;mail_home&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/srv/mail/%d/%n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;mail_location&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;maildir:~/Maildir&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make sure the location set by &lt;code&gt;mail_home&lt;/code&gt; exists and is owned by &lt;code&gt;postfix&lt;/code&gt;!&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkdir -p /srv/mail
chown postfix:postfix /srv/mail
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;confd10-authconf&#34;&gt;conf.d/10-auth.conf&lt;/h3&gt;
&lt;p&gt;This file deals with the authentication provided by Dovecot. Mostly, which
mechanisms should be supported and what mechanism should be used to get the
actual credentials to check against.  Make sure the following options are set
as given&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;disable_plaintext_auth&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;yes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;auth_mechanisms&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;plain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also, make sure &lt;code&gt;!include auth-system.conf.ext&lt;/code&gt; is commented &lt;strong&gt;out&lt;/strong&gt;. It is not
commented out by default, so you will have to do this manually. In addition,
you have to uncomment &lt;code&gt;!include auth-sql.conf.ext&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;confdauth-sqlconfext&#34;&gt;conf.d/auth-sql.conf.ext&lt;/h3&gt;
&lt;p&gt;This is the file included from &lt;code&gt;10-auth.conf&lt;/code&gt;. It instructs Dovecot to use SQL as
the driver for the password and user back-ends.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;passdb {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;driver&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    args = /usr/local/etc/dovecot/dovecot-sql-conf.ext&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;userdb {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;driver&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;prefetch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;userdb {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;na&#34;&gt;driver&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    args = /usr/local/etc/dovecot/dovecot-sql-conf.ext&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;dovecot-sqlconfext&#34;&gt;dovecot-sql.conf.ext&lt;/h3&gt;
&lt;p&gt;The final configuration file entails the queries which should be used to get the
required information about the users. Make sure to update the &lt;code&gt;password&lt;/code&gt; and possibly
other parameters used to connect to the database. You may have to update the &lt;code&gt;125&lt;/code&gt; as
well, as this has to be identical to the &lt;code&gt;UID&lt;/code&gt; of &lt;code&gt;postfix&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As a side note, if you are following this tutorial on a machine that does
&lt;strong&gt;not&lt;/strong&gt; support Blowfish in the default glib, which is nearly every GNU+Linux
setup, you &lt;strong&gt;can not&lt;/strong&gt; use &lt;code&gt;BLF-CRYPT&lt;/code&gt; as the &lt;code&gt;default_pass_scheme&lt;/code&gt;. You will
have to settle for the &lt;code&gt;SHA-512&lt;/code&gt; scheme instead.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;driver&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;pgsql&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;connect&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;host=127.1 dbname=mail user=postfix password=incredibly-secret!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;default_pass_scheme&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;BLF-CRYPT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;password_query&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    SELECT \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        local AS user, \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        password, \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        &amp;#39;/srv/mail/%d/%n&amp;#39; AS userdb_home, \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        125 AS userdb_uid, \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        125 AS userdb_gid \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    FROM users \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    WHERE local=&amp;#39;%n&amp;#39; AND domain=&amp;#39;%d&amp;#39;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;user_query&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    SELECT \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        &amp;#39;/srv/mail/%d/%n&amp;#39; AS home \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        125 AS uid, \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        125 AS gid \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    FROM users \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    WHERE local=&amp;#39;%n&amp;#39; AND domain=&amp;#39;%d&amp;#39;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;After this part, you should be left with a functioning email server that
provides IMAP over a secure connection. While this is great on itself, for
actual use in the wild, you should setup some additional services. Therefore,
in the next part, we will deal with practices that &amp;ldquo;authenticate&amp;rdquo; your emails
as legit messages. Be sure to read up on it!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>FreeBSD email server - Part 2: Mailing with Postfix</title>
      <link>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-2-mailing-with-postfix/</link>
      <pubDate>Mon, 31 Oct 2016 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/31/freebsd-email-server-part-2-mailing-with-postfix/</guid>
      <description>&lt;p&gt;Welcome to the second part of my FreeBSD email server series. In this series, I
will guide you through setting up your own email service. Be sure to done the
preparations from &lt;a href=&#34;https://www.tyil.nl/post/2016/10/31/freebsd-mailserver-part-1-preparations/&#34;&gt;part 1&lt;/a&gt; of this series.&lt;/p&gt;
&lt;p&gt;This part will guide you through setting up email service on your machine using
&lt;a href=&#34;http://www.postfix.org/&#34;&gt;Postfix&lt;/a&gt;. Basic installation is pretty straightforward, but there is
a lot to configure. If you are not sure what some configuration options do,
please read up on them. There is a lot to do wrong with a mail server, and
doing things wrong will likely get you on a blacklist which will make other
servers stop processing the mail you are trying to send out.&lt;/p&gt;
&lt;p&gt;Setting up Postfix is one of the harder parts of configuring a mail server. If
you have questions after reading the full guide, please find me on IRC. You can
find details on how to do so on &lt;a href=&#34;https://www.tyil.nl/&#34;&gt;my homepage&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;installing-postfix&#34;&gt;Installing Postfix&lt;/h2&gt;
&lt;p&gt;Installation procedures on FreeBSD are pretty straightforward. Unlike &lt;code&gt;certbot&lt;/code&gt;
from the previous part, we will need to compile Postfix from source in order to
use PostgreSQL as a database back-end. Thanks to FreeBSD&amp;rsquo;s
&lt;a href=&#34;https://www.freebsd.org/ports/&#34;&gt;ports&lt;/a&gt;, this is not difficult either. If this is your first
port to compile, you probably need to get the ports tree first. You can
download and extract this using the following command.&lt;/p&gt;
&lt;p&gt;{% highlight sh %}
portsnap fetch extract
{% endhighlight %}&lt;/p&gt;
&lt;p&gt;Once that has finished running, go into the directory containing the build
instructions for Postfix, and start the installation process.&lt;/p&gt;
&lt;p&gt;{% highlight sh %}
cd /usr/ports/mail/postfix
make configure install
{% endhighlight %}&lt;/p&gt;
&lt;p&gt;This will open a pop up with a number of options you can enable or disable. The
enabled defaults are fine, but you will have to enable the &lt;code&gt;PGSQL&lt;/code&gt; option. This
will allow you to use the configuration tables created in part 1.&lt;/p&gt;
&lt;h2 id=&#34;enabling-postfix&#34;&gt;Enabling Postfix&lt;/h2&gt;
&lt;p&gt;Enable the Postfix service for rcinit. This allows you to use &lt;code&gt;service postfix start&lt;/code&gt; once configuration is done, and will auto start the service on system
boot. In addition, the default mailer on FreeBSD, &lt;a href=&#34;http://www.sendmail.com/sm/open_source/&#34;&gt;sendmail&lt;/a&gt; should
be disabled so nothing is in Postfix&amp;rsquo;s way when trying to deal with processing
email traffic.&lt;/p&gt;
&lt;p&gt;{% highlight sh %}&lt;/p&gt;
&lt;h1 id=&#34;disable-the-default-sendmail-system&#34;&gt;disable the default sendmail system&lt;/h1&gt;
&lt;p&gt;echo &amp;lsquo;daily_clean_hoststat_enable=&amp;ldquo;NO&amp;rdquo;&amp;rsquo; &amp;raquo; /etc/periodic.conf.local
echo &amp;lsquo;daily_status_mail_rejects_enable=&amp;ldquo;NO&amp;rdquo;&amp;rsquo; &amp;raquo; /etc/periodic.conf.local
echo &amp;lsquo;daily_status_include_submit_mailq=&amp;ldquo;NO&amp;rdquo;&amp;rsquo; &amp;raquo; /etc/periodic.conf.local
echo &amp;lsquo;daily_submit_queuerun=&amp;ldquo;NO&amp;rdquo;&amp;rsquo; &amp;raquo; /etc/periodic.conf.local
echo &amp;lsquo;sendmail_enable=&amp;ldquo;NONE&amp;rdquo;&amp;rsquo; &amp;raquo; /etc/rc.conf.local&lt;/p&gt;
&lt;h1 id=&#34;enable-postfix&#34;&gt;enable postfix&lt;/h1&gt;
&lt;p&gt;echo &amp;lsquo;postfix_enable=&amp;ldquo;YES&amp;rdquo;&amp;rsquo; &amp;raquo; /etc/rc.conf.local
{% endhighlight %}&lt;/p&gt;
&lt;h2 id=&#34;configuring-postfix&#34;&gt;Configuring Postfix&lt;/h2&gt;
&lt;p&gt;There is a ton to configure for Postfix. This configuration happens in two
files, &lt;code&gt;main.cf&lt;/code&gt; and &lt;code&gt;master.cf&lt;/code&gt;. Additionally, as some data is in the
PostgreSQL database, three files with information on how to query for this
information are needed. All of these files are in &lt;code&gt;/usr/local/etc/postfix&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The guide has a comment line for most blocks. It is advised that &lt;strong&gt;if&lt;/strong&gt; you
decide to just copy and paste the contents, you copy that along so you have
some sort of indication of what is where. This could help you out if you ever
need to change anything later on.&lt;/p&gt;
&lt;h3 id=&#34;maincf&#34;&gt;main.cf&lt;/h3&gt;
&lt;h4 id=&#34;compatibility&#34;&gt;Compatibility&lt;/h4&gt;
&lt;p&gt;The configuration file starts off by setting the compatibility level. If
postfix updates the configuration scheme and deprecates certain options, you
will be notified of this in the logs.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;compatibility-1&#34;&gt;compatibility&lt;/h1&gt;
&lt;p&gt;compatibility_level = 2
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;directory-paths&#34;&gt;Directory paths&lt;/h4&gt;
&lt;p&gt;These options indicate where Postfix will look and keep certain files required
for correct operation.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;directory-paths-1&#34;&gt;directory paths&lt;/h1&gt;
&lt;p&gt;queue_directory = /var/spool/postfix
command_directory = /usr/local/sbin
daemon_directory = /usr/local/libexec/postfix
data_directory = /var/db/postfix
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;domain-configuration&#34;&gt;Domain configuration&lt;/h4&gt;
&lt;p&gt;The domain configuration instruct the server of the domain(s) it should serve
for. Use your FQDN without sub domains for &lt;code&gt;mydomain&lt;/code&gt;. You can use a sub domain
for &lt;code&gt;myhostname&lt;/code&gt;, but you are not required to. The most common setting is
using a &lt;code&gt;mail&lt;/code&gt; sub domain for all mail related activities, which would
result in something like this.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;domain-configuration-1&#34;&gt;domain configuration&lt;/h1&gt;
&lt;p&gt;myhostname = mail.domain.tld
mydomain = domain.tld
myorigin = $mydomain
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;listening-directives&#34;&gt;Listening directives&lt;/h4&gt;
&lt;p&gt;All internet devices it should listen on, and all domains this server should
consider itself the endpoint for, should be listed here. The defaults in the
example block are good enough, as we put some of our data in the PostgreSQL
database instead.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;listening-directives-1&#34;&gt;listening directives&lt;/h1&gt;
&lt;p&gt;inet_interfaces = all
mydestination = $myhostname, localhost.$mydomain, localhost
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;reject-unknown-recipients&#34;&gt;Reject unknown recipients&lt;/h4&gt;
&lt;p&gt;How to deal with messages sent to an email address whose domain points to your
server&amp;rsquo;s address, but have no actual mailbox. A code of &lt;code&gt;550&lt;/code&gt; means to inform
the remote server that delivery is not possible and will not be possible. This
should stop the remote server from trying it again.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;reject-unknown-recipients-1&#34;&gt;reject unknown recipients&lt;/h1&gt;
&lt;p&gt;unknown_local_recipient_reject_code = 550
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;trust&#34;&gt;Trust&lt;/h4&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;trust-1&#34;&gt;trust&lt;/h1&gt;
&lt;p&gt;mynetworks_style = host
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;address-extensions&#34;&gt;Address extensions&lt;/h4&gt;
&lt;p&gt;This block is optional. It allows you to use email address extensions. These
are addresses with an additional character in them that will drop the email in
the non extended address&amp;rsquo; mailbox, but allows you to quickly filter on them as
the sent-to address contains the extension.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;address-extensions-1&#34;&gt;address extensions&lt;/h1&gt;
&lt;p&gt;recipient_delimiter = +
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;virtual-domain-directives&#34;&gt;Virtual domain directives&lt;/h4&gt;
&lt;p&gt;This part is where things get important. Virtual domains allow you to handle
mail for a large number of domains that are different from the actual server&amp;rsquo;s
domain. This is where the database configuration comes in to play. It is
important to note the &lt;code&gt;static:125&lt;/code&gt; values. The &lt;code&gt;125&lt;/code&gt; should map to the &lt;code&gt;UID&lt;/code&gt; of
the &lt;code&gt;postfix&lt;/code&gt; user account on your system.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;virtual-domain-directives-1&#34;&gt;virtual domain directives&lt;/h1&gt;
&lt;p&gt;virtual_mailbox_base = /srv/mail
virtual_mailbox_domains = pgsql:/usr/local/etc/postfix/pgsql-virtual-domains.cf
virtual_mailbox_maps = pgsql:/usr/local/etc/postfix/pgsql-virtual-users.cf
virtual_alias_maps = pgsql:/usr/local/etc/postfix/pgsql-virtual-aliases.cf
virtual_uid_maps = static:125
virtual_gid_maps = static:125
virtual_transport = lmtp:unix:private/dovecot-lmtp
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;tls-setup&#34;&gt;TLS setup&lt;/h4&gt;
&lt;p&gt;The TLS setup configures your server to use secure connections. The keys used
here have been generated in the previous part of this series.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;tls-setup-1&#34;&gt;TLS setup&lt;/h1&gt;
&lt;p&gt;smtpd_tls_cert_file = /usr/local/etc/letsencrypt/live/domain.tld/fullchain.pem
smtpd_tls_key_file = /usr/local/etc/letsencrypt/live/domain.tld/privkey.pem
smtpd_use_tls = yes
smtpd_tls_auth_only = yes
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;sasl-setup&#34;&gt;SASL setup&lt;/h4&gt;
&lt;p&gt;SASL deals with the authentication of the users to your email server.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;sasl-setup-1&#34;&gt;SASL setup&lt;/h1&gt;
&lt;p&gt;smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
smtpd_relay_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;debugging&#34;&gt;Debugging&lt;/h4&gt;
&lt;p&gt;The debugging options are generally useful in case things break. If you have
little traffic, you could leave them on forever in case you want to debug
something later on. Once your server is working as intended, you should turn
these options off. The postfix logs get pretty big in a short amount of time.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;debugging-1&#34;&gt;debugging&lt;/h1&gt;
&lt;p&gt;debug_peer_level = 2
debugger_command =
PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/binary
ddd $daemon_directory/$process_name $process_id &amp;amp; sleep 5
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;installation-time-defaults&#34;&gt;Installation time defaults&lt;/h4&gt;
&lt;p&gt;These options should not be touched, but are very important to have for your
server.&lt;/p&gt;
&lt;p&gt;{% highlight ini %}&lt;/p&gt;
&lt;h1 id=&#34;install-time-defaults&#34;&gt;install-time defaults&lt;/h1&gt;
&lt;p&gt;sendmail_path = /usr/local/sbin/sendmail
newaliases_path = /usr/local/bin/newaliases
mailq_path = /usr/local/bin/mailq
setgid_group = maildrop
html_directory = /usr/local/share/doc/postfix
manpage_directory = /usr/local/man
sample_directory = /usr/local/etc/postfix
readme_directory = /usr/local/share/doc/postfix
inet_protocols = ipv4
meta_directory = /usr/local/libexec/postfix
shlib_directory = /usr/local/lib/postfix
{% endhighlight %}&lt;/p&gt;
&lt;h3 id=&#34;mastercf&#34;&gt;master.cf&lt;/h3&gt;
&lt;p&gt;For the &lt;code&gt;master.cf&lt;/code&gt; file, you can use the following configuration block.&lt;/p&gt;
&lt;p&gt;{% highlight cfg %}
submission    inet  n       -       n       -       -       smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
pickup        unix  n       -       n       60      1       pickup
cleanup       unix  n       -       n       -       0       cleanup
qmgr          unix  n       -       n       300     1       qmgr
tlsmgr        unix  -       -       n       1000?   1       tlsmgr
rewrite       unix  -       -       n       -       -       trivial-rewrite
bounce        unix  -       -       n       -       0       bounce
defer         unix  -       -       n       -       0       bounce
trace         unix  -       -       n       -       0       bounce
verify        unix  -       -       n       -       1       verify
flush         unix  n       -       n       1000?   0       flush
proxymap      unix  -       -       n       -       -       proxymap
proxywrite    unix  -       -       n       -       1       proxymap
smtp          unix  -       -       n       -       -       smtp
relay         unix  -       -       n       -       -       smtp
showq         unix  n       -       n       -       -       showq
error         unix  -       -       n       -       -       error
retry         unix  -       -       n       -       -       error
discard       unix  -       -       n       -       -       discard
local         unix  -       n       n       -       -       local
virtual       unix  -       n       n       -       -       virtual
lmtp          unix  -       -       n       -       -       lmtp
anvil         unix  -       -       n       -       1       anvil
scache        unix  -       -       n       -       1       scache
{% endhighlight %}&lt;/p&gt;
&lt;h3 id=&#34;sql-query-files&#34;&gt;SQL query files&lt;/h3&gt;
&lt;p&gt;The following three configuration files deal with the SQL query files to make
Postfix able of getting some of its configuration from a database. You
obviously have to change the first 4 directives to match your database
authentication credentials.&lt;/p&gt;
&lt;h4 id=&#34;pgsql-virtual-domainscf&#34;&gt;pgsql-virtual-domains.cf&lt;/h4&gt;
&lt;p&gt;{% highlight ini %}
user = postgres
password = incredibly-secret!
hosts = 127.1
dbname = mail
query = SELECT 1 FROM domains WHERE name=&amp;rsquo;%s&amp;rsquo;;
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;pgsql-virtual-userscf&#34;&gt;pgsql-virtual-users.cf&lt;/h4&gt;
&lt;p&gt;{% highlight ini %}
user = postgres
password = incredibly-secret!
hosts = 127.1
dbname = mail
query = SELECT 1 FROM users WHERE local=&amp;rsquo;%u&amp;rsquo; AND domain=&amp;rsquo;%d&amp;rsquo;;
{% endhighlight %}&lt;/p&gt;
&lt;h4 id=&#34;pgsql-virtual-aliasescf&#34;&gt;pgsql-virtual-aliases.cf&lt;/h4&gt;
&lt;p&gt;{% highlight ini %}
user = postfix
password = nope
hosts = 127.1
dbname = mail
query = SELECT destination FROM aliases WHERE origin=&amp;rsquo;%s&amp;rsquo;;
{% endhighlight %}&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This should be enough Postfix configuration, for now. Next part involves
Dovecot, which will enable IMAP. It will also provide the SASL mechanism
defined in this part.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setup a VPN with cjdns</title>
      <link>https://www.tyil.nl/post/2016/10/25/setup-a-vpn-with-cjdns/</link>
      <pubDate>Tue, 25 Oct 2016 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/25/setup-a-vpn-with-cjdns/</guid>
      <description>&lt;p&gt;In this tutorial I will outline a simple setup for a &lt;a href=&#34;https://en.wikipedia.org/wiki/Virtual_private_network&#34;&gt;VPN&lt;/a&gt; using
&lt;a href=&#34;https://github.com/cjdelisle/cjdns&#34;&gt;&lt;code&gt;cjdns&lt;/code&gt;&lt;/a&gt;. Cjdns will allow you to setup a secure mesh vpn which uses
IPv6 internally.&lt;/p&gt;
&lt;h2 id=&#34;requirements&#34;&gt;Requirements&lt;/h2&gt;
&lt;p&gt;For this tutorial, I have used two client machines, both running Funtoo. A
FreeBSD 11 server is used as a global connection point.&lt;/p&gt;
&lt;p&gt;You are ofcourse able to use any other OS or distro supported by cjdns, but you
may have to update some steps to work on your environment in that case.&lt;/p&gt;
&lt;h2 id=&#34;installation-of-the-server&#34;&gt;Installation of the server&lt;/h2&gt;
&lt;h3 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h3&gt;
&lt;p&gt;Before you can begin, we need some dependencies. There&amp;rsquo;s only two of those, and
they are available via &lt;code&gt;pkg&lt;/code&gt; to make it even easier. Install them as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pkg install gmake node
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;compiling&#34;&gt;Compiling&lt;/h3&gt;
&lt;p&gt;Next up is getting the cjdns sources and compile these, as cjdns is not
available as a prebuilt package:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkdir -p ~/.local/src
cd $_
git clone https://github.com/cjdelisle/cjdns.git cjdns
cd $_
./do
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To make the compiled binary available system-wide so we can use it with a
system service, copy it to &lt;code&gt;/usr/local/bin&lt;/code&gt; and rehash to make it available as
a direct command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cp cjdroute /usr/local/bin/.
hash -r
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;configuring&#34;&gt;Configuring&lt;/h3&gt;
&lt;p&gt;Cjdns provides a flag to generate the initial configuration. This will provide
you with some sane defaults where only a couple of small changes are needed to
make it work properly. Generate these defaults with &lt;code&gt;--genconf&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(umask 177 &amp;amp;&amp;amp; cjdroute --genconf &amp;gt; /usr/local/etc/cjdroute.conf)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The umask will make all following commands write files using &lt;code&gt;600&lt;/code&gt; permissions.
This makes sure the config file is not readable by people who shouldn&amp;rsquo;t be able
to read it. Be sure to check wether the owner of the file is &lt;code&gt;root&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Now you can start actually configuring the node to allow incoming connections.
You have to find the &lt;code&gt;authorizedPasswords&lt;/code&gt; array in the &lt;code&gt;cjdroute.conf&lt;/code&gt; file
and remove the contents of it. Then you can add your own machines in it. This
guide follows the assumption of two clients, so the config for two clients will
be shown here. You can add more clients if you wish, ofcourse.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;authorizedPasswords&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aeQu6pa4Vuecai3iebah7ogeiShaeDaepha6Mae1yooThoF0oa0Eetha9oox&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;client_1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aiweequuthohkahx4tahLohPiezee9OhweiShoNeephe0iekai2jo9Toorah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;client_2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you need to generate a password, you can make use of the tool &lt;code&gt;pwgen&lt;/code&gt;,
available at your local package manager. You can then generate new passwords by
running &lt;code&gt;pwgen 60 -1&lt;/code&gt;. Change the &lt;code&gt;60&lt;/code&gt; around if you want passwords of a
different size.&lt;/p&gt;
&lt;h3 id=&#34;adding-a-startup-service&#34;&gt;Adding a startup service&lt;/h3&gt;
&lt;p&gt;rcinit has deceptively easy scripts to make applications available as services.
This in turn allows you to enable a service at startup. This way you can make
sure cjdns starts whenever the server boots. You can copy the following
contents directly into &lt;code&gt;/usr/local/etc/rc.d/cjdroute&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# PROVIDE: cjdroute&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# KEYWORD: shutdown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Add the following lines to /etc/rc.conf to enable cjdroute:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#cjdroute_enable=&amp;#34;YES&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;. /etc/rc.subr
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cjdroute&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;rcvar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cjdroute_enable&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;load_rc_config &lt;span class=&#34;nv&#34;&gt;$name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;: &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;cjdroute_config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:=/usr/local/etc/cjdroute.conf&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/local/bin/cjdroute&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;command_args&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; &amp;lt; &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;cjdroute_config&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;run_rc_command &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Afterwards, you must enable the service in &lt;code&gt;/etc/rc.conf.local&lt;/code&gt; like follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;echo &amp;#39;cjdroute_enable=&amp;#34;YES&amp;#34;&amp;#39; &amp;gt;&amp;gt; /etc/rc.conf.local
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;installation-of-the-clients&#34;&gt;Installation of the clients&lt;/h2&gt;
&lt;h3 id=&#34;dependencies-1&#34;&gt;Dependencies&lt;/h3&gt;
&lt;p&gt;The dependencies are still on &lt;code&gt;gmake&lt;/code&gt; and &lt;code&gt;node&lt;/code&gt;, so simply install those on
your clients. This guide assumes using Funtoo for the clients, so installation
would go as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;emerge gmake nodejs
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;compiling-1&#34;&gt;Compiling&lt;/h3&gt;
&lt;p&gt;Compilation is the same as for the server, so check back there for more
information if you have already forgotten.&lt;/p&gt;
&lt;h3 id=&#34;configuring-1&#34;&gt;Configuring&lt;/h3&gt;
&lt;p&gt;Generating the base configuration is again done using &lt;code&gt;cjdroute --genconf&lt;/code&gt;,
just like on the server. On Funtoo, config files generally reside in &lt;code&gt;/etc&lt;/code&gt;
instead of &lt;code&gt;/usr/local/etc&lt;/code&gt;, so you should set the filepath you write the
configuration to accordingly:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cjdroute --genconf &amp;gt; /etc/cjdroute.conf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Setting up the connections differs as well, as the clients are going to make an
outbound connection to the server, which is configured to accept inbound
connections.&lt;/p&gt;
&lt;p&gt;You should still clean the &lt;code&gt;authorizedPasswords&lt;/code&gt; array, as it comes with a
default entry that is uncommented.&lt;/p&gt;
&lt;p&gt;Now you can setup outbound connections on the clients. You set these up in the
&lt;code&gt;connectTo&lt;/code&gt; block of &lt;code&gt;cjdroute.conf&lt;/code&gt;. For this example, the IP 192.168.1.1 is
used to denote the server IP. Unsurprisingly, you should change this to your
server&amp;rsquo;s actual IP. You can find the &lt;code&gt;publicKey&lt;/code&gt; value at the top of your
server&amp;rsquo;s &lt;code&gt;cjdroute.conf&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;On client 1, put the following in your &lt;code&gt;cjdroute.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;connectTo&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nt&#34;&gt;&amp;#34;192.168.1.1:9416&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nt&#34;&gt;&amp;#34;login&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;client_1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aeQu6pa4Vuecai3iebah7ogeiShaeDaepha6Mae1yooThoF0oa0Eetha9oox&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nt&#34;&gt;&amp;#34;publicKey&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;thisIsJustForAnExampleDoNotUseThisInYourConfFile_1.k&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On client 2:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;connectTo&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nt&#34;&gt;&amp;#34;192.168.1.1:9416&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nt&#34;&gt;&amp;#34;login&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;client_2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nt&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aiweequuthohkahx4tahLohPiezee9OhweiShoNeephe0iekai2jo9Toorah&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		&lt;span class=&#34;nt&#34;&gt;&amp;#34;publicKey&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;thisIsJustForAnExampleDoNotUseThisInYourConfFile_1.k&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is all for configuring the nodes.&lt;/p&gt;
&lt;h3 id=&#34;adding-a-startup-service-1&#34;&gt;Adding a startup service&lt;/h3&gt;
&lt;p&gt;You probably want cjdroute to run at system startup so you can immediatly use
your VPN. For openrc based systems, such as Funtoo, cjdns comes with a ready to
use service script. To make this available to your system, copy it over to the
right directory:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cp ~/.local/src/cjdns/contrib/openrc/cjdns /etc/init.d/cjdroute
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now add the service to system startup and start the service:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;rc-update add cjdroute default
rc-service cjdroute start
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That should be sufficient to get cjdns up and running for an encrypted VPN. You
can find the IPs of each of your systems at the top of your &lt;code&gt;cjdroute.conf&lt;/code&gt;
files, in the &lt;code&gt;ipv6&lt;/code&gt; attribute.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setup nginx with Let&#39;s Encrypt SSL</title>
      <link>https://www.tyil.nl/post/2016/10/25/setup-nginx-with-lets-encrypt-ssl/</link>
      <pubDate>Tue, 25 Oct 2016 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/25/setup-nginx-with-lets-encrypt-ssl/</guid>
      <description>&lt;p&gt;This is a small tutorial to setup nginx with Let&amp;rsquo;s Encrypt on a FreeBSD server
to host a static site.&lt;/p&gt;
&lt;h2 id=&#34;install-required-software&#34;&gt;Install required software&lt;/h2&gt;
&lt;p&gt;First you have to install all the packages we need in order to get this server
going:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pkg install nginx py27-certbot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configure-nginx&#34;&gt;Configure nginx&lt;/h2&gt;
&lt;p&gt;Next is nginx. To make life easier, you should configure nginx to read all
configuration files from another directory. This allows you to store all your sites in
separate configurations in a separate directory. Such a setup is a regular site on
nginx installations on GNU+Linux distributions, but not default on FreeBSD.&lt;/p&gt;
&lt;p&gt;Open up &lt;code&gt;/usr/local/etc/nginx/nginx.conf&lt;/code&gt; and make the contents of the &lt;code&gt;http&lt;/code&gt;
block look a as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;http&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt;       &lt;span class=&#34;s&#34;&gt;mime.types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;default_type&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;application/octet-stream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;sendfile&lt;/span&gt;     &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;#tcp_nopush  on;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;keepalive_timeout&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;65&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# default paths
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;index.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# disable gzip - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=773332
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;gzip&lt;/span&gt;  &lt;span class=&#34;no&#34;&gt;off&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# default ssl settings
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_session_cache&lt;/span&gt;          &lt;span class=&#34;s&#34;&gt;shared:SSL:1m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_session_timeout&lt;/span&gt;        &lt;span class=&#34;mi&#34;&gt;5m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_ciphers&lt;/span&gt;                &lt;span class=&#34;s&#34;&gt;HIGH:!aNULL:!MD5:!AES128:!CAMELLIA128&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_protocols&lt;/span&gt;              &lt;span class=&#34;s&#34;&gt;TLSv1.2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_prefer_server_ciphers&lt;/span&gt;  &lt;span class=&#34;no&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_dhparam&lt;/span&gt;                &lt;span class=&#34;s&#34;&gt;/usr/local/etc/ssl/dhparam.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# default logs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;error_log&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;/var/log/nginx/error.log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;access_log&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/var/log/nginx/acces.log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# default server
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt;       &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;localhost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt;   &lt;span class=&#34;s&#34;&gt;/usr/local/www/nginx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kn&#34;&gt;index&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;index.html&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;index.htm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;error_page&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;              &lt;span class=&#34;s&#34;&gt;/404.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;error_page&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;500&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;502&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;503&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;504&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;/50x.html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/50x.html&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt;   &lt;span class=&#34;s&#34;&gt;/usr/local/www/nginx-dist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# include site-specific configs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;sites/*.conf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This sets default ssl settings for all server blocks that enable ssl. Note that
these are settings I use, and are in no way guaranteed to be perfect. I did some
minor research on these settings to get an acceptable rating on
&lt;a href=&#34;https://www.ssllabs.com/ssltest/analyze.html?d=tyil.work&amp;amp;latest&#34;&gt;SSL Labs&lt;/a&gt;. However, security is not standing still, and there is a
decent chance that my settings will become outdated. If you have better settings
that result in a safer setup, please &lt;a href=&#34;https://www.tyil.work/&#34;&gt;contact me&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;setup-http&#34;&gt;Setup HTTP&lt;/h3&gt;
&lt;p&gt;Due to the way &lt;code&gt;certbot&lt;/code&gt; works, you need a functioning web server. Since there
is no usable cert yet, this means hosting a HTTP version first. The tutorial
assumes a static HTML website to be hosted, so the configuration is pretty
easy.&lt;/p&gt;
&lt;p&gt;Put the following in &lt;code&gt;/usr/local/etc/nginx/sites/domain.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# static HTTP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# listeners
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;domain.tld&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;www.domain.tld&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# site path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/srv/www/domain/_site&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# / handler
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kn&#34;&gt;try_files&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# logs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;error_log&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;/var/log/nginx/error.log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;access_log&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/var/log/nginx/access.log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If your site&amp;rsquo;s sources do not reside in &lt;code&gt;/srv/www/domain/_site&lt;/code&gt;, change the
path accordingly. This guide will continue using this path for all examples, so
be sure to modify this where needed. In the same vein, the domain &lt;code&gt;domain.tld&lt;/code&gt;
will be used. Modify this to your own domain.&lt;/p&gt;
&lt;h3 id=&#34;start-nginx&#34;&gt;Start nginx&lt;/h3&gt;
&lt;p&gt;Nginx is now configured to host a single site over HTTP. Now is the time to enable
the nginx service. Execute the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;nginx_enable=&amp;#34;YES&amp;#34;&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /etc/rc.conf.local
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will enable nginx as a system service. On reboots, it will be started
automatically. You can also start it up without rebooting by running the
following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;service nginx start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configure-lets-encrypt&#34;&gt;Configure Let&amp;rsquo;s Encrypt&lt;/h2&gt;
&lt;p&gt;Nginx is now running as your web server on port 80. Now you can request Let&amp;rsquo;s
Encrypt certificates using &lt;code&gt;certbot&lt;/code&gt;. You can do so as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;certbot certonly --webroot -w /srv/www/domain/_site -d domain.tld -d www.domain.tld
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In case you want to add any sub domains, simply add more &lt;code&gt;-d sub.domain.tld&lt;/code&gt;
arguments at the end. If the DNS entries for the domains resolve properly, and
no unexpected errors occur on the Let&amp;rsquo;s Encrypt side, you should see a message
congratulating you with your new certs.&lt;/p&gt;
&lt;p&gt;If your domains do not resolve correctly, &lt;code&gt;certbot&lt;/code&gt; will complain about this.
You will have to resolve your DNS issues before attempting again.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;certbot&lt;/code&gt; complains about an unexpected error on their side, wait a couple
minutes and retry the command. It should work, eventually.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;certbot&lt;/code&gt; has ran without errors, the required files should be available
in &lt;code&gt;/usr/local/etc/letsencrypt/live/domain.tld&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;configure-nginx-with-ssl&#34;&gt;Configure nginx with SSL&lt;/h2&gt;
&lt;p&gt;The certificate has been issued and base nginx is running. Now is the time to
re-configure your site on nginx to host the HTTPS version of your site instead.
Open up &lt;code&gt;/usr/local/etc/nginx/sites/domain.conf&lt;/code&gt; again, and make the contents
look like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# redirect HTTPS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# listeners
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;domain.tld&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;*.domain.tld&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# redirects
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;301&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;https://&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$host$request_uri&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# static HTTPS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# listeners
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;listen&lt;/span&gt;  &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ssl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;domain.tld&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;www.domain.tld&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# site path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;root&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;/srv/www/domain/_site&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# / handler
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kn&#34;&gt;try_files&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$uri/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# enable HSTS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;add_header&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;max-age=31536000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;includeSubdomains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;preload&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# keys
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate&lt;/span&gt;      &lt;span class=&#34;s&#34;&gt;/usr/local/etc/letsencrypt/live/domain.tld/fullchain.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;ssl_certificate_key&lt;/span&gt;  &lt;span class=&#34;s&#34;&gt;/usr/local/etc/letsencrypt/live/domain.tld/privkey.pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Do not forget to update all the paths to match your setup!&lt;/p&gt;
&lt;p&gt;As a final step, you should generate the dhparam file. This is to avoid the
issues as described on &lt;a href=&#34;https://weakdh.org/&#34;&gt;Weak DH&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl gendh -out /usr/local/etc/ssl/dhparam.pem &lt;span class=&#34;m&#34;&gt;4096&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Be aware that this step can take a &lt;strong&gt;very&lt;/strong&gt; long time. On the test machine I
used to test this tutorial, with 1 core and 1 GB ram, it took nearly 1 hour to
generate this file.&lt;/p&gt;
&lt;h3 id=&#34;reload-nginx&#34;&gt;Reload nginx&lt;/h3&gt;
&lt;p&gt;The final step is to reload the nginx configuration so it hosts the SSL version
of your site, and redirects the HTTP version to the HTTPS version. To do this,
simply run&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;service nginx reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That should be all to get your site working with HTTP redirecting to HTTPS, and
HTTPS running using a gratis Let&amp;rsquo;s Encrypt certificate.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Pastebin</title>
      <link>https://www.tyil.nl/post/2016/10/01/on-pastebin/</link>
      <pubDate>Sat, 01 Oct 2016 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/01/on-pastebin/</guid>
      <description>&lt;p&gt;Pastebin offers itself as a gratis paste service. Although it is probably the
most well known option out there, it is certainly not the best.&lt;/p&gt;
&lt;h2 id=&#34;the-security-issue&#34;&gt;The security issue&lt;/h2&gt;
&lt;p&gt;Pastebin has a couple of issues that harm the visitor&amp;rsquo;s security. This on
itself should be considered such a bad practice that no-one should consider
their service at all.&lt;/p&gt;
&lt;h3 id=&#34;cloudflare&#34;&gt;Cloudflare&lt;/h3&gt;
&lt;p&gt;Cloudflare is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Man-in-the-middle_attack&#34;&gt;MITM&lt;/a&gt;. It completely breaks the secure chain of TLS on
the web, and should not be used. Any service still using Cloudflare should be
shunned.  There is &lt;a href=&#34;https://www.tyil.nl/articles/on-cloudflare/&#34;&gt;another article&lt;/a&gt; on this site which has more
information on this specific issue. In addition, Cloudflare can be considered a
privacy issue for the same reasons, as is detailed below.&lt;/p&gt;
&lt;h3 id=&#34;advertisements&#34;&gt;Advertisements&lt;/h3&gt;
&lt;p&gt;Another issue with regards to security on pastebin are the advertisements.
While it can be argued that &amp;ldquo;they need to make money somehow&amp;rdquo;, using ads always
seems like the worst possible solution. Especially given the way they&amp;rsquo;re
serving it. The past couple years have shown that advertisements on the web are
easily abused to serve malware to good netizens who decided to not block all
ads.&lt;/p&gt;
&lt;p&gt;A rant on the state of ads might be appropriate, but this article is
specifically about Pastebin, so I will just keep it at &amp;ldquo;third party
advertisements are a security risk, avoid sites who use them&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;the-privacy-issue&#34;&gt;The privacy issue&lt;/h2&gt;
&lt;p&gt;Apart from their security issues, Pastebin also offers some privacy issues. As
stated above, they make use of Cloudflare. This means that whenever you visit
them, Cloudflare takes note of this. They may even decide that you need to
perform some additional tasks in order to be allowed to the resource. This
doesn&amp;rsquo;t happen to most users, but if you&amp;rsquo;re using any anonymization practices,
this will happen almost every time you visit a site behind Cloudflare.&lt;/p&gt;
&lt;p&gt;In addition to telling Cloudflare, you will also tell another third party,
Google, in case this &amp;ldquo;additional step&amp;rdquo; is required. This is done via the new
reCaptcha system which will inform Google of almost every detail of your
browser and the behaviour used to solve the puzzle. Incredibly useful for
fingerprinting you accross multiple locations.&lt;/p&gt;
&lt;h3 id=&#34;then-there-is-tor&#34;&gt;Then there is Tor&lt;/h3&gt;
&lt;p&gt;But, if you&amp;rsquo;re using an anonymization proxy such as Tor, even if you do not
care about the Cloudflare issue, and you solve the &amp;ldquo;security check&amp;rdquo; presented
to you, Pastebin still refuses to offer you their service. If they are going to
refuse you service, they should tell you up front, not after you have already
informed two other harmful parties of your attempt of accessing the resource.&lt;/p&gt;
&lt;p&gt;Actually, they should not. They should simply not require you to give up your
privacy and serve you the content you were looking for. Blocking resources to a
certain group of users is simply censorship, and should not be the status quo
on the free internet.&lt;/p&gt;
&lt;h2 id=&#34;alternatives&#34;&gt;Alternatives&lt;/h2&gt;
&lt;p&gt;Luckily, there are plenty of alternatives that do not treat their users with
such disrespect. I ask anyone who is still using Pastebin to stop doing this,
and use any of the alternatives.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://0bin.net/&#34;&gt;0bin.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cry.nu&#34;&gt;cry.nu&lt;/a&gt; (works like termbin: &lt;code&gt;nc cry.nu 9999 &amp;lt; file&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://ix.io/&#34;&gt;ix.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.tyil.nl/&#34;&gt;p.tyil.nl&lt;/a&gt; (works like termbin: &lt;code&gt;nc p.tyil.nl 9999 &amp;lt; file&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>On Systemd</title>
      <link>https://www.tyil.nl/post/2016/10/01/on-systemd/</link>
      <pubDate>Sat, 01 Oct 2016 00:00:00 +0000</pubDate>
      
      <guid>https://www.tyil.nl/post/2016/10/01/on-systemd/</guid>
      <description>&lt;p&gt;Systemd once presented itself as being the next generation init system for
GNU+Linux. When the project started it seemed to be headed in a good direction.
Unfortunately, it quickly became clear that systemd&amp;rsquo;s goal was not only to
bring you a quick, new init system.  It planned to do so much more.  This was
part of the plan, since init systems were generally considered to be in a bad
state overall it was quickly accepted by most mainstream GNU+Linux
distributions.  What was at first only an init system became so much more:
systemd-logind was made to manage tty&amp;rsquo;s, systemd-resolvd was added to act as a
caching DNS server.  Even networking was added with systemd-networkd to manage
network interfaces.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt;: Systemd is a fast moving project, this may result in
information here to becoming outdated. If you find any information that is no
longer correct, please contact me. You can find my contact details &lt;a href=&#34;http://tyil.work&#34;&gt;on my
homepage&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;technical-issues&#34;&gt;Technical issues&lt;/h2&gt;
&lt;h3 id=&#34;security&#34;&gt;Security&lt;/h3&gt;
&lt;p&gt;From experience, we have seen that systemd&amp;rsquo;s creator, Lennart Poettering, will
try to assimilate any functionality he can find and add it into systemd.  This
causes systemd to have a large surface area of attack, adding to and magnifying
security attack vectors. An init system should be exactly the opposite. To
compound this issue, we have bugs like &lt;a href=&#34;https://github.com/systemd/systemd/blob/b8fafaf4a1cffd02389d61ed92ca7acb1b8c739c/src/core/manager.c#L1666&#34;&gt;the user-level DoS&lt;/a&gt;,
which seem to indicate that the software is hardly tested or written by
programmers who don&amp;rsquo;t use best practices.&lt;/p&gt;
&lt;h3 id=&#34;posix&#34;&gt;POSIX&lt;/h3&gt;
&lt;p&gt;POSIX compliance. Systemd developers seem to detest it. Their common argument
against retaining POSIX compliance is that &amp;ldquo;systemd must break POSIX compliance
in order to further the development of GNU+Linux userland utilities&amp;rdquo;. While
this may be true in some sense, it is a very bad idea to ignore POSIX
altogether.&lt;/p&gt;
&lt;p&gt;POSIX is one of the reasons that most applications running on GNU+Linux and
other Unix like systems are very portable. It&amp;rsquo;s a standard that most OS&amp;rsquo;s and
distro&amp;rsquo;s try to meet, making it easy to port software.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.reddit.com/r/linux/comments/132gle/eli5_the_systemd_vs_initupstart_controversy/c70hrsq&#34;&gt;natermeer on Reddit&lt;/a&gt; said&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;POSIX has almost no relevance anymore.&lt;/p&gt;
&lt;p&gt;[&amp;hellip;]&lt;/p&gt;
&lt;p&gt;If you care about portability you care about it running on OS X and Windows
as well as your favorite *nix system. POSIX gains you nothing here. A lot
of the APIs from many of these systems will resemble POSIX closely, but if
you don&amp;rsquo;t take system-specific differences into account you are not going
to accomplish much.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I really doubt that any Init system from any Unix system uses only POSIX
interfaces, except maybe NetBSD. All of them are going to use scripts and
services that are going to be running commands that use kernel-specific
features at some point. Maybe a init will compile and can be executed on
pure POSIX api, but that is a FAR FAR cry from actually having a booted and
running system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which was replied to by &lt;a href=&#34;https://www.reddit.com/r/linux/comments/132gle/eli5_the_systemd_vs_initupstart_controversy/c72saay&#34;&gt;aidanjt&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Wrong, both OS X and Windows have POSIX support, although Window&amp;rsquo;s is emulated,
OS X certainly is not, it&amp;rsquo;s fully POSIX compliant. and b) POSIX doesn&amp;rsquo;t have to
work identically everywhere, it only has to be more or less the same in most
places and downstream can easily patch around OS-specific quirks. Even
GNU/Linux and a bunch of the BSDs are merely regarded as &amp;lsquo;mostly&amp;rsquo; POSIX
compliant, after all. But if you ignore POSIX entirely, there&amp;rsquo;s ZERO hope of
portability.&lt;/p&gt;
&lt;p&gt;Actually sysvinit is very portable, init.c only has 1 single Linux header which
has been #ifdef&amp;rsquo;ed, to handle the three-finger-salute. You see, init really
isn&amp;rsquo;t that complicated a programme, you tell the kernel to load it after it&amp;rsquo;s
done it&amp;rsquo;s thing, init starts, and loads distro scripts which starts userspace
programmes to carry on booting. No special voodoo magic is really required.
POSIX is to thank for that. POSIX doesn&amp;rsquo;t need to be the only library eva, it
only needs to handle most of the things you can&amp;rsquo;t do without, without having to
directly poke at kernel-specific interfaces.&lt;/p&gt;
&lt;p&gt;This is why with POSIX, we can take a piece of software written for a PPC AIX
mainframe, and make it work on x86 Linux without a complete rewrite, usually
with only trivial changes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;dependencies-and-unportability&#34;&gt;Dependencies and unportability&lt;/h3&gt;
&lt;p&gt;Another common issue with systemd is that applications have started to
needlessly depend on it, forcing systemd onto users that do not wish to use
systemd for obvious reasons outlined here, reasons outside of this article, or
simply being unable to use it. Because systemd complies to no cross-platform
standard and uses many features only available in recent Linux version, it&amp;rsquo;s
either very hard or impossible to implement systemd in some circumstances.&lt;/p&gt;
&lt;p&gt;The list of features it requires is no small one either, as you can see in the
list &lt;a href=&#34;https://www.reddit.com/r/linux/comments/132gle/eli5_the_systemd_vs_initupstart_controversy/c70cao2&#34;&gt;posted by ohset&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/dev/char&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/disk/by-label&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/disk/by-uuid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/random&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/rtc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/tty0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/$PID/cgroup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/${PID}/cmdline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/${PID}/comm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/${PID}/fd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/${PID}/root&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/${PID}/stat&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/proc/cmdline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/sys/class/dmi/id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/sys/class/tty/console/active&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BTRFS_IOC_DEFRAG&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLONE_xxx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F_SETPIPE_SZ&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IP_TRANSPORT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KDSKBMODE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O_CLOEXEC&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PR_CAPBSET_DROP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PR_GET_SECUREBITS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PR_SET_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PR_SET_PDEATHSIG&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RLIMIT_RTPRIO&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RLIMIT_RTTIME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SCHED_RESET_ON_FORK&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SOCK_CLOEXEC&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TIOCLINUX&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TIOCNXCL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TIOCVHANGUP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VT_ACTIVATE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\033[3J&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;audit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;autofs4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;capabilities&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cgroups&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fanotify&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;inotify&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ionice&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;namespaces&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oom score adjust&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openat()&lt;/code&gt; and friends&lt;/li&gt;
&lt;li&gt;&lt;code&gt;selinux&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;settimeofday()&lt;/code&gt; and its semantics&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;waitid()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;numerous GNU APIs like &lt;code&gt;asprintf&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This made &lt;a href=&#34;http://www.gnome.org/&#34;&gt;Gnome&lt;/a&gt; unavailable for a long time to BSD users and GNU+Linux
users who wanted to remain with a sane and proven system. Utilities like
&lt;a href=&#34;https://en.wikipedia.org/wiki/Gummiboot_(software)&#34;&gt;Gummiboot&lt;/a&gt; are now being absorbed by systemd too. It is only a
matter of time before you can no longer use this utility without a systemd init
behind it. There are too many examples of software to list, which are being
assimilated or made unavailable by lazy or bad developers who choose to depend
on systemd for whatever reason.&lt;/p&gt;
&lt;h3 id=&#34;speed&#34;&gt;Speed&lt;/h3&gt;
&lt;p&gt;The main selling point many systemd users hail all the time, is speed. They
place an unusual high amount of value on being a couple seconds faster on boot.
Systemd gains this speed gain by using parallelization, and many think this is
unique to systemd. Luckily for those who want to stick to a more sane system,
this is false. Other init systems, such as &lt;a href=&#34;https://en.wikipedia.org/wiki/OpenRC&#34;&gt;OpenRC&lt;/a&gt;, used by
&lt;a href=&#34;http://www.funtoo.org/Welcome&#34;&gt;Funtoo&lt;/a&gt;, and &lt;a href=&#34;http://smarden.org/runit/&#34;&gt;runit&lt;/a&gt;, used by &lt;a href=&#34;http://www.voidlinux.eu/&#34;&gt;Voidlinux&lt;/a&gt; both
support parallel startup of services.  Both these systems use small and
effective shell scripts for this, and support startup dependencies and the
like. Systemd brings nothing new to the init world, it just advertises these
features more agressively.&lt;/p&gt;
&lt;h3 id=&#34;modularity&#34;&gt;Modularity&lt;/h3&gt;
&lt;p&gt;The UNIX principle, &lt;em&gt;make an application perform one task very well&lt;/em&gt;, seems to
be very unpopular among systemd developers. This principle is one of the
reasons why UNIX based systems have gotten so popular. Yet, the systemd
developers seem to despise this principle, and even try to argue that systemd
actually is modular because &lt;strong&gt;it compiles down to multiple binaries&lt;/strong&gt;. This
shows a lack of understanding, which would make most users uneasy when they
consider that these people are working on one of the most critical pieces of
their OS.&lt;/p&gt;
&lt;p&gt;The technical problem this brings is that it is very hard to use systemd with
existing tools. &lt;code&gt;journald&lt;/code&gt; for instance doesn&amp;rsquo;t just output plain text you can
easily filter through, save or apply to a pager. I decides for you how to
represent this information, even if this might be an ineffective way to go
about it.&lt;/p&gt;
&lt;h3 id=&#34;binary-logs&#34;&gt;Binary logs&lt;/h3&gt;
&lt;p&gt;Hailed by systemd users and developers as a more efficient, fast and secure way
to store your logs, it is yet another middle finger to the UNIX principles,
which state that documents intended for the user should be human readable.
Binary logs are exactly not that. This forces you to use the tools bundled with
systemd, instead of your preferred solution. This means you need a system with
systemd in order to read your logs, which you generally need the most when the
system that generated it crashed. Thanks to systemd, these logs are now useless
unless you have another systemd available for it.&lt;/p&gt;
&lt;p&gt;These logs are also very fragile. It is a common &amp;ldquo;issue&amp;rdquo; to have corrupted logs
when using systemd. Corrupted is here within quotes because the systemd
developers do not recognize this as a bug. Instead, you should just rotate your
logs and hope it does not happen again.&lt;/p&gt;
&lt;p&gt;The usual counter to this issue is that you &lt;em&gt;can&lt;/em&gt; tell systemd to use another
logger. However, this does not stop &lt;code&gt;journald&lt;/code&gt; from processing them first or
just not having &lt;code&gt;journald&lt;/code&gt; at all. As systemd is not modular, you will always
have all the pieces installed. It should also be noted that this is a
&lt;em&gt;workaround&lt;/em&gt;, not a fix to the underlying problem.&lt;/p&gt;
&lt;h2 id=&#34;political-issues&#34;&gt;Political issues&lt;/h2&gt;
&lt;h3 id=&#34;aggressively-forced-upon-users&#34;&gt;Aggressively forced upon users&lt;/h3&gt;
&lt;p&gt;A point that has made many systemd opponents very wary of this huge piece of
software is the way it was introduced. Unlike most free software packages,
systemd was forced into the lives of many users by getting hard dependencies on
them, or simply absorbing a critical piece of software by the use of political
power. The two most prominent pieces of software where this has happened are
&lt;a href=&#34;http://www.gnome.org/&#34;&gt;Gnome&lt;/a&gt; and &lt;a href=&#34;https://wiki.gentoo.org/wiki/Eudev&#34;&gt;&lt;code&gt;udev&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Gnome developers made a hard dependency on systemd. This in effect made
every gnome user suddenly require systemd. As a result, FreeBSD had to actually
drop Gnome for a while, as systemd does not run outside of GNU+Linux.&lt;/p&gt;
&lt;p&gt;The other, &lt;code&gt;udev&lt;/code&gt;, was a critical piece of software to manage devices in
GNU+Linux. Sadly, some political power was shown by Red Hat and &lt;code&gt;udev&lt;/code&gt; got
absorbed into systemd. Luckily, the Gentoo guys saw this issue and tried to
resolve it. As the systemd developers dislike anything that&amp;rsquo;s not systemd
itself, they stubbornly refused the patches from the Gentoo folks which would
keep &lt;code&gt;udev&lt;/code&gt; a single component (and thus usable without systemd). In the end,
the Gentoo developers forked &lt;code&gt;udev&lt;/code&gt; into &lt;a href=&#34;https://wiki.gentoo.org/wiki/Eudev&#34;&gt;&lt;code&gt;eudev&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;unwillingness-to-cooperate&#34;&gt;Unwillingness to cooperate&lt;/h3&gt;
&lt;p&gt;Whenever someone from outside the systemd fangroups steps up to actually
improve systemd in whatever way, the systemd devs seem to be rather
uncooperative. It is not uncommon for developers from other projects to make a
change in order for their projects (and usually others) to improve. This
removes a lot of the cost for the systemd maintainers to deal with all the
issues created they are creating.&lt;/p&gt;
&lt;p&gt;There are some references to the systemd developers being against changes that
might make systemd less of a problem, but these changes are usually denied with
petty excuses.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://lists.freedesktop.org/archives/systemd-devel/2012-June/005466.html&#34;&gt;https://lists.freedesktop.org/archives/systemd-devel/2012-June/005466.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lists.freedesktop.org/archives/systemd-devel/2012-June/005507.html&#34;&gt;https://lists.freedesktop.org/archives/systemd-devel/2012-June/005507.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-to-avoid-it&#34;&gt;How to avoid it&lt;/h2&gt;
&lt;h3 id=&#34;choosing-a-better-os-or-distribution&#34;&gt;Choosing a better OS or distribution&lt;/h3&gt;
&lt;p&gt;Nowadays, the only way to avoid it without too much trouble, is by simply
choosing a better OS or distro that does not depend on systemd at all. There
are a few choices for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;*BSD (&lt;a href=&#34;https://www.freebsd.org/&#34;&gt;FreeBSD&lt;/a&gt;, &lt;a href=&#34;https://www.openbsd.org/&#34;&gt;OpenBSD&lt;/a&gt;, and others)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://devuan.org/&#34;&gt;Devuan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.funtoo.org/Welcome&#34;&gt;Funtoo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.voidlinux.eu/&#34;&gt;Voidlinux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is a shame that it renders a very large chunk of the GNU+Linux world
unavailable when choosing a distro, but they have chosen laziness over a
working system. The only way to tell them at this point that they have made a
wrong decision, is to simply stop using these distros.&lt;/p&gt;
&lt;h3 id=&#34;more-links&#34;&gt;More links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://ewontfix.com/14/&#34;&gt;Broken by design: systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://without-systemd.org/wiki/index.php/Main_Page&#34;&gt;Without systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://suckless.org/sucks/systemd&#34;&gt;systemd is the best example of Suck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.agwa.name/blog/post/thoughts_on_the_systemd_root_exploit&#34;&gt;Thoughts on the systemd root exploit&lt;/a&gt; (In response to &lt;a href=&#34;http://www.openwall.com/lists/oss-security/2017/01/24/4&#34;&gt;CVE-2016-10156&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://fromthecodefront.blogspot.nl/2017/10/systemd-no.html&#34;&gt;&amp;ldquo;systemd: Please, No, Not Like This&amp;rdquo;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
