<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">

<channel>
<title>Martin's Blog</title>
<description>Random writings and notes from Martin's keyboard to your screen.</description>
<language>en-us</language>
<link>http://m-chrzan.xyz/rss.xml</link>
<atom:link href="http://m-chrzan.xyz/rss.xml" rel="self" type="application/rss+xml" />


  <item>
  <title>Books Read in 2023</title>
  <guid>http://m-chrzan.xyz/blog/books-read-in-2023.html</guid>
  <pubDate>2024-01-31T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Figured I should write this before January is over, so that I can claim I
published it just after the year turned over.
</p>

<p>
Well, turns out, reading-wise, 2023 was the year of Plato. As mentioned back in
<a href="books-read-in-2021.html">the 2021 list</a>, I've set
out on reading through his works. At this point I'm about half-way through. I
didn't know what I was getting myself into when I first opened up the
collection, but I have to say I'm really enjoying myself.  Not only are Plato's
words worthy of pondering and contrasting with modern thought, they're also
entertaining. Ideas that shaped over two millenia of philosophy are interspersed
with wit, humor, sometimes even sharp sarcasm.
</p>

<p>
Anyways, here's the list of dialogues I got through last year:
</p>

<p>
<ol>
  <li> Parmenides </li>
  <li> The Sophist </li>
  <li> The Statesman </li>
  <li> Philebus </li>
  <li> Symposium </li>
  <li> Phaedrus </li>
  <li> Alcibiades </li>
  <li> Second Alcibiades </li>
  <li> Hipparchus </li>
  <li> Rival Lovers </li>
  <li> Theages </li>
  <li> Charmides </li>
  <li> Lysis </li>
  <li> Protagoras </li>
  <li> Gorgias </li>
</ol>
</p>
  ]]></description>
  </item>

  <item>
  <title>Sending mail to gmail</title>
  <guid>http://m-chrzan.xyz/blog/sending-mail-to-gmail.html</guid>
  <pubDate>2024-01-17T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
<i>
    Update: it has been brought to my attention that I may have been incorrect
    in identifying a potential Google policy around SPF mechanics as the root
    cause of the issue. If you were in a similar situation as me and this
    approach resolves your problem - great! But it's likely that this is a
    workaround rather than fundamentally addressing the issue.
</i>
</p>

<p>
If you're a selfhosting enjoyer like me, you may have been using Luke Smith's
wonderful <a href="https://github.com/LukeSmithxyz/emailwiz">emailwiz</a> to
host your very own email server. It's a simple script that allows you to not
think much: just run it on your Debian box, and it will install everything, and
then tell you all the DNS records you need to set for your email server to work
correctly.  This includes records for the DMARC, DKIM, and SPF protocols, which
increase security and help prevent spam. So just make sure to follow these
instructions, and you can send mail anywhere.
</p>

<p>
Or can you?
</p>

<p>
It's a common complaint you hear from people selfhosting their email server: the
server works fine, you can send and receive email for the most part, but there
are several email hosts that just will never accept mail from you, replying with
those dreaded "Undelivered Mail Returned to Sender" messages. The biggest
culprit being gmail, which also happens to have the biggest market share of
personal emails. Meaning you can't send anything to your friends, or even less
established businesses and organizations that simply keep using their personal
gmail accounts for communications.
</p>

<p>
The error message gmail will return contains something like this:

<code><pre>
The IP address sending this 550-5.7.25 message does not have a PTR record setup,
or the corresponding 550-5.7.25 forward DNS entry does not point to the sending
IP. As a policy, 550-5.7.25 Gmail does not accept messages from IPs with missing
PTR records. 550-5.7.25 For more information, go to 550 5.7.25
</pre></code>

Which is not very helpful if you do have a reverse DNS PTR record set up
correctly.
</p>

<p>
My, and probably many others', conspiracy theory has always been that gmail is
just malicious towards new email hosts, especially small selfhosting landchads.
Turns out that it's something different: gmail just happens to be more strict
about SPF records! Something that Luke missed.
</p>

<p>
SPF (the Sender Policy Framework) specifies several mechanisms. You can read
more about them <a href="http://www.open-spf.org/SPF_Record_Syntax/">here</a>.
Luke's script only specifies <code>a</code> and <code>mx</code> mechanisms (plus
the general <code>-all</code> to reject anything not matching those). But you
can also explicitly specify the IP addresses allowed to send mail from your
domain with <code>ip4</code> and <code>ip6</code> mechanisms. And it looks like
gmail requires these to be specified to accept your email. I guess you could
look for some maliciousness in the fact that they're not clearer about what one
has to fix to comply with their policies. But in the end, the solution is...
</p>

<h3>TL;DR</h3>

<p>
If you're getting <code>550-5.7.25</code> errors from gmail, make sure you do
have your PTR record set up correctly, but also that your SPF record looks
something like this:

<code><pre>
v=spf1 mx a:&lt;your mail host&gt; ip4:&lt;your IPv4 address&gt; ip6:&lt;your IPv6 address&gt; -all
</pre></code>
</p>

<h3>Credits</h3>
<p>
I got to this solution after the owner of
<a href="https://storin.nl/">storin.nl</a> emailed me about
<a href="https://nocss.club/">nocss.club</a>. When I tried to reply to him, I
got an "Undelivered Mail Returned to Sender" reply, but with a different error
message than gmail's:

<code><pre>
not allowed to send mail from 550 m-chrzan.xyz: Please see
http://www.open-spf.org/Why : Reason: mechanism (in reply to RCPT TO command)
</pre></code>

This led me to reading more about the SPF spec, finding
<a href="https://bobcares.com/blog/550-is-not-allowed-to-send-mail-from/">
    this
</a> article, and formulating the hypothesis that maybe gmail's policy has
something to do with SPF mechanisms used.
</p>

<p>
Tom from <a href="https://tfaz.xyz/">tfaz.xyz</a> is working on a PR to emailwiz
to correct the SPF record.
</p>

<p>
And big thanks to <a href="https://lukesmith.xyz/">Luke</a> for emailwiz,
without it I wouldn't even have my own mail server to begin with.
</p>
  ]]></description>
  </item>

  <item>
  <title>Switching from GitJournal to LogSeq</title>
  <guid>http://m-chrzan.xyz/blog/logseq.html</guid>
  <pubDate>2023-07-15T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
As mentioned previously on the blog, for a long time I was relying on
<a href='gitjournal.html'>
  GitJournal
</a> for note taking. There were quite a few reasons I really liked it:
</p>

<ul>
  <li>It's open source.</li>
  <li>Syncs with <code>git</code> (which I can selfhost).</li>
  <li>
    The underlying format is just MarkDown, which means that I can easily edit
    the notes on my laptop with my editor of choice.
  </li>
</ul>

<p>
But there's also some issues I kept running into:

<ul>
  <li>
    Search is not very good (in particular, there's no in-page search, only
    searching for notes that contain a certain string)
  </li>
  <li>
    The hierarchical organization of notes is nice and simple, but has its
    limits, not all relationships between ideas are tree-like.
  </li>
  <li>
    The <code>git</code> implementation on the mobile app has its problems. E.g.
    merge conflicts are not going to be resolved, sometimes leading to forced
    commits that remove notes. All of this can be resolved on a computer with
    access to the repo, but it can become a hassle.
  </li>
  <li>
    There's no easy way to link between notes, which could have been very nice.
  </li>
  <li>
    Some other bugs I've noticed, e.g. sometimes duplicate notes appearing, or
    others completely disappearing (could be caused by the git issues mentioned
    above, or the way I was managing notes computer-side away from the app).
  </li>
</ul>
</p>

<p>
None of these issues are absolute deal breakers, and I still think GitJournal is
a really nice solution that I can recommend, but all the slight annoyances led
me on a search for a potential replacement. If I didn't find anything better, I
would have been happy to stick with GitJournal, but maybe there's a note taking
solution somewhere out there that would suit me better?
</p>

<p>
If you do some research on note taking apps you'll be greeted with a flood of
content from "productivity" YouTubers, bloggers, etc. Turns out there's a ton of
options for different types of workflows and preferences, however most of them
are
</p>

<ul>
  <li>proprietary</li>
  <li>bloated</li>
  <li>
    for syncing, require registration with a SaaS startup that you have no idea
    how long it will stay in business, or how much of their monetization model
    depends on scraping your data.
  </li>
</ul>

<p>
Enter <a href='https://logseq.com/'>LogSeq</a>.
</p>

<h3> Enter LogSeq </h3>
<p>
Let me start off by saying that LogSeq is not <i>the</i> perfect solution for
me, but I'll go over my personal gripes with it later. First, let's talk about
what LogSeq even is and what's good about it.
</p>

<p>
LogSeq is an open source note taking application that is privacy-first,
outline-based, non-hierarchical, and features bidirectional linking. Let's break
that down:
</p>

<ul>
  <li>
    <i>Privacy-first</i>: all notes are stored as plain-text files on your
    computer. By default, there's no synchronization; in particular, there's no
    central server the app talks to.
  </li>
  <li>
    <i>Outline-based</i>: each line of text is a bullet point (and these can be
    nested), there's no plain paragraphs/headings like in MarkDown or basic
    HTML.
  </li>
  <li>
    <i>Non-hierarchical</i>: notes are not organized into a tree-like directory
    structure. Instead, each note is a separate page, and you organize your
    notes by linking/tagging between pages.
  </li>
  <li>
    <i>Bidirectional linking</i>: these links between pages go in two directions,
    i.e. when page A links to page B, page B will have a section of
    <i>backlinks</i>, making it easy to jump back to where B is referred from
    (be that back to page A, or any other pages that link to B).

    <ul>
      <li>
        This feature is inspired by another app, Obisidan, which you might be
        familiar with. Both Obisdian and LogSeq are touted by some to be good
        tools for building a so-called "second brain."
    </ul>
  </li>
</ul>

<p>
Some other features to mention:
</p>

<h4> Journal </h4>
<p>
LogSeq automatically creates daily journal pages. These can be used for
journaling or as a dumping spot of random ideas, links you encounter, notes to
jot down. Thanks to backlinks, if you include tags or links to topic pages in
your notes, you'll be able to find these seemingly chaotic daily notes when
looking at a specific topic later.
</p>

<h4> Plugins </h4>
<p>
There is a plugin interface, and an already fairly large marketplace of
community-built plugins available. Many are just visual tweaks you can add if
you like them, there's also a bunch of integrations with various other note
taking/productivity apps (which I personally don't use, but many find them
useful, from what I hear, in their workflows). There's really only one plugin
I'm using currently, more on that in the next section, but overall its nice to
see extensibility.
</p>

<h4> Synchronization </h4>
<p>
As mentioned, there's no synchronization enabled by default. There is a
recommended cloud sync option with LogSeq's servers, which respects your privacy
by end-to-end encrypting all your content, but you know me. I prefer to sync my
notes with <code>git</code>. Not only does this allow me to selfhost my notes,
it also gives me all the benefits of version control (history, a way to deal
with merge conflicts when dealing with multiple devices).
</p>

<p>
Since all of LogSeq's note files are just plain-text on your disk, setting up
git synchronization is trivial: just make the notes directory a git repo, add a
remote, and voilá.
</p>

<p>
Here's also where a plugin comes in useful: there's a <code>git</code> plugin
that makes it easy to commit, push, and pull changes from within LogSeq itself,
rather than having to switch between it and a terminal.
</p>

<p>
Note: LogSeq does have a separate built-in version control feature that's based
on <code>git</code>, but I recommend against it. It's based on automatically
committing every <i>x</i> seconds, so you never exactly know if your latest
changes are already committed, and you end up with a very messy history of
partial and/or unrelated changes going into individual commits.
</p>

<h4> Mobile app </h4>
<p>
There is a functional mobile app, with the same look and most of the same
features as the desktop application. It is missing plugins, so in particular, no
git plugin. <code>git</code> synchronization on Android is still possible with
Termux, but less convenient than on a desktop, since it requires you to manually
go into Termux to commit/push/pull changes (and be very careful of ending up
with a merge conflict on your phone, that's much less fun to deal with on a
phone screen than in your favorite desktop editor).
</p>

<h4> Other features </h4>

Some other features include:

<ul>
  <li>
    Aliases: you can create aliases for tags, so that when you tag a note with
    any of the aliases, it will still point to the original tag. For example, if
    you have a tag "book", and add an alias "books", you won't have to remember
    if the tag was in the singular or plural.
  </li>
  <li>
    To-do lists.
  </li>
  <li>
    Scripting/queries: this is a feature I haven't looked into much yet, but it
    sounds very useful. You can write queries in a very general syntax to find
    notes that satisfy particular conditions. E.g. find all articles (tagged
    with <code>#article</code>) that are about graph theory (tagged with
    <code>#[[graph theory]]</code>) published in the last year (a date property
    is within a particular time frame).
  </li>
  <li>
    Embedding blocks: a bullet point (with everything nested below it) can be
    directly embedded elsewhere (such that editing either instance changes
    both).
  <li>
    Whiteboards.
  </li>
  <li>
    <i>The Graph</i>: a visualization of links/relationships between your pages.
  </li>
  <li>
    Flash cards.
  </li>
</ul>

<p>
The first two I use regularly. Queries I want to eventually get into. The others
I don't really find use for.
</p>

<h3> Problems </h3>
<p>
There are a few things I'm personally not a fan of with LogSeq.
</p>

<h4> Bloat </h4>
<p>
The biggest general problem for me is that despite a nice, minimal interface,
LogSeq is an Electron application. The <code>logseq-desktop-bin</code> AUR
package is <i>500MB</i>! All for a note taking application. In addition to the
bloat coming from Electron, I think a lot of it is also features that I have
absolutely no use for (like the aforementioned graph view and whiteboards, which
go way beyond basic text editing and markup).
</p>

<p>
Many people these days brush bloat under the rug, saying that on modern machines
it doesn't matter, and frameworks like Electron are good because it means
faster, simpler development. But these problems do end up having consequences.
For example, LogSeq struggles with large files.
</p>

<p>
The full text of Shakespeare's <i>Othello</i> (around 160KB) takes several
seconds to load, and the app displays a bright orange warning message that
"Large blocks will not be editable or searchable to not slow down the app."
<code>vim</code> opens the same file in a fraction of a second, a web browser
takes maybe about a second.
</p>

<h4> Not exactly MarkDown </h4>
<p>
The LogSeq team say that the underlying text files are just MarkDown, but that's
not exactly true.
</p>

<p>
The notes have to be all organized into bullet points, you don't have plain
paragraphs or headings. Furthermore, additional markup is inserted, e.g. for
block references. All of that makes the text less human-readable/-editable, a
core feature of MarkDown.
</p>

<p>
Which leads to the next very sad problem...
</p>

<h4> It's not <code>vim</code> </h4>
<p>
Once you get used to <code>vim</code>, all other text editing options are just
inferior. Bram's gift to humanity is both a blessing and a curse, since there's
no getting away from sometimes needing to edit text outside of it.
</p>

<p>
Yes, there's a "vim" plugin that implements some keybindings, but I tend to stay
away from those, as they're always disappointing: they implement some of the
most basic keys, but others I'm used to relying on are not supported. So I'd
have to remember which plugin for which piece of software implements which ones,
and it just becomes more of a hassle than a convenience.
</p>

<p>
As mentioned above, using <code>vim</code> directly on the text files is also
not a solution, since the notes format is not exactly standard MarkDown, and
either way you'd lose all the benefits of LogSeq, especially links/backlinks.
</p>

<h3> Conclusions </h3>
<p>
So, with that ranty section above over, what's my final verdict on LogSeq? I've
been using it daily for the past several months, and until something better
comes along, I don't plan on switching. Despite all its shortcomings, I really
like it, it helps me organize my notes and thoughts.
</p>

<p>
I was actually aware of Obsidian before, and it had piqued my interest, seemed
like a useful tool.  However, I didn't want to jump onto a proprietary
note taking solution. When doing my recent research on these apps, I found
LogSeq, which is a FOSS alternative. I was reluctant at first, for all the
reasons stated above, but after just a bit of test running, turns out the
benefits outweigh the trade-offs I have to make, and LogSeq has replaced
GitJournal for me.
</p>

<h4> The pipe dream </h4>
<p>
To finish this post, let me go on a little fantastical journey into what my
ideal note taking solution would look like.
</p>

<ul>
  <li>
    Given my <code>vim</code> addiction, it would be a <code>vim</code> plugin.
  </li>
  <li>
    Given my newly-found backlink addiction, the plugin would implement
    backlinking (e.g. displaying backlinks to a note in a separate pane, with
    links that can be followed VimWiki-style).
  </li>
  <li>
    Plain MarkDown syntax for notes.
  </li>
  <li>
    An easy-to-use query syntax.
  </li>
  <li>
    An Android application that supports backlinks, queries, and
    <code>git</code>.
  </li>
</ul>
  ]]></description>
  </item>

  <item>
  <title>One Author</title>
  <guid>http://m-chrzan.xyz/blog/one-author.html</guid>
  <pubDate>2023-02-10T11:51:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Microsoft just <a href='https://news.microsoft.com/the-new-Bing/'>announced</a>
their ChatGPT-powered Bing experience. While Google is worried and scrambling to
catch up on the new space race, we as humans have even more important things to
worry about than our bottom line.
</p>

<p>
From a purely technological perspective, these AI developments look incredible.
We're getting to the point where computer assistants we know from sci-fi films
are becoming an every day reality.
</p>

<p>
But what are the wider implications of this?
</p>

<p>
There's tons of potential societal outcomes this could lead to, starting with
kids a few years from now wondering why Tony Stark is the only character with an
all-knowing AI assistant in Iron Man, all the way to a full singularity event.
For this article, I'll focus on just one aspect: Internet content.
</p>

<h3>AI content on the web</h3>

<p>
For some time already, there's been a ton of AI-composed or -aided "content" on
the web. You may have heard others (e.g.
<a href="https://www.youtube.com/watch?v=N8P6MTOQlyk">
  Luke Smith</a>, <a href="https://jacobwsmith.xyz/stories/human_writing.html">
  Jacob Smith</a>; no relation between the two as far as I'm aware) complaining
about the generic SEO garbage sites that just produce tons of generic,
search-engine friendly articles on commonly searched topics, just to farm clicks
and ad views.
</p>

<p>
More recently, as various advanced language models started being published,
people started speculating that eventually AI will start replacing even the
higher-class content. YouTuber penguinz0
<a href="https://www.youtube.com/watch?v=3iQ8RXhkNwQ"> claims </a> that, in his
opinion, a bit of playing around with ChatGPT produced a better game review than
a popular game journalism website. BuzzFeed
<a href="https://www.buzzfeed.com/jonah/our-way-forward">
  openly stated
</a> that they will start publishing AI generated articles.
</p>

<h3>One author</h3>

<p>
While people have been suspecting that <i>eventually</i> more and more content will
be generated, Microsoft is now straight up recommending you do it <i>now</i> (see
minute 37:30 of the press conference, linked above).
</p>

<p>
Composing individual social media posts might not seem like that big of a
deal. But it is if you take into account how media builds upon itself. Much
content is created by citing, commenting on, being inspired by other content.
And AI is itself specifically suited to quickly generating this sort of
derivative material. As the density of AI generated content increases, its
<i>rate of increase</i> will start to grow non-linearly.
</p>

<p>
Just imagine. A Wikipedia article cites that so-and-so said something on
Twitter. That The Guardian reported that this or that happened. A leaked email
from so-and-so revealed that... But all those sources were written by AI. And
maybe the article itself was composed with the help of Bing's compose
functionality?
</p>

<p>
Then you search for something relevant and the Bing bot reads the article and
provides you an answer based on it. Maybe you're smart enough to double check
there are "real" sources to corroborate the bot's answers and it's not something
it made up, so you find the Wikipedia article yourself, full of external
citations, and are satisfied.
</p>

<p>
But fool you are, the internet has one author.
</p>
  ]]></description>
  </item>

  <item>
  <title>Some Life Updates For 2022</title>
  <guid>http://m-chrzan.xyz/blog/some-life-updates-for-2022.html</guid>
  <pubDate>2022-12-21T23:13:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Haven't written much on the blog this year, but not to worry, I'm still alive!
Here's some updates as to what I've been up to:

<ul>
  <li>
    Joined WLOD, a brass orchestra that plays traditional dance music from the
    Roztocze region of Poland (actually joined it late last year, but this
    year's season was much more active).
  </li>
  <li>
    Also joined WLOD's funeral sub-orchestra, where we play traditional funeral
    marches from the repertoire of Roztocze orchestras.
  <li>
    Joined Bonanza, a small ensemble playing mostly oldies music from the
    1920s-30s popular in Polish cities (initially as a substitute, now as a full
    member).
  </li>
  <li>
    Spent most of the summer going to various traiditional music events.
  </li>
  <li>
    Finally completed my master's in computer science! You can check out my
    thesis <a href='../thesis.pdf'>here</a> and the LaTeX sources for
    it <a href='https://git.m-chrzan.xyz/mgr/about' %>here</a>.
  </li>
  <li>
    Don't expect a "Books read in 2022" article, didn't read that much this
    year, unfortunately, but planning on doing so more in the coming months.
  </li>
  <li>
    The Sunday Corner was empty for quite a stretch of time, but I've been more
    regular on updating it recently. No promises as to whether this trend will
    continue, but you might want to start checking it out once in a while again
    if you enjoyed it but stopped after it was inactive for a while.
  </li>
</ul>
</p>

<p>
So in summary, a year of traditional music. This has been my main focus in the
past months, and I want to continue in this direction, widening my repertoire,
improving my fiddling skills, expanding
<a href='https://tuturutu.net'>
  tuturutu.net
</a>, and maybe even starting my own band eventually.
</p>

<p>
And since it's that time of the year, Dear Reader, I'm wishing you a Merry
Christmas and a happy New Year!
</p>
  ]]></description>
  </item>

  <item>
  <title>Books Read in 2021</title>
  <guid>http://m-chrzan.xyz/blog/books-read-in-2021.html</guid>
  <pubDate>2022-01-17T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Here's a list of books I read this past year. As mentioned at the end of
<a href='books-read-in-2020.html'>last year's post</a>, I
started out the year with quite a few positions finished cover-to-cover, but as
the amount of time I needed to spend on work and my thesis increased during the
year, there were a few months where I had no time at all for leisure reading. By
the end of the year I once again started finding more time for reading and
decided to jump into Plato's works.
</p>

<p>
Anyways, here's the list:
</p>

<p>
<ol>
  <li>
    <p>
    <b>The Shallows</b>, Nicholas G. Carr. Words of warning about how the
    internet and computer technologies negatively affect our lives. In my
    opinion, Carr's takes aren't quite spicy enough, and the Ellul book
    mentioned below gives a much fuller picture of how cautious we should be of
    technology. That said, this might work as a decent wake up call for anyone
    not quite yet ready to jump into 300 pages of dense sociology that Ellul
    presents.
    </p>
  </li>
  <li>
    <p>
    <b>The Great Gatsby</b>, F. Scott Fitzgerald. Finally got around to reading
    this classic.
    </p>
  </li>
  <li>
    <p>
    <b>The Enchiridion</b>, Epictetus. A very quick read, works as a nice
    summary of the stoic position.
    </p>
  </li>
  <li>
    <p>
    <b>Foundation</b>, Isaac Asimov. Finally got around to reading this sci-fi
    classic.
    </p>
  </li>
  <li>
    <p> <b>The Technological Society</b>, Jacques Ellul. I actually started
    reading this in 2020 but didn't get far, picked it up and finished the rest
    of the way in 2021. A very good read, highly recommended reading if at any
    point you've run across Ted Kaczynski's manifesto and any part of it
    intrigued you.
    </p>

    <p>
    Ellul takes a deep dive into how technology (or more generally,
    <i>technique</i>, which by his definition is any process that is the best
    currently known for achieving a particular goal) has interacted with and
    changed our humanity.
    </p>
  </li>
  <li>
    <p>
    <b>First Philosophers</b> (second half, on the sophists), Robin
    Waterfield. As mentioned in last year's post, I read half of this in 2020.
    Honestly the first half on the presocratics was much more interesting. Now,
    at the end of 2021, I don't remember much from the sophists at all.
    </p>
  </li>
  <li>
    <p>
    <b>Can Life Prevail</b>, Pentti Linkola. A much more radical take on
    what it means to "save the envrionment" than what you'll hear on TV. Linkola
    spent his life studying the declining health of European forests and bird
    life, and fishing using traditional methods. He speaks about nature from the
    point of view of a brother, not a virtue signaling committee.
    </p>
  </li>
  <li>
    <p>
    <b>Crisis of the Modern World</b>, René Guénon. Though written in the
    1920s, the symptoms of crisis Guénon described have only grown stronger by
    the 2020s. He ends up dealing with some of the same subjects as the Ellul
    book mentioned previously, but from the point of view of esoterics and
    tradition rather than technology.
    </p>
  </li>
  <li>
    <p>
    Plato's dialogues. I'm reading from the version of his complete works edited
    by John M. Cooper and D. S. Hutchinson. I haven't officially committed to
    going through the whole thing, I'll just keep going until I'm bored and
    decide to switch to something else. Here's the dialogues I got through this
    year:
    <ol>
      <li>
        <p> <b>Euthyphro</b>. Happens days before Socrates' trial. Socrates
        and Euthyphro discuss the meaning of piety and justice.
        </p>
      </li>
      <li>
        <p>
        <b>Socrates' apology</b>. Plato's rendition of Socrates defending
        himself in court against the charges of impiety and corruption of the
        youth.
        </p>
      </li>
      <li>
        <p>
        <b>Crito</b>. Socrates' friend visits him in prison, trying to convince
        him to attempt an escape before his execution. Socrates explains, much
        to Crito's disappointment, why it would be improper for him to do so.
        </p></li>
      <li>
        <p>
        <b>Phaedo</b>. Several of Socrates' friends visit him the day of his
        execution. Socrates attempts to lift their spirits with a discussion
        about death and the afterlife, before (spoiler alert) drinking the
        kool-aid.
        </p></li>
      <li>
        <p>
        <b>Theaetetus</b>. Socrates speaks with a young student of philosophy
        about the nature of knowledge. They try out a few different potential
        definitions, unhappy with each, arriving at absurdities and
        contradictions with each attempt. This dialogue has an overall very
        conversational and even light-hearted style, while getting into some
        really complex and dense case analysis at times.
        </p>
      </li>
    </ol>
  </li>
</ol>
</p>
  ]]></description>
  </item>

  <item>
  <title>The NFT+VR Cash Grab</title>
  <guid>http://m-chrzan.xyz/blog/the-nft-vr-cash-grab.html</guid>
  <pubDate>2021-12-29T20:32:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I used to play Team Fortress 2, a 3D hat simulator also known for having a
decent team based first person shooter included as a mini game. The game, and
gameplay time on official servers, was offered for free, but Valve still made a
ton of money by selling cosmetic items to players. I don't play games these
days, but from what I hear, there are many other modern games like League of
Legends or Fortnite whose business models center around selling player skins.
</p>

<p>
Making money off of video game skins is a very shrewd idea: you pay for a few
hours of a graphic designer's time, and from then on can sell limitless copies
of the item, each sale being instantaneous, requiring no shipping, no storage,
no store front. If you're able to pull it off, it's better than print-on-demand
or drop shipping.
</p>

<p>
But so far this sort of model has been mostly limited to gaming. This is a very
specific demographic, mostly male, skews towards younger people, and even among
them, only the gamers most emotionally invested in the game will be willing to
spend money on cosmetic items.
</p>

<p>
Now, if Fortnite skin sales can support a large development studio that then
ends up with enough money for some of the craziest marketing stunts, imagine the
profits if you could widen the demographic from "gamer" to "your average
internet user".
</p>

<p>
This is exactly the opportunity that Zuck and others are investing billions of
dollars into under the memes of VR and NFTs. By directing your average internet
user into a virtual reality as a primary means of interacting with social
circles, work, and digital media, and doing so in an anthropomorphized avatar (a
word that became increasingly creepy with each repetition of it from Zuck's
mouth during the Meta announcement), Meta and others focused on VR will create
emotional attachment to a gamified reality where products can be produced even
more cheaply than previously possible with humid sweatshops and child labor.
</p>

<p>
VR didn't succeed at first because it was just a gimmick that made some people
puke, but there wasn't enough profit to be made for companies to keep investing
into research and development around it. With the perfect storm of "NFT"
becoming the hottest Silicon Valley buzzword and more people switching to a
work-from-home lifestyle, some of the tech opportunists, including Zuck, must
have finally put 2 and 2 together, finally seeing a reason to begin pumping cash
into VR again.
</p>

<p>
The medium is the message. It is far less important what VR will be used for (it
will still be just social media, online shopping, communications, gaming, and
other entertainment, like current technologies), far more important how it can
be used to affect people and their behaviors.
</p>

<p>
Furthermore, do not get your hopes up thinking that by embracing NFTs, Facebook
or any other big tech silo is going to move towards decentralization. "NFT" just
stands for "non-fungible token", there's nothing about decentralization in the
name. Do you really think Zuck will use decentralized, trustless, uncensorable
protocols like Ethereum, or will it be safer (from his profits' perspective) to
put everything on Libra/Diem, or even more likely, just chuck it all in a
database? Oh, Zuck promised you interoperability with other "metaverse"
protocols? Yeah, that's just going to be an API hosted in Meta's data centers.
</p>

<p>
TF2 hats are NFTs. Fortnite skins are NFTs. To illustrate the point, this post
is now an NFT, running on the m-chrzan.xyz ledger.
</p>

<p>
I'm air dropping it to Tom Fasano. The bottom of this post will work as a public
block explorer of the ledger. If the current owner of the NFT wants to transfer
it, he or she should email me and provide me with acceptable contact details
(ideally email+PGP key or an XMPP address) of the new owner.
</p>

<p>
For those curious about technical details of the m-chrzan.xyz ledger, here is
the whitepaper:

<blockquote>
  The m-chrzan.xyz ledger is based on novel PoM (Proof of Martin) technology.
  The ledger is guaranteed to be secure and live as long as 100% of the Martins
  running it act honestly and maintain uptime.
</blockquote>
</p>

<h3> The m-chrzan.xyz Ledger: </h3>

<p>
History of ownership of the m-chrzan.xyz/blog/the-nft-vr-cash-grab.html NFT:

<ol>
  <li>Tom Fasano (minted on December 29, 2021)</li>
</ol>
  ]]></description>
  </item>

  <item>
  <title>Why is all music in 4/4?</title>
  <guid>http://m-chrzan.xyz/blog/why-is-all-music-in-4-4.html</guid>
  <pubDate>2021-12-12T10:49:00+00:00</pubDate>
  <description><![CDATA[
    <p>
It is a favorite pastime for music aficionados (where music aficionado =
someone who listens to something other than Top 40 and knows two things about
music theory) to bemoan the tragic state of modern music, specifically the fact
that everything sounds the same. Same chord progressions, rhythms, song
structure. The reason why this happened is fairly obvious: the genesis of a
music industry created an optimization problem, and we're living in a time where
this is a mostly solved problem. It's cheaper to produce a thousand items from a
plastic injection mould than carve each piece by hand. It's more scalable to
sell the same music globally than cultivate local traditions.
</p>

<p>
But there is a second, more technical <i>why</i> question we could ask: why did
modern music arrive at the specific qualities it arrived at? I want to posit a
potential, partial reason for why all songs on the radio are in a 4/4 time
signature.  The idea to write this came to while thinking about whether Polish
traditional music could have evolved into something popular with the modern
masses, the way Negro spirituals evolved into blues, evolved into rock.
</p>

<p>
Much of Polish music, before it became replaced by Western influences, was based
on 3/4 rhythms. The wild, trance-like spinning of the <i>mazurek</i> and
<i>oberek</i>, or the slower, more waltz-like <i>kujawiak</i> are all counted in
3. Why could these rhythms not survive the musical revolutions of the mid 20th
century?
</p>

<p>
It is not for a lack of energy and liveliness in the music. Just look at the
sort of a musical fire an old man from a small village can spark up with a
fiddle: <a href="https://www.youtube.com/watch?v=io-RC6USnyk">
www.youtube.com/watch?v=io-RC6USnyk</a>. It's not even necessarily the crazy,
non uniform rhythms of the music that would have been a problem. Should the
mazurek have been destined to influence modern popular music, the industry would
have watered down and simplified its complexities for the shopping center
visitor's ears. That of course would have been a great disservice to the music
and I'm glad it didn't happen, but it was possible.
</p>

<p>
The problem I see is technical, and comes down to the fundamental design of the
mazurek, its purpose being contrary to what is necessary for modern popular
success. So let's first talk about the technical requirements of modern music.
</p>

<p>
During the 20th century music became an entertainment product for individuals.
It is a music you listen to on the radio while cleaning the house, in headphones
on a commute. You don't go to a concert just to spend time with friends, but in
large part to have an individual experience of the music. To say "<i>I</i> was
there, <i>I</i> heard them, <i>I</i> saw them". Of course there is still a
social aspect to concert-going, but in centuries past, it was the primary aspect
of music making. It was a social glue and lubricant in the same way as alcohol.
These days when you go to a concert, you sit or stand in your own spot, facing
the stage, and experiencing the music on your own, inside your own mind, inside
your own body. Maybe jumping up and down with the rest of the crowd, but you're
doing the jumping by yourself.
</p>

<p>
You can't jump to an oberek in a mosh pit under the stage. It simply won't work.
If you jump on every beat, it will be too fast. If once a measure, it's going to
be awkward and too slow. Either way, it's just not going to feel right. The
oberek is a dance fundamentally designed to be danced spinning with a partner.
That's what the accents guide you towards. There is a flywheel effect of one
partner leading in one measure, the other in the other measure. It simply is not
a music for individuals. And that's a good thing.
</p>
  ]]></description>
  </item>

  <item>
  <title>On Zuck's Metaverse &#x2014; Ads Everywhere</title>
  <guid>http://m-chrzan.xyz/blog/on-zucks-metaverse.html</guid>
  <pubDate>2021-11-25T14:06:00+00:00</pubDate>
  <description><![CDATA[
    <p>
First off, let's just call it VR. "Metaverse" is a brand name invented to
separate current efforts from the previously failed hype around VR. The reasons
VR has failed before are interesting of themselves, but more on that in a future
post. Here I want to talk about Zuck's ideas for social VR and what I think
about the whole concept.
</p>

<p>
VR as a social and communication medium, which appears to be Zuck's focus, and
is likely to end up being one of the widest VR markets in terms of number of
users and overall man-hour usage time, is not progress in any way. It doesn't
actually introduce any new features that would make life better. It will never
replace real world human connections as Zuck seems to imply it can. At least not
until we reach Matrix-levels of simulation, but that comes with a whole host of
other obvious problems.
</p>

<p>
Remember &mdash;
<a href='its-not-social-media-its-marketing-medial.html'>
  it's not social media, it's marketing media</a>. VR social media is simply
a huge investment of developer time, and a trade off of internet bandwidth, to
squeeze extra attention and shopfront-facing time from users. VR is fancy,
engaging, and can probably be made to be even more addicting than the current
flat phone screens.  Just like it's hard to take your eyes off of a flashing TV
screen, even if you're not actively interested in what's currently on, it will
be difficult for the people that plug themselves into VR to take the headset
off. Zuck talked a lot about interoperability of "metaverse" systems: this is
also going to be useful from a profits perspective, by seamlessly guiding users
(<i>useds</i> in Stallmanian terms) from a chat with friends to purchasing a new
product.
</p>

<p>
Internet ads already replaced magazine, newspaper, radio, and to an extent even
TV ads. By building a simulated world for people to spend time in and move
around, Zuck (and other VR investors) could now also steal market share from
highway billboards, sidewalk posters, and other places we encounter marketing
while moving around the physical world.
</p>

<p>
That's all there is to it. If you see Zuck investing billions into a new effort,
remember that it's to see a return on investment. And for a marketing product
like Facebook, returns come from sapping human attention.
</p>
  ]]></description>
  </item>

  <item>
  <title>I Made a Songbook</title>
  <guid>http://m-chrzan.xyz/blog/i-made-a-songbook.html</guid>
  <pubDate>2021-09-16T15:18:00+00:00</pubDate>
  <description><![CDATA[
    <p>
A bit over a year ago I started exploring the world of Polish traditional music.
I might write a post later talking more about my journey with that, but for
today I just want to talk about a project related to this that I created last
weekend: <a href='https://tuturutu.net'>tuturutu.net</a>.
</p>

<p>
Quick word of warning: it's 100% in Polish, so you probably won't get much use
out of it if you're not a Polish speaker. Even if you are, you probably still
won't get much use out of it if you're not interested in traditional music.
</p>

<p>
In this post I'll go over three things: the <i>what</i>, the <i>why</i>, and the
<i>how</i> of tuturutu.net.
</p>

<h3>What is tuturutu.net?</h3>

<p>
Right now the website is just a simple listing of a few songs from Polish
villages, mostly the types that would be sung at a wedding, or otherwise related
to the concept of love. I got most of the lyrics at a week-long workshop I went
to a year ago.
</p>

<p>
Most of the lyrics also have short sheet music notating the main vocal melody.
For the most part, these are transcribed by ear by me, hopefully there aren't
any inaccuracies. That said, the sheet music is only provided for reference, and
honestly should not be used to learn the songs. Classical notation is an
imperfect tool for this purpose, as in the real world the melodies often vary
from performer to performer and freedoms can be taken with both rhythm and
pitches. The best way to learn is to listen to performances (if possible, live
performances; and in those cases, bonus points if it's in an environment where
you can <i>join</i> the singing, not just listen to it like at a concert,
further bonus points if you're dancing while doing it).

<p>
Where I could find them, recordings from YouTube or Bandcamp are linked.
</p>

<p>
Each song is downloadable as an individual PDF for convenient offline storage,
or for printing individual songs for distribution to a group learning them.
</p>

<p>
On the main page, there's also a PDF that's a songbook containing all of the
songs. This is, in my opinion, the main "product" on the website. All the song
pages on the website are nice if you need to quickly look something up, but if
you're going to be needing the songbook at a party, it's probably best to
download the whole thing and use that (it looks good on large screen ereaders,
and has a clickable index). Very printable, too!
</p>

<h3>Why did I build this?</h3>

<p>
Primarily just for myself. In the recent months I'd been going to some parties
where this sort of traditional music would be played, but when trying to Google
some of the lyrics I would hear people singing, I wasn't able to find anything.
Additionally, the lyrics I did have from the workshop I mentioned before were
stored in my phone's camera's folder as photos I took of somebody else's phone
screen showing a photo from a Facebook post of a laptop screen with a Word
document open. Seriously. They weren't easy to find and definitely weren't
searchable. So if I couldn't rely on lyrics websites that already exist, I
decided I need to create something of my own. And the nice thing is, my website
can be as clean as I want it to be, rather than a bloated mess of newsletter
popups, cookie preferences, and flashing ads.
</p>

<p>
As mentioned above, the sheet music on the website is not really provided for
anyone to use to learn the melodies. Instead, since currently I myself am the
target audience of this website, the sheet music is there to remind <i>me</i>
the melodies if I ever forget them (which I sometimes do). If anyone else finds
them useful, great. Again, I do recommend learning by ear instead.
</p>

<p>
For now the website is just a small index of a few songs I know. If I have the
time and motivation (or funding/volunteers), I would love to expand it to a much
wider and more general archive of this sort of music.
</p>

<p>
The songbook will probably receive a field test in the coming days, as the next
edition of the workshop I went to last year is coming up, and we might end up
using tuturutu.net as the "official" songbook.
</p>

<h3>How is tuturutu.net implemented technically?</h3>

<p>
Just like this website, tuturutu.net is a static site. I adapted the
<a href='https://git.m-chrzan.xyz/website/about'>Ruby script that generates this website</a> to
build the songbook website, which is simpler in some ways, and more complex in
others. Instead of a list of blog articles written in (potentially templated)
HTML, the sonbgook website has, for each song, up to three files:

<ol>
  <li> <code>song.txt</code>: a plain text file with the lyrics </li>
  <li>
    <code>song.ly</code>: a(n optional) Lilypond source file notating the
    melody. <a href='http://lilypond.org/'>Lilypond</a> is a music notation
    language with a compiler that beautifully engraves it as sheet music to
    various formats.
  </li>
  <li>
    <code>song.yaml</code>: a metadata file that contains information like
    the song's title or links to recordings. In the future these might also
    contain important tags like the genre of music, region from which the song
    originates, etc.
  </li>
</ol>
</p>

<p>
The Lilypond files are compiled into SVG images that are then displayed on
individual songs' pages, PDFs, as well as the main songbook PDF. The PDFs
themselves are compiled from LaTeX templates.
</p>

<p>
Future additions to the website will probably include a search feature, tags,
and, if enough songs are added, multiple songbooks that represent curated
collections, rather than just the one that lists all the songs on the website.
</p>
  ]]></description>
  </item>

  <item>
  <title>Don't Fall For Fear Propaganda</title>
  <guid>http://m-chrzan.xyz/blog/dont-fall-for-fear-propaganda.html</guid>
  <pubDate>2021-08-27T01:08:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I was recently traveling from a busy US airport. If you've been to a larger
American airport in recent years, you may have noticed, near the security line,
a bunch of booths from this company called Clear. They essentially allow you to
exchange your personal information, including a biometric scan of your iris, in
exchange for quicker processing through TSA security. (and they also take a
monthly subscription fee, billed annually, so you pay even for months you're not
traveling in)
</p>

<p>
When I got to the airport, I was a little annoyed because the line through the
security check looked really long. I had lots of spare time arriving at the
airport, so I wasn't worried about missing my flight, but standing in an airport
line is never fun.
</p>

<p>
What annoyed me more, however, were Clear employees shouting as loud as they
could, for everyone in the security line to hear, that "you're going to miss
your flight!", "you're going to have to wait through this entire security
line!", unless you enroll with Clear (which is quick, easy, and <i>only</i>
requires you to give away a scan of your personally identifiable biometric data
to some new, overfunded tech company).
</p>

<p>
I was initially annoyed by this just because I'm not sympathetic to either
marketing or privacy violating technologies. But I got really mad when I
realized that

<ol>
  <li> the line was actually moving really fast; </li>
  <li>
    the spacer tape appeared to deliberately be set up in a way that made
    the line visually appear as long as possible.
  </li>
</ol>

What I mean by the second point is that there was an entire row of unutilized
space that the line could have been directed to, but that row was the one
<i>furthest away</i> from the spot you enter the security line from. This made
it seem like there was an entire additional long bend to the line. Was this
collusion between Clear and airport staff? I don't know. But Clear definitely
capitalized on this illusion.
</p>

<p>
Like I said, the line was moving quickly, there were many TSA lines open
processing a lot of people at a time. The Clear employees, standing by the line
for hours at a time, would have known this, but of course they're going to use
the scary looking queue as a fear tactics marketing opportunity. I got through
the line in about 30 minutes, which is a perfectly reasonable amount of safety
margin time any air traveler will include in their schedule.
</p>

<h3> Moral of the story </h3>
<p>
When you're being marketed something on the basis of fear (or on any basis,
really, but especially when your emotional instincts might be exploited), take
the time to really consider whether the bogeyman you're supposed to fear
actually exists and is that scary. I'm sure you'll find other situations where
similar logic applies.
</p>
  ]]></description>
  </item>

  <item>
  <title>Introducing Martin's Sunday Corner</title>
  <guid>http://m-chrzan.xyz/blog/sunday-corner.html</guid>
  <pubDate>2021-07-25T15:55:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I spent Friday night implementing a new part of my website:
<a href='../sunday-corner'>Martin's Sunday Corner</a>. It's going to be
a list of thoughts from the past week available once a week on Sundays. Think of
it like a once-a-week ephemeral microblog. A Twitter without any of the features
of Twitter. Or any of its limitations.
</p>

<p>
You can try to access the page on other days but there's no use, you'll just be
told to return next Sunday.
</p>

<p>
For now I'm going to treat this as an experiment. I'm not sure how long I'll
keep it running, but I'm hoping it's going to be light enough for me to not need
to care about it much. I won't mind if it remains empty some weeks, but if it
stays empty for weeks in a row, I'll probably shut it down and consider the
experiment as failed.
</p>

<p>
The Corner is going to be an outlet for

<ul>
  <li> random thoughts </li>
  <li> musings </li>
  <li> small updates </li>
  <li> tiny poems or jokes; why not? </li>
  <li>
    maybe small rants? I don't usually complain much, but maybe that's just
    because I've never had a good place to do so at.
  </li>
</ul>
</p>

<p>
I hope the ephemerality of this project will help me be more off-the-cuff and
share thoughts that I normally wouldn't in full blog posts. And, assuming this
actually leads to me thinking and sharing more thoughts, maybe it will
additionally lead to more interesting ideas that can evolve into full articles.
</p>

<h3>Anti Social Media</h3>
<p>
This experiment is anti social media. Not in the sense that it's anti social
&mdash; quite the opposite, since as always you are welcome to email me if you
have any comments about anything I've written, and that includes anything you
happen to see in the Corner. It's anti social media in the sense of being
opposed to
<a href='its-not-social-media-its-marketing-medial.html'>
  "social"
</a> media.
</p>

<p>
Social media tries to keep you hooked in as long as possible every day. It wants
to bombard you with all sorts of random content, then learn which of it sticks.
The Corner is available only one day a week. Once you've seen a Sunday's
contents, there's no reason to stay on the page, to refresh, to wait for a
notification (none will come). You'll have to wait until next Sunday for fresh
thoughts from a new week. The thoughts will all come from me. They might be
boring, they won't be targeted, you might not relate to most of them. I might
even write some of them in Polish so who knows if you'll be able to read them.
</p>

<h3>Technical</h3>
<p>
The Corner is implemented as a CGI script written in Ruby. You can find the
source code <a href='https://git.m-chrzan.xyz/sunday/about'>here</a>. Feel free to copy my code or
implement a similar idea for your own personal website!
</p>

<p>
I also just wrote an
<a href='https://landchad.net/cgi/'>article for LandChad</a> about CGI
scripting, if you're interested in CGI scripting in general. The example used
there is in concept very similar to the Corner.
</p>
  ]]></description>
  </item>

  <item>
  <title>It's not Social Media - It's Marketing Media</title>
  <guid>http://m-chrzan.xyz/blog/its-not-social-media-its-marketing-medial.html</guid>
  <pubDate>2021-06-28T18:27:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Richard Stallman has said that social media sites don't have users &mdash; they have
<em>useds</em>. The primary reason for sites like Facebook to exist is to
generate revenue for the company (obviously), and they do so by using their
webpage as a terminal for advertisements. To maximize revenues, users (useds)
are incentivized and otherwise manipulated to stay online, giving as much
eyeball time to the ads as possible.
</p>

<p>
Much has been said about how social media companies achieve this maximization
from a psychological point of view. Dopamine-inducing "likes", targeting users
with news/messages that will cause strong emotions (such as anger) to keep them
engaged, things like that. But what is the real technical innovation that
has made social media so irreplaceable in many people's lives, even those that
are rational enough to see that those previously mentioned "incentives" are
negatives for their lives?
</p>

<p>
The basic functionality that a social media platform like Facebook offers its
users is a non-intrusive broadcast to all your friends and family.  Now, the
"broadcast to all your friends and family" is supposedly the "social" part of
social media. But what do I mean by "non-intrusive"? I mean that, on Facebook,
your life updates don't necessitate a reply, don't start personal conversations
(at best, just a stream of "Congratulations!" or "Thoughts and prayers..."
comments), and it's not rude to just completely ignore them.
</p>

<p>
Before, to announce a new baby or invite friends to party, you had to manage
your own contacts list.
</p>

<p>
You would send postcards from vacation to a few select people, now you just post
the best pictures you got taken of yourself to Facebook, maybe with a witty
caption, and the best one of them all might even be worthy of Instagram.
</p>

<p>
Instead of discussing clever and/or silly ideas with your buds and a beer in
hand, you post them on Twitter, hoping for retweets and likes rather than a
conversation partner.
</p>

<p>
So while social media has made all sorts of social interactions much more time
and energy efficient than phone calls, email, snail mail, and real life
conversations, it also has replaced all these social interactions with much less
social alternatives. Additionally, we have to realize that most of the content
on "social" media isn't even social &mdash; it's marketing!
</p>

<p>
Any platform that allows non-personal broadcast &mdash; like Facebook pages or
any account on asymmetrical follow platforms (Instagram, Twitter) &mdash;
becomes a marketing platform rather than a social platform. Even without the
concept of promoted posts, Instagram has influencers and paid posts. Twitter is
all about thought leaders preaching their takes, politicians jumping at each
other's throats, and celebrities gossiping out in the open. TikTok perfected the
promotion algorithms to launch the careers of thousands of self-made 5-second
comedians and snake-oil salesmen.
</p>

<p>
"Perfecting the promotion algorithm" is something you do to build an advertising
platform, not a human social network.
</p>

<p>
Stop calling it social media. It's marketing media.
</p>
  ]]></description>
  </item>

  <item>
  <title>Linked Brain Clone: A Problem of Consciousness Thought Experiment</title>
  <guid>http://m-chrzan.xyz/blog/linked-brain-clone.html</guid>
  <pubDate>2021-05-25T15:11:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Here's a thought experiment I haven't heard before. I'm not sure if it's novel,
but even if it isn't, it's definitely less well known than the popular thought
experiments about cloning and the nature consciousness.
</p>

<p>
The question of whether it is possible to preserve a person's mind and
consciousness is a well known idea in speculative fiction and transhumanist
thought, as well as touching on more general philosophy and the hard problem of
consciousness.
</p>

<p>
The first thought experiment to consider is one many people have already played
around with on their own. What happens when you create a perfect copy of a
brain? If you copy a brain, molecule by molecule, atom by atom, even
electrical charge by electrical charge to achieve the same state, will the
object you create be conscious? Would it have the same memories and ideas as the
source brain?
</p>

<p>
Assuming no hidden metaphysics that somehow breathe consciousness into our human
beings, the intuitive answer to the above questions seems to be yes. If our
intelligence, memories, etc., are all just based on brain structures and
electrical charges within the brain, a perfect copy should function exactly the
same as the original.
</p>

<p>
Now suppose we clone your entire body, brain and guts and all. Is this clone
also "you"? I think the intuitive answer here is that no, it isn't.  You would
end up with two different seats of consciousness. Each with exactly the same
memories and ideas, but still different individuals, like identical twins on
steroids. You wouldn't be able to see through your clone's eyes, hear their
thoughts. If you were killed, it's not like your consciousness would be
preserved in your clone's body.
</p>

<p>
This so far has been fairly common thought experiments that probably most fans
of sci-fi had encountered at one point or another. This is for example related
to a common depiction of teleportation, in which the teleportee's body is
perfectly scanned and destroyed on one end, then rebuilt molecule by molecule
as an identical copy on the other end. Personally I would never use such a
device &mdash; while I might appear as the same "he" to my friends after
teleportation, I don't see any reason to believe the person on the other end
would be the same "I", and I myself would be dead.
</p>

<p>
Now comes the "linked brain" part. What if the clone, instead of being built as
a separate person standing next to you, was initially built as an
<em>extension</em> of you? What happens if  we first build a second brain
attached to your own? Let's say the two brains are connected at the brain stem,
allowing for communication between them. We create a siamese twin for you,
joined at your brain stem and we give some time for your
consciousness to "spread" to both brains.
</p>

<p>
Eventually, we separate the two copies. Which one is now "you"? Who is the other
person and at which point did they become a second consciousness?
</p>

<p>
Maybe split brain studies can shed some intuitions on these questions. In
certain experiments, split brain patients appear to be inhabited by two separate
conscious entities where there once was one. Maybe there's no such thing as
consciousness and "conscious" thought is just a weird sensory artefact.
</p>

<p>
<em>I</em> for one definitely feel like I have a singular (though multi-faceted)
"I" inside of me. But maybe I'm just not enlightened.
</p>

<h3>Addendum</h3>

<p>
I guess this thought experiment could also be a modified to an "upload your
brain to a computer" variant. Suppose we can perfectly simulate a brain inside a
supercomputer. Let's also assume we have a perfect brain-computer interface (the
1000th generation of Neuralink) and we link a living human brain to a simulated
replica of a brain on a super computer. Can these merge into a single
consciousness? What happens when you unplug?
</p>
  ]]></description>
  </item>

  <item>
  <title>Dovecot SSL Certificate Renewal</title>
  <guid>http://m-chrzan.xyz/blog/dovecot-ssl-certificate-renewal.html</guid>
  <pubDate>2021-05-12T11:06:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Just a quick technical note.
</p>

<p>
As mentioned before, I
<a href='hosting-on-vultr-vps.html'>
  started self hosting email
</a> a while ago. Exactly three months ago, in fact, and I know this because
that's how long Let's Encrypt SSL certificates are valid
for. Yesterday, my email clients started complaining about an invalid
certificate coming from my mail server.
</p>

<p>
After a few minutes of worrying if I'm being man-in-the-middled, I ssh'd into my
VPS to debug.
</p>

<h3>TL;DR: Root cause and solution</h3>
<p>
Turns out when configuring Dovecot (the IMAP server I use), the SSL certificate
you set is just a static string, not a filename pointing to a file that Dovecot
will read every time it needs to serve it. As such, even after Certbot correctly
got a new certificate, Dovecot was still using the old one.
</p>

<p>
This can be fixed by restarting Dovecot, allowing it to read the new
certificate, assuming its available at the same path.
</p>

<h4>Long term solution</h4>
<p>
I already have a cronjob that runs <code>certbot renew</code> once a month to
renew any SSL certificates going stale. I'm going to change it to instead point
to a script that does

<pre>
certbot renew
systemctl restart dovecot
</pre>

to automate reloading the certificate.
</p>

<h3>Learnings</h3>
<p>
A few things I learned while debugging this issue:
</p>

<h4>Viewing certificate info on the command line</h4>
<p>
My first step was to check the certificate I thought should have been served by
Dovecot. In particular, I wanted to see its expiry date as well as compare its
fingerprint to that reported by my mail client.
</p>

<p>
Certificates are stored in ASCII armor format, which is not human readable.
Turns out you can get a human readable interpretation of your certificate with
the <code>openssl</code> CLI tool:

<pre>
openssl x509 -text -noout -in &lt;certificate file&gt;
</pre>

will get you a detailed, human readable output. For example, you can find a
"Validity" section, specifying the time period during which the certificate is
valid.
</p>

<p>
Adding the <code>-fingerprint</code> flag will also output the fingerprint hash.
The <code>-noout</code> flag is just there to suppress the output of the
non-readable ASCII armor text.
</p>

<h4>Let's Encrypt certificate locations</h4>
<p>
Currently valid certificates from Let's Encrypt are stored under
<code>/etc/letsencrypt/live/&lt;domain name&gt;/</code>. This is where my
Dovecot server was configured to get its certificate from, so before I realized
that the certificate is only read once, I was surprised that the fingerprint of
the certificate stored here did not match the one my mail client was receiving.
</p>

<p>
Turns out Let's Encrypt also stores historic certificates under
<code>/etc/letsencrypt/archive/</code>. Here I was able to find the certificate
with a matching fingerprint to the faulty one received by my client, and confirm
that it did indeed expire yesterday. Finding this is what led me to realizing
how Dovecot handles its certificate configuration.
</p>
  ]]></description>
  </item>

  <item>
  <title>Termite Has Been Deprecated</title>
  <guid>http://m-chrzan.xyz/blog/termite-has-been-deprecated.html</guid>
  <pubDate>2021-05-08T22:23:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I have been using <a href='https://github.com/thestinger/termite'>termite</a> as
my daily driver terminal emulator for over five years now. Turns out that its
creator and maintainer just announced that he's stopping development and
abandoning the project. The reason?
<a href='https://github.com/alacritty/alacritty'>Alacritty</a>, another terminal
emulator, by termite maintainer's own judgment, is basically strictly better.
</p>

<p>
My main reasons for using termite were its minimalistic design, keyboard-driven,
vi-like selection mode, and easy configuration. Alacritty, turns out, does all
of these, while also being faster and more secure.
</p>

<h3>Switching to alacritty</h3>
<p>
On first hearing this, I immediately installed alacritty, just to check out if
it really is a viable option for me. Before the installation completed, my first
instinct was that I would have probably needed about a week or so to tweak my
config and get used to the interface. I was expecting some switching friction.
</p>

<p>
Turns out there was zero friction.  After 5 minutes of test running, I'd set
alacritty to be my default terminal. I'm writing this post from inside
it, and I don't even notice the difference, other than a slightly darker default
color scheme. The vi-like selection mode is still at the familiar
<code>ctrl+shift+space</code> keybinding, and it seems like that was the biggest
termite-specific feature I was used to.
</p>

<p>
If anything, I'm already seeing big improvements when compared to termite.
Specifically, the vi mode in termite had slight annoying issues: jumping by word
didn't always work exactly the way I expected it to, and when copying selected
text, the cursor actually had to be <em>one</em> character right of the last
character you wanted to copy. I'm not seeing these issues with alacritty's
vi mode, and so far it feels even more intuitive than termite's.
</p>

<h3>Bonus tip: terminal vs. ssh</h3>
<p>
On the occasion of getting a new terminal, I'm reminded of a very useful
<a href='https://www.yaroslavps.com/weblog/fix-broken-terminal-ssh/'>
  blogpost by Yaroslav de la Peña Smirnov
</a> thanks to which I solved a mild annoyance I've had with termite for years.
Basically, when logging into an ssh session on various machines from termite, I
would have weird problems in the shell, the remote machine apparently not
understanding how to communicate properly with my terminal. This would make
commands like <code>clear</code> or <code>tmux</code> not work, or even more
annoyingly, cursor navigation and erasing characters with backspace were broken.
</p>

<p>
Previously, I had fixed this by hacking environment variables, but you can solve
it much more cleanly with the following:
<pre>
# on your local machine:
infocmp $TERM &gt; $TERM.terminfo
scp $TERM.terminfo &lt;remote machine&gt;:~/

# on remote machine:
tic -x $TERM.terminfo
</pre>
</p>

<p>
I had to repeat this little ritual with alacritty and the remote machines I
frequent, and now everything is working smoothly!
</p>
  ]]></description>
  </item>

  <item>
  <title>Selfhosted Crossplatform Notes with Gitjournal</title>
  <guid>http://m-chrzan.xyz/blog/gitjournal.html</guid>
  <pubDate>2021-04-23T20:07:00+00:00</pubDate>
  <description><![CDATA[
    <h3>The problem</h3>
<p>
The "app market" for mobile phones is a very sad place. I wish it had followed
the direction of Linux distributions, with their repositories containing all the
useful software tools you'd ever need, free and open source. Instead we have a
duopoly of app stores that are both filled with "app developers" trying to make
a buck instead of tools that people actually want.
</p>

<p>
As such, when I find a phone application that is actually built for usefulness,
follows decent design principles, and allows for a personalized workflow, I
think it's necessary to give it a shoutout.
</p>

<p>
For a long time I've wanted a digital note taking solution that would allow me
to synchronize notes across multiple devices. I wouldn't want to use Evernote or
any other proprietary, centralized service. Selfhosting something like Nextcloud
just for the purpose of notes feels like shooting a fly with a cannon. If I were
limited to computers, it would be a no-brainer to just keep the notes in a git
repo (like I do with my <a href='cheatsheets.html'>command line
cheatsheets</a>). How do we synchronize with a phone though?
</p>

<p>
Enter gitjournal.
</p>

<h3>Enter gitjournal</h3>
<p>
<a href='https://gitjournal.io/'>Gitjournal</a> essentially provides a git
client on your phone whose design is centered around note taking. I won't go
into all the features and design details, but it's got everything I need for
simple note taking:

<ul>
    <li>Edit notes in markdown</li>
    <li>Todo list mode</li>
    <li>Organize notes in a directory structure</li>
</ul>

And the killer feature is that you also provide a link and login credentials for
an external git repo. Every edit to the notes is committed and eventually pushed
to that repo. Any changes that appear in the repo will be pulled to your device.
</p>

<p>
And of course the git repository can be hosted anywhere. If you trust your
forge with your personal notes, be it GitHub, GitLab, sourcehut, or anywhere
else, your forge is now also your note synchronization hub. Personally, I host
my notes on my VPS.
</p>

<p>
Computer side, I've written a short script that takes care of git operations and
some of the metadata in gitjournal notes, and lets me select a note to edit with
<code>fzf</code>. But even manually navigating to notes and handling git
operations wouldn't be a bad experience.
</p>

<h3>Caveats</h3>
<p>
The app, in the end, is made for profit. In particular, on the Apple App Store
even the basic version costs money (it's free on Android). There are some
additional features that are further paywalled. This includes tagging and
multiple repositories, both of which would be nice to haves for me, but their
lack is not a deal breaker in my case. However, the app is open source (I think
including the "pro" features) and AGPL'd so if you know how to build it for your
phone, it's yours.
</p>

<p>
I haven't had to deal with merge conflicts in the app. I'm not sure how they're
handled. I'm not too worried though, if anything weird does happen, I'm sure
I'll be able to fix it computer-side. Nothing a little force pushing to master
can't solve.
</p>

<p>
You might have to be careful with some symbols in note titles that are treated
as special symbols by your OS (e.g. ~, !, [, ], etc.). The app won't complain
about them, they might cause problems on your computer.
</p>

<p>
As mentioned, the app commits every change you make to your notes. As such, I
prefer to use it for jotting down ideas, archiving links, writing down
interesting thoughts I have, long term todos  &mdash; in other words, more
persistent notes. For daily todo or shopping lists I use a different app. This
isn't a huge problem since I don't really need those things synchronized across
devices. I think in my ideal world, gitjournal would have "scratch notes" that
are not tracked in the git repo, so that I can keep all these things in one app
without littering git history with "buy eggs" every week.
</p>
  ]]></description>
  </item>

  <item>
  <title>Downloading Articles for my Ebook Reader</title>
  <guid>http://m-chrzan.xyz/blog/downloading-articles-for-my-ebook-reader.html</guid>
  <pubDate>2021-04-08T14:40:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I've recently taken to reading blog posts and other internet articles on my
ereader. And I don't mean using my tablet's browser and wifi connection to load
up websites. Instead, I convert the articles I want to read to PDF and read them
like I would any other ebook (I have a large screen tablet on which reading PDFs
is very comfortable; I would probably be playing around with EPUB conversion if
I had a smaller screen).
</p>

<p>
The obvious way to get a PDF of a website would be to use my browser's built in
print-to-PDF feature. But this has some minor problems for me:

<ul>
    <li>
        Articles from different websites will look very differently. I can't
        anticipate how the website's CSS will affect readability (things like
        font, text size, etc.).
    </li>
    <li>
        It's not super easy to automate. Maybe this is possible with headless
        browsers? But I haven't played around with those much and it feels silly
        to spin up a whole browser just to render some HTML as a PDF.
    </li>
</ul>
</p>

<p>
That second point &mdash; about automation and scripting &mdash; was
particularly important to me. So the obvious tool for the job was the Swiss-army
knife of document conversions, <code>pandoc</code>.
</p>

<p>
For a while I was wondering if I would have to write some clever script that
downloads all of the article's HTML and other resources (like images) and then
inputs them to <code>pandoc</code>. Fortunately, it turns out that <code>pandoc
&lt;article url&gt; -o &lt;output file&gt;</code> does exactly what you think it
does. The article ends up converted to PDF, with LaTeX used as an intermediate
step, so everything is in the beautiful LaTeX font. <code>pandoc</code> also
takes care of downloading and including images.
</p>

<h3>Hotkeys</h3>
<p>
I wrote a short script that calls <code>pandoc</code> and saves the PDF in a
specific directory. With that script available and working, I added hotkeys to
my browser and RSS reader that invoke it. These are the two programs in which I
might find articles to read, and now I can easily generate PDFs from both.
</p>

<p>
Here's what the <code>newsboat</code> config looks like:

<pre>
macro p set browser "article2pdf %u" ; open-in-browser ; set browser "elinks %u"
</pre>

And here's the <code>qutebrowser</code> binding:

<pre>
config.bind(
        '"P',
        'spawn article2pdf {url}'
)
</pre>

(<code>article2pdf</code> being the name of my script)
</p>

<h3>Caveats</h3>
<p>
This doesn't work perfectly.

<ul>
    <li>
        There's some issues with certain Unicode characters (including emojis)
        that LaTeX apparently can't handle. Adding the
        <code>--pdf-engine=xelatex</code> flag when calling <code>pandoc</code>
        doesn't fully mitigate the issue, but it will produce reasonable output
        without completely failing.
    </li>
    <li>
        Sometimes images are not handled great. For example they might not fit
        width-wise. LaTeX completely fails on images in the WebP format.
    </li>
    <li>
        Similarly, sometimes code blocks might get cut off and not fit
        width-wise. This is admittedly a pretty big problem.
    </li>
    <li>
        Headers and footers from many sites will not be rendered great. This
        doesn't bother me, all I care about is the main article contents.
    </li>
</ul>
</p>
  ]]></description>
  </item>

  <item>
  <title>I Don't Believe in the Snooze Button</title>
  <guid>http://m-chrzan.xyz/blog/i-dont-believe-in-the-snooze-button.html</guid>
  <pubDate>2021-03-31T13:42:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I never understood the purpose of a snooze button. The default behavior, on both
physical alarm clocks and now on phone apps, is to give you 5-10 minutes before
the alarm sounds again. Supposedly to give you a time for a "snooze" &mdash;
meaning a short sleep, a nap.
</p>

<p>
But "sleeping" for 5 minutes, after already having been awoken by a loud alarm,
is useless, at least in my experience. If I have 5 minutes left before I know
the alarm will sound again, I'll a) not have time to actually fall asleep again,
b) spend the whole time angry at the alarm knowing it will start buzzing again
any second now. Any difference in grogginess between getting up immediately and
after the 5 minutes would be insignificant, so might as well get up without the
snooze.
</p>

<h3>How I snooze</h3>

<p>
The above is not to say that I'm the perfect human who is always able to wake up
full of energy no matter how early my alarm is set. But instead of torturing
myself with 5 minute fragments of back-and-forth between trying to force myself
to pass out and a blaring alarm, I take an actual nap.
</p>

<p>
If I wake up sleep deprived and I know I can delay my morning by at least half
an hour (or maybe skip whatever first commitment I had, assuming it's lower
priority than me not being a zombie all day), I'll set a fresh alarm half an
hour (or more if I'm more groggy and have a wider margin available) ahead. That
is usually enough time to actually fall asleep and actually start the day with a
clearer mind.
</p>

<p>
The most baffling use of a snooze button to me is people saying that it's normal
for them to wake up after hitting "snooze" several times. If you're able to stay
in bed for several snooze button hits without missing any absolutely critical
scheduled events, you should forget the snooze button and do what I do &mdash;
set an alarm that gives you time for a real nap.
</p>
  ]]></description>
  </item>

  <item>
  <title>Nothing is "Good for You"</title>
  <guid>http://m-chrzan.xyz/blog/nothing-is-good-for-you.html</guid>
  <pubDate>2021-03-16T15:06:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Popular science websites or lifestyle bloggers often tout headlines along the
lines of "Is X good for you? Science says YES!" These articles usually cite some
new study, though without going into the details of it, rarely discussing even
the surface level of the methodology or significance of results.
</p>

<p>
The study measures the impact of X on some output(s) Y, and just based on the
fact that it concludes with a positive impact on Y (no matter the magnitude),
the pop science article reports that X is objectively good, and will end by
recommending that every reader should now do X, incorporate X into your daily
routine, need never feel guilty again doing X (since often X is something
associated with bad habits, like drinking a particular alcohol, playing video
games, etc).
</p>

<p>
Clickbaity headlines like that will easily imprint a positive association with X
in most readers' minds. But just because a study found a positive impact of X on
Y doesn't necessarily warrant saying that it is objectively "good for you".
</p>

<p>
Even barring general problems with many studies (was it performed on a large
enough population? was the methodology correct? were there any biases
introduced? was the population representative of the reader?), any study will be
able to test only a limited number of outputs Y. So even if the study was
performed perfectly, and you agree with the authors on what change in Y is
positive or not, the study can make no claim on all other aspects that could be
potentially impacted by X, including ones that we wouldn't even expect to be
affected.
</p>

<p>
You will most likely benefit more from using "Lindy" things (per Nassim Taleb's
terminology) than jumping on any particular pop science trend.
</p>
  ]]></description>
  </item>

  <item>
  <title>Hosting on Vultr VPS</title>
  <guid>http://m-chrzan.xyz/blog/hosting-on-vultr-vps.html</guid>
  <pubDate>2021-02-16T18:10:00+00:00</pubDate>
  <description><![CDATA[
    <p>
This website was originally hosted for free with GitLab Pages. As of a few weeks
ago, it's now being served by Nginx from a Vultr VPS. Here's a few reasons why I
made the switch:

<ul>
    <li>
        I get a bit of insight into website traffic from Nginx logs, without
        needing to resort to something like Google analytics.
    </li>
    <li>
        I gain some internet independence. Sure, now I'm relying on Vultr
        instead of GitLab, but I can always switch to a different VPS provider
        (or, with enough grit, use own hardware) which will have the same
        environment, while GitLab Pages is a setup specific to GitLab.
    <li>
        I've wanted to play around with self-hosting various services for a
        while now.
    </li>
</ul>
</p>

<h3>Current setup</h3>

<p>
As mentioned, the website is served with Nginx. It's still statically generated
with my Ruby script. I build locally and use <code>rsync</code> to incrementally
update the hosted files.
</p>

<p>
I use <a href='https://certbot.eff.org/'>certbot</a> with the Nginx plugin to
get an SSL certificate. A cron job should ensure the certificate is updated
automatically before expiry.
</p>

<h3>Website traffic analysis</h3>
<p>
I've already gotten some interesting statistics from server logs. The day I
posted a Hacker News comment linking to one of my blog posts, there were four
times as many HTTP requests received, including around 200 unique IPs referred
from <code>news.ycombinator.com</code>.
</p>

<p>
So far my log analysis has been very ad hoc &mdash; just manually parsing the
log files with command line tools and Vim. For example, to get that 200 number
from above I ran

<pre>
awk '/ycombinator/ { print $1 }' logs | sort | uniq | wc -l
</pre>

I wonder if there are any good tools for parsing and analyzing Nginx logs, or if
I should build something simple of my own.
</p>

<h3>Self-hosting</h3>
<p>
In addition to this website, I'm also using the VPS to host a personal email
server and some <a href='https://git.m-chrzan.xyz'>git repos</a>. The email
server is based on Postfix and Dovecot and was painlessly installed and
configured thanks to
<a href='https://github.com/LukeSmithxyz/emailwiz'>emailwiz</a>. The git
frontend is <a href='https://git.zx2c4.com/cgit/'>cgit</a>. I might write a post
about setting up and configuring it later. Overall quite happy with what it
looks like and what it offers.
</p>
  ]]></description>
  </item>

  <item>
  <title>Books Read in 2020</title>
  <guid>http://m-chrzan.xyz/blog/books-read-in-2020.html</guid>
  <pubDate>2021-02-02T22:35:00+00:00</pubDate>
  <description><![CDATA[
    <p>
If memory serves me right, I read around 10 books last year. Here's the list,
with a few words about each entry.
</p>

<p>
<ol>
    <li>
        <p><b>Moby Dick</b>, Herman Melville. The Great American Novel. Haven't
        actually quite finished this one, but I've gotten most of the way
        through it, so feels more appropriate to include it in the 2020 list
        rather than the 2021 one (I do intend on finishing this book).</p>
        <p>Interestingly, despite its length, I found it fairly easy to pick the
        book up midway after a longer pause from reading it. This might be due
        to quite a different structure compared to the more modern novels I'm
        used to. Though it happens mostly chronologically, it's not really a
        linear, continuous story where you have to keep track of the plot and
        characters. Instead, you're presented with short vignettes of whaling
        life (and, sometimes, everyone's favorite cetology lectures).</p>
    </li>
    <li>
        <p><b>Się</b>, Edward Stachura. A collection of short stories by the
        Polish poet, one of his last published works before his suicide two
        years later. The stories are mostly set in travels around both Poland
        and the American continent.</p>
    </li>
    <li>
        <p><b>The First Philosophers</b>, Robin Waterfield. Guess you could say
        I'm finally starting with the Greeks (I did read The Republic the year
        prior, reading it was an interesting experience but I'm sure a lot of it
        went way over my head at the time).</p>
        <p>Like Moby Dick, I haven't quite finished the whole book yet &mdash;
        I read the first half on the presocratics, but will need to get back to
        the part on the sophists at some point.
    </li>
    <li>
        <p><b>Fight Club</b>, Chuck Palahniuk. Probably shouldn't talk about
        this one much, but man, this is the first time in a while a book sucked
        me in this strongly. A literal page turner that kept me awake late into
        the night.</p>
    </li>
    <li>
        <p><b>Antifragile</b>, Nassim Taleb. Very glad I finally got around to
        reading something from Taleb.</p>
    </li>
    <li>
        <p><b>Zen and the Art of Motorcycle Maintenance</b>, Robert M. Pirsig.
        Another foray into philosophy, though definitely more modern than the
        Greeks. It wasn't until I was about halfway through the book that I
        found out it's mostly autobiographical rather than pure fiction.</p>
    </li>
    <li>
        <p><b>Vagabonding</b>, Rolf Potts. I've published extended reading
        notes <a href='book-notes-vagabonding.html'>here</a>.
        </p>
    </li>
    <li>
        <p><b>The Sovereign Individual</b>, James Dale Davidson and William
        Rees-Mogg. Widely popular amongst and recommended by Bitcoin/blockchain
        fans.</p>
    </li>
    <li>
        <p><b>Brave New World</b>, Aldous Huxley. Probably my favorite
        dystopian novel I've read so far.</p>
    </li>
    <li>
        <p><b>Thinking, Fast and Slow</b>, Daniel Kahneman. An interesting (and
        long!) volume on how our brains work.</p>
    </li>
</ol>
</p>

<p>
Interestingly, in the first month alone of 2021, I've already read nearly half as
many new books. Granted, none of them were quite as long as, say, Antifragile or
the Kahneman book, but still a welcome metric. Off to more reading, now!
</p>
  ]]></description>
  </item>

  <item>
  <title>Advent of Code 2020</title>
  <guid>http://m-chrzan.xyz/blog/advent-of-code-2020.html</guid>
  <pubDate>2020-12-25T17:35:00+00:00</pubDate>
  <description><![CDATA[
    <p> Merry Christmas to all!</p>

<p>
<a href='https://adventofcode.com/2020'>Advent of Code 2020</a> has come to an
end. At the time of this writing, around seven thousand people finished the
whole thing, out of the 150000 that had submitted a solution to the first day's
problem. And I'm one of them!
</p>

<p>
You can see my solutions <a href='https://git.m-chrzan.xyz/aoc2020/about'>here</a>.
I started out with C, moved on to Ruby, and near the end switched to Lua. I'm
going to need some Lua knowledge for the Redis section of the databases class
I'm taking right now, so AoC was a good time to get familiar with the language.
</p>

<h3>Thoughts on AoC 2020</h3>

<p>
I found most of the problems fairly simple, but it was fun to go through them
all anyways.
</p>

<h4>Most Tedious Award goes to...</h4>

<p>
<i>Day 20 Part 2</i>. My solution ended up being a good 340 lines of Lua split
between two scripts. This is the last problem I solved because I knew it would
require a lot of work to get working, so I procrastinated on it until the end.
</p>

<p>
In the end, the process of solving this was actually kind of fun, and getting
the final solution satisfying. I think it might have something to do with the
geometric/visual nature of the problem. There's just something nice about
rotating and flipping ASCII squares with code, and seeing them all line up
correctly in the end.
</p>

<h4>Most Cheesed Award goes to...</h4>
<p>
<i>Day 19 Part 2</i>. Rather than implementing a parser generator that could
handle this grammar by hand, I bodged together a
<a href='https://git.m-chrzan.xyz/aoc2020/tree/19/b'>
    Bison (aka Yacc) solution
</a>. It's not very pretty, especially the Bash script that just calls the
parser with each input word separately and counts how many times it didn't exit
with a syntax error. But it works! And I finally learned something about
Bison/Yacc and Flex/Lex, which I've had indirect exposure to before, but never
actually used.
</p>

<p>
I was quite pleased with my Part 1 solution though, where I implemented a simple
recursive parser generator.
</p>

<h5>Runner-up in the Most Cheesed category</h5>
<p>
<i>Day 13 Part 2</i>. I'll admit it, I just copied a Chinese Remainder Theorem
implementation from Rosetta Code. I'm sure the Python guys just used
<code>numpy</code>'s CRT, so treating it as a library function doesn't seem too
cheaty for me.
</p>

<h4>Most Fun Award goes to...</h4>
<p>
Probably a tie between the already mentioned <i>Day 20 Part 2</i> and <i>Day 19
Part 1</i>.  Day 20 had that cool geometric component and satisfaction of
putting a puzzle together, but I'm a sucker for recursive parsers implemented
with higher order functions.
</p>

<h3>Thoughts on Lua</h3>
<p>
Learning Lua was probably the most useful outcome of this year's AoC for me. I
feel pretty comfortable with the basic constructs of the language, and even
played around a bit with metatable-driven OOP. I don't know how closely I stuck
to general conventions, and didn't interact with any external libraries, but can
definitely read and write the language comfortably now.
</p>

<p>
My one sentence summary of feelings towards Lua is that Go is to Java what Lua
is to Python/Ruby/JavaScript. Its very small vocabulary means you learn the
language quickly and don't need to think much while writing it, but your
programs can end up more verbose than the equivalent Ruby/Python.
</p>
  ]]></description>
  </item>

  <item>
  <title>Decentralized Auction Variants</title>
  <guid>http://m-chrzan.xyz/blog/decentralized-auction-variants.html</guid>
  <pubDate>2020-11-25T22:39:00+00:00</pubDate>
  <description><![CDATA[
    <p>
This is a follow up to my previous post on <a href='bst-auction.html'>
    smart contract auctions
</a> - it assumes familiarity with the algorithm introduced there.
</p>

<p>
It turns out the original design is very flexible. We can modify the auction's
properties to incentivize different behaviors or handle specific scenarios. For
example, we'll be able to mimic the auction mechanism used by Google's AdWords
to auction off search results positions.
</p>

<h4>Quick recap</h4>

<p>
The previous article introduced an auction that works in two phases:

<ol>
    <li><b>bid</b> phase
        <p>
        Users submit bids stating "I'm willing to pay <i>x</i> XYZ for
        <i>y</i> ABC." The transaction should include a payment of <i>x</i> XYZ
        so the user can't back out later.
        </p>
        <p>
        The user's transaction not only records this bid in the contract's
        storage, but also inserts it into a balanced BST (sorted by XYZ/ABC
        ratios).
        </p>
    </li>
    <li><b>fill</b> phase
        <p>
        Users with highest XYZ/ABC ratios can receive their bought ABC, while
        the remaining users are refunded their locked up XYZ. We can determine
        which bids to fill and which to refund thanks to an additional value
        we've been keeping in our BST's nodes (the total amount of ABC in the
        node's subtree)
        </p>
    </li>
</ol>

Our modifications will involve adding and/or modifying phases to the process,
but the generic idea of using a BST to store sorted bids remains the same.
</p>

<h3>Blind Auction</h3>
<p>
As mentioned in a footnote to the previous article, we can hide users' bids
during the auctioning phase. This is something that's probably desired in most
smart contract auctions of this sort, for example to avoid frontrunning or block
congestion in the last few moment's of the bidding phase. In an open bid
auction, bidders could compete for the final strike price in the gas auction if
all bids were open.
</p>

<p>
Closed bids also mean that users submit their own personal valuation of the
auctioned asset, rather than one influenced by other market players.
</p>

<p>
Blinding bids was omitted from the original description for brevity and clarity.
The central idea in the first article was using a BST for an on-chain auction,
blinding bids is just a technical detail. Here's how we can implement it.
</p>

<p>
We replace the <b>bid</b> phase with a <b>commit</b> phase and a
<b>reveal</b> phase:

<ul>
    <li>
        In the <b>commit</b> phase, users submit a hash of their bid
        information (<code>buyAmount</code>, <code>sellAmount</code>), and a
        random <code>salt</code>. They also send a prepayment that should be
        equal to or greater than their <code>sellAmount</code>. We save this
        hash and the amount sent in the smart contract.
    </li>
    <li>
        <p>
        The <b>reveal</b> phase is very similar to the original <b>bid</b>
        phase, where users submit their <code>buyAmount</code> and
        <code>sellAmount</code>. Now they additionally reveal their
        <code>salt</code>. The smart contract hashes these inputs and verifies
        this hash matches the one submitted during <b>commit</b> &mdash; if it
        does not, the bid is invalid and will not participate in the auction. We
        also verify that the amount prepaid during <b>commit</b> was at least as
        much as the revealed <code>sellAmount</code>.
        </p>
        <p>
        After these additional verification steps, we proceed as in <b>bid</b>,
        sorting the bid information into a BST.
        </p>
    </li>
</ul>
</p>

<p>
Note a positive property that this auction has over a simple single-unit blind
auction. While the prepayment sent with <b>commit</b> reveals an upper bound of
how much the bidder is willing to invest in this auction, it doesn't reveal
anything about how they price the sold asset &mdash; they could be sending a bid
with a high price purchasing few tokens, or vice versa, attempting to purchase a
lot of tokens but for a low price.
</p>

<h3>Second price auction</h3>
<p>
A second price auction<a href='#note-1' id='ref-1'><sup>1</sup></a> is the
Google AdWords mechanism mentioned in the introduction. In this type of auction,
users bid on multiple items (e.g. ordered slots on a search results page). The
items are distributed to the highest bidders, but the bidders actually pay less
than they bid. Specifically, the highest bidder pays the second-highest bidder's
price, the second pays the third's, and so on.
</p>

<p>
We can implement this in our BST auction design by modifying the <b>fill</b>
phase. When filling a user's bid, we can check the next highest bid's price by
walking to the next node in the inorder traversal order. On a balanced tree
like we're keeping, this will take time <i>O</i>(log n).
</p>

<h3>Uniform price auction</h3>
<p>
A similar modification we can make is to have all the auction participants pay
the same exact price &mdash; that of the last winning bidder. We will add a
<b>set price</b> phase in which the smart contract finds and verifies this
lowest accepted price. During the <b>fill</b> phase, we will charge winning
bidders based on this price, refunding them the rest of their prepayment.
</p>

<p>
This modification is possible thanks to the <code>total</code> value we've
decorated our BST's nodes with. In our new <b>set price</b> phase, we can start
in the tree's root and binary search down to the last bid that is fillable. We
note down this node's price as the strike price of the auction for all
participants.
</p>

<p>
Note that we described <b>set price</b> as a separate phase, but it could
instead be included into the <b>fill</b> phase, running the above algorithm on
the first call to <code>fill</code>.
</p>

<h3>Conclusions</h3>
<p>
In these two articles I described several novel smart contract decentralized
auction designs. They can be implemented to be completely trustless,
tamperproof, and censorship resistant, leveraging these properties of
blockchains.
</p>

<p>
The important thing to note about these designs is that, while offering the
above properties, they are relatively gas efficient. Individual operations
work in time (and thus gas cost) <i>O</i>(log n), where n is the number of
auction participants. This can still get fairly expensive, especially given the
number of storage writes when inserting into a BST. However, we spread the cost
of these operations evenly between auction participants.
</p>

<p>
The classic Dutch auction wins out on the efficiency front, having very cheap
<i>O</i>(1) operations, but has its problems. Dutch auction bids can be easily
frontrun. These auctions can cause sudden block congestion spikes when a price
favored by many participants is reached. And in some cases, you just want an
auction with different game theoretic properties than those the Dutch auction
offers. The BST auctions described here widen your decentralized auction
toolbox.
</p>

<p id='note-1'>
<a href='#ref-1'>1.</a> Or more precisely, a <i>generalized</i> second price
auction. The term <i>second price auction</i> (also known as a Vickrey auction)
is usually reserved for single unit auctions.
</p>
  ]]></description>
  </item>

  <item>
  <title>Vim Keybindings in All CLIs</title>
  <guid>http://m-chrzan.xyz/blog/vim-keybindings-in-all-clis.html</guid>
  <pubDate>2020-10-05T20:52:00+00:00</pubDate>
  <description><![CDATA[
    <p>
This post already lives as part of my
<a href='cheatsheets.html'>cheatsheets</a>, but I think this is
one of those things that's worth putting out there.
</p>

<p>
<code>readline</code> is a library that can be used to add keyboard shortcuts to
various CLI programs. If you're a command line user on Linux or Mac, you're
probably already using it even if you don't know it &mdash; <code>bash</code>
keyboard shortcuts come from <code>readline</code>.
</p>

<p>
By default, <code>readline</code> uses Emacs-like bindings. If you were ever
confused by the weird key combinations in <code>bash</code>, this is where they
come from.
</p>

<p>
<code>readline</code> can be configured via an <code>~/.inputrc</code> config
file in your home directory.
</p>

<p>
There are several other libraries that provide similar functionality that are
used by certain CLI programs. <code>libedit</code> is a popular alternative with
a more permissive ("less free") license. Many Haskell projects use
<code>haskeline</code>, created specifically for <code>ghc</code>.
</p>

<h3>Configuring readline</h3>

<p>
All of the above libraries can be configured with config files in your home
directory. Here's what to put in your dotfiles to use vi keybindings by default:

<pre>
# ~/.inputrc
set editing-mode vi

# ~/.editrc
bind -v

# ~/.haskeline
editMode: Vi
</pre>
</p>

<p>
One other useful tip: if you want to just quickly switch <code>bash</code> to vi
keys, perhaps on an account that's temporary and you won't be putting effort
into customizing it, you can do so by running <code>set -o vi</code>.
</p>

<h3>Where this doesn't work</h3>
<p>
The above configs cover some of my most used CLI tools, like <code>bash</code>,
REPLs for Ruby, Python, Haskell. My biggest pain is that I haven't found a
satisfactory way of getting vi keybindings in a Node.js shell.
</p>

<p>
In some cases you can hack your way around a CLI not using any of the
<code>readline</code>-like libraries using a tool called <code>rlwrap</code>.
Unfortunately, it's not a silver bullet. I remember it working fine enough for
the OCaml REPL, but it doesn't play well with Node.
</p>
  ]]></description>
  </item>

  <item>
  <title>Book notes: Vagabonding by Rolf Potts</title>
  <guid>http://m-chrzan.xyz/blog/book-notes-vagabonding.html</guid>
  <pubDate>2020-08-31T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Really enjoyed this short volume on long term travel. It resonates with my
belief (one that runs counter to what most people these days seem to believe,
and definitely counter to how they behave) that a career should not be the
central piece of one's life. It also challenges any person who says they
believe this (such as myself) to <i>prove</i> that they actually believe it.
</p>

<p>
Long term travel, the sort that isn't just plain tourism, is something I've had
in the back of my mind for a while. This read definitely pushed it farther
forward in my mind &mdash; who knows, maybe I'll end up in Asia or South
America for a few months after I'm done with university.
</p>

<h3>Quotes</h3>

<p>
(many of these are from other sources, quoted by Rolf; he's quite the quote
aggregator!)
</p>

<p>
These first three quotes are basically on that philosophy I mentioned above,
that if you feel a personal need for more to life than the modern day to day,
don't let the material world hold you down.
<blockquote>
    we end up spending (as Thoreau put it) "the best part of one's life earning
    money in order to enjoy a questionable liberty during the least valuable
    part of it."
</blockquote>

<blockquote>
    This notion &mdash; that material investment is somehow more important to
    life than personal investment &mdash; is exactly what leads so many of us to
    believe we could never afford to go vagabonding.
</blockquote>

<blockquote>
    Vagabonding sage Ed Buryn knew as much: "By switching to a new game, which
    in this case involves vagabonding, time becomes the only possession and
    everyone is equally rich in it by biological inheritance."
</blockquote>
</p>

<p>
On spontaneity, unplanned travel (what Nassim Taleb would call <i>flânerie</i>
over tourism).
<blockquote>
    John Muir used to say that the best way to prepare for a trip was to "throw
    some tea and bread into an old sack and jump over the back fence."
</blockquote>
</p>

<p>
On planning a little bit, after all.
<blockquote>
    And, as Phil Cousineau pointed out in <i>The Art of Pilgrimage</i>, I tend
    to believe that "preparation no more spoils the chance for spontaneity and
    serendipity than discipline ruins the opportunity for genuine
    self-expression in sports, acting, or the tea ceremony."
</blockquote>
</p>

<p>
On "seeing beyond the guidebook" (from Mark Twain's <i>The Innocents
    Abroad</i>).
<blockquote>
    "The pilgrims will tell of Palestine, when they get home, not as it appeared
    to <i>them</i>, but as it appeared in [the guidebooks]."
</blockquote>
</p>
  ]]></description>
  </item>

  <item>
  <title>Extra contracts in Truffle tests</title>
  <guid>http://m-chrzan.xyz/blog/extra-contracts-in-truffle-tests.html</guid>
  <pubDate>2020-08-26T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Sometimes you just need a Truffle contract object. If you're developing a
full-blown Truffle project, this is easy for your "core" contracts, the ones you
keep in your <code>contracts/</code> directory. Just
<code>artifacts.require</code> and call it a day. But what if you want to
interact with a different contract?
</p>

<p>
I ran into this while working on meta-tooling around smart contracts,
specifically scripts for running upgrades of proxied contracts. To this end, I
wanted to write unit tests that would use minimal example contracts with
edge cases that might not easily appear "in nature". I want to keep these dummy
contracts in a test resources directory rather than cluttering up the main
<code>contracts/</code>.
</p>

<p>
Unfortunately, not all parts of the Truffle API are very well documented, but
here's how I got my sweet, sweet Truffle contract wrappers.
</p>

<h3>Getting the contract object</h3>
<pre>
import truffleContract = require('truffle-contract')
const Contract = truffleContract({
    abi: artifact.abi,
    unlinked_binary: artifact.bytecode,
})
</pre>

where <code>artifact</code> is a standard solc build artifact, say parsed from a
JSON file that <code>truffle compile</code> outputs. The thing to note is that
the bytecode is under an <code>unlinked_binary</code> property.

<h3>Making it usable</h3>
<p>
Before you go run off playing with your new <code>Contract</code>, a few last
housekeeping things to take care of. If you try, say, deploying this contract
with <code>Contract.new()</code>, you'll get a nasty error message saying

<pre>
Error: Contract error: Please call setProvider() first before calling new().
</pre>
</p>

<p>
Actually not that nasty since it tells you what you need to do to fix things!
Call <code>Contract.setProvider()</code> with a standard Web3 provider. If you
already have a Web3 instance lying around,

<pre>
Contract.setProvider(web3.currentProvider)
</pre>

should do.
</p>

<p>
Last things last, at this point you would start seeing the following:

<pre>
Error: Contract has no network id set, cannot lookup artifact data. Either set
the network manually using Contract.setNetwork(), run Contract.detectNetwork(),
or use new(), at() or deployed() as a thenable which will detect the network
automatically.
</pre>

I resolved this by calling <code>Contract.setNetwork('development')</code>,
given that this was in a unit tests context and would always execute against the
<code>'development'</code> Truffle network. You might need to pass in a
different network name, or feel free to look into one of the other solutions
suggested by the above error message.
</p>

<p>
At this point you'll be able to call <code>Contract.new()</code> to get a handle
to a freshly deployed contract, or <code>Contract.at(address)</code> to interact
with an already deployed instance of the contract.
</p>

<h3>TL;DR</h3>
<p>
Here's the little helper function I wrote to construct these Truffle objects:

<pre>
import truffleContract = require('truffle-contract')

const makeTruffleContract = (artifact) => {
  const Contract = truffleContract({
    abi: artifact.abi,
    unlinked_binary: artifact.bytecode,
  })
  Contract.setProvider(web3.currentProvider)
  Contract.setNetwork('development')

  return Contract
}
</pre>
</p>

<h3>Final note on linking</h3>
<p>
If you're using any linked libraries in your contract, first deploy the
libraries, then link them with
<code>Contract.link('LibraryName', libraryAddress)</code>.
</p>
  ]]></description>
  </item>

  <item>
  <title>How to Build a Decentralized Auction</title>
  <guid>http://m-chrzan.xyz/blog/bst-auction.html</guid>
  <pubDate>2020-06-24T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p><em>
    this post assumes familiarity with the concept of smart contracts and
    uses Ethereum and Solidity for concrete examples
</em></p>

<h3>Multiunit auctions</h3>
<p>
Multiunit auctions are a generic financial primitive that could be useful in
various decentralized financial systems. I'd like to present an alternative to
the Dutch auction that may be more suitable in certain situations.
</p>

<h3>Designing an auction smart contract</h3>

<p>
Let's first define the basic behavior we want from our auction. Then we'll be
able to see which steps would break if implemented naively, and how to address
those problems. Let's frame it in the context of a currency auction, where
currency <strong>A</strong> is being sold for currency <strong>B</strong>:

<ol>
    <li>
        The auctioneer decides he wants to sell a pot of <em>x</em> units of
        <strong>A</strong>.
    </li>
    <li>
        Users submit bids that say "I want to buy <em>y</em> units of
        <strong>A</strong> for <em>z</em> units of <strong>B</strong>."
    </li>
    <li>
        We go through the bids from highest <em>z</em>/<em>y</em> ratio to
        lowest, filling them as we go. We stop if the sum of paid out
        <em>y</em>s reaches the size of the pot <em>x</em>.
        <a href='#note-1' id='ref-1'><sup>1</sup></a>
    </li>
</ol>

A higher <em>z</em>/<em>y</em> ratio means a higher price (in
<strong>B</strong>) is being offered by the user, so step 3 ensures that the
winners of the auction are the highest bidders, as we'd expect.
</p>

<p>
To make this all a little more concrete, let's define an interface we might
expect from an auction smart contract:

<pre>
interface IAuction {
    function start(uint pot);
    function bid(uint sellAmount, uint buyAmount) returns (uint bidId);
    function end();
    function fill(uint bidId);
}
</pre>
</p>

<p>
The above is very similar to
<a href='https://solidity.readthedocs.io/en/v0.5.11/solidity-by-example.html#blind-auction'>
    an example from the Solidity tutorial
</a>
showing how to build a single unit auction. The big difference is that when
making a bid, we specify how much we're willing to pay <em>and</em> how many
units of the auctioned asset we're bidding
on.<a href='#note-2' id='ref-2'><sup>2</sup></a>
</p>

<p>
The problem, as hinted at earlier, is that we can't just sort the list of bids
and go through it, bid by bid, when implementing step 3 of our protocol. Sorting
is an <em>O</em>(<em>n</em>log<em>n</em>) operation, and even the linear scan
would greatly limit the allowable number of participants in an auction given
block gas limits.
</p>

<h3>Sorting</h3>

<p>
The solution to this problem, as is often the case in computer science, comes in
the shape of a tree.
</p>

<p>
We'll have <code>bid</code> store bids in a balanced binary search tree. The
in-order traversal of the resulting tree gives us a sorted list of all bids.
We're essentially having each auction participant pay the necessary gas to sort
just their bid into the data structure.
</p>

<p>
Here's a scaffold of what <code>bid</code> could look like:

<pre>
contract Auction {
    // Node in a red-black tree
    struct Node {
        uint sellAmount;
        uint buyAmount;
        uint left;
        uint right;
        uint parent;
        bool red;
    }

    Node[] tree;
    uint root;

    function greaterThan(Node n1, Node n2) {
        return n1.sellAmount * n2.buyAmount &gt;= n1.buyAmount * n2.sellAmount;
    }

    function <b>bid</b>(uint sellAmount, uint buyAmount) payable returns (uint bidId) {
        bidId = tree.length;
        tree.push(Node(sellAmount, buyAmount, 0, 0, 0, false));
        ... // <b>do RBT insertion with greaterThan as the comparison function</b>
    }

    ...
}
</pre>
</p>

<p>
When all bids have been submitted and sorted into the tree, we need to be able
to fill the bids of those users that submitted the highest bids. When a user
comes in and says "Hey, look at my bid, I paid <em>z</em> amount of
<strong>B</strong> so now I deserve <em>y</em> amount of <strong>A</strong>!",
we need to be able to verify that their bid actually fit into the pot and can be
filled.
</p>

<p>
At this point, when implementing <code>fill</code> naively, we would still need
a linear traversal to determine whether a given <code>bidId</code> should be
filled or not. To finish off this auction design, we'll augment the BST such
that we can implement an <em>O</em>(log<em>n</em>) <code>fill</code>.
</p>

<h3>Filling</h3>

<p>
We'll augment each node of the tree to contain the total amount of
<code>buyToken</code> in the node's subtree. This will allow us to quickly
calculate the amount of <code>buyToken</code> in all nodes with a better price
than a given bid.
</p>

<p>
This augmentation is possible because updating this total is efficient when
updating the tree. We only need to modify the insert and rotation operations:

<pre>
function insert(Bid bid, uint bidId) {
    uint current = tree.root;
    uint parent = current;
    while (current != 0) {
        parent = current;

        <b>// Increase totals on the path from root to newly inserted node
        tree.[parent].total = tree[parent].total + bid.buyAmount;</b>

        if (greaterThan(bid, tree.bids[current])) {
            current = tree.bids[current].left;
        } else {
            current = tree.bids[current].right;
        }
    }
    
    // ...
}

<b>// Called at the end of each rotation operation</b>
function recalculateTotalsAfterRotation(uint n, uint pivot) {
    tree[pivot].total = tree[n].total;
    tree[n].total = tree[tree.bids[n].left].total +
        tree[tree[n].right].total +
        tree[n].buyAmount
}
</pre>

<p>
This in our data structure gives us a logarithmic algorithm for checking how
much of the token would be bought by bidders at higher exchange rates. We walk
up from our node to the root. Any time we step up from a right child, we'll add
the total from our sibling's subtree and parent node's value (since, by the BST
property, those are the nodes with greater rates than our starting node).

<pre>
function totalFromHigherExchangeRates(uint bidId) view returns (uint) {
    uint total = tree[tree[bidId].left].total;
    uint parent = tree[bidId].parent;
    uint currentNode = bidId;

    while (parent != 0) {
      <b>if (tree[parent].right == currentNode) {
        total += tree[tree[parent].left].total;
        total += tree[parent].buyAmount;
      }</b>

      currentNode = parent;
      parent = tree[parent].parent;
    }

    return total;
}
</pre>
</p>

<p>
<code>fill</code> can now use <code>totalFromHigherExchangeRates</code> to
determine whether or not a given bid should be filled. The per-bid
computational complexity (and thus gas cost) is logarithmic with respect to the
total number of bids.
</p>

<h3>Origin</h3>
<p>
I originally came up with this auction design while working on
<a href="https://celo.org">Celo's</a> on-chain stability mechanism. The idea was
to use periodic currency auctions to expand or contract the supply of a token to
match demand, thus stabilizing its price.
</p>

<p>
Later we came up with an even more clever stabilization mechanism, one that can
function continuously. It's based on <a href="https://uniswap.io/">Uniswap</a>
and you can read more about it in
<a href="https://medium.com/celohq/zooming-in-on-the-celo-expansion-contraction-mechanism-446ca7abe4f">
    this Medium post
</a>.

<p id='note-1'>
<a href='#ref-1'>1.</a> The last successful bid might only be partially filled.
This is a detail that needs to be addressed, but it's easy to do so. For
simplicity of the rest of the text, I'll avoid discussing it.
</p>

<p id='note-2'>
<a href='#ref-2'>2.</a> For simplicity, we'll talk about a two phase, open bid
auction. As with the single unit auction, this could be modified to a hidden bid
auction by adding a commit step.
</p>
  ]]></description>
  </item>

  <item>
  <title>Lock-down workouts</title>
  <guid>http://m-chrzan.xyz/blog/lock-down-workouts.html</guid>
  <pubDate>2020-05-08T16:37:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I bought a 14kg kettlebell for myself just as the coronavirus situation was
escalating into lock down here in Poland. I wrote up a simple exercise plan to
follow every week and have been following it for over a month now. Gotta stay in
motion while the world has seemingly stopped!
</p>

<p>
I'm definitely not a fitness expert, and I'm sure this program is far from
perfect. But it hits most of the major muscle groups, is an energizing way to
start each day, and definitely gets me sweaty, so I'll use that as a metric of
it being sufficient for now.
</p>

<h3>Workout A: Upper</h3>
<ul>
    <li>
        Push-ups 3xFailure (this is around 20 for me, up from 10 at the
        beginning of lockdown)
    </li>
    <li> Kettlebell shoulder press 3x7 per arm </li>
    <li> Kettlebell row 3x12 per arm </li>
</ul>

I do the push-ups on a slight decline from a stool.

<h3>Workout B: Lower and Core</h3>
<ul>
    <li> Plank 3x1:45 </li>
    <li> Kettlebell swings 3x1:45 </li>
    <li> Kettlebell goblet squats 3x12 </li>
</ul>

I superset the planks and swings.

<p>
I also throw in chin-ups or pull-ups throughout the day on both workouts.
</p>

<p>
I alternate these workouts for five days a week, with two rest days a week.
</p>

<h3>Thoughts</h3>
<ul>
    <li>
        I definitely can recommend getting a kettlebell. If you get the right
        weight for you (the recommended starter weights are 16kg for men, 8kg
        for women), it's a single piece of equipment that can be used for a wide
        range of exercises. A very affordable option in general, and especially
        right now while gyms are closed.
    </li>
    <li>
        If you've never touched a kettlebell before, Pavel Tsatsouline's
        <a href='https://www.youtube.com/watch?v=cKx8xE8jJZs'>
            Enter the Kettlebell
        </a> is a great starting point, going over general use and the most
        popular exercises.
    </li>
    <li>
        Working out is not just for getting in shape, but will also have a very
        positive impact on mental well-being. It can be especially useful now as
        a way of jump-starting days in lock-down.
    </li>
    <li>
        Doing something is better than doing nothing. I don't recommend
        following my plan - it's based entirely on intuition, what feels good,
        and the equipment I have available. If you find any other home workout
        plan that seems to suit your likes - follow it. Then again if you don't,
        following mine or whatever plan you come up with for yourself is going
        to be better than nothing.
    </li>
</ul>
  ]]></description>
  </item>

  <item>
  <title>An experiment in programmatic and artistic constraints</title>
  <guid>http://m-chrzan.xyz/blog/sixteen.html</guid>
  <pubDate>2020-02-08T13:29:00+00:00</pubDate>
  <description><![CDATA[
    <h3>
</h3>

<p>
Write a piece of JavaScript that defines a function named <code>s</code> that

<ul>
    <li>
        Takes three non-negative integer arguments: <code>x</code>,
        <code>y</code>, <code>t</code> (<code>x</code> and <code>y</code>
        less than <b>16</b>).
    </li>
    <li> Returns a non-negative integer less than <b>16</b><sup>2</sup>. </li>
    <li>
        The code can be syntactically written in a <b>16</b>x<b>16</b> block of
        characters.
    </li>
</ul>
</p>

<p>
The source code defining <code>s</code> will be visually displayed on a 16x16
grid. 32 times a second, it will be separately called for each <code>x</code>,
<code>y</code> coordinate of the grid, initially with a <code>t</code> parameter
equal to 0, and incrementing by one with each series of calls.
</p>

<p>
The result of each call to <code>s</code> will determine the shade of the
character in the corresponding grid location.
</p>

<h3>In code:</h3>
<pre>
var step = 0
_frame = function() {
    for (var row = 0; row &lt; 16; row++) {
        for (var column = 0; column &lt; 16; column++) {
            <b>var shade = s(row, column, step)</b>
            var cell = display.children[row].children[column]
            <b>cell.style.color = `rgb(${shade}, ${shade}, ${shade})`</b>
        }
    }
    step++
}
setInterval(_frame, 31.25)
</pre>

<h3>An example</h3>
<script>
    s=(x,y,t)=>{r=(t
    )=>Math.floor(t%
    256/16);c=t=>r(t
    )%2==0?(t%16):-(
    t%16)+15;m=(x,y,
    t)=>(x==r(t))&&(
    y==c(t));d=Array
    .from({length:4*
    64}).map((_,i)=>
    i).find((d)=>m(x
    ,y,256+t-d));var
    b=Math.log(d+1)/
    5.54517744447956
    return(t<d?2*2*2
    *2*2*2*2*2:b*2*2
    *2*2*2*2*2*2);};
</script>
<div id='display'>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
    <div class='row'>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
        <code class='cell'></code>
    </div>
</div>

<script>
var display = document.getElementById('display')

var str=`s=(x,y,t)=>{r=(t
)=>Math.floor(t%
256/16);c=t=>r(t
)%2==0?(t%16):-(
t%16)+15;m=(x,y,
t)=>(x==r(t))&&(
y==c(t));d=Array
.from({length:4*
64}).map((_,i)=>
i).find((d)=>m(x
,y,256+t-d));var
b=Math.log(d+1)/
5.54517744447956
return(t<d?2*2*2
*2*2*2*2*2:b*2*2
*2*2*2*2*2*2);};`

var characters = str.split('\n').map(s => s.split(''))
for (var row = 0; row < 16; row++) {
    for (var column = 0; column < 16; column++) {
        var cell = display.children[row].children[column]
        cell.innerHTML = characters[row][column]
    }
}

function makeColor(shade) {
    return `rgb(${shade}, ${shade}, ${shade})`
}

var step = 0
_frame = function() {
    for (var row = 0; row < 16; row++) {
        for (var column = 0; column < 16; column++) {
            var shade = s(row, column, step)
            var cell = display.children[row].children[column]
            cell.style.color = makeColor(shade)
        }
    }
    step++
}
setInterval(_frame, 31.25)
</script>
  ]]></description>
  </item>

  <item>
  <title>Small Big Sites</title>
  <guid>http://m-chrzan.xyz/blog/small-big-sites.html</guid>
  <pubDate>2019-12-29T10:32:00+00:00</pubDate>
  <description><![CDATA[
    <p>
NPR.org pleasantly surprised me when I randomly entered their site to read an
article recommended to me.  Upon clicking the link, a full-page privacy advisory
greeted me. "We use cookies to personalize your content bla bla bla". Two large
buttons awaited below the wall of text, waiting on a choice from me, the user.
</p>

<p>
What surprised me was the choice I was offered. Normally, it's either accept all
cookies vs. leave the site, or the ability to turn off some unnecessary tracking
cookies. In this case, one option was the classic "Agree and Continue", but the
second was "Decline and visit plain text site".
</p>

<p>
Not "Decline and go away". Not "Decline and never speak to us ever again". But
"Decline and visit plain text site".
</p>

<p>
Wait, so I can use a streamlined, easy to read, nondistracting version of your
website <em>and</em> not be tracked by multinational advertising megacorps?
<a href="https://text.npr.org/">Nice</a>.
</p>

<h3>Other small big sites</h3>
<p>
Turns out this text-only version has been around since 2005.  CNN also has a
<a href="https://lite.cnn.io">text-only version</a>, though it's not as easy to
find. It also includes analytics javascript which is not as cool as NPR's pure
HTML. In both cases, these light versions were created to make news accessible
in locations or times with low connectivity &mdash; for example during natural
disasters.
</p>

<p>
As much as I'm not a fan of Facebook, I'm very impressed with
<a href="https://mbasic.facebook.com/">mbasic.facebook.com</a>. This is a
minimal, no-ads, no-javascript version of the service, that offers most of the
functionality of the full website or app. It was created with emerging markets
in mind, but I find it to be a good daily driver that doesn't distract and suck
away attention as much as Facebook's products normally are supposed to.
</p>

<p>
To mention a few other big sites with very minimalist design,
<a href="https://news.ycombinator.com/">Hacker News</a> is a news aggregator
known by all in the tech community that relies on minimal javascript and is
mostly text-based. Warren Buffett didn't shell out much on the design for
Berkshire Hathaway's <a href="https://www.berkshirehathaway.com/">corporate
website</a>.
</p>
  ]]></description>
  </item>

  <item>
  <title>Added an RSS feed</title>
  <guid>http://m-chrzan.xyz/blog/added-an-rss-feed.html</guid>
  <pubDate>2019-12-15T14:47:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I just added a really simple RSS feed to syndicate my website! Add
<a href="http://m-chrzan.xyz/rss.xml"><code>m-chrzan.xyz/rss.xml</code></a> to
your favorite RSS reader and you'll get updates whenever I post something new.
</p>
  ]]></description>
  </item>

  <item>
  <title>Vim Keybindings for Newsboat</title>
  <guid>http://m-chrzan.xyz/blog/vim-keybindings-for-newsboat.html</guid>
  <pubDate>2019-11-21T21:36:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Put in <code>~/.config/newsboat/config</code>.

<pre>
# jk - move up/down
unbind-key j
unbind-key k
bind-key j down
bind-key k up

# hl - move in/out of feeds/articles
bind-key h quit
bind-key l open

# q quits the program
unbind-key q
bind-key q hard-quit

# g/G to go to top/bottom
unbind-key g
unbind-key G
bind-key g home
bind-key G end

# f to follow links (like qutebrowser)
unbind-key f
bind-key f goto-url
</pre>
</p>
  ]]></description>
  </item>

  <item>
  <title>Cheatsheets to Stay Productive in the Command Line</title>
  <guid>http://m-chrzan.xyz/blog/cheatsheets.html</guid>
  <pubDate>2019-11-03T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
To stay productive in the command line, I maintain a personal
<a href="https://git.m-chrzan.xyz/cheatsheets/about">"cheatsheets" repository</a>.
There are many commands, or particular options of certain commands, that I don't
use often enough for them to become muscle memory, but often enough for
<code>man</code>ning them or internet searching for "how do I resize an image with
imagemagick" to become tedious.
</p>

<p>
I can recommend following a similar practice to anyone, though I don't recommend
using my cheatsheets. They are usually quick notes that follow my own mental
shortcuts, and in general are optimized to be quickly understood by <em>me</em>,
based solely on my past experiences with these tools. I won't include options
that are already obvious to me (if you haven't used <code>git add -p</code> or
<code>git rebase -i</code>, you should go learn about them!), and might instead
include tools that are already a second language to you (any <code>ffmpeg</code>
ninjas out there?).
</p>

<p>
There are existing tools that aim to improve command line productivity in
similar ways, like <a href="https://github.com/tldr-pages/tldr">
    <code>tldr</code>
</a> or <a href="https://github.com/cheat/cheat"><code>cheat</code></a>. For my
personal workflow, I figured that there's no reason to overcomplicate things
when tools I already have (<code>vim</code> + <code>git</code>) do the job fine.
</p>

<p>
Now, for any new UNIX system I intend on spending a lot of time on, I'll
<code>git pull gitlab.com/m-chrzan/cheatsheets</code> in my home directory. When
I want to remind myself of a command's use case, or have just figured out a new
cool trick that I know I'm going to forget if I don't write it down, my notes
are just a <code>vim ~/ch&lt;TAB&gt; tool.md</code> away. If I do write any new
tips down, a quick <code>git push</code> allows me to keep the cheatsheets
synced across all other systems.
</p>
  ]]></description>
  </item>

  <item>
  <title>Hex Curler: A Minimalist Webgame</title>
  <guid>http://m-chrzan.xyz/blog/hex-curler.html</guid>
  <pubDate>2019-08-29T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I just published Hex Curler, a tiny dungeon crawler, based on Jeff Moore's
<a href='http://www.1km1kt.net/rpg/hex'>Hex</a>.
</p>

<p>
You can play it by running the following in a bash shell:

<pre>
c=x; while [ $c ]; do clear; curl -c k -b k hex.m-chrzan.xyz/$c; read c; done
</pre>
</p>

<p>
This was an exercise in minimalism. The game server is implemented in less than
a thousand lines of Ruby code. It is completely stateless, requiring no
database. The front end "client" is a single line of bash, less than 80
characters long. The only dependency is <code>curl</code>, a CLI tool already
available on most Unix-like systems.
</p>

<p>
The source code is available <a href='https://git.m-chrzan.xyz/hex-curler/about'>here</a>.
</p>

<h3>Let's get curling</h3>
<p>
The whole concept arose from two ideas I had:

<ul>
    <li>
        <code>curl</code> and a simple web server could be used to create a
        simple remote CLI program.
    </li>
    <li>
        For a webapp that implements a simple state-transition system (like a
        simple game), one could forget about session management and a database,
        and just store the state client-side in a cookie.
    </li>
</ul>

I abstracted these ideas away into a Ruby class called <code>Engine</code> and a
skeleton Sinatra app.
</p>

<p>
To create a new "curling" system, you extend <code>Engine</code> and implement
four methods:

<ul>
    <li>
        <code>step</code>: performs a single step of the state-transition
        function.
    </li>
    <li>
        <code>message</code>: outputs a message related to the most recent
        <code>step</code>.
    </li>
    <li>
        <code>hash_to_state</code> and <code>state_to_hash</code>: these are
        just overhead glue methods. They should deserialize and serialize
        between your engine's internal state and a Ruby <code>Hash</code>.
    </li>
</ul>

You also need to define a <code>secret</code> which is a string that is used to
validate that a submitted cookie represents a valid state. More on this later.
</p>

<p>
The Sinatra skeleton instantiates an engine with the received cookie,
runs a step, sends back the new state in the returned cookie, and responds with
the engine's message. The code for it fits in half of a browser window:

<pre>
require 'sinatra'
require 'sinatra/cookies'

# exposes `Hex`, which extends `Engine`, implementing a simple dungeon crawler
require './hex_engine'

def secret
  # get secret from environment
  ENV['HEX_SECRET']
end

get '/:command' do |command|
  # `new` uses `hash_to_state` to initialize the engine's state
  engine = Hex.new cookies.to_h
  engine.step command
  # `state_h` uses `state_to_hash` to serialize the engine's new state
  engine.state_h.each_pair do |key, value|
    cookies[key] = value
  end
  engine.message
end
</pre>
</p>

<h3><em>O</em>(1) space webapp</h3>
<p>
Hex Curler is hosted online but has no session management, no database. It's an
<em>O</em>(1) space webapp.
</p>

<p>
As mentioned before, the game's state is stored in a cookie. The server need
only know the contents of that cookie to return a new state back to the user.
</p>

<p>
To prevent a user from tampering with the cookie, for example by increasing
their health to a ridiculous number, becoming invulnerable to enemies in Hex,
the cookie also contains a <code>checksum</code> field. This checksum is the
hash of the state together with an appended secret only known by the game
server. The server will refuse to respond to requests whose cookie does not have
a valid checksum.
</p>

<p>
This introduces some interesting possibilities. For example, let's say Alice
wants to boast to her friends about how she just beat Hex, ended with 100 HP
remaining, and had upgraded her magic armor to level 5. Her friend Bob doesn't
just have to take her word for it, or trust a screenshot that could have easily
been photoshopped.
</p>

<p>
Alice can send Bob her state cookie. If a request to the game server with it
succeeds, Bob can be assured that he has cryptographic proof of Alice's
claims.
</p>
  ]]></description>
  </item>

  <item>
  <title>Malinówka - Raspberry Nalewka Recipe</title>
  <guid>http://m-chrzan.xyz/blog/malinówka.html</guid>
  <pubDate>2019-08-12T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Here's another nalewka you can make. This one is extremely simple and produces a
very sweet, fruity liqueur. 
</p>

<h3>Ingredients:</h3>
<p>
<ul>
    <li>raspberries</li>
    <li>vodka or neutral spirits mixed with water to desired alcohol content</li>
    <li>sugar</li>
</ul>
Yep, that's it, just three ingredients.
</p>

<h3>Steps</h3>
<h4>Stage 1: Preparation (3 minutes)</h4>
<p>
<ol>
    <li>Fill jar with raspberries.</li>
    <li>Cover raspberries with alcohol.</li>
    <li>Shut jar and find spot to store.</li>
</ol>
Did I mention it's extremely simple to make?
</p>

<h4>Stage 2: Infusion (~2 weeks+)</h4>
<p>
Let sit for about two weeks, giving the jar a good shake once a day.
With time, the liquid will become a deep, clear red.
</p>

<h4>Stage 3: Maceration (~1 week+)</h4>
<p>
<ol>
    <li>Pour through strainer. Store liquid in a bottle or another jar.</li>
    <li>Throw raspberries back into jar. Cover with sugar.</li>
    <li>
        Like really cover them. Give the jar a few good shakes and add more
        sugar.
    </li>
    <li>Let sit again for a few days, shaking once a day.</li>
</ol>

The sugar will start breaking down the fruit, and you'll end up with a jar-full
of sweet, fruity syrup.

<h4>Stage 4: Aging  (1 week - until the heat death of the universe)</h4>
<ol>
    <li>
        Pour contents of jar through strainer. Throw away whatever solids remain
        of the fruits.
    </li>
    <li>Combine this syrup with the previously set aside raspberry alcohol.</li>
    <li>
        Let sit for at least a few days or weeks. As with the krupnik, the
        mixture will become smoother with shelf time.
    </li>
</ol>

If you want to be really fancy, you can filter this to get a perfectly clear
liquid, but personally I don't find that necessary.

<h3>Related posts</h3>
<ul>
  <li><a href='krupnik.html'>Krupnik recipe</a></li>
</ul>
  ]]></description>
  </item>

  <item>
  <title>Krupnik Recipe - Polish Honey Liqueur</title>
  <guid>http://m-chrzan.xyz/blog/krupnik.html</guid>
  <pubDate>2019-08-11T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
I've recently started playing around with making <em>nalewkas</em> - traditional
Polish liqueurs based on vodka/neutral spirits infusion. 
</p>

<p>
The only good recipes I've seen are in Polish, so wanted to share some with the
English speaking world.
</p>

<p>
In this post I'll give a recipe for <em>krupnik staropolski</em>
<sup id='ref-1'>
    <a href='#note-1'>1</a>
</sup>.
</p>

<h3>Ingredients:</h3>
<p>
(makes a 16oz. Mason jar of liqueur)
<ul>
    <li>vodka or neutral spirits mixed with water to desired alcohol content</li>
    <li>4oz. honey </li>
    <li>spices:
        <ul>
            <li>5-10 cloves</li>
            <li>3-7 allspice berries</li>
            <li>½ stick of cinnamon</li>
            <li>3-7 whole cardamom pods</li>
            <li>others as you like! (star anise, nutmeg, vanilla beans or
                extract, whole peppercorns, etc.)</li>
        </ul>
    </li>
    <li>fresh ginger</li>
    <li>juice from half a lemon</li>
    <li>lemon peel</li>
    <li>orange peel</li>
</ul>
One thing to note is that nalewka making is not an exact science. Pick and
choose the spices. Adjust amounts to your liking. Experiment and have fun!
</p>

<h3>Steps</h3>
<h4>Stage 1: Preparation (10-30 minutes)</h4>
<ol>
    <li>Boil honey in pot.</li>
    <li>The honey should have separated. Remove the top foamy layer.</li>
    <li>Pour the liquid honey into jar.</li>
    <li>Add rest of ingredients to jar.</li>
    <li>Fill the rest of the way up with alcohol.</li>
    <li>Shut jar and find spot to store.</li>
</ol>
<h4>Stage 2: Infusion + Filtration (~2 weeks+)</h4>
<ol>
    <li>
        Let sit. For like 2 weeks or so. This is where the alcohol mixes with
        honey and becomes infused with flavors from the spices.
    </li>
    <li>Pour through strainer and throw away the solids.</li>
    <li>
        Filter through several layers of gauze or a coffee filter. This is to
        remove that nasty layer of sediment at the bottom of the otherwise
        beautifully golden liquid.<sup id='ref-2'><a href='#note-2'>2</a></sup>
    </li>
</ol>

<h4>Stage 3: Aging (1 week - multiple years)</h4>
<p>
Finally, it's good to let the krupnik sit on a shelf at least a few days before
drinking. If you try it right after the infusion stage, you might notice it
still reeking of ethanol and being hard on your taste buds and throat.
</p>

<p>
With time, the liqueur will become very smooth and all the flavors will mix and
complement each other.
</p>

<h3>Related posts</h3>
<ul>
  <li><a href='malinówka.html'>Malinówka recipe</a></li>
</ul>


<p id='note-1'>
    <a href='#ref-1'>1.</a> Not to be confused with the tasty Polish grain soup.
</p>

<p id='note-2'>
    <a href='#ref-2'>2.</a> Honestly, sometimes I skip this step because I'm too
    lazy. I'll just pour the liquid out to a new jar and stop pouring when the
    sediments start mixing with the good stuff.
</p>
  ]]></description>
  </item>

  <item>
  <title>Polymorphism in Solidity</title>
  <guid>http://m-chrzan.xyz/blog/polymorphism-in-solidity.html</guid>
  <pubDate>2019-07-31T16:44:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Solidity is in many ways similar to C. It's a low-level language, sitting just a
thin layer of abstraction above its underlying bytecode. Just like C, it lacks
some convenient mechanisms from higher level languages.
</p>

<p>
There's a cool method for implementing simple object-orientation (complete with
polymorphism) in C that can also be applied in Solidity to solve similar
problems.
</p>

<p>
Let's look at how Linux kernel programmers deal with filesystems.
</p>

<p>
Each filesystem needs its own low-level implementation. At the same time, it
would be nice to have the abstract concept of a <em>file</em>, and be able to
write generic code that can interact with <em>files</em> living on any sort of
filesystem. Sounds like polymorphism.
</p>

<p>
Here's the first few lines of the definition of
<a href='https://elixir.bootlin.com/linux/v5.2.4/source/include/linux/fs.h#L922'>
    <code>struct file</code>
</a>, used
to keep track of a file in the Linux kernel.

<pre>
struct file {
    struct path                     f_path;
    struct inode                    *f_inode;
    <b>const struct file_operations    *f_op</b>;
    // ...
};
</pre>
</p>

<p>
The important bit is the <code>f_op</code> field, of type <code>struct
    file_operations</code>. Let's
look at (an abridged version of)
<a href='https://elixir.bootlin.com/linux/v5.2.4/source/include/linux/fs.h#L1791'>
    <em>its</em> definition
</a>.

<pre>
struct file_operations {
    struct module *owner;
    loff_t (*<b>llseek</b>) (struct file *, loff_t, int);
    ssize_t (*<b>read</b>) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*<b>write</b>) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*<b>read_iter</b>) (struct kiocb *, struct iov_iter *);
    ssize_t (*<b>write_iter</b>) (struct kiocb *, struct iov_iter *);
    int (*<b>mmap</b>) (struct file *, struct vm_area_struct *);
    int (*<b>open</b>) (struct inode *, struct file *);
    // ...
};
</pre>

It's mostly a bunch of function pointers that appear to be... operations one
might want to perform on a file.
</p>

<p>
To emulate OO, inside our "object" (<code>struct file</code>) we manually store
a container for its "methods" (<code>struct file_operations</code>). Each of
these, as its first argument, takes a pointer to a <code>struct file</code> that
it's going to operate on.
</p>

<p>
With this in place, we can now define a generic
<a href='https://elixir.bootlin.com/linux/v5.2.4/source/fs/read_write.c#L421'>
    <code>read</code>
</a> system call:

<pre>
ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,
           loff_t *pos)
{
    if (<b>file-&gt;f_op-&gt;read</b>)
        return <b>file-&gt;f_op-&gt;read(</b>file, buf, count, pos<b>)</b>;
    else if (<b>file-&gt;f_op-&gt;read_iter</b>)
        return new_sync_read(file, buf, count, pos);
    else
        return -EINVAL;
}
</pre>

On the other hand, the file
<a href='https://elixir.bootlin.com/linux/v5.2.4/source/fs/ext4/file.c#L508'>
    <code>fs/ext4/file.c</code>
</a> defines operations specific to the ext4 file system and a
<code>file_operations</code> struct:

<pre>
const struct file_operations ext4_file_operations = {
    .llseek     = ext4_llseek,
    .read_iter  = ext4_file_read_iter,
    .write_iter = ext4_file_write_iter,
    // ...
};
</pre>
</p>

<p>
We can do the same thing in Solidity!
</p>

<p>
The example we'll work with is a decentralized exchange. Users can call a
<code>trade</code> function with the following signature:

<pre>
function trade(address sellCurrency, address buyCurrency, uint256 sellAmount);
</pre>

They specify a currency pair (<code>sellCurrency</code> and <code>buyCurrency</code> -
the currency they're selling to and buying from the exchange) and the amount of
<code>sellCurrency</code> they're giving to the exchange. The smart contract then
calculates the amount of <code>buyCurrency</code> the user should receive and
transfers that to them.
</p>

<p>
To compilcate things a little, let's say that the exchange deals with more than
just ERC20 tokens. Let's allow for ERC20 - ERC20, ERC20 - Ether, and Ether -
ERC20 trades.
</p>

<p>
Here's what a first attempt at implementing this might look like:

<pre>
// Let address(0) denote Ether
function trade(address sellCurrency, address buyCurrency, uint256 sellAmount) {
  uint256 buyAmount = calculateBuyAmount(sellCurrency, buyCurrency, sellAmount);

  // <b>take the user's sellCurrency</b>
  if (sellCurrency == address(0)) {
    require(msg.value == sellAmount);
  } else {
    ERC20(sellCurrency).transferFrom(msg.sender, address(this), sellAmount);
  }

  // <b>give the user their new buyCurrency</b>
  if (buyCurrency == address(0)) {
    msg.sender.transfer(buyAmount);
  } else {
    ERC20(buyCurrency).transfer(msg.sender, buyAmount);
  }
}
</pre>
</p>

<p>
This doesn't look terrible yet.
</p>

<p>
Now imagine that you wanted to handle even more asset classes.
</p>

<p>
What if there was a token <code>YourToken</code> that had <code>mint</code> and
<code>burn</code> functions callable by the exchange contract? Instead of
holding a balance of <code>YourToken</code> you just want to either take tokens
out of ciruclation when they're sold, or mint new ones into existence when
they're bought.
</p>

<p>
Or you want to support <code>MyToken</code> which I annoyingly implemented
without following the ERC20 standard and function names differ from other tokens.
</p>

<p>
With more and more asset classes, the complexity of the code above would
increase.
</p>

<p>
Now let's try to implement the same logic but taking inspiration from the Linux
kernel's generic handling of files.
</p>

<p>
First, let's declare the struct that will hold a currency's information and methods
for interacting with it. This corresponds to <code>struct file</code>:

<pre>
struct Currency {
  function (Currency, uint256) take;
  function (Currency, uint256) give;
  address currencyAddress;
}
</pre>
</p>

<p>
Now let's implement taking and giving tokens for two different asset classes.

<pre>
function <b>ethTake</b>(Currency currencyS, uint256 amount) {
  require(msg.value == sellAmount);
}

function <b>ethGive</b>(Currency currencyS, uint256 amount) {
  msg.sender.transfer(buyAmount);
}

function <b>erc20Take</b>(Currency currencyS, uint256 amount) {
  ERC20 token = ERC20(currencyS.currencyAddress);
  token.transferFrom(msg.sender, address(this), amount);
}

function <b>erc20Give</b>(Currency currencyS, uint256 amount) {
  ERC20 token = ERC20(currencyS.currencyAddress);
  token.transfer(msg.sender, amount);
}
</pre>
</p>

<p>
Finally, we can perform generic operations on currencies:

<pre>
function trade(Currency sellCurrency, Currency buyCurrency, uint256 sellAmount
) {
  uint256 buyAmount = calculateBuyAmount(sellCurrency, buyCurrency, sellAmount);

  <b>sellCurrency.take(</b>sellCurrency, sellAmount<b>)</b>;
  <b>buyCurrency.give(</b>buyCurrency, buyAmount<b>)</b>;
}
</pre>
</p>

<p>
Adding support for a new asset class is now as simple as defining a pair of take/give
functions. The code inside of <code>trade</code> need never be touched again,
following the Open/Closed principle.
</p>
  ]]></description>
  </item>

  <item>
  <title>GitLab Pages + Namecheap</title>
  <guid>http://m-chrzan.xyz/blog/gitlab-namecheap.html</guid>
  <pubDate>2019-06-29T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
This website is currently hosted as a GitLab Pages page. In addition to the
free m-chrzan.gitlab.io domain name that GitLab provides, it's also
pointed to by m-chrzan.xyz. This was my first time hosting a static
site and interacting with a domain name registrar/DNS settings, so I wanted to
write down some thoughts and tips while it's all still fresh in my mind.
</p>

<h3>Static site generation for GitLab Pages</h3>
<p>
Unlike some other static site hosts (like Git<em>Hub</em> Pages), GitLab doesn't
have you just upload the html/css/js files for your static site and host them
directly, but gives you the option to maintain a repo that uses some sort of
static site generator (say Jekyll, a popular option). It then has you use the
gitlab-ci system available for every repo to actually compile the website, and
if that succeeds, makes your website available. 
</p>

<p>
You still have the option to host a set of plain html files, and GitLab provides
<a href='https://gitlab.com/pages/plain-html'>a template</a> for that setup.
</p>

<p>
One slight point of confusion I encountered was when I missed that the build
directory has to be called <code>public</code>. Just looking at different
<code>.gitlab-ci.yml</code> examples, I thought GitLab would look under whatever
path was specified with
<code><pre>
pages:
    artifacts:
        paths:
        - &lt;path&gt;
</pre></code>
Initially my generator was putting build files in a <code>build</code>
directory. This would result in my CI build succeeding, but the external build
that GitLab does to actually publish the website would fail, without a useful
error message. A little embarassing how much time I spent debugging this one...
</p>

<h3>DNS configuration</h3>
<p>
I picked Namecheap as my registrar because they're considered reliable, have
cheap registration (got this .xyz for $1 for the first year!), and they're not
GoDaddy. Registration and payment was super simple, would recommend. The DNS
configuration page can be slow to respond unfortunately.
</p>

<p>
I've never played around with configuring DNS records, but GitLab's
documentation had fairly clear explanations. The biggest gripe I had was that
the official documentation
<a href='https://docs.gitlab.com/ee/user/project/pages/getting_started_part_three.html#dns-a-record'>
    recommends against using a CNAME record
</a>, while the Pages UI under my repo had a CNAME record (and no A record) made
available for copying, as if that were the recommended route. I ended up
creating an A record pointing to 35.185.44.232 as described in the docs.
</p>

<p>
I had a few issues that I'm not sure if they were caused by Namecheap or just
expected DNS propagation delays. It took several hours for the A record to
appear in DNS queries. Additionally, I missed the part where when creating the
<a href='https://docs.gitlab.com/ee/user/project/pages/getting_started_part_three.html#dns-txt-record'>
    TXT record
</a> that verifies ownership of a domain name, if you're creating it for an A
record, you should just use your top-level domain. Again, confusingly, the
Pages UI provides a copyable text field to a dummy subdomain and I had to dig
through the docs to find the correct approach.
</p>
  ]]></description>
  </item>

  <item>
  <title>Welcome!</title>
  <guid>http://m-chrzan.xyz/blog/welcome.html</guid>
  <pubDate>2019-06-26T00:00:00+00:00</pubDate>
  <description><![CDATA[
    <p>
Hello, internet! In the spirit of
<a href='http://www.alwaysownyourplatform.com/'>
<pre>
(•_•)
&lt;)  )╯Always
/  \

\(•_•)
 (  (&gt;  Own
 /  \

 (•_•)
&lt;)  )&gt;  Your Platform
 /  \
</pre>
</a>
I've decided to launch my own website and blog.
</p>
<p>
In the spirit of minimalism, it's (as you can see) very bare-bones.
</p>
<p>
In the spirit of self-reliance, it's built with a custom static site generator
(which is really just a few glorified Ruby scripts) that (again, in the spirit
of minimalism) do only what I need them to do (and hopefully do so well!).
  ]]></description>
  </item>

</channel>
</rss>
