<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Chiraag&#39;s Musings</title>
    <link>https://blog.chiraag.me/</link>
    <description>Ramblings at the intersection of socialism and engineering</description>
    <pubDate>Mon, 08 Jun 2026 07:59:21 +0000</pubDate>
    <item>
      <title>How I set up my self-hosted services</title>
      <link>https://blog.chiraag.me/how-i-set-up-my-self-hosted-services</link>
      <description>&lt;![CDATA[I want to expand on my previous post a little bit to explain the steps I took to setup PeerTube, Ente, and WriteFreely.&#xA;&#xA;Prerequisites&#xA;&#xA;I set everything up on two different VPSs. PeerTube was on a new domain, so I used an Ionos VPS. Ente and WriteFreely are on my main VPS (powering this website) since they were on subdomains of this domain.&#xA;&#xA;All intensive storage (photos and videos) happens on Backblaze B2. I love Backblaze for its reliably cheap storage and excellent APIs (including, crucially for this use-case, S3-compatible APIs).&#xA;&#xA;!--more--&#xA;&#xA;PeerTube&#xA;&#xA;I first followed the steps here to do the basic setup. I followed all of those instructions (including setting up the reverse proxy and systemd service. Because this is a fresh VPS, I did not have any port conflicts (this was not the case with the others as will be clear) and I could use the support reverse proxy (nginx).&#xA;&#xA;Then, it was time to setup the object storage according to this page. I went to Backblaze B2, created a new bucket for PeerTube, and made it public. Then, I generated an application key (restricted only to that bucket), ensuring that the key had the ability to list all buckets (necessary for an S3-compatible key). Then, all I had to do was fill in all of the information in config/production.yaml according to the documentation, restart PeerTube (systemctl restart peertube), and I was good to go!&#xA;&#xA;Initially, I had disabled transcoding, opting only to keep one resolution of the video (effectively just the original file). I later enabled HLS transcoding and manually ran the HLS transcoding jobs on the uploaded videos (possible through the admin interface). Everything went smoothly and the existing videos were (slowly) transcoded. I say slowly simply because the VPS only has 2 cores, so I can&#39;t really run transcoding jobs in parallel.&#xA;&#xA;I initially had tried to setup a remote runner, but it was having trouble with transcoding large files, so I disabled it for now. I will probably retry the runner with the next upload and see how it goes.&#xA;&#xA;Ente&#xA;&#xA;I had initially tried self-hosting Ente a few months ago when I first heard about it after asking for a self-hosted gallery with object storage support over on Mastodon. However, while I was able to get it up and running, I was running into issues completing the registration and getting the object storage backend up and running, so I gave up.&#xA;&#xA;Then, about a week ago, I decided to try again and actually got it working. I followed the self-hosting directions and got it installed, but I kept running into CORS issues when I attempted to register (the same issue I ran into earlier).&#xA;&#xA;I then realized that I had to set the environment block in compose.yaml to the IP address (rather than localhost), and that was enough to get it working. I then similarly set up a bucket in B2 for Ente and set up the object storage backend. I then tried a few test uploads and it worked!&#xA;&#xA;When I went to configure the reverse-proxy, I had to set it up with apache instead of nginx because apache was already being used for my main web server. There are numerous guides for doing it, so I won&#39;t elaborate here, but it&#39;s pretty straightforward.&#xA;&#xA;What I also hadn&#39;t realized originally was that I needed to enable all of the components on subdomains. For example, the main Ente domain is https://photos.chiraag.me, and all of the sub-components needed to be hosted on subdomains of that (e.g. https://api.photos.chiraag.me and so on --- I just used the standard subdomain names given in the documentation). Once I had configured this, added all of the subdomain reverse proxy entries, and used cerbot --apache to generate the SSL certificates (or, more accurately, expand the existing one to cover the new subdomains), everything worked great. Everything is automatically encrypted client-side, then uploaded to my B2 bucket. Then I can create public albums and share them (like at https://chiraag.me/albums).&#xA;&#xA;WriteFreely&#xA;&#xA;There&#39;s honestly not a lot to write about here. It&#39;s a static binary that I just downloaded and configured with the wizard. Everything just worked (I used the SQLite backend rather than MySQL) and it has everything I want and nothing that I don&#39;t.&#xA;&#xA;Conclusion&#xA;&#xA;This is honestly just a plug for the different services I&#39;ve setup. Our PeerTube is here. Albums shared through Ente can be found here. And, of course, you&#39;re already on the blog!]]&gt;</description>
      <content:encoded><![CDATA[<p>I want to expand on my previous post a little bit to explain the steps I took to setup PeerTube, Ente, and WriteFreely.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>I set everything up on two different VPSs. PeerTube was on a new domain, so I used an Ionos VPS. Ente and WriteFreely are on my main VPS (powering this website) since they were on subdomains of this domain.</p>

<p>All intensive storage (photos and videos) happens on <a href="https://www.backblaze.com/cloud-storage">Backblaze B2</a>. I love Backblaze for its reliably cheap storage and excellent APIs (including, crucially for this use-case, S3-compatible APIs).</p>



<h2 id="peertube">PeerTube</h2>

<p>I first followed the steps <a href="https://docs.joinpeertube.org/install/any-os">here</a> to do the basic setup. I followed all of those instructions (including setting up the reverse proxy and <code>systemd</code> service. Because this is a fresh VPS, I did not have any port conflicts (this was not the case with the others as will be clear) and I could use the support reverse proxy (<code>nginx</code>).</p>

<p>Then, it was time to setup the object storage according to <a href="https://docs.joinpeertube.org/maintain/remote-storage">this page</a>. I went to Backblaze B2, created a new bucket for PeerTube, and made it public. Then, I generated an application key (restricted only to <em>that bucket</em>), ensuring that the key had the ability to list all buckets (necessary for an S3-compatible key). Then, all I had to do was fill in all of the information in <code>config/production.yaml</code> according to the documentation, restart PeerTube (<code>systemctl restart peertube</code>), and I was good to go!</p>

<p>Initially, I had disabled transcoding, opting only to keep one resolution of the video (effectively just the original file). I later enabled HLS transcoding and manually ran the HLS transcoding jobs on the uploaded videos (possible through the admin interface). Everything went smoothly and the existing videos were (slowly) transcoded. I say slowly simply because the VPS only has 2 cores, so I can&#39;t really run transcoding jobs in parallel.</p>

<p>I initially had tried to setup a <a href="https://docs.joinpeertube.org/admin/remote-runners">remote runner</a>, but it was having trouble with transcoding large files, so I disabled it for now. I will probably retry the runner with the next upload and see how it goes.</p>

<h2 id="ente">Ente</h2>

<p>I had initially tried self-hosting Ente a few months ago when I first heard about it after asking for a self-hosted gallery with object storage support over on Mastodon. However, while I was able to get it up and running, I was running into issues completing the registration and getting the object storage backend up and running, so I gave up.</p>

<p>Then, about a week ago, I decided to try again and actually got it working. I followed the <a href="https://ente.com/help/self-hosting/installation/quickstart">self-hosting directions</a> and got it installed, but I kept running into CORS issues when I attempted to register (the same issue I ran into earlier).</p>

<p>I then realized that I had to set the <code>environment</code> block in <code>compose.yaml</code> to the IP address (rather than <code>localhost</code>), and that was enough to get it working. I then similarly set up a bucket in B2 for Ente and set up the <a href="https://ente.com/help/self-hosting/administration/object-storage">object storage backend</a>. I then tried a few test uploads and it worked!</p>

<p>When I went to configure the reverse-proxy, I had to set it up with <code>apache</code> instead of <code>nginx</code> because <code>apache</code> was already being used for my main web server. There are numerous guides for doing it, so I won&#39;t elaborate here, but it&#39;s pretty straightforward.</p>

<p>What I also hadn&#39;t realized originally was that I needed to enable all of the components on <em>sub</em>domains. For example, the main Ente domain is <a href="https://photos.chiraag.me">https://photos.chiraag.me</a>, and all of the sub-components needed to be hosted on subdomains of that (e.g. <a href="https://api.photos.chiraag.me">https://api.photos.chiraag.me</a> and so on —– I just used the standard subdomain names given in the documentation). Once I had configured this, added all of the subdomain reverse proxy entries, and used <code>cerbot --apache</code> to generate the SSL certificates (or, more accurately, expand the existing one to cover the new subdomains), everything worked great. Everything is automatically encrypted client-side, then uploaded to my B2 bucket. Then I can create public albums and share them (like at <a href="https://chiraag.me/albums">https://chiraag.me/albums</a>).</p>

<h2 id="writefreely">WriteFreely</h2>

<p>There&#39;s honestly not a lot to write about here. It&#39;s a static binary that I just downloaded and configured with the wizard. Everything just worked (I used the SQLite backend rather than MySQL) and it has everything I want and nothing that I don&#39;t.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This is honestly just a plug for the different services I&#39;ve setup. Our PeerTube is <a href="https://%E0%B2%AE%E0%B2%BF%E0%B2%B2%E0%B2%A8.net">here</a>. Albums shared through Ente can be found <a href="https://chiraag.me/albums">here</a>. And, of course, you&#39;re already on the blog!</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/how-i-set-up-my-self-hosted-services</guid>
      <pubDate>Sat, 25 Apr 2026 22:35:55 +0000</pubDate>
    </item>
    <item>
      <title>My self-hosting journey</title>
      <link>https://blog.chiraag.me/my-self-hosting-journey</link>
      <description>&lt;![CDATA[Not too long ago, I was not self-hosting anything (other than building my website from scratch). It&#39;s not because I couldn&#39;t (I have enough GNU/Linux knowledge and I&#39;ve programmed in a fair bunch of languages), but because it felt too complicated and involved.&#xA;&#xA;All of that changed about a week ago.&#xA;&#xA;!--more--&#xA;&#xA;I had been thinking of self-hosting stuff for a while (an image gallery, a blog, and perhaps even a site for videos). The main problem I kept running into is that storage on VPSs is quite expensive, so I needed software with object storage support (I prefer Backblaze B2). A lot of image gallery software, for example, handles local images fine but doesn&#39;t have built-in support for S3-type storage.&#xA;&#xA;Ironically enough, I started with PeerTube. It&#39;s the most storage-intensive of the services I have started hosting, but it has object storage support so I set it up with a B2 bucket. Setting it up was relatively easy and things magically show up across the Fediverse (it&#39;s still amazing to me how seamlessly it happens). I still need to setup transcoding, but that&#39;s the last bit.&#xA;&#xA;The next one was Ente. Interestingly, I had started configuring this a while ago, ran into problems with the object storage configuration, and gave up. I decided to give it another go and actually got everything set up this time! It&#39;s a little complicated because they have several different services that should be run on separate subdomains (the docs do a decent job of explaining it), but after a bit of fiddling I have the main interface (for authorized users to upload photos), the albums subdomain (for public albums), and the embed subdomain (for embedding public albums in other pages) and everything works.&#xA;&#xA;The final bit was actually easiest: this blog (through WriteFreely. I imported my old blog posts (from my previous WordPress-powered blog) and everything pretty much worked without a hitch.&#xA;&#xA;The coolest part is that uploaded videos and blog posts (like this) automatically federate with the rest of the Fediverse. I have a Mastodon account, so you can reach out to me there about my posts (WriteFreely currently does not have comments, so the easiest way to discuss it is to tag my Mastodon profile in your replies to this post).&#xA;&#xA;I am more than happy to discuss how I set these services up. Everything is on VPSs (WriteFreely and Ente are on the VPS for this website, PeerTube is on a different VPS for the other domain) and most stuff is relatively minimal in terms of resource footprint (PeerTube is relatively heavy and needs... 2 GB of RAM and 2 cores!). It&#39;s way more accessible than I had thought, and I am definitely glad I bit the bullet and looked into this.&#xA;&#xA;Take control of your digital life!]]&gt;</description>
      <content:encoded><![CDATA[<p>Not too long ago, I was not self-hosting anything (other than building my website from scratch). It&#39;s not because I couldn&#39;t (I have enough GNU/Linux knowledge and I&#39;ve programmed in a fair bunch of languages), but because it <em>felt</em> too complicated and involved.</p>

<p>All of that changed about a week ago.</p>



<p>I had been thinking of self-hosting stuff for a while (an image gallery, a blog, and perhaps even a site for videos). The main problem I kept running into is that storage on VPSs is quite expensive, so I needed software with object storage support (I prefer Backblaze B2). A lot of image gallery software, for example, handles local images fine but doesn&#39;t have built-in support for S3-type storage.</p>

<p>Ironically enough, I started with <a href="https://joinpeertube.org">PeerTube</a>. It&#39;s <em>the</em> most storage-intensive of the services I have started hosting, but it has object storage support so I set it up with a B2 bucket. Setting it up was relatively easy and things magically show up across the Fediverse (it&#39;s still amazing to me how seamlessly it happens). I still need to setup transcoding, but that&#39;s the last bit.</p>

<p>The next one was <a href="https://ente.io">Ente</a>. Interestingly, I had started configuring this a while ago, ran into problems with the object storage configuration, and gave up. I decided to give it another go and actually got everything set up this time! It&#39;s a little complicated because they have several different services that should be run on separate subdomains (the docs do a decent job of explaining it), but after a bit of fiddling I have the main interface (for authorized users to upload photos), the albums subdomain (for public albums), and the embed subdomain (for embedding public albums in other pages) and everything works.</p>

<p>The final bit was actually easiest: this blog (through <a href="https://writefreely.org">WriteFreely</a>. I imported my old blog posts (from my previous WordPress-powered blog) and everything pretty much worked without a hitch.</p>

<p>The coolest part is that uploaded videos and blog posts (like this) automatically federate with the rest of the Fediverse. I have a <a href="https://mastodon.online/@chiraag">Mastodon</a> account, so you can reach out to me there about my posts (WriteFreely currently does not have comments, so the easiest way to discuss it is to tag my Mastodon profile in your replies to this post).</p>

<p>I am more than happy to discuss how I set these services up. Everything is on VPSs (WriteFreely and Ente are on the VPS for this website, PeerTube is on a different VPS for the other domain) and most stuff is relatively minimal in terms of resource footprint (PeerTube is relatively heavy and needs... 2 GB of RAM and 2 cores!). It&#39;s way more accessible than I had thought, and I am definitely glad I bit the bullet and looked into this.</p>

<p>Take control of your digital life!</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/my-self-hosting-journey</guid>
      <pubDate>Fri, 24 Apr 2026 22:32:24 +0000</pubDate>
    </item>
    <item>
      <title>Rethinking the Calendar and Our Measures of Time</title>
      <link>https://blog.chiraag.me/rethinking-the-calendar-and-our-measures-of-time</link>
      <description>&lt;![CDATA[We all know the Gregorian calendar, right? It&#39;s the &#34;standard&#34; one, with 12 months, each one with a variable number of days from 28 to 31. And we may not love it, but we use it. But what if...there were a better way?&#xA;&#xA;I started thinking about this at first when I watched Kurzgesagt&#39;s a href=&#34;https://www.youtube.com/watch?v=czgOWmtGVGs&#34;video/a, which argues that we should find a new year &#34;0&#34; that better reflects the sheer magnitude of all that we have accomplished as a species. They end up settling on 10000 BC/BCE as the appropriate year &#34;0&#34;, for reasons that they describe in the video. In practice, in order to use the Human Era system, we just tack on an extra &#34;1&#34; at the beginning of the year. So the year 2020 AD/CE becomes the year 12,020 HE (Human Era).&#xA;&#xA;That got me thinking, though, that there must be a better way to distribute the days of the year. I mean, the sheer madness that is the Gregorian calendar would be hilarious if it weren&#39;t so ingrained into us. Instead of each month having a fixed number of days (possibly with some minor adjustment at the end to account for imperfections), we end up with a monstrosity where you have to memorize the number of days in each month. Of course, this happened because the calendar was iteratively refined due to increased knowledge about exactly how long one revolution around the sun takes, adding/subtracting months to please Roman emperors, and so on. But if we were to design a calendar from scratch, what might it look like?&#xA;&#xA;!--more--&#xA;&#xA;Well, a standard year (ignoring leap years for a second) has 365 days. The prime factors of 365 are 73 and 5, so let&#39;s assign 5 days to a week, instead of the current 7. This leaves us with a year that has 73 full weeks. Now, unfortunately 73 is prime, but 72 --- 72 is a number we can work very nicely with. 72 gives us prime factors of 3,3,2,2,2. This means that we can have 18 months with 4 weeks each, 9 months with 8 weeks each, or even 36 months with 2 weeks each if we want. Let&#39;s go with 9 months of 8 weeks each for now. This leaves the last week --- the leap week, as it were --- by itself. We can call that the 10th month or leave it by itself --- either way, it just hangs out at the end of the year. &#xA;&#xA;What happens in a leap year? Just tack on the leap day to the end of the leap week. This design ensures that all of the first 9 months are completely untouched every single year. The same date falls on the same day of the year, every single year. No shifting dates. No memorizing how many days a month has. It&#39;s nice and simple.&#xA;&#xA;We can do a similar thing with time, too. Why exactly do we have 24 hours in a day, with 60 minutes per hour and 60 seconds per minute? Why not have nice, round, powers of 10 for each of those conversions? In this case, however, we will have to redefine the second. Let us define that 1 day has 100,000 seconds. If we set reasonable conversions of 100 seconds per minute and 100 minutes per hour, this gets us a nice, even 10 hours per day.&#xA;&#xA;Unfortunately, this means that a second under this system is quite a bit shorter than a second in the current system. To be exact, one new second is 0.864 current seconds. That is, unlike the new calendar system which is day-for-day compatible with the current calendar system, this new system of measuring time has conversion factors.&#xA;&#xA;In summary, here is the new system I am proposing (and have implemented on my laptop):&#xA;&#xA;ul&#xA;li100 seconds per minute/li&#xA;li100 minutes per hour/li&#xA;li10 hours per day/li&#xA;li5 days per week/li&#xA;li8 weeks per month/li&#xA;li9 months per year with a leap week to round out the year (so effectively 9.125 months or 10 months where the 10th month is highly irregular, whichever you prefer)/li&#xA;liOn leap years, the leap week gets a leap day/li&#xA;liAdd 10000 to the current year to utilize Human Era years/li&#xA;/ul&#xA;&#xA;Also, days and months should be 0-indexed as our hours (in 24-hour time), minutes, seconds, and years are. So month numbers go from 0 to 8 (or 9 if you could the last week as a month) and day numbers go from 0 to 39.&#xA;&#xA;This is certainly a change from our current way of measuring and dealing with time. But I tend to view this as a much more rational approach, one that better incorporates the additional knowledge we now have. Additionally, it makes the calendar as regular as humanly possible. The same holds true for the new time system --- indeed, it&#39;s far more regular than even the new calendar system. It takes adjustment, but so does emany/em new system. That&#39;s what happens when everyone is brought up on a particular system. It&#39;s why transitioning between operating systems can be so painful, or daring to dream big about systemic change can feel scary. We find comfort in the familiar but sometimes...the familiar just emreally/em sucks.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>We all know the Gregorian calendar, right? It&#39;s the “standard” one, with 12 months, each one with a variable number of days from 28 to 31. And we may not love it, but we use it. But what if...there were a better way?</p>

<p>I started thinking about this at first when I watched Kurzgesagt&#39;s <a href="https://www.youtube.com/watch?v=czgOWmtGVGs">video</a>, which argues that we should find a new year “0” that better reflects the sheer magnitude of all that we have accomplished as a species. They end up settling on 10000 BC/BCE as the appropriate year “0”, for reasons that they describe in the video. In practice, in order to use the Human Era system, we just tack on an extra “1” at the beginning of the year. So the year 2020 AD/CE becomes the year 12,020 HE (Human Era).</p>

<p>That got me thinking, though, that there must be a better way to distribute the days of the year. I mean, the sheer madness that is the Gregorian calendar would be hilarious if it weren&#39;t so ingrained into us. Instead of each month having a fixed number of days (possibly with some minor adjustment at the end to account for imperfections), we end up with a monstrosity where you have to memorize the number of days in each month. Of course, this happened because the calendar was iteratively refined due to increased knowledge about exactly how long one revolution around the sun takes, adding/subtracting months to please Roman emperors, and so on. But if we were to design a calendar from scratch, what might it look like?</p>



<p>Well, a standard year (ignoring leap years for a second) has 365 days. The prime factors of 365 are 73 and 5, so let&#39;s assign 5 days to a week, instead of the current 7. This leaves us with a year that has 73 full weeks. Now, unfortunately 73 is prime, but 72 —– 72 is a number we can work very nicely with. 72 gives us prime factors of 3,3,2,2,2. This means that we can have 18 months with 4 weeks each, 9 months with 8 weeks each, or even 36 months with 2 weeks each if we want. Let&#39;s go with 9 months of 8 weeks each for now. This leaves the last week —– the leap week, as it were —– by itself. We can call that the 10th month or leave it by itself —– either way, it just hangs out at the end of the year.</p>

<p>What happens in a leap year? Just tack on the leap day to the end of the leap week. This design ensures that all of the first 9 months are completely untouched every single year. The same date falls on the same day of the year, every single year. No shifting dates. No memorizing how many days a month has. It&#39;s nice and simple.</p>

<p>We can do a similar thing with time, too. Why exactly do we have 24 hours in a day, with 60 minutes per hour and 60 seconds per minute? Why not have nice, round, powers of 10 for each of those conversions? In this case, however, we will have to redefine the second. Let us define that 1 day has 100,000 seconds. If we set reasonable conversions of 100 seconds per minute and 100 minutes per hour, this gets us a nice, even 10 hours per day.</p>

<p>Unfortunately, this means that a second under this system is quite a bit shorter than a second in the current system. To be exact, one new second is 0.864 current seconds. That is, unlike the new calendar system which is day-for-day compatible with the current calendar system, this new system of measuring time has conversion factors.</p>

<p>In summary, here is the new system I am proposing (and have implemented on my laptop):</p>

<ul><li>100 seconds per minute</li>
<li>100 minutes per hour</li>
<li>10 hours per day</li>
<li>5 days per week</li>
<li>8 weeks per month</li>
<li>9 months per year with a leap week to round out the year (so effectively 9.125 months or 10 months where the 10th month is highly irregular, whichever you prefer)</li>
<li>On leap years, the leap week gets a leap day</li>
<li>Add 10000 to the current year to utilize Human Era years</li></ul>

<p>Also, days and months should be 0-indexed as our hours (in 24-hour time), minutes, seconds, and years are. So month numbers go from 0 to 8 (or 9 if you could the last week as a month) and day numbers go from 0 to 39.</p>

<p>This is certainly a change from our current way of measuring and dealing with time. But I tend to view this as a much more rational approach, one that better incorporates the additional knowledge we now have. Additionally, it makes the calendar as regular as humanly possible. The same holds true for the new time system —– indeed, it&#39;s far more regular than even the new calendar system. It takes adjustment, but so does <em>any</em> new system. That&#39;s what happens when everyone is brought up on a particular system. It&#39;s why transitioning between operating systems can be so painful, or daring to dream big about systemic change can feel scary. We find comfort in the familiar but sometimes...the familiar just <em>really</em> sucks.</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/rethinking-the-calendar-and-our-measures-of-time</guid>
      <pubDate>Sun, 18 Oct 2020 07:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Managing multiple email accounts with Mutt and Fetchmail</title>
      <link>https://blog.chiraag.me/managing-multiple-email-accounts-with-mutt-and-fetchmail</link>
      <description>&lt;![CDATA[Recently, I switched to a href=&#34;https://protonmail.com&#34;ProtonMail/a as a way of increasing my privacy online and moving away from the &#34;data as currency&#34; model of Google and Facebook (as part of this same effort, I closed my Facebook in favor of creating this blog!). However, most of what I describe here has been my setup for a while due to my penchant for two things:&#xA;ol&#xA;  liWanting to keep my emails backed up (and a local copy always at hand)./li&#xA;  liWanting a simple but powerful email reader so that it gets out of the way while enabling as much customization as needed./li&#xA;/ol&#xA;At the same time, I wanted a system where I could easily add and remove accounts while adding/removing as little configuration as possible, and this informs the way I partition my configuration files (as will be apparent).&#xA;&#xA;As the title suggests, there are two different parts to this setup: kbdfetchmail/kbd and kbdmutt/kbd.&#xA;&#xA;!--more--&#xA;&#xA;Since kbdfetchmail/kbd does not natively provide for encryption of the configuration file (and the file contains sensitive information), I wrote a wrapper around it which decrypts the file and passes it to kbdfetchmail/kbd via kbdstdin/kbd. That part will not be discussed here and can be set up after glancing at the kbdfetchmail/kbd manual page (in short, do something like kbdgpg -d -o - /path/to/encrypted/config | fetchmail -d &amp;lt;n&amp;gt; -f -/kbd).&#xA;&#xA;As for the configuration file itself, this should serve as a good template:&#xA;precodeset postmaster &#34;&amp;lt;local username&amp;gt;&#34;&#xA;set bouncemail&#xA;set no spambounce&#xA;set softbounce&#xA;set properties &#34;&#34;&#xA;poll &amp;lt;imap server&amp;gt; with proto IMAP&#xA;       user &#39;username&#39; there with password &#39;password&#39; is &#39;&amp;lt;local username&amp;gt;&#39; here&#xA;       options keep ssl sslproto &#34;TLS1+&#34; mda &#34;&amp;lt;mda&amp;gt; $HOME/.local/mail/Gmail/&#34;&#xA;  folder &#34;&amp;lt;folder&amp;gt;&#34;&#xA;/code/pre&#xA;You can replicate the last bit (kbdpoll ...folder &#34;&amp;lt;folder&amp;gt;&#34;/kbd) as many times as you need to add multiple accounts. kbd&amp;lt;local username&amp;gt;/kbd is the username you want mail to be delivered under (I recommend running kbdfetchmail/kbd as emyour user/em unless there are extenuating circumstances). kbdusername/kbd and kbdpassword/kbd are self-explanatory. The exact structure of kbd&amp;lt;folder&amp;gt;/kbd can differ based on the server (for example, with Gmail, some folders require a prefix of kbd[Gmail]/kbd), but usually kbdInbox/kbd will work without any prefix. Oh, and kbd/kbd is a Mail Delivery Agent — you&#39;ll want one that delivers to a emmaildir/em. The one I use is the stand-alone MDA from kbdgetmail/kbd called kbdgetmailmaildir/kbd and requires no configuration.&#xA;&#xA;embA special note for Protonmail Bridge users/b/em: In the kbdoptions/kbd section, you&#39;ll want kbdkeep no sslcertck sslfingerprint &#34;48:63:2E:30:62:FC:23:B3:9A:42:CD:02:1E:FF:F8:52&#34;/kbd since otherwise kbdfetchmail/kbd will throw an error as the Bridge&#39;s certificate isn&#39;t trusted.&#xA;&#xA;At this point, kbdfetchmail/kbd should be good to go. Before you try running it, you should create the maildirs you setup in the kbdfetchmail/kbd config. This is really easy; if the kbdmaildir/kbd is kbd$DIR/kbd, you can just run kbdmkdir -p $DIR/{cur,tmp,new}/kbd and you&#39;re done. Now, you can try running kbdfetchmail -f /path/to/config/file/kbd. If all goes well and you don&#39;t have any unread emails, kbdfetchmail/kbd should exit normally without downloading any emails. At this point, I&#39;d run kbdfetchmail -a -f /path/to/config/file/kbd to get a copy of emall/em emails, read or not. Note that kbdfetchmail/kbd uses whether an email is read to determine whether or not to download it.&#xA;&#xA;The next piece of the puzzle is kbdmutt/kbd. All of my kbdmutt/kbd configuration goes in kbd~/.config/mutt/muttrc/kbd. I have a master RC file that looks something like this (along with many other variables that I won&#39;t go into here):&#xA;precodeset folder=&#34;~/.local/mail&#34;&#xA;&#xA;mailboxes &#34;+Gmail&#34; &#34;+Brown&#34; &#34;+CaltechAlumni&#34; &#34;+ProtonMail&#34; &#34;+ProtonMail-Custom&#34;&#xA;&#xA;folder-hook Brown &#39;source ~/.config/mutt/muttrc/muttrc.localb; macro index cb &#39;&#xA;folder-hook Gmail &#39;source ~/.config/mutt/muttrc/muttrc.localg; macro index cg &#39;&#xA;folder-hook CaltechAlumni &#39;source ~/.config/mutt/muttrc/muttrc.localca; macro index ca &#39;&#xA;folder-hook ProtonMail &#39;source ~/.config/mutt/muttrc/muttrc.localp; macro index cp &#39;&#xA;folder-hook ProtonMail-Custom &#39;source ~/.config/mutt/muttrc/muttrc.localpc; macro index cq &#39;&#xA;&#xA;source ~/.config/mutt/muttrc/muttrc.macros&#xA;/code/pre&#xA;In my case, I store all of my email in kbd~/.local/mail/kbd, so I set that as the &#34;base&#34; location, if you will, by setting the kbdfolder/kbd variable. Then, kbd+Gmail/kbd expands to kbd~/.local/mail/Gmail/kbd. In this way, I don&#39;t need to spell out the full location for each mailbox.&#xA;&#xA;Additionally, when I switch mailboxes, I load a mailbox-specific config file. For example, the Gmail one looks like this:&#xA;precodeset spoolfile=&#34;~/.local/mail/Gmail&#34;&#xA;&#xA;set smtpurl=&#34;smtps://chiraag.nataraj\@gmail.com@smtp.gmail.com/&#34;&#xA;set from=&#34;ಚಿರಾಗ್ ನಟರಾಜ್ &amp;lt;chiraag.nataraj@gmail.com&amp;gt;&#34;&#xA;set realname=&#34;ಚಿರಾಗ್ ಮಂಜುನಾಥ ನಟರಾಜ್&#34;&#xA;set imapuser=chiraag.nataraj@gmail.com&#xA;set record=&#34;^&#34;&#xA;&#xA;set signature=&#34;~/.config/mutt/signatures/signature.home&#34;&#xA;set headercache=~/.cache/mutt/muttcache.gmail&#xA;&#xA;send-hook . &#34;source &#39;gpg -d ~/.config/mutt/muttsecure/gmail.password.gpg |&#39;&#34;&#xA;&#xA;source ~/.config/mutt/muttrc/muttrc.macros&#xA;source ~/.config/mutt/muttrc/muttrc.autocrypt&#xA;/code/pre&#xA;In other words, I set the SMTP URL (for sending email from this mailbox), name, signature, and so on. Additionally, I tell it to decrypt the file containing the password for my Gmail account before opening up my editor to compose an email. This means I don&#39;t need to needlessly decrypt the file until necessary.&#xA;&#xA;You might notice this kbdmutt.macros/kbd thing lying around. That file looks like this:&#xA;precodemacro index ca &#39;&amp;lt;sync-mailbox&amp;gt;&amp;lt;enter-command&amp;gt;source ~/.config/mutt/muttrc/muttrc.localca&amp;lt;enter&amp;gt;&amp;lt;change-folder&amp;gt;!&amp;lt;enter&amp;gt;&#39;&#xA;macro index cb &#39;&amp;lt;sync-mailbox&amp;gt;&amp;lt;enter-command&amp;gt;source ~/.config/mutt/muttrc/muttrc.localb&amp;lt;enter&amp;gt;&amp;lt;change-folder&amp;gt;!&amp;lt;enter&amp;gt;&#39;&#xA;macro index cg &#39;&amp;lt;sync-mailbox&amp;gt;&amp;lt;enter-command&amp;gt;source ~/.config/mutt/muttrc/muttrc.localg&amp;lt;enter&amp;gt;&amp;lt;change-folder&amp;gt;!&amp;lt;enter&amp;gt;&#39;&#xA;macro index cp &#39;&amp;lt;sync-mailbox&amp;gt;&amp;lt;enter-command&amp;gt;source ~/.config/mutt/muttrc/muttrc.localp&amp;lt;enter&amp;gt;&amp;lt;change-folder&amp;gt;!&amp;lt;enter&amp;gt;&#39;&#xA;/code/pre&#xA;This is just an easy way for me to switch between different mailboxes. Each of my mailbox-specific config files sources this file. In the kbdfolder-hook/kbd, I then &#39;reset&#39; the macro for the emcurrent/em mailbox to simply refresh the screen (basically a no-op).&#xA;&#xA;This system means that if I want to add a new mailbox to kbdmutt/kbd, I do the following:&#xA;ol&#xA; &#x9;liCreate a new mailbox-specific config file with the necessary details./li&#xA; &#x9;liAdd the mailbox to my master configuration file./li&#xA; &#x9;liAdd a keybinding for switching to that mailbox to kbdmutt.macros/kbd./li&#xA;/ol&#xA;In addition, I need to do silly things like creating a mailbox-specific signature and stuff, but that&#39;s not too bad.&#xA;&#xA;With some work, I could probably even automate the current system for adding a mailbox, but I am fairly satisfied with how the system currently works.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Recently, I switched to <a href="https://protonmail.com">ProtonMail</a> as a way of increasing my privacy online and moving away from the “data as currency” model of Google and Facebook (as part of this same effort, I closed my Facebook in favor of creating this blog!). However, most of what I describe here has been my setup for a while due to my penchant for two things:
<ol>  <li>Wanting to keep my emails backed up (and a local copy always at hand).</li>
  <li>Wanting a simple but powerful email reader so that it gets out of the way while enabling as much customization as needed.</li></ol>
At the same time, I wanted a system where I could easily add and remove accounts while adding/removing as little configuration as possible, and this informs the way I partition my configuration files (as will be apparent).</p>

<p>As the title suggests, there are two different parts to this setup: fetchmail and mutt.</p>



<p>Since fetchmail does not natively provide for encryption of the configuration file (and the file contains sensitive information), I wrote a wrapper around it which decrypts the file and passes it to fetchmail via stdin. That part will not be discussed here and can be set up after glancing at the fetchmail manual page (in short, do something like gpg -d -o – /path/to/encrypted/config | fetchmail -d &lt;n&gt; -f -).</p>

<p>As for the configuration file itself, this should serve as a good template:
<pre><code>set postmaster “&lt;local username&gt;“
set bouncemail
set no spambounce
set softbounce
set properties “”
poll &lt;imap server&gt; with proto IMAP
       user &#39;username&#39; there with password &#39;password&#39; is &#39;&lt;local username&gt;&#39; here
       options keep ssl sslproto “TLS1+” mda “&lt;mda&gt; $HOME/.local/mail/Gmail/”
  folder “&lt;folder&gt;“
</code></pre>
You can replicate the last bit (poll ...folder “&lt;folder&gt;“) as many times as you need to add multiple accounts. &lt;local username&gt; is the username you want mail to be delivered under (I recommend running fetchmail as <em>your user</em> unless there are extenuating circumstances). username and password are self-explanatory. The exact structure of &lt;folder&gt; can differ based on the server (for example, with Gmail, some folders require a prefix of [Gmail]), but usually Inbox will work without any prefix. Oh, and  is a Mail Delivery Agent — you&#39;ll want one that delivers to a <em>maildir</em>. The one I use is the stand-alone MDA from getmail called getmail_maildir and requires no configuration.</p>

<p><em><b>A special note for Protonmail Bridge users</b></em>: In the options section, you&#39;ll want keep no sslcertck sslfingerprint “48:63:2E:30:62:FC:23:B3:9A:42:CD:02:1E:FF:F8:52” since otherwise fetchmail will throw an error as the Bridge&#39;s certificate isn&#39;t trusted.</p>

<p>At this point, fetchmail should be good to go. Before you try running it, you should create the maildirs you setup in the fetchmail config. This is really easy; if the maildir is $DIR, you can just run mkdir -p $DIR/{cur,tmp,new} and you&#39;re done. Now, you can try running fetchmail -f /path/to/config/file. If all goes well and you don&#39;t have any unread emails, fetchmail should exit normally without downloading any emails. At this point, I&#39;d run fetchmail -a -f /path/to/config/file to get a copy of <em>all</em> emails, read or not. Note that fetchmail uses whether an email is read to determine whether or not to download it.</p>

<p>The next piece of the puzzle is mutt. All of my mutt configuration goes in ~/.config/mutt/muttrc. I have a master RC file that looks something like this (along with many other variables that I won&#39;t go into here):
<pre><code>set folder=”~/.local/mail”</p>

<p>mailboxes “+Gmail” “+Brown” “+CaltechAlumni” “+ProtonMail” “+ProtonMail-Custom”</p>

<p>folder-hook Brown &#39;source ~/.config/mutt/muttrc/muttrc.localb; macro index cb &#39;
folder-hook Gmail &#39;source ~/.config/mutt/muttrc/muttrc.localg; macro index cg &#39;
folder-hook CaltechAlumni &#39;source ~/.config/mutt/muttrc/muttrc.localca; macro index ca &#39;
folder-hook ProtonMail &#39;source ~/.config/mutt/muttrc/muttrc.localp; macro index cp &#39;
folder-hook ProtonMail-Custom &#39;source ~/.config/mutt/muttrc/muttrc.localpc; macro index cq &#39;</p>

<p>source ~/.config/mutt/muttrc/muttrc.macros
</code></pre>
In my case, I store all of my email in ~/.local/mail, so I set that as the “base” location, if you will, by setting the folder variable. Then, +Gmail expands to ~/.local/mail/Gmail. In this way, I don&#39;t need to spell out the full location for each mailbox.</p>

<p>Additionally, when I switch mailboxes, I load a mailbox-specific config file. For example, the Gmail one looks like this:
<pre><code>set spoolfile=”~/.local/mail/Gmail”</p>

<p>set smtp<em>url=“smtps://chiraag.nataraj\<a href="https://blog.chiraag.me/@/gmail.com@smtp.gmail.com" class="u-url mention">@<span>gmail.com@smtp.gmail.com</span></a>/”
set from=“ಚಿರಾಗ್ ನಟರಾಜ್ &lt;chiraag.nataraj@gmail.com&gt;“
set realname=“ಚಿರಾಗ್ ಮಂಜುನಾಥ ನಟರಾಜ್”
set imap</em>user=chiraag.nataraj@gmail.com
set record=“^”</p>

<p>set signature=”~/.config/mutt/signatures/signature.home”
set header<em>cache=~/.cache/mutt/mutt</em>cache.gmail</p>

<p>send-hook . “source &#39;gpg -d ~/.config/mutt/mutt_secure/gmail.password.gpg |&#39;”</p>

<p>source ~/.config/mutt/muttrc/muttrc.macros
source ~/.config/mutt/muttrc/muttrc.autocrypt
</code></pre>
In other words, I set the SMTP URL (for sending email from this mailbox), name, signature, and so on. Additionally, I tell it to decrypt the file containing the password for my Gmail account before opening up my editor to compose an email. This means I don&#39;t need to needlessly decrypt the file until necessary.</p>

<p>You might notice this mutt.macros thing lying around. That file looks like this:
<pre><code>macro index ca &#39;&lt;sync-mailbox&gt;&lt;enter-command&gt;source ~/.config/mutt/muttrc/muttrc.localca&lt;enter&gt;&lt;change-folder&gt;!&lt;enter&gt;&#39;
macro index cb &#39;&lt;sync-mailbox&gt;&lt;enter-command&gt;source ~/.config/mutt/muttrc/muttrc.localb&lt;enter&gt;&lt;change-folder&gt;!&lt;enter&gt;&#39;
macro index cg &#39;&lt;sync-mailbox&gt;&lt;enter-command&gt;source ~/.config/mutt/muttrc/muttrc.localg&lt;enter&gt;&lt;change-folder&gt;!&lt;enter&gt;&#39;
macro index cp &#39;&lt;sync-mailbox&gt;&lt;enter-command&gt;source ~/.config/mutt/muttrc/muttrc.localp&lt;enter&gt;&lt;change-folder&gt;!&lt;enter&gt;&#39;
</code></pre>
This is just an easy way for me to switch between different mailboxes. Each of my mailbox-specific config files sources this file. In the folder-hook, I then &#39;reset&#39; the macro for the <em>current</em> mailbox to simply refresh the screen (basically a no-op).</p>

<p>This system means that if I want to add a new mailbox to mutt, I do the following:
<ol>    <li>Create a new mailbox-specific config file with the necessary details.</li>
    <li>Add the mailbox to my master configuration file.</li>
    <li>Add a keybinding for switching to that mailbox to mutt.macros.</li></ol>
In addition, I need to do silly things like creating a mailbox-specific signature and stuff, but that&#39;s not too bad.</p>

<p>With some work, I could probably even automate the current system for adding a mailbox, but I am fairly satisfied with how the system currently works.</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/managing-multiple-email-accounts-with-mutt-and-fetchmail</guid>
      <pubDate>Wed, 21 Aug 2019 07:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Kashmir and Anti-Imperialism</title>
      <link>https://blog.chiraag.me/kashmir-and-anti-imperialism</link>
      <description>&lt;![CDATA[Recently, Prime Minister Modi of India decided to abrogate Articles 370 and 35A of the Indian Constitution and fully annex Jammu and Kashmir. a href=&#34;https://jacobinmag.com/2019/08/indian-occupation-kashmir-srinagar-bjp-curfew&#34;This article/a from inside Kashmir talks about police- and military-enforced curfews, internet and phones being blocked, and a general sense of oppression.&#xA;&#xA;Defenders will point to Pakistan&#39;s a href=&#34;https://thediplomat.com/2016/07/pakistans-failed-kashmir-policy/&#34;decades-long policy/a of interfering in Indian-controlled parts of Jammu and Kashmir. This, in its own right, is a form of imperialism and bmust be opposed/b. However, this is no excuse for Indian imperialism.&#xA;&#xA;This whole thing is complicated, right? Because there are two conflicting desires.&#xA;&#xA;!--more--&#xA;&#xA;On the one hand, there is a sense that a country has to mean something, that states (and territories) shouldn&#39;t just be able to break away. This tendency is best exemplified by the US Civil War, where the southern, slave-holding states wanted to secede to preserve slavery and the northern states refused to let them. In my personal conversations, this is the point that comes up the most, where there&#39;s a sense of &#39;India First&#39;. &#34;National Security Concerns&#34;™ also sort of fall under this category, since it&#39;s often used to justify holding on to territories even if there are strains of an independence movement within them (Jammu and Kashmir, Tibet, Hong Kong, the various Kurdistans, and so on).&#xA;&#xA;Unfortunately, this line of thinking often ends up justifying naked imperialism, since it tends to override any decision-making based on what the people in that territory want. That is, emit doesn&#39;t matter what the people of Tibet want/em &amp;mdash; China&#39;s interests come first. emIt doesn&#39;t matter what various Kurdish populations want/em &amp;mdash; the national interests of Turkey, Iran, Iraq, and Syria come first (based on whichever Kurdish population we&#39;re talking about). In the same way, I&#39;ve had it explained that emit doesn&#39;t matter what the people of Jammu and Kashmir want/em &amp;mdash; India&#39;s national interests come first. Indeed, this line of thinking is extremely problematic coming from a former British colony, as this logic was used to hold together Britain&#39;s vast holdings and justify oppressing the peoples therein.&#xA;&#xA;On the other hand, at least in India&#39;s case, there is a supposed committment to democracy. That is, there is a notion that &#34;the people&#34; should be able to determine for themselves the fate of their government. This often goes hand-in-hand with a supposed committment to &#34;self-determination&#34;.&#xA;&#xA;The problem is that one cannot emboth/em be imperialist and committed to democracy. One can either be committed to honoring the will of Jammu and Kashmir or overriding it. And take note that emnone of this/em depends on whether Pakistan is itself imperialist.&#xA;&#xA;If Jammu and Kashmir were to have a vote and voted to become independent countries (or one independent country) and Pakistan refused to honor it, that is problematic. It would be emjust/em as problematic if India refused to honor it. But those situations can and should be dealt with as they arise, and such hypotheticals should not prevent the expressiong of the right to self-determination.&#xA;&#xA;As a postscript, I&#39;ll note that &#34;national security concerns&#34; can justify anything. If annexing Jammu and Kashmir can be justified under that umbrella, why not just annex and invade Pakistan? Pakistan, after all, is India&#39;s biggest national security threat. And why stop there? Why not annex and invade Afghanistan? Once India annexes Pakistan, Afghanistan will pose the same threat Pakistan currently does.&#xA;&#xA;At this point, it is clear that the people pushing annexation of Jammu and Kashmir are doing so without regard for what the people of Jammu and Kashmir want. bI do not claim to speak for them/b. All I hope is that India and Pakistan listen to what they want and honor the right to self-determination. The recent actions make clear that India, at least, most likely will not go down that path.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Recently, Prime Minister Modi of India decided to abrogate Articles 370 and 35A of the Indian Constitution and fully annex Jammu and Kashmir. <a href="https://jacobinmag.com/2019/08/indian-occupation-kashmir-srinagar-bjp-curfew">This article</a> from inside Kashmir talks about police- and military-enforced curfews, internet and phones being blocked, and a general sense of oppression.</p>

<p>Defenders will point to Pakistan&#39;s <a href="https://thediplomat.com/2016/07/pakistans-failed-kashmir-policy/">decades-long policy</a> of interfering in Indian-controlled parts of Jammu and Kashmir. This, in its own right, is a form of imperialism and <b>must be opposed</b>. However, this is no excuse for Indian imperialism.</p>

<p>This whole thing is complicated, right? Because there are two conflicting desires.</p>



<p>On the one hand, there is a sense that a country has to mean something, that states (and territories) shouldn&#39;t just be able to break away. This tendency is best exemplified by the US Civil War, where the southern, slave-holding states wanted to secede to preserve slavery and the northern states refused to let them. In my personal conversations, this is the point that comes up the most, where there&#39;s a sense of &#39;India First&#39;. “National Security Concerns”™ also sort of fall under this category, since it&#39;s often used to justify holding on to territories even if there are strains of an independence movement within them (Jammu and Kashmir, Tibet, Hong Kong, the various Kurdistans, and so on).</p>

<p>Unfortunately, this line of thinking often ends up justifying naked imperialism, since it tends to override any decision-making based on what the people in that territory want. That is, <em>it doesn&#39;t matter what the people of Tibet want</em> — China&#39;s interests come first. <em>It doesn&#39;t matter what various Kurdish populations want</em> — the national interests of Turkey, Iran, Iraq, and Syria come first (based on whichever Kurdish population we&#39;re talking about). In the same way, I&#39;ve had it explained that <em>it doesn&#39;t matter what the people of Jammu and Kashmir want</em> — India&#39;s national interests come first. Indeed, this line of thinking is extremely problematic coming from a former British colony, as this logic was used to hold together Britain&#39;s vast holdings and justify oppressing the peoples therein.</p>

<p>On the other hand, at least in India&#39;s case, there is a supposed committment to democracy. That is, there is a notion that “the people” should be able to determine for themselves the fate of their government. This often goes hand-in-hand with a supposed committment to “self-determination”.</p>

<p>The problem is that one cannot <em>both</em> be imperialist and committed to democracy. One can either be committed to honoring the will of Jammu and Kashmir or overriding it. And take note that <em>none of this</em> depends on whether Pakistan is itself imperialist.</p>

<p>If Jammu and Kashmir were to have a vote and voted to become independent countries (or one independent country) and Pakistan refused to honor it, that is problematic. It would be <em>just</em> as problematic if India refused to honor it. But those situations can and should be dealt with as they arise, and such hypotheticals should not prevent the expressiong of the right to self-determination.</p>

<p>As a postscript, I&#39;ll note that “national security concerns” can justify anything. If annexing Jammu and Kashmir can be justified under that umbrella, why not just annex and invade Pakistan? Pakistan, after all, is India&#39;s biggest national security threat. And why stop there? Why not annex and invade Afghanistan? Once India annexes Pakistan, Afghanistan will pose the same threat Pakistan currently does.</p>

<p>At this point, it is clear that the people pushing annexation of Jammu and Kashmir are doing so without regard for what the people of Jammu and Kashmir want. <b>I do not claim to speak for them</b>. All I hope is that India and Pakistan listen to what they want and honor the right to self-determination. The recent actions make clear that India, at least, most likely will not go down that path.</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/kashmir-and-anti-imperialism</guid>
      <pubDate>Sun, 18 Aug 2019 07:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Converting Raspbian to Debian sid</title>
      <link>https://blog.chiraag.me/converting-raspbian-to-debian-sid</link>
      <description>&lt;![CDATA[So a while back, I decided to get a Raspberry Pi to setup as a UPnP media server for my local network (emnot/em accessible from outside the network). Of course, the standard procedure is to install Raspbian, which is a customized Debian-based distribution that works well with the Raspberry Pi.&#xA;&#xA;However, this presents an annoyance: the Raspbian repositories contain a emvery/em tiny subset of the Debian respositories. This means that your software choice is reduced by a lot and, given that I wanted this to be a &#34;set it and forget it&#34; type of media server, compiling and updating programs from source was mostly out of the question (bespecially/b when that software was available in Debian!). I decided to go on an adventure and convert Raspbian to Debian proper.&#xA;&#xA;Note: I did this a while ago, so I may be missing some steps. Please let me know in the comments and I will update it with more details if need be!&#xA;&#xA;!--more--&#xA;&#xA;First, I noticed that the architecture Raspbian refers to as codearmhf/code is actually an amalgamation of both codearmel/code and codearmhf/code in Debian. That is, bjust adding the Debian repositories and upgrading will probably break your system/b. This was the first issue I ran into when I naïvely just edited code/etc/apt/sources.list/code to include codedeb http://ftp.us.debian.org/debian/ sid main contrib non-free/code. When I ran codesudo apt update &amp;&amp; sudo apt dist-upgrade/code, everything completely broke.&#xA;&#xA;This meant I needed to switch the architecture from codearmhf/code to codearmel/code. The first step to doing this was the following:&#xA;Run codesudo apt update &amp;&amp; sudo apt dist-upgrade/code. You want a clean, fully upgraded state to start from.&#xA;In code/etc/apt/sources.list/code, change codedeb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi/code to codedeb [arch=armhf] http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi/code.&#xA;In the same file, add the line codedeb [arch=armel] http://ftp.us.debian.org/debian/ sid main contrib non-free/code.&#xA;In code/etc/apt/sources.list.d/raspi.list/code, change codedeb http://archive.raspberrypi.org/debian/ buster main ui/code to codedeb [arch=armhf] http://archive.raspberrypi.org/debian/ buster main ui/code.&#xA;Run codesudo dpkg --add-architecture armel/code.&#xA;Run codesudo apt update/code.&#xA;Here&#39;s the emreally/em messy part. You need to slowly migrate every package you can from the codearmhf/code version in Raspbian to codearmel/code in Debian sid. This involves running codesudo apt install package-name:armel/code and manually resolving dependencies. Do bnot/b run codesudo apt upgrade/code because, at this stage, the dependency resolver will probably just choke. My advice here is to start with core libraries and then slowly move to bigger and bigger packages. Once you&#39;ve switched codedpkg/code and codeapt/code over, it definitely gets easier, but it&#39;s still a tedious process.&#xA;Once you&#39;ve done this, you can now use codesudo apt update/code and stuff as normal and it should &#34;Just Work&#34;™.&#xA;&#xA;An alternative to this horrifically messy process is to flash a href=&#34;https://f002.backblazeb2.com/file/chiraag-public/rpi-backup.img&#34;this image/a instead of the default Raspbian image. It bis/b out of date, so you&#39;ll have a large system update to run after flashing it, but it should save you the horrifically messy transition.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>So a while back, I decided to get a Raspberry Pi to setup as a UPnP media server for my local network (<em>not</em> accessible from outside the network). Of course, the standard procedure is to install Raspbian, which is a customized Debian-based distribution that works well with the Raspberry Pi.</p>

<p>However, this presents an annoyance: the Raspbian repositories contain a <em>very</em> tiny subset of the Debian respositories. This means that your software choice is reduced by a lot and, given that I wanted this to be a “set it and forget it” type of media server, compiling and updating programs from source was mostly out of the question (<b>especially</b> when that software was available in Debian!). I decided to go on an adventure and convert Raspbian to Debian proper.</p>

<p>Note: I did this a while ago, so I may be missing some steps. Please let me know in the comments and I will update it with more details if need be!</p>



<p>First, I noticed that the architecture Raspbian refers to as <code>armhf</code> is actually an amalgamation of both <code>armel</code> and <code>armhf</code> in Debian. That is, <b>just adding the Debian repositories and upgrading will probably break your system</b>. This was the first issue I ran into when I naïvely just edited <code>/etc/apt/sources.list</code> to include <code>deb <a href="http://ftp.us.debian.org/debian/">http://ftp.us.debian.org/debian/</a> sid main contrib non-free</code>. When I ran <code>sudo apt update &amp;&amp; sudo apt dist-upgrade</code>, everything completely broke.</p>

<p>This meant I needed to switch the architecture from <code>armhf</code> to <code>armel</code>. The first step to doing this was the following:
1. Run <code>sudo apt update &amp;&amp; sudo apt dist-upgrade</code>. You want a clean, fully upgraded state to start from.
2. In <code>/etc/apt/sources.list</code>, change <code>deb <a href="http://raspbian.raspberrypi.org/raspbian/">http://raspbian.raspberrypi.org/raspbian/</a> buster main contrib non-free rpi</code> to <code>deb [arch=armhf] <a href="http://raspbian.raspberrypi.org/raspbian/">http://raspbian.raspberrypi.org/raspbian/</a> buster main contrib non-free rpi</code>.
3. In the same file, add the line <code>deb [arch=armel] <a href="http://ftp.us.debian.org/debian/">http://ftp.us.debian.org/debian/</a> sid main contrib non-free</code>.
4. In <code>/etc/apt/sources.list.d/raspi.list</code>, change <code>deb <a href="http://archive.raspberrypi.org/debian/">http://archive.raspberrypi.org/debian/</a> buster main ui</code> to <code>deb [arch=armhf] <a href="http://archive.raspberrypi.org/debian/">http://archive.raspberrypi.org/debian/</a> buster main ui</code>.
5. Run <code>sudo dpkg —add-architecture armel</code>.
6. Run <code>sudo apt update</code>.
7. Here&#39;s the <em>really</em> messy part. You need to slowly migrate every package you can from the <code>armhf</code> version in Raspbian to <code>armel</code> in Debian sid. This involves running <code>sudo apt install :armel</code> and manually resolving dependencies. Do <b>not</b> run <code>sudo apt upgrade</code> because, at this stage, the dependency resolver will probably just choke. My advice here is to start with core libraries and then slowly move to bigger and bigger packages. Once you&#39;ve switched <code>dpkg</code> and <code>apt</code> over, it definitely gets easier, but it&#39;s still a tedious process.
8. Once you&#39;ve done this, you can now use <code>sudo apt update</code> and stuff as normal and it should “Just Work”™.</p>

<p>An alternative to this horrifically messy process is to flash <a href="https://f002.backblazeb2.com/file/chiraag-public/rpi-backup.img">this image</a> instead of the default Raspbian image. It <b>is</b> out of date, so you&#39;ll have a large system update to run after flashing it, but it should save you the horrifically messy transition.</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/converting-raspbian-to-debian-sid</guid>
      <pubDate>Sun, 28 Jul 2019 07:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Privacy and security on the modern web</title>
      <link>https://blog.chiraag.me/privacy-and-security-on-the-modern-web</link>
      <description>&lt;![CDATA[The modern web is fantastic &amp;mdash; it offers super advanced functionality and APIs to the point where Progressive Web Apps are actually plausible replacements for full-blown native programs (although I personally am against them, it&#39;s clear that they satisfy the use cases for the vast majority of users, given their popularity).&#xA;&#xA;But the modern web is also a privacy nightmare. Trackers, analytics, you name it, it&#39;s there. Javascript APIs to sniff location, bluetooth state, embattery state/em &amp;mdash; it&#39;s ludicrous. Browser fingerprinting, where the interested party uses attributes of your browser (such as screen size, color depth, operating system, and so on) to track you (possibly emwithout cookies/em), is more of a concern than ever. All of this on top of the fact that embrowser makers themselves/em have access to a wealth of information about the user, something that doesn&#39;t escape the notice of e.g. Google or Microsoft.&#xA;&#xA;So what can we do? What can we, the humble users of these products, do to protect ourselves from all this tracking? Well&amp;hellip;as someone who&#39;s been trying to fight this stuff since I learned about it, I&#39;ve come up with a couple of tricks, which seem to work fairly well.&#xA;&#xA;!--more--&#xA;&#xA;Of course, standard disclaimer: Please take this with a grain of salt! There may be things I&#39;m missing and haven&#39;t thought about. If you&#39;re in a situation where a slip-up may cost you your life, emplease seek out solutions built for your specific situation/em &amp;mdash; this is more for the general user who cares about privacy but doesn&#39;t face dire consequences should they be unmasked.&#xA;&#xA;ol&#xA;li&#xA;strongUse Firefox/strong. It&#39;s by far the most privacy-respecting browser out there of the mainstream browsers and they&#39;ve been integrating some awesome stuff from Tor (via the &#34;Tor Uplift&#34;) that we&#39;ll cover in later points. Some of the later mitigation techniques emrely/em on Firefox (or a fork which implements those points). See my note about Chromium-based privacy browsers at the end for more information.&#xA;/li&#xA;li&#xA;strongUse a VPN/strong. I won&#39;t go into it here (maybe I&#39;ll do that in a future post), but one of the biggest identifiers you will emalways/em send is your IP address &amp;mdash; it&#39;s how your browser is able to get any websites at all. The problem is that your IP address can easily be tied to a general vicinity (so doing things like disabling the Geolocation API as we do below won&#39;t really help, since they can just look up your IP address). Using a VPN (look for a strongno-logging/strong VPN) can help by effectively disguising your location. As a bonus, using one that is fairly popular means that your IP address becomes mostly worthless, at least on its own. And since that&#39;s largely the one identifier you emhave/em to send, masking it effectively is emcrucial/em to a proper privacy routine.&#xA;/li&#xA;li&#xA;strongDisable various Javascript APIs/strong. In Firefox, you can disable the following APIs by going to kbdabout:config/kbd:&#xA;ul&#xA;liBeacon API (Set kbdbeacon.enabled/kbd to kbdfalse/kbd)/li&#xA;liBattery API (Set kbddom.battery.enabled/kbd to kbdfalse/kbd)/li&#xA;liPerformance API (Set kbddom.enableperformance/kbd to kbdfalse/kbd)/li&#xA;liResource Timing API (Set kbddom.enableresourcetiming/kbd to kbdfalse/kbd)/li&#xA;liClipboard events API (Set kbddom.event.clipboardevents.enabled/kbd to kbdfalse/kbd)/li&#xA;liContext menu event (Set kbddom.event.contextmenu.enabled/kbd to kbdfalse/kbd)/li&#xA;liHigh resolution timestamp (Set kbddom.event.hirestimestamp.enabled/kbd to kbdfalse/kbd)/li&#xA;liAsynchronous Clipboard API (Set kbddom.event.asyncClipboard.enabled/kbd to kbdfalse/kbd)/li&#xA;liFileHandle API (Set kbddom.event.filehandle.enabled/kbd to kbdfalse/kbd)/li&#xA;liGamepad API (Set kbddom.gamepad.enabled/kbd, kbddom.gamepad.extensions.enabled/kbd, and kbddom.gamepad.hapticfeedback.enabled/kbd to kbdfalse/kbd)/li&#xA;liIndexedDB API (Set kbddom.indexedDB.enabled/kbd to kbdfalse/kbd)/li&#xA;liPointer Lock API (Set kbddom.pointer-lock.enabled/kbd to kbdfalse/kbd)/li&#xA;liService Worker API (Set kbddom.serviceWorkers.enabled/kbd to kbdfalse/kbd)/li&#xA;lidelStorage API (Set kbddom.storage.enabled/kbd to kbdfalse/kbd)/delinsLeave this one enabled! Disabling it tends to break many websites/ins/li&#xA;liVibration API (Set kbddom.vibrator.enabled/kbd to kbdfalse/kbd)/li&#xA;liOculus VR API (Set kbddom.vr.oculus.enabled/kbd and kbddom.vr.oculus.invisible.enabled/kbd to kbdfalse/kbd)/li&#xA;liWebAudio API (Set kbddom.webaudio.enabled/kbd to kbdfalse/kbd)/li&#xA;liNotifications API (Set kbddom.webnotifications.enabled/kbd to kbdfalse/kbd)/li&#xA;liWindow.event API (Set kbddom.window.event.enabled/kbd to kbdfalse/kbd)/li&#xA;liGeolocation API (Set kbdgeo.enabled/kbd to kbdfalse/kbd)/li&#xA;liWeb Speech API (Set kbdmedia.webspeech.synth.enabled/kbd to kbdfalse/kbd)/li&#xA;liWebGL (Set kbdwebgl.disabled/kbd and kbdwebl.disable-webgl/kbd to kbdtrue/kbd)/li&#xA;/ul&#xA;There are more preferences for other APIs which I did not include as they are disabled by default, and some of these may break some websites or your particular workflow, so play around with enabling and disabling these APIs until you reach a state that works for you.&#xA;/li&#xA;li&#xA;strongEnable Contextual Identities (Containers)/strong. In order to prevent various methods of client-side tracking (think: cookies, local storage, etc), enable and emuse/em Firefox Containers. It is basically a generalization of Private Browsing mode, where each container has its own set of cookies, local storage, and so on. Containers are built-in to more recent versions of Firefox, but to enable them and use them effectively, you should install a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Multi-Account Containers/a. Using that extension, you can create new containers, open a tab in a specific container, and so on. A great companion extension is a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/temporary-containers/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Temporary Containers/a, which can be set up to automatically create and delete containers based on certain rules. For maxmium privacy, enable &#34;Deletes History&#34; Temporary Containers (in &#34;Advanced&#34;) and tell the extension to isolate subdomains. strongWarning/strong: This emwill/em break logins on several webpages, so either be prepared to create exceptions for certain websites or have a separate profile for logging into sites.&#xA;/li&#xA;li&#xA;strongEnable First-Party Isolation/strong. First-Party isolation automates (to some extent) the segregation provided by Containers and can act as another layer of defense, emespecially/em in the case that you don&#39;t use Temporary Containers or another extension like it to automate the segregation. Imagine that sites A and B both have a Facebook tracker embedded. When Facebook stores a cookie through site A, the cookie will be called kbdA.facebook.com/kbd. When Facebook stores a cookie through site B, the cookie will be called kbdB.facebook.com/kbd. This is a very simplistic explanation, but it hopefully helps get the point across of the value of First-Party Isolation &amp;mdash; it helps ensure that common third parties (read: Facebook, Google, Amazon) cannot easily track you across sites &amp;mdash; emeven/em if they&#39;re all loaded in the same container. To enable First-Party Isolation, go to kbdabout:config/kbd and set kbdprivacy.firstparty.isolate/kbd to kbdtrue/kbd.&#xA;/li&#xA;li&#xA;p&#xA;strongEnable fingerprinting resistance/strong. This preference helps ensure that your computer looks &#34;the same&#34; as any other computer using this preference. That is, it tries to help foil the browser fingerprinting techniques discussed in the opening of this post. Keep in mind that this emmay/em cause issues with certain websites, and unfortunately there is no easy way to disable the preference on a specific site. That being said, I personally have not run into too many issues when browsing with this preference enabled. To enable fingerprinting resistance, go to kbdabout:config/kbd and set kbdprivacy.resistFingerprinting/kbd to kbdtrue/kbd. If you want to be notified of websites attempting to read your canvas data (the HTML5 Canvas is yet emanother/em fingerprinting mechanism, so fingerprinting resistance obfuscates your canvas data), you should also set kbdprivacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts/kbd to kbdfalse/kbd &amp;mdash; this can help if something like WhatsApp Web (which generates a QR code) silently fails.&#xA;/p&#xA;p&#xA;strongNote/strong: This preference emcan/em break some things. Most notably, it may prevent you from installing certain extensions the usual way (e.g. by clicking on the &#34;Add to Firefox&#34; button on addons.mozilla.org. You can get around this two ways:&#xA;/p&#xA;ul&#xA;liTemporarily disable the preference, install the extension, and toggle it back./li&#xA;liRight click on the &#34;Add to Firefox&#34; button and click &#34;Inspect Element&#34;. Copy the kbdhref/kbd attribute and paste it &amp;mdash; this will bypass the user agent check that addons.mozilla.org does and allow you to install the extension./li&#xA;/ul&#xA;p&#xA;That being said, emmost/em extensions (in my experience) install just fine even when fingerprinting resistance is enabled.&#xA;/p&#xA;/li&#xA;li&#xA;strongEnable built-in tracking protection/strong. It&#39;s useful to have a decent content blocker enabled as a fallback to when you need to disable some of the extensions below (e.g. uBlock Origin or uMatrix) because they&#39;re too aggressive (it happens rarely, but it emdoes/em happen). To enable this, go to kbdabout:preferences/kbd and select the Privacy tab. You should enable Firefox&#39;s built-in tracking protection using the strict list in all windows (select &#34;Custom&#34; and tick the appropriate boxes). You can also tell Firefox to reject all third-party cookies as well to at least get emsome/em additional privacy if you&#39;re not going to use Cookie AutoDelete. If you&#39;re using Cookie AutoDelete, you can go ahead and uncheck the Cookie box entirely, since having multiple places cookies are handled can get a bit confusing and hard to manage.&#xA;/li&#xA;li&#xA;strongChange your search engine/strong. All of this is useless if you&#39;re still using Google as your search engine! I recommend either a href=&#34;https://startpage.com&#34;Startpage/a or a href=&#34;https://duckduckgo.com&#34;DuckDuckGo/a. In Firefox, you can do this by going to kbdabout:preferences/kbd and clicking on the Search tab.&#xA;/li&#xA;li&#xA;strongInstall privacy-enhancing extensions/strong. While the last few tricks only worked on Firefox (now you can see why I recommend it), this tip should work on both Chromium-based and Firefox-based browsers. The list of extensions I use as well as what they do is given below:&#xA;ul&#xA;listrongCookie AutoDelete/strong: Automatically delete cookies once they&#39;re no longer needed. a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/cookie-autodelete/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listrongDecentraleyes/strong: Intercept and serve scripts and stylesheets from popular content delivery networks (CDNs), thereby ensuring you never connect to them. a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listrongfirewall/strong: Allow editing of user agent, headers, etc. a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/firewall-2017/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listrongHTTPS Everywhere/strong: Easily disable all unencrypted connections (prevents eavesdropping, man-in-the-middle-style attacks). a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/https-everywhere/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listrongPrivacy Possum/strong: Obfuscates data commonly used by trackers (mainly a fallback mechanism if everything else fails). a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listrongRequest Control/strong: Remove tracking parameters from URLs, disable certain classes of resources from loading. a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/requestcontrol/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listrongSkip Redirect/strong: Automatically skip to the final request (useful when you have a tracking URL that redirects to the actual website after logging that you&#39;re visiting it &amp;mdash; see: Google Search). a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/skip-redirect/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listronguBlock Origin/strong: Flexible content blocker (blocks ads, malware, etc). a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/&#34; target=&#34;blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;listronguMatrix/strong: More advanced content blocker (you can block or allow certain requests from certain domains on certain domains &amp;mdash; for example, you can allow facebook to load, but emonly/em on facebook.com (and block it everywhere else)). a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/umatrix/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;Firefox/a/li&#xA;/ul&#xA;/ol&#xA;&#xA;There are probably things I missed &amp;mdash; stuff I did long ago that I set once and completely forgot about. Let me know if I missed something &amp;mdash; if there are additional APIs I should be disabling or additional extensions I should be using.&#xA;&#xA;I&#39;ll also try to keep this updated as additional APIs (and methods to disable them) crop up or as additional privacy-enhancing extensions appear.&#xA;&#xA;emNote/em: Many of the mitigation techniques I describe here (for example, First-Party Isolation, fingerprinting resistance, and containers) are only available on Firefox. While you can obtain some measure of protection through the extensions listed at the end (which exist on Firefox-based and Chromium-based browsers) and while there are emsome/em fingerprinting mitigations available in e.g. a href=&#34;https://github.com/Eloston/ungoogled-chromium/blob/master/docs/flags.md&#34;ungoogled-chromium/a, I have come to the conclusion that if you want privacy, you emneed/em to use something Firefox-based (whether Firefox or a fork/derivative that keeps up with the latest features privacy-wise). There is simply no match for the depth and breadth of features and options available to Firefox users to lock down the browser.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>The modern web is fantastic — it offers super advanced functionality and APIs to the point where Progressive Web Apps are actually plausible replacements for full-blown native programs (although I personally am against them, it&#39;s clear that they satisfy the use cases for the vast majority of users, given their popularity).</p>

<p>But the modern web is also a privacy nightmare. Trackers, analytics, you name it, it&#39;s there. Javascript APIs to sniff location, bluetooth state, <em>battery state</em> — it&#39;s ludicrous. Browser fingerprinting, where the interested party uses attributes of your browser (such as screen size, color depth, operating system, and so on) to track you (possibly <em>without cookies</em>), is more of a concern than ever. All of this on top of the fact that <em>browser makers themselves</em> have access to a wealth of information about the user, something that doesn&#39;t escape the notice of e.g. Google or Microsoft.</p>

<p>So what can we do? What can we, the humble users of these products, do to protect ourselves from all this tracking? Well…as someone who&#39;s been trying to fight this stuff since I learned about it, I&#39;ve come up with a couple of tricks, which seem to work fairly well.</p>



<p>Of course, standard disclaimer: Please take this with a grain of salt! There may be things I&#39;m missing and haven&#39;t thought about. If you&#39;re in a situation where a slip-up may cost you your life, <em>please seek out solutions built for your specific situation</em> — this is more for the general user who cares about privacy but doesn&#39;t face dire consequences should they be unmasked.</p>

<ol><li>
<strong>Use Firefox</strong>. It&#39;s by far the most privacy-respecting browser out there of the mainstream browsers and they&#39;ve been integrating some awesome stuff from Tor (via the &#34;Tor Uplift&#34;) that we&#39;ll cover in later points. Some of the later mitigation techniques <em>rely</em> on Firefox (or a fork which implements those points). See my note about Chromium-based privacy browsers at the end for more information.
</li>
<li>
<strong>Use a VPN</strong>. I won&#39;t go into it here (maybe I&#39;ll do that in a future post), but one of the biggest identifiers you will <em>always</em> send is your IP address — it&#39;s how your browser is able to get any websites at all. The problem is that your IP address can easily be tied to a general vicinity (so doing things like disabling the Geolocation API as we do below won&#39;t really help, since they can just look up your IP address). Using a VPN (look for a <strong>no-logging</strong> VPN) can help by effectively disguising your location. As a bonus, using one that is fairly popular means that your IP address becomes mostly worthless, at least on its own. And since that&#39;s largely the one identifier you <em>have</em> to send, masking it effectively is <em>crucial</em> to a proper privacy routine.
</li>
<li>
<strong>Disable various Javascript APIs</strong>. In Firefox, you can disable the following APIs by going to about:config:
<ul><li>Beacon API (Set beacon.enabled to false)</li>
<li>Battery API (Set dom.battery.enabled to false)</li>
<li>Performance API (Set dom.enable_performance to false)</li>
<li>Resource Timing API (Set dom.enable_resource_timing to false)</li>
<li>Clipboard events API (Set dom.event.clipboardevents.enabled to false)</li>
<li>Context menu event (Set dom.event.contextmenu.enabled to false)</li>
<li>High resolution timestamp (Set dom.event.hirestimestamp.enabled to false)</li>
<li>Asynchronous Clipboard API (Set dom.event.asyncClipboard.enabled to false)</li>
<li>FileHandle API (Set dom.event.filehandle.enabled to false)</li>
<li>Gamepad API (Set dom.gamepad.enabled, dom.gamepad.extensions.enabled, and dom.gamepad.haptic_feedback.enabled to false)</li>
<li>IndexedDB API (Set dom.indexedDB.enabled to false)</li>
<li>Pointer Lock API (Set dom.pointer-lock.enabled to false)</li>
<li>Service Worker API (Set dom.serviceWorkers.enabled to false)</li>
<li><del>Storage API (Set dom.storage.enabled to false)</del><ins>Leave this one enabled! Disabling it tends to break many websites</ins></li>
<li>Vibration API (Set dom.vibrator.enabled to false)</li>
<li>Oculus VR API (Set dom.vr.oculus.enabled and dom.vr.oculus.invisible.enabled to false)</li>
<li>WebAudio API (Set dom.webaudio.enabled to false)</li>
<li>Notifications API (Set dom.webnotifications.enabled to false)</li>
<li>Window.event API (Set dom.window.event.enabled to false)</li>
<li>Geolocation API (Set geo.enabled to false)</li>
<li>Web Speech API (Set media.webspeech.synth.enabled to false)</li>
<li>WebGL (Set webgl.disabled and webl.disable-webgl to true)</li></ul>
There are more preferences for other APIs which I did not include as they are disabled by default, and some of these may break some websites or your particular workflow, so play around with enabling and disabling these APIs until you reach a state that works for you.
</li>
<li>
<strong>Enable Contextual Identities (Containers)</strong>. In order to prevent various methods of client-side tracking (think: cookies, local storage, etc), enable and <em>use</em> Firefox Containers. It is basically a generalization of Private Browsing mode, where each container has its own set of cookies, local storage, and so on. Containers are built-in to more recent versions of Firefox, but to enable them and use them effectively, you should install <a href="https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/" target="_blank">Multi-Account Containers</a>. Using that extension, you can create new containers, open a tab in a specific container, and so on. A great companion extension is <a href="https://addons.mozilla.org/en-US/firefox/addon/temporary-containers/" target="_blank">Temporary Containers</a>, which can be set up to automatically create and delete containers based on certain rules. For maxmium privacy, enable &#34;Deletes History&#34; Temporary Containers (in &#34;Advanced&#34;) and tell the extension to isolate subdomains. <strong>Warning</strong>: This <em>will</em> break logins on several webpages, so either be prepared to create exceptions for certain websites or have a separate profile for logging into sites.
</li>
<li>
<strong>Enable First-Party Isolation</strong>. First-Party isolation automates (to some extent) the segregation provided by Containers and can act as another layer of defense, <em>especially</em> in the case that you don&#39;t use Temporary Containers or another extension like it to automate the segregation. Imagine that sites A and B both have a Facebook tracker embedded. When Facebook stores a cookie through site A, the cookie will be called A.facebook.com. When Facebook stores a cookie through site B, the cookie will be called B.facebook.com. This is a very simplistic explanation, but it hopefully helps get the point across of the value of First-Party Isolation — it helps ensure that common third parties (read: Facebook, Google, Amazon) cannot easily track you across sites — <em>even</em> if they&#39;re all loaded in the same container. To enable First-Party Isolation, go to about:config and set privacy.firstparty.isolate to true.
</li>
<li>
<p>
<strong>Enable fingerprinting resistance</strong>. This preference helps ensure that your computer looks &#34;the same&#34; as any other computer using this preference. That is, it tries to help foil the browser fingerprinting techniques discussed in the opening of this post. Keep in mind that this <em>may</em> cause issues with certain websites, and unfortunately there is no easy way to disable the preference on a specific site. That being said, I personally have not run into too many issues when browsing with this preference enabled. To enable fingerprinting resistance, go to about:config and set privacy.resistFingerprinting to true. If you want to be notified of websites attempting to read your canvas data (the HTML5 Canvas is yet <em>another</em> fingerprinting mechanism, so fingerprinting resistance obfuscates your canvas data), you should also set privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts to false — this can help if something like WhatsApp Web (which generates a QR code) silently fails.
</p>
<p>
<strong>Note</strong>: This preference <em>can</em> break some things. Most notably, it may prevent you from installing certain extensions the usual way (e.g. by clicking on the &#34;Add to Firefox&#34; button on addons.mozilla.org. You can get around this two ways:
</p>
<ul><li>Temporarily disable the preference, install the extension, and toggle it back.</li>
<li>Right click on the &#34;Add to Firefox&#34; button and click &#34;Inspect Element&#34;. Copy the href attribute and paste it — this will bypass the user agent check that addons.mozilla.org does and allow you to install the extension.</li></ul>
<p>
That being said, <em>most</em> extensions (in my experience) install just fine even when fingerprinting resistance is enabled.
</p>
</li>
<li>
<strong>Enable built-in tracking protection</strong>. It&#39;s useful to have a decent content blocker enabled as a fallback to when you need to disable some of the extensions below (e.g. uBlock Origin or uMatrix) because they&#39;re too aggressive (it happens rarely, but it <em>does</em> happen). To enable this, go to about:preferences and select the Privacy tab. You should enable Firefox&#39;s built-in tracking protection using the strict list in all windows (select &#34;Custom&#34; and tick the appropriate boxes). You can also tell Firefox to reject all third-party cookies as well to at least get <em>some</em> additional privacy if you&#39;re not going to use Cookie AutoDelete. If you&#39;re using Cookie AutoDelete, you can go ahead and uncheck the Cookie box entirely, since having multiple places cookies are handled can get a bit confusing and hard to manage.
</li>
<li>
<strong>Change your search engine</strong>. All of this is useless if you&#39;re still using Google as your search engine! I recommend either <a href="https://startpage.com">Startpage</a> or <a href="https://duckduckgo.com">DuckDuckGo</a>. In Firefox, you can do this by going to about:preferences and clicking on the Search tab.
</li>
<li>
<strong>Install privacy-enhancing extensions</strong>. While the last few tricks only worked on Firefox (now you can see why I recommend it), this tip should work on both Chromium-based and Firefox-based browsers. The list of extensions I use as well as what they do is given below:
<ul><li><strong>Cookie AutoDelete</strong>: Automatically delete cookies once they&#39;re no longer needed. <a href="https://addons.mozilla.org/en-US/firefox/addon/cookie-autodelete/" target="_blank">Firefox</a></li>
<li><strong>Decentraleyes</strong>: Intercept and serve scripts and stylesheets from popular content delivery networks (CDNs), thereby ensuring you never connect to them. <a href="https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/" target="_blank">Firefox</a></li>
<li><strong>firewall</strong>: Allow editing of user agent, headers, etc. <a href="https://addons.mozilla.org/en-US/firefox/addon/firewall-2017/" target="_blank">Firefox</a></li>
<li><strong>HTTPS Everywhere</strong>: Easily disable all unencrypted connections (prevents eavesdropping, man-in-the-middle-style attacks). <a href="https://addons.mozilla.org/en-US/firefox/addon/https-everywhere/" target="_blank">Firefox</a></li>
<li><strong>Privacy Possum</strong>: Obfuscates data commonly used by trackers (mainly a fallback mechanism if everything else fails). <a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/" target="_blank">Firefox</a></li>
<li><strong>Request Control</strong>: Remove tracking parameters from URLs, disable certain classes of resources from loading. <a href="https://addons.mozilla.org/en-US/firefox/addon/requestcontrol/" target="_blank">Firefox</a></li>
<li><strong>Skip Redirect</strong>: Automatically skip to the final request (useful when you have a tracking URL that redirects to the actual website after logging that you&#39;re visiting it — see: Google Search). <a href="https://addons.mozilla.org/en-US/firefox/addon/skip-redirect/" target="_blank">Firefox</a></li>
<li><strong>uBlock Origin</strong>: Flexible content blocker (blocks ads, malware, etc). <a href="https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/" target="_blank">Firefox</a></li>
<li><strong>uMatrix</strong>: More advanced content blocker (you can block or allow certain requests from certain domains on certain domains — for example, you can allow facebook to load, but <em>only</em> on facebook.com (and block it everywhere else)). <a href="https://addons.mozilla.org/en-US/firefox/addon/umatrix/" target="_blank">Firefox</a></li></ul>
</ol>

<p>There are probably things I missed — stuff I did long ago that I set once and completely forgot about. Let me know if I missed something — if there are additional APIs I should be disabling or additional extensions I should be using.</p>

<p>I&#39;ll also try to keep this updated as additional APIs (and methods to disable them) crop up or as additional privacy-enhancing extensions appear.</p>

<p><em>Note</em>: Many of the mitigation techniques I describe here (for example, First-Party Isolation, fingerprinting resistance, and containers) are only available on Firefox. While you can obtain some measure of protection through the extensions listed at the end (which exist on Firefox-based and Chromium-based browsers) and while there are <em>some</em> fingerprinting mitigations available in e.g. <a href="https://github.com/Eloston/ungoogled-chromium/blob/master/docs/flags.md">ungoogled-chromium</a>, I have come to the conclusion that if you want privacy, you <em>need</em> to use something Firefox-based (whether Firefox or a fork/derivative that keeps up with the latest features privacy-wise). There is simply no match for the depth and breadth of features and options available to Firefox users to lock down the browser.</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/privacy-and-security-on-the-modern-web</guid>
      <pubDate>Fri, 17 May 2019 07:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Divestment, Israel, and Apartheid</title>
      <link>https://blog.chiraag.me/divestment-israel-and-apartheid</link>
      <description>&lt;![CDATA[Before you read further, I want you, the reader, to head over to a href=&#34;https://www.reddit.com/r/socialism/comments/azfjpl/israelisthenationstateofjewsalonenetanyahu/ei7rpdm/&#34;this comment on Reddit/a which details, with nauseating clarity, just emhow many times/em unarmed Palestinians have been killed by the IDF, Israeli police officers, or other agents of the state (with sources). Go ahead, I&#39;ll wait.&#xA;&#xA;It has always been clear to me that the Israeli government actively wants to kill and displace Palestinians and that they have treated them as second-class citizens. Archbishop Desmond Tutu a href=&#34;http://www.pcusa.org/sitemedia/media/uploads/oga/pdf/2014-journal%2C_parti-electronic.pdf&#34;said/a:&#xA;&#xA;blockquote&#xA;I have been to the Occupied Palestinian Territory, and I have witnessed the racially segregated roads and housing that reminded me so much of the conditions we experienced in South Africa under the racist system of Apartheid. I have witnessed the humiliation of Palestinian men, women, and children made to wait hours at Israeli military checkpoints routinely when trying to make the most basic of trips to visit relatives or attend school or college, and this humiliation is familiar to me and the many black South Africans who were corralled and regularly insulted by the security forces of the Apartheid government. It is not with rancor that we criticize the Israeli government, but with hope, a hope that a better future can be made for both Israelis and Palestinians.&#xA;/blockquote&#xA;&#xA;The UN has a href=&#34;https://www.un.org/press/en/2016/sc12657.doc.htm&#34;criticised/a Israeli settlements as illegal, saying:&#xA;&#xA;blockquote&#xA;Israel’s establishment of settlements in Palestinian territory occupied since 1967, including East Jerusalem, had no legal validity, constituting a flagrant violation under international law and a major obstacle to the vision of two States living side-by-side in peace and security, within internationally recognized borders.&#xA;/blockquote&#xA;&#xA;and reiterated its demand that &#34;Israel immediately and completely cease all settlement activities in the occupied Palestinian territory, including East Jerusalem.&#34;&#xA;&#xA;!--more--&#xA;&#xA;When Archbishop Tutu (who has lived through South African apartheid) is calling something apartheid and the UN is saying it is a flagrant violation of international law, it seems extremely odd to me that people would spend political (and literal) capital defending that system.&#xA;&#xA;Assuming these people are arguing in good faith, it&#39;s quite interesting that they spend so much political capital defending the murder of unarmed Palestinians or the building of illegal settlements. The first emshould/em be indefensible and the second emshould/em be obviously wrong, yet we have a political establishment which tries to do whatever it can to defend Israel. Why?&#xA;&#xA;These people constantly provide Israel with military aid and technology, which is used in service of their apartheid regime. They run a free PR campaign for them when they launch a ground assault and murder civilians. They defend them during all of these transgressions, these morally indefensible acts.&#xA;&#xA;Through all of this, what can we, as individual citizens do? Well...in the middle of all of this violence, this violent suppression of Palestinians, several well-known companies, such as HP and Motorola, enable the IDF and the rest of the Israeli state in committing their crimes against humanity.&#xA;&#xA;Divesting from these companies (and calling on others to do likewise) sends a signal to these companies that, indeed, their enabling of Israeli apartheid is not unnoticed and that, moreover, there is a emfinancial/em penalty they will pay should they choose to continue enabling the IDF and the rest of the Israeli state.&#xA;&#xA;Until and unless the Israeli state stops its murder and displacement of Palestinians in service of its apartheid regime, we should do what we can to push the institutions we are part of to divest from the companies complicit in these heinous acts. Only by introducing a financial penalty will we see a material change in the currently deplorable state of affairs, and only emthen/em may we obtain real peace.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Before you read further, I want you, the reader, to head over to <a href="https://www.reddit.com/r/socialism/comments/azfjpl/israel_is_the_nationstate_of_jews_alone_netanyahu/ei7rpdm/">this comment on Reddit</a> which details, with nauseating clarity, just <em>how many times</em> unarmed Palestinians have been killed by the IDF, Israeli police officers, or other agents of the state (with sources). Go ahead, I&#39;ll wait.</p>

<p>It has always been clear to me that the Israeli government actively wants to kill and displace Palestinians and that they have treated them as second-class citizens. Archbishop Desmond Tutu <a href="http://www.pcusa.org/site_media/media/uploads/oga/pdf/2014-journal%2C_parti-electronic.pdf">said</a>:</p>

<blockquote>I have been to the Occupied Palestinian Territory, and I have witnessed the racially segregated roads and housing that reminded me so much of the conditions we experienced in South Africa under the racist system of Apartheid. I have witnessed the humiliation of Palestinian men, women, and children made to wait hours at Israeli military checkpoints routinely when trying to make the most basic of trips to visit relatives or attend school or college, and this humiliation is familiar to me and the many black South Africans who were corralled and regularly insulted by the security forces of the Apartheid government. It is not with rancor that we criticize the Israeli government, but with hope, a hope that a better future can be made for both Israelis and Palestinians.
</blockquote>

<p>The UN has <a href="https://www.un.org/press/en/2016/sc12657.doc.htm">criticised</a> Israeli settlements as illegal, saying:</p>

<blockquote>Israel’s establishment of settlements in Palestinian territory occupied since 1967, including East Jerusalem, had no legal validity, constituting a flagrant violation under international law and a major obstacle to the vision of two States living side-by-side in peace and security, within internationally recognized borders.
</blockquote>

<p>and reiterated its demand that “Israel immediately and completely cease all settlement activities in the occupied Palestinian territory, including East Jerusalem.”</p>



<p>When Archbishop Tutu (who has lived through South African apartheid) is calling something apartheid and the UN is saying it is a flagrant violation of international law, it seems extremely odd to me that people would spend political (and literal) capital defending that system.</p>

<p>Assuming these people are arguing in good faith, it&#39;s quite interesting that they spend so much political capital defending the murder of unarmed Palestinians or the building of illegal settlements. The first <em>should</em> be indefensible and the second <em>should</em> be obviously wrong, yet we have a political establishment which tries to do whatever it can to defend Israel. Why?</p>

<p>These people constantly provide Israel with military aid and technology, which is used in service of their apartheid regime. They run a free PR campaign for them when they launch a ground assault and murder civilians. They defend them during all of these transgressions, these morally indefensible acts.</p>

<p>Through all of this, what can we, as individual citizens do? Well...in the middle of all of this violence, this violent suppression of Palestinians, several well-known companies, such as HP and Motorola, enable the IDF and the rest of the Israeli state in committing their crimes against humanity.</p>

<p>Divesting from these companies (and calling on others to do likewise) sends a signal to these companies that, indeed, their enabling of Israeli apartheid is not unnoticed and that, moreover, there is a <em>financial</em> penalty they will pay should they choose to continue enabling the IDF and the rest of the Israeli state.</p>

<p>Until and unless the Israeli state stops its murder and displacement of Palestinians in service of its apartheid regime, we should do what we can to push the institutions we are part of to divest from the companies complicit in these heinous acts. Only by introducing a financial penalty will we see a material change in the currently deplorable state of affairs, and only <em>then</em> may we obtain real peace.</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/divestment-israel-and-apartheid</guid>
      <pubDate>Mon, 06 May 2019 07:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Encrypted, incremental backups to the cloud</title>
      <link>https://blog.chiraag.me/encrypted-incremental-backups-to-the-cloud</link>
      <description>&lt;![CDATA[A couple of weeks ago, my laptop&#39;s hard drive died and I ended up purchasing a new one. I hadn&#39;t taken a backup in about 3 weeks, so I lost some progress on my research. This got me thinking about having a backup method with less friction than my current one.&#xA;&#xA;My existing backup routine consists of plugging in my external hard drive, mounting it, running my backup script, taking a full image of my boot drive, and unmounting it. Not entirely awful (most of it is automated and it&#39;s mostly just waiting), but there&#39;s still friction around the initial steps of plugging in the drive and mounting it. Additionally, taking a full system image means having a read/write LVM snapshot (which becomes an issue if it is completely filled), which means I need to sit and wait around for the system image to finish — if I leave it for too long, the writes to the snapshot will accumulate, the snapshot will fill up, and bad things will happen.&#xA;&#xA;I have long resisted backing up to the cloud due to the inherent privacy and security issues at stake. As the saying goes, the &#34;cloud&#34; is just someone else&#39;s computer, and storing your files there is giving them access should they want it. You&#39;re also at their mercy regarding security practices - if they&#39;re breached, byour/b data is at stake. At the same time, the golden rule for backups is 3-2-1: 3 total copies of your data, 2 on-site and 1 off-site. The 2 on-site copies are satisfied by my laptop and external hard drive. But the off-site version was not something I had ever seriously considered.&#xA;&#xA;After this latest incident, I decided to investigate inexpensive cloud storage providers — given that my biggest usage will be storage (and I hope I never have to download the data), I don&#39;t mind some tiny charges on downloads and other transactions. I found a href=&#34;https://backblaze.com/b2/cloud-storage.html&#34;Backblaze B2/a, which offers storage for b$0.005/GB/month/b, or $5/TB/month, after the first 10 GB free. While there emare/em other charges (for example, $0.01/GB download charges after 1 GB free per day), my main concern is storage charges.&#xA;&#xA;!--more--&#xA;&#xA;My next concern was privacy and security. Unlike their &#34;Personal Backup&#34; solution (which offers client-side encryption), B2 is just a set of servers waiting to receive files. They offer extensive APIs to allow third-party software to integrate with it, and I&#39;ll come back to that later. Fundamentally, though, I realized that if I was going to have client-side encryption, I&#39;d have to deal with it myself.&#xA;&#xA;My main requirements were as follows:&#xA;ul&#xA;liEncryption with GPG — I already use many other tools that integrate seamlessly with GPG, and managing yet emanother/em set of keys simply for retrieving my backups is impractical./li&#xA;liObfuscation of file and directory structure with a way to restore it — Just as important as the file emcontents/em is the file emmetadata/em — how large it is, what type of file it is, how many other files are like it in that directory, and so on. Even if the filenames and directory names are obfuscated, if the directory hierarchy is left intact, it leaks tons of metadata./li&#xA;liCompression — This one should be obvious — I&#39;m paying per GB, so I better compress stuff to minimize storage costs./li&#xA;liThe state of the backup should be recoverable — I should be able to download files from my last backup and continue my backup where I left off./li&#xA;/ul&#xA;&#xA;There are many different backup programs which fit emsome/em of these requirements, but none seemed to fit emall/em. Some encrypt file and directory names, but leave the hierarchy intact. Others use GPG and tar to effectively obscure the file and directory structure, but I&#39;d still have to manage the names of those tar archives to obfuscate them while still allowing me to determine which directory it is a backup of.&#xA;&#xA;After looking for a bit, I decided to roll my own script to deal with this problem. The program is called &#34;bkup&#34; and can be found at a href=&#34;https://gitlab.com/chiraag-nataraj/bkup&#34;https://gitlab.com/chiraag-nataraj/bkup/a. The design is as follows:&#xA;&#xA;ul&#xA;liWhen you pass a directory to bkup to back up, it first resolves the path of the directory relative to code$HOME/code and creates that directory hierarchy under code~/.local/share/bkup//code and code~/.cache/bkup//code. Let&#39;s call the first directory code$datadir/code for short and let&#39;s call the other one code$cachedir/code./li&#xA;liIt then generates a salt (code$salt/code) and writes it to code$datadir/.salt.gpg/code. At the same time, it writes the value of code$datadir/code to code$datadir/.name.gpg/code. Both of these files are also hard linked to code$cachedir/code. /li&#xA;liThe encrypted name of the archive is derived from the SHA1 sum of code$datadir/code and code$salt/code. This structure means that once the salt is written once and as long as it is not erased, the encrypted name of the archive will be stable. At the same time, one can always change the encrypted name by merely deleting code$datadir/.salt.gpg/code./li&#xA;liThe program then proceeds to generate the archive. As this is the first time the program is being run on this directory, the program finds all files created after epoch and adds them to a giant tar archive. This tar archive is then passed through zstsd (a compressor) and subsequently split into 50MB chunks (configurable by setting a variable in the script). Finally, each chunk is encrypted before being written out to code$cachedir/$hash-$date.tar.zst.nnnnnn.gpg/code, where nnnnnn are the 6 suffix digits indicating the number of the chunk./li&#xA;liOnce the backup finishes successfully (which can take a long time during the initial run), it writes out the date and time at which the backup started to code$datadir/.date.gpg/code and hard links it to code$cachedir/.date.gpg/code./li&#xA;liOn subsequent runs, bkup will read the salt from code$datadir/.salt.gpg/code and the date of the last backup from code$datadir/.date.gpg/code. It will then only look for files modified after that date and time, ensuring that backups after the first one are incremental./li&#xA;/ul&#xA;&#xA;The design of this program ensures that once you complete an initial backup and push it to the cloud, you emnever/em have to do a full backup again. You can merely download the code.date.gpg/code, code.salt.gpg/code, and code.name.gpg/code files from your backup to determine which hash corresponds to which directory, recreate the proper hierarchy within code~/.local/share/bkup/code, and you&#39;re good to go. This is emespecially/em important when you pay to download large quantities of files (as is the case with B2) - being able to minimize the amount of data you need to download in order to figure out which folder is what is quite convenient.&#xA;&#xA;The syntax and an example run are provided in the README of the gitlab repository, which should hopefully clear up any confusions you may have.&#xA;&#xA;I have started using this program already and am in the middle of completing my initial backup to B2. Once I have done that, I will probably set up automatic incremental backups and push them to the cloud with a href=&#34;https://rclone.org&#34;rclone/a (an emamazing/em piece of software).]]&gt;</description>
      <content:encoded><![CDATA[<p>A couple of weeks ago, my laptop&#39;s hard drive died and I ended up purchasing a new one. I hadn&#39;t taken a backup in about 3 weeks, so I lost some progress on my research. This got me thinking about having a backup method with less friction than my current one.</p>

<p>My existing backup routine consists of plugging in my external hard drive, mounting it, running my backup script, taking a full image of my boot drive, and unmounting it. Not entirely awful (most of it is automated and it&#39;s mostly just waiting), but there&#39;s still friction around the initial steps of plugging in the drive and mounting it. Additionally, taking a full system image means having a read/write LVM snapshot (which becomes an issue if it is completely filled), which means I need to sit and wait around for the system image to finish — if I leave it for too long, the writes to the snapshot will accumulate, the snapshot will fill up, and bad things will happen.</p>

<p>I have long resisted backing up to the cloud due to the inherent privacy and security issues at stake. As the saying goes, the “cloud” is just someone else&#39;s computer, and storing your files there is giving them access should they want it. You&#39;re also at their mercy regarding security practices – if they&#39;re breached, <b>your</b> data is at stake. At the same time, the golden rule for backups is 3-2-1: 3 total copies of your data, 2 on-site and 1 off-site. The 2 on-site copies are satisfied by my laptop and external hard drive. But the off-site version was not something I had ever seriously considered.</p>

<p>After this latest incident, I decided to investigate inexpensive cloud storage providers — given that my biggest usage will be storage (and I hope I never have to download the data), I don&#39;t mind some tiny charges on downloads and other transactions. I found <a href="https://backblaze.com/b2/cloud-storage.html">Backblaze B2</a>, which offers storage for <b>$0.005/GB/month</b>, or $5/TB/month, after the first 10 GB free. While there <em>are</em> other charges (for example, $0.01/GB download charges after 1 GB free per day), my main concern is storage charges.</p>



<p>My next concern was privacy and security. Unlike their “Personal Backup” solution (which offers client-side encryption), B2 is just a set of servers waiting to receive files. They offer extensive APIs to allow third-party software to integrate with it, and I&#39;ll come back to that later. Fundamentally, though, I realized that if I was going to have client-side encryption, I&#39;d have to deal with it myself.</p>

<p>My main requirements were as follows:
<ul><li>Encryption with GPG — I already use many other tools that integrate seamlessly with GPG, and managing yet <em>another</em> set of keys simply for retrieving my backups is impractical.</li>
<li>Obfuscation of file and directory structure with a way to restore it — Just as important as the file <em>contents</em> is the file <em>metadata</em> — how large it is, what type of file it is, how many other files are like it in that directory, and so on. Even if the filenames and directory names are obfuscated, if the directory hierarchy is left intact, it leaks tons of metadata.</li>
<li>Compression — This one should be obvious — I&#39;m paying per GB, so I better compress stuff to minimize storage costs.</li>
<li>The state of the backup should be recoverable — I should be able to download files from my last backup and continue my backup where I left off.</li></ul></p>

<p>There are many different backup programs which fit <em>some</em> of these requirements, but none seemed to fit <em>all</em>. Some encrypt file and directory names, but leave the hierarchy intact. Others use GPG and tar to effectively obscure the file and directory structure, but I&#39;d still have to manage the names of those tar archives to obfuscate them while still allowing me to determine which directory it is a backup of.</p>

<p>After looking for a bit, I decided to roll my own script to deal with this problem. The program is called “bkup” and can be found at <a href="https://gitlab.com/chiraag-nataraj/bkup">https://gitlab.com/chiraag-nataraj/bkup</a>. The design is as follows:</p>

<ul><li>When you pass a directory to bkup to back up, it first resolves the path of the directory relative to <code>$HOME</code> and creates that directory hierarchy under <code>~/.local/share/bkup/</code> and <code>~/.cache/bkup/</code>. Let&#39;s call the first directory <code>$datadir</code> for short and let&#39;s call the other one <code>$cachedir</code>.</li>
<li>It then generates a salt (<code>$salt</code>) and writes it to <code>$datadir/.salt.gpg</code>. At the same time, it writes the value of <code>$datadir</code> to <code>$datadir/.name.gpg</code>. Both of these files are also hard linked to <code>$cachedir</code>. </li>
<li>The encrypted name of the archive is derived from the SHA1 sum of <code>$datadir</code> and <code>$salt</code>. This structure means that once the salt is written once and as long as it is not erased, the encrypted name of the archive will be stable. At the same time, one can always change the encrypted name by merely deleting <code>$datadir/.salt.gpg</code>.</li>
<li>The program then proceeds to generate the archive. As this is the first time the program is being run on this directory, the program finds all files created after epoch and adds them to a giant tar archive. This tar archive is then passed through zstsd (a compressor) and subsequently split into 50MB chunks (configurable by setting a variable in the script). Finally, each chunk is encrypted before being written out to <code>$cachedir/$hash-$date.tar.zst.nnnnnn.gpg</code>, where nnnnnn are the 6 suffix digits indicating the number of the chunk.</li>
<li>Once the backup finishes successfully (which can take a long time during the initial run), it writes out the date and time at which the backup started to <code>$datadir/.date.gpg</code> and hard links it to <code>$cachedir/.date.gpg</code>.</li>
<li>On subsequent runs, bkup will read the salt from <code>$datadir/.salt.gpg</code> and the date of the last backup from <code>$datadir/.date.gpg</code>. It will then only look for files modified after that date and time, ensuring that backups after the first one are incremental.</li></ul>

<p>The design of this program ensures that once you complete an initial backup and push it to the cloud, you <em>never</em> have to do a full backup again. You can merely download the <code>.date.gpg</code>, <code>.salt.gpg</code>, and <code>.name.gpg</code> files from your backup to determine which hash corresponds to which directory, recreate the proper hierarchy within <code>~/.local/share/bkup</code>, and you&#39;re good to go. This is <em>especially</em> important when you pay to download large quantities of files (as is the case with B2) – being able to minimize the amount of data you need to download in order to figure out which folder is what is quite convenient.</p>

<p>The syntax and an example run are provided in the README of the gitlab repository, which should hopefully clear up any confusions you may have.</p>

<p>I have started using this program already and am in the middle of completing my initial backup to B2. Once I have done that, I will probably set up automatic incremental backups and push them to the cloud with <a href="https://rclone.org">rclone</a> (an <em>amazing</em> piece of software).</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/encrypted-incremental-backups-to-the-cloud</guid>
      <pubDate>Fri, 26 Apr 2019 07:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Taxes on Linux</title>
      <link>https://blog.chiraag.me/taxes-on-linux</link>
      <description>&lt;![CDATA[Taxes have always been an annoyance, at least here in America where the tax code is as long as your intestine (and just as convoluted!). But taxes are bextra/b annoying on Linux due to the scarce availability of tax prep software on the platform. When I first started doing my taxes, I copped out and used a version of Turbo Tax on a Windows virtual machine I had (my parents had TurboTax already, so it was just a matter of installing it). But that year, I resolved to make taxes work on Linux.&#xA;&#xA;Given that my taxes are generally fairly simple (I&#39;m a graduate student...generally it&#39;s just a W-2 and 1099-INT), the biggest hurdle I faced was finding a way to (easily) fill in the PDF forms. Enter codepdftk/code.&#xA;&#xA;a href=&#34;https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/&#34;pdftk/a is a fantastic piece of software for emmany/em reasons. In this case, though, it&#39;s particularly useful because of two commands: codedumpdatafields/code and codefillform/code. You might be able to see where this is going.&#xA;&#xA;!--more--&#xA;&#xA;I wrote up the following (very simple) script to help make my life a bit easier:&#xA;precode class=&#34;language-bash&#34;#!/bin/bash&#xA;&#xA;FILES=( find -iname &#39;*.pdf&#39; | grep -v -- &#39;-filled&#39; | tr &#39; &#39; &#39;&amp;&#39; )&#xA;&#xA;case &#34;$1&#34; in&#xA;    prepare)&#xA;&#x9;for i in ${FILES[@]}&#xA;&#x9;do&#xA;&#x9;    FILE=${i//&amp;/ }&#xA;&#x9;    FIELDS=${FILE%.pdf}.fields&#xA;&#x9;    FDF=${FILE%.pdf}.fdf&#xA;&#x9;    pdftk &#34;$FILE&#34; dumpdatafieldsutf8 output &#34;$FIELDS&#34;&#xA;&#x9;    if [ ! -e &#34;$FDF&#34; ]&#xA;&#x9;    then&#xA;&#x9;&#x9;echo -e &#39;%FDF-1.2\n1 0 obj&lt;/FDF&lt;&lt; /Fields[\n]       \nendobj\ntrailer\n&lt;/Root 1 0 R  \n%%EOF&#39;   &#34;$FDF&#34;&#xA;&#x9;    fi&#x9;&#xA;&#x9;done&#xA;&#x9;;;&#xA;    fill)&#xA;&#x9;for i in ${FILES[@]}&#xA;&#x9;do&#xA;&#x9;    FILE=${i//&amp;/ }&#xA;&#x9;    FDF=${FILE%.pdf}.fdf&#xA;&#x9;    FILLED=${FILE%.pdf}-filled.pdf&#xA;&#x9;    pdftk &#34;$FILE&#34; fillform &#34;$FDF&#34; output &#34;$FILLED&#34;&#xA;&#x9;done&#xA;&#x9;;;&#xA;    final)&#xA;&#x9;for i in ${FILES[@]}&#xA;&#x9;do&#xA;&#x9;    FILE=${i//&amp;/ }&#xA;&#x9;    FDF=${FILE%.pdf}.fdf&#xA;&#x9;    FILLED=${FILE%.pdf}-filled-final.pdf&#xA;&#x9;    pdftk &#34;$FILE&#34; fillform &#34;$FDF&#34; output &#34;$FILLED&#34; flatten&#xA;&#x9;done&#xA;&#x9;;;&#xA;esac&#xA;/code/pre&#xA;&#xA;The script takes one of three arguments (anything else will quietly exit).&#xA;&#xA;ul&#xA;licodeprepare/code generates a codefields/code and codefdf/code file for each PDF file in that directory (that doesn&#39;t have code-filled/code in the name). Of course, it takes care not to emoverwrite/em existing FDF files, so you can drop new PDF files in (as you realize your taxes are getting more complicated) and re-run codeprepare/code without any issues./li&#xA;licodefill/code fills in all PDFs with their corresponding FDF files and outputs to codename-filled.pdf/code./li&#xA;licodefinal/code fills in all PDFs with their corresponding FDF files, emflattens/em it (permanently merges PDF and FDF content together), and outputs to codename-filled-final.pdf/code./li&#xA;/ul&#xA;&#xA;My workflow at this point is something as follows:&#xA;&#xA;ol&#xA;liDownload the basic forms from the IRS (1040 and any schedules I think I may need). It often helps for me to look at my previous year&#39;s returns to figure out which forms I used (this year, they somewhat rearranged some of the forms, but it was still helpful). I save these in a subdirectory called codeOriginal Forms/code (I also save form instructions here)./li&#xA;liMake another subdirectory called codeFilled Forms/code and copy the fillable forms to that directory. The rest of my work is largely done in the codeFilled Forms/code folder./li&#xA;liRun codebuild.sh prepare/code./li&#xA;liUse the code.fields/code file to fill in the FDF file (syntax is just code&lt;/T(FORM ID)/V(VALUE)  /code - start inserting those after code/Fields[/code). This is the annoying part, since often the form fields are given nonsensical names - the IRS is actually decent here, since they (largely) sequentially number them, and some of the form fields have meaningful names./li&#xA;liRun codebuild.sh fill/code and open the code-filled/code PDF in a new window (it will auto-reload if your PDF viewer is any good, which makes everything easier)./li&#xA;liOnce I&#39;m emactually/em done, I run codebuild.sh final/code./li&#xA;/ol&#xA;&#xA;Of course, part of the problem is that to e-file, you will end up using some official software of some sort - I usually use one of the web-based efile providers (much as I loathe the idea of putting this sensitive stuff on their servers...) simply because other options don&#39;t really exist. I still use this method to double-check my federal returns (sometimes the website can be a bit limiting, and you don&#39;t know it until you already know which forms you&#39;ll need) and file my state returns (since for a while, my state situation was actually annoyingly complicated).&#xA;&#xA;This method (of course) will work for emany/em fillable forms - nothing here is actually limited in any way to tax forms. But this workflow tends to work particularly well for me with my taxes, since I can gradually build up my return, adding schedules and forms as I need to.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Taxes have always been an annoyance, at least here in America where the tax code is as long as your intestine (and just as convoluted!). But taxes are <b>extra</b> annoying on Linux due to the scarce availability of tax prep software on the platform. When I first started doing my taxes, I copped out and used a version of Turbo Tax on a Windows virtual machine I had (my parents had TurboTax already, so it was just a matter of installing it). But that year, I resolved to make taxes work on Linux.</p>

<p>Given that my taxes are generally fairly simple (I&#39;m a graduate student...generally it&#39;s just a W-2 and 1099-INT), the biggest hurdle I faced was finding a way to (easily) fill in the PDF forms. Enter <code>pdftk</code>.</p>

<p><a href="https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/">pdftk</a> is a fantastic piece of software for <em>many</em> reasons. In this case, though, it&#39;s particularly useful because of two commands: <code>dump<em>data</em>fields</code> and <code>fill_form</code>. You might be able to see where this is going.</p>



<p>I wrote up the following (very simple) script to help make my life a bit easier:
<pre><code class="language-bash">#!/bin/bash</p>

<p>FILES=( <code>find -iname &#39;*.pdf&#39; | grep -v -- &#39;-filled&#39; | tr &#39; &#39; &#39;&amp;&#39;</code> )</p>

<p>case “$1” in
    prepare)
    for i in ${FILES[@]}
    do
        FILE=${i//&amp;/ }
        FIELDS=${FILE%.pdf}.fields
        FDF=${FILE%.pdf}.fdf
        pdftk “$FILE” dump<em>data</em>fields<em>utf8 output “$FIELDS”
        if [ ! -e “$FDF” ]
        then
        echo -e &#39;%FDF-1.2\n1 0 obj&lt;&gt; &gt;&gt;\nendobj\ntrailer\n&lt;&gt;\n%%EOF&#39; &gt; “$FDF”
        fi<br>
    done
    ;;
    fill)
    for i in ${FILES[@]}
    do
        FILE=${i//&amp;/ }
        FDF=${FILE%.pdf}.fdf
        FILLED=${FILE%.pdf}-filled.pdf
        pdftk “$FILE” fill</em>form “$FDF” output “$FILLED”
    done
    ;;
    final)
    for i in ${FILES[@]}
    do
        FILE=${i//&amp;/ }
        FDF=${FILE%.pdf}.fdf
        FILLED=${FILE%.pdf}-filled-final.pdf
        pdftk “$FILE” fill_form “$FDF” output “$FILLED” flatten
    done
    ;;
esac
</code></pre></p>

<p>The script takes one of three arguments (anything else will quietly exit).</p>

<ul><li><code>prepare</code> generates a <code>fields</code> and <code>fdf</code> file for each PDF file in that directory (that doesn&#39;t have <code>-filled</code> in the name). Of course, it takes care not to <em>overwrite</em> existing FDF files, so you can drop new PDF files in (as you realize your taxes are getting more complicated) and re-run <code>prepare</code> without any issues.</li>
<li><code>fill</code> fills in all PDFs with their corresponding FDF files and outputs to <code>-filled.pdf</code>.</li>
<li><code>final</code> fills in all PDFs with their corresponding FDF files, <em>flattens</em> it (permanently merges PDF and FDF content together), and outputs to <code>-filled-final.pdf</code>.</li></ul>

<p>My workflow at this point is something as follows:</p>

<ol><li>Download the basic forms from the IRS (1040 and any schedules I think I may need). It often helps for me to look at my previous year&#39;s returns to figure out which forms I used (this year, they somewhat rearranged some of the forms, but it was still helpful). I save these in a subdirectory called <code>Original Forms</code> (I also save form instructions here).</li>
<li>Make another subdirectory called <code>Filled Forms</code> and copy the fillable forms to that directory. The rest of my work is largely done in the <code>Filled Forms</code> folder.</li>
<li>Run <code>build.sh prepare</code>.</li>
<li>Use the <code>.fields</code> file to fill in the FDF file (syntax is just <code>&lt;&gt;</code> - start inserting those after <code>/Fields[</code>). This is the annoying part, since often the form fields are given nonsensical names - the IRS is actually decent here, since they (largely) sequentially number them, and some of the form fields have meaningful names.</li>
<li>Run <code>build.sh fill</code> and open the <code>-filled</code> PDF in a new window (it will auto-reload if your PDF viewer is any good, which makes everything easier).</li>
<li>Once I&#39;m <em>actually</em> done, I run <code>build.sh final</code>.</li></ol>

<p>Of course, part of the problem is that to e-file, you will end up using some official software of some sort – I usually use one of the web-based efile providers (much as I loathe the idea of putting this sensitive stuff on their servers...) simply because other options don&#39;t really exist. I still use this method to double-check my federal returns (sometimes the website can be a bit limiting, and you don&#39;t know it until you already know which forms you&#39;ll need) and file my state returns (since for a while, my state situation was actually annoyingly complicated).</p>

<p>This method (of course) will work for <em>any</em> fillable forms – nothing here is actually limited in any way to tax forms. But this workflow tends to work particularly well for me with my taxes, since I can gradually build up my return, adding schedules and forms as I need to.</p>
]]></content:encoded>
      <guid>https://blog.chiraag.me/taxes-on-linux</guid>
      <pubDate>Mon, 15 Apr 2019 07:00:00 +0000</pubDate>
    </item>
  </channel>
</rss>