<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>MAKC | All</title>
    <link>https://makc.co/</link>
    <description>All of the writing published on MAKC.CO.</description>
    <language>en-us</language>
    <lastBuildDate>Sun, 11 Oct 2026 00:00:00 +0000</lastBuildDate>

    
      <item>
        <title>Digger is Actually Pretty Good</title>
        <link>https://makc.co/essays/digger-is-not-that-bad/</link>
        <guid>https://makc.co/essays/digger-is-not-that-bad/</guid>
        <pubDate>Sun, 11 Oct 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<img class="full" src="/essays/img/2026-10-11.jpg" alt="Tom Cruise in Digger"/>
<p class="caption">
An actor, playing an actor, who's pretending to be a fake oil magnate named Digger Rockwell, addressing the film's version of the European Union.
</p>
<p>I saw director <a href="https://www.imdb.com/name/nm0327944/">Alejandro G. Iñárritu's</a> newest film <a href="https://www.imdb.com/title/tt31450459/">&quot;Digger&quot;</a> on a whim. I remember seeing the trailer several months ago and being reminded of Stanley Kubrick's classic war satire, <a href="https://www.imdb.com/title/tt0057012/">&quot;Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb&quot;</a>, but giving the preview little more thought than that. Then, one day after work I drove past a local Cinemark with nothing better to do, and saw the poster for the film on the bricks outside. Without another thought, I pulled into the parking lot and bought a ticket on my phone. It wasn't until I was already in the building and using the restroom before the screening that I realized I was familiar with two of the director's previous films: <a href="https://www.imdb.com/title/tt2562232/">&quot;Birdman or (The Unexpected Virtue of Ignorance)&quot;</a> and <a href="https://www.imdb.com/title/tt1663202/">&quot;The Revenant&quot;</a>. I had reasonably fond memories of both films, even if neither were my favorite film of the year or anything. But, when I was turning off my phone while walking into the theater, I had reasonably high hopes for the movie, despite the fact that all I remembered from the trailer was the image of a cat somehow still breathing, despite being well past Death's door.</p>
<p>To put it briefly: I left the film getting exactly what I felt I had paid for: A few hours of laughs at the expense of fictionalized versions of both America's current political leadership and business class, as well as basically every other character type that appeared in the film. I didn't find the film to be perfect by any stretch of the imagination. But considering that the only other films I've bothered to watch in theaters this year were Christopher Nolan's <a href="https://www.imdb.com/title/tt33764258">&quot;Odyssey&quot;</a> and David Ayer's <a href="https://www.imdb.com/title/tt7526136">&quot;Heart of the Beast&quot;</a>, Digger may have actually sneaked its way into a spot as one of my favorite film's of 2026. It wasn't until I was perusing YouTube earlier today and saw videos from both <a href="https://www.youtube.com/watch?v=wa9OF5UgoBU">Man Carrying Things</a> &amp; <a href="https://www.youtube.com/watch?v=gLmVVZl_cFU">The Critical Drinker</a> poking fun at the film, that I realized this was a controversial take, but I really liked Digger.</p>
<p>It does seem worth mentioning that Digger is, in my opinion, no where close to as good as the film that it seemed to be trying to follow in the footsteps of based on the early trailers: Dr. Strangelove. But I also don't really think that I was right in my initial assessment that Digger was simply trying to be a modern version of Dr. Strangelove. Both films are satirical lambastings of the military and political establishment, and both films feature cataclysmic outcomes based entirely on the actions of an incompetent American government. But beyond that, there aren't a whole lot of similarities between the two. Digger, much to my surprise, ended up being, not a hit piece aimed at Trump, Musk and company, but rather a film with a strong message about climate change, and our collective societal refusal to actually move with the aggression required to prevent some of the worst predicted outcomes, though I don't know that this was made clear in the early trailers. In fact, many of the complaints about Digger do seem to stem from the fact that the marketing doesn't line up with what the film actually is.</p>
<img class="half" src="/essays/img/2026-10-11-A.jpg" alt="Slim Pickens in Dr. Strangelove"/>
<p class="caption">
<a href="https://www.imdb.com/name/nm0001620/" target"_blank">Slim Pickens</a> on an Atom bomb behind the scenes of Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb.
</p>
<p>The biggest problem with the film however is not the marketing, but rather the unneeded complexity of the story-line. I guess the writers threw out the <em>K.I.S.S. rule</em> early in the process, because not only does Digger attempt to satirize, Trump's administration, the billionaire class of capitalists in America, climate change activists, climate change deniers, Hollywood, and probably a few other groups that I can't recall; the film also adds a whole level of meta weirdness on top of what was already a weird and meta film by having Tom Cruise's Digger Rockwell be, not actually Digger Rockwell, but an actor portraying Digger in a play that's being funded by the actual Digger Rockwell's nephew, while the actor playing Digger Rockwell's daughter is potentially dying on a plane crash ultimately caused by the actions of the real Digger Rockwell a few decades in the past. If that ridiculous run-on sentence sounded trippy and needlessly complex to the reader, you will now have a good idea of what watching Digger feels like. However weird all of that is though, the film was still genuinely hilarious throughout it's runtime. This shouldn't be surprising, what with John Goodman playing the President of the United States, both Riz Ahmed and Sandra Hüller playing amazing supporting characters, and pairing it all with what is a legitimately amazing performance from Tom Cruise. I don't think the most aggressive critics of Digger would disagree that it boasts some incredible acting work, thought some might disagree that the film is as funny as I personally found it to be.</p>
<p>Yet, to hear the rest of the world tell it, Digger is one of the worst films of the year, and maybe the worst of Cruise's career. At the very least the film is not doing the numbers that it needed to break even. Digger made barely over $8 million on it's opening weekend and only a little over <a href="https://www.boxofficemojo.com/release/rl790986753/">$35 million dollars internationally</a> at the time of writing. Those numbers, on a budget that was reported to be <a href="https://successknocks.com/tom-cruise-digger-budget-and-salary/">$125 million</a> before marketing or any other production costs ensure that Digger is virtually guaranteed to be a box office bomb of epic proportions. But the fact that the film isn't popular isn't surprising to me. Iñárritu's previous film &quot;Birdman&quot; only managed to not be an epic bomb in it's own right because it was made with a scrappy budget of $18 million. While &quot;The Revenant&quot; did make over half a billion dollars internationally, as an R-rated film no less (cleanly in the top twenty R-rated films of all time), it did so with an all-star cast including Tom Hardy, Will Poulter, Domnhall Gleeson and, most importantly, Leonard DiCaprio when he was in full <em><a href="https://knowyourmeme.com/memes/leonardo-dicaprios-oscar">please give me an Oscar</a></em> mode, not to mention, a wildly more interesting premise. Digger was every bit as much of an art-house and experimental film as Iñárritu's previous work, and I think the trailers and VistaVision marketing made it clear that it would be that from the start, and it's not it's not like Tom Cruise's last few films have been wild successes. Digger never didn't feel like a film that could have done well, if only it had been released at a different point in time. What I can't quite wrap my head around is the folks who saw the film, and hated it.</p>
<p>Some of the backlash is certainly due to the film's political satire. Armond White, of the National Review for example <a href="https://www.nationalreview.com/2026/10/digger-is-tom-cruises-climate-hoax/">was no fan of Digger</a>. As stupid as I think is to be such a big fan of a political administration that you can't even laugh at a joke about the absurdities present within said administration, this back-lash isn't surprising. Hollywood critics however, aren't exactly know for being big fans of the Republican party, and certainly not Donald Trump's administration specifically, yet, even among that crowd, Digger was only able to scrape together <a href="https://www.rottentomatoes.com/m/digger_2026">53% on Rotten Tomatoes</a>. The film's 74% audience score on Rotten Tomatoes and <a href="https://www.imdb.com/title/tt31450459/">7.4 stars on IMdB</a> might be early clues towards this film's status as a potential cult classic. But that feels a bit unlikely based on the current discourse surrounding the film. Even those who enjoyed the film, myself included, seem to lead any discussion about the film by making fun of the marketing or opening with copious acknowledgments the parts of the movie that missed the mark.</p>
<p>At least a little bit of the criticism around the film seems to be fully misguided. For example, in The Critical Drinker video that I mentioned earlier, I found that much of what Jordan complained about: the lack of any interest in examining or critiquing Hollywood and it's filmmaker's, simply didn't reflect what I saw in the film. As far as I can tell, the entire point of Jesse Plemons' character in the film, was to act as a counter-balance to Tom Cruise's absurd depiction of Digger Rockwell and Sophie Wilde's almost equally absurd role as the <em>art is the most important thing in the world</em>-style director of the play that the film centers around. I suppose it is possible that Digger has unreported varied versions, the same way that <a href="https://collider.com/clue-alternate-endings-untold-story/">&quot;Clue&quot;</a> shipped to theaters with multiple endings, but I doubt it. More likely, I think most normal folks that didn't care for the movie, didn't like it simply because of how overly-convoluted the story and it's messaging truly are. A fair point, I think, as this is precisely the same criticism that I have of the film. I do think however, that much of this undue complexity is actually part of the film's charm.</p>
<p>Digger's comedy, and particularly it's political messaging is more reminiscent of something like <a href="https://www.imdb.com/title/tt0472954/">&quot;It's Always Sunny in Philadelphia&quot;</a> than it is &quot;Dr. Strangelove&quot;, or even more recent straight-satires like <a href="https://www.imdb.com/title/tt0458254">&quot;The Colbert Report&quot;</a> or <a href="https://www.imdb.com/title/tt4686844">&quot;The Death of Stalin&quot;</a>. The film, as much as it might be mistaken for any of Hollywood's recent other efforts at political propaganda, is simply not that. Digger satirizes the current political administration and the people over-reacting to them, climate change activists and climate change deniers, artists and the people who think art is useless, even the film's most despicable character, Digger Rockwell himself, the man who sends a iceberg barreling towards Europe and another one towards Alaska causing the literal apocalypse, is not without his sympathetic side. Certainly, it might be a reasonable criticism to say that the film refuses to take a stand on anything, and is only interested in making jokes. I for one would claim that it's more accurate to say that Digger is simply a satire in the purest sense, and isn't interested in letting anyone out of it's cross-hairs. Most importantly however, Digger does exactly what a good film is supposed to do: entertain us for a few hours, and provide a few good laughs along the way.</p>
]]></description>
      </item>
    
      <item>
        <title>My Writerdeck</title>
        <link>https://makc.co/essays/writerdeck/</link>
        <guid>https://makc.co/essays/writerdeck/</guid>
        <pubDate>Mon, 05 Oct 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p><strong>Note</strong>: The core concepts for this project were heavily inspired by the <a href="https://veronicaexplains.net/my-first-writerdeck/">Veronica Explains</a> article on the same topic. Good artists borrow, great artists steal. I'm a good artist.</p>
<h2>The Idea</h2>
<p>The ideal <em>writerdeck</em> as it is defined by me, is a device that can be used to write with as little distraction as possible. It's not hard to see why the mythical device is idealized, but in my experience using a designated device to write that only is capable of writing is every bit as helpful as one might imagine. My first experience writing in this type of environment was using a mechanical Smith Corona typewriter that I bought because I read that Hemmingway used the <a href="https://www.ithacajournal.com/story/news/local/2016/04/29/hemingway-groton-and-corona-typewriter/78308130/">same one</a>. The typewriter was a great experience, other than the fact that mechanical typewriters aren't nearly as good at capturing input as modern laptops,and forced you to type much slower. In the year of our lord 2026, there are much less pretentious ways to build a distraction free space for writing that are much more effective than a fifteen pound hunk of metal from the 70s (even if it is an objectively cool thing to own).</p>
<h3>Device Choice</h3>
<p>The most important factor however, is the device choice for our writerdeck. For my part, I have long found that a simple desktop with a good mechanical keyboard is just about the best writing set-up possible. But the older I've gotten, the more I've found my time spread across multiple activities, and more generally chaotic, resulting in a reality in which I am far less likely to actually end up sitting in front of that <em>ideal</em> desktop set-up than I would have been even a few years ago. Thus, life has mandated that I find a more portable solution that allows me to write from whatever environment I might find myself in. While I wasn't thrilled about the idea of writing full-time on a laptop, I have been able to write from the beach, from the middle of the woods and from the roof of at least on thirty story building, so I guess that's cool.</p>
<p>There are two main factors that must be considered when selecting one's laptop of choice for this mission (and several smaller factors):</p>
<ol>
<li>The keyboard</li>
<li>Battery life</li>
</ol>
<p>There are a whole lot of laptops to consider, and a lot of trade-offs to be made. MacBooks for example will offer supreme battery life, but not without sacrificing keyboard quality. Also much of the battery life gains are lost if replacing MacOS with Linux, and I'm not willing to run MacOS on a writing machine. Old Thinkpads have amazing keyboards (for a laptop), but have limited battery life and are pretty cumbersome when compared with anything even close to modern. E-ink laptops are an interesting consideration, as battery life is almost guaranteed to be great, but build quality is often lacking here. Truth be told, I have yet to find the perfect device for a dedicated writerdeck, and have opted to just use my EDC laptop of choice, the Thinkpad P1 as a general purpose laptop, and a writerdeck for the time being. This laptop is not ideal, as it has a fifteen inch screen (a bit bigger than I'd like), dedicated graphics, and an older Intel chipset that isn't nearly as efficient as modern options. It's OLED screen and reasonably large built in battery however, make it more than acceptable for temporary use.</p>
<img class="full" alt="My writerdeck" src="/essays/img/2026-10-05-deck.jpg"/>
<p class="caption">
My writerdeck in action on the front porch of a cabin in the middle of the woods. 
</p>
<h3>Operating System</h3>
<p>As far as I'm concerned the only real option for OS is Linux (maybe a BSD flavor if you really wanted native zfs). The whole point of a writerdeck is to foster a distraction free writing environment for one's self, and there's nothing better for this, than a CLI only system. Linux alone as an OS offers the freedom to uninstall a GUI and create the perfect environment. Since we'd be uninstalling a GUI anyway, one of the most compelling choices here is to use <a href="https://archlinux.org/">Arch Linux</a>, as we can simply never install a GUI to begin with. For my part, my Thinkpad P1 as already running CachyOS (an Arch derivate), so I just chose to employ Linux's virtual terminals to easily swap over into my writerdeck environment. For readers unfamiliar, nearly ever Linux distribution allows easy swapping between multiple virtual TTYs using the keybinding: <code>Ctrl+Alt+F*</code>. For example: <code>Ctrl+Alt+F3</code> will load <code>TTY3</code> and <code>Ctril+Alt+F4</code> will load <code>TTY4</code>. Generally, Linux distributions will load the user space into TTY1 or TTY2, so using TTY3 or above is generally a safe bet.</p>
<h4>KMSCON and TTY Customization</h4>
<p>These virtual terminals, while great for quickly troubleshooting a system, are not built for long term use by the end user. Luckily, there are projects that attempt to solve this problem. The best known of which is an application called <code>kmscon</code>, which enables themeing, font customization, and most importantly: font size scaling with <code>Ctrl +</code> &amp; <code>Ctrl -</code>. We can install kmscon with:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span> kmscon</code></pre>
<p>We then can enable kmscon for either the entire system, or (in my case) just one TTY. If you are using a dedicated writerdeck machine and want kmscon to be enabled system wide, we would need to both (a) disable getty (the default TTY service), and (b) enable kmscon:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> systemctl disable getty@.service <span class="token operator">&amp;&amp;</span><span class="token punctuation">\</span>
<span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> kmsconvt@.service <span class="token operator">&amp;&amp;</span><span class="token punctuation">\</span>
<span class="token function">reboot</span></code></pre>
<p>However, if like me, you want to preserve your ability to use your already installed GUI and simply intend to swap over to TTY3 when you're writing, this can be enabled with:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> <span class="token parameter variable">--now</span> kmsconvt@tty3.service</code></pre>
<p>After this process has been completed, we should be able to launch TTY3 with <code>Ctrl+Alt+F3</code> and use all of kmscon's features, a reboot may be required though. The only real complaint that I had about kmscon was the fact that it used the default terminal colors, rather than my much subtler colorscheme that I set-up for my terminals and Neovim. To fix this we need to create a config in <code>/etc/kmscon/kmscon.conf</code>. There is a sample config located in <code>/etc/kmscon/kmscon.conf.example</code>, but I'll walk through the configuration changes I made below:</p>
<ol>
<li>I swapped the font to Hack Nerd font with <code>font-name=Hack Nerd Font</code> and made the default text size bigger with <code>font-size=30</code>.</li>
<li>I incorporated a custom color scheme (Material Ocean).
<ul>
<li>The version of Kmscon currently being shipped through Pacman (v10.0.4 as of writing), does not accept color values defined with hexadecimal, bur rather, requires colors defined with an RGB value, for example: <code>palette-black=76,86,106</code> or <code>palette-foreground=216,222,233</code>.</li>
</ul>
</li>
<li>Additionally I made sure that the kmscon background was set to pure black, <code>palette-background=0,0,0</code>, so that I could make sure I was taking advantage of the battery life savings and crispness of my laptop's OLED screen.</li>
</ol>
<p>The full config is below:</p>
<pre class="language-c"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span> <span class="token expression">Font</span></span>
font<span class="token operator">-</span>engine<span class="token operator">=</span>freetype
font<span class="token operator">-</span>name<span class="token operator">=</span>Hack Nerd Font
font<span class="token operator">-</span>size<span class="token operator">=</span><span class="token number">30</span>

<span class="token macro property"><span class="token directive-hash">#</span> <span class="token expression">Colors</span></span>
palette<span class="token operator">=</span>custom

palette<span class="token operator">-</span>black<span class="token operator">=</span><span class="token number">76</span><span class="token punctuation">,</span><span class="token number">86</span><span class="token punctuation">,</span><span class="token number">106</span>
palette<span class="token operator">-</span>red<span class="token operator">=</span><span class="token number">191</span><span class="token punctuation">,</span><span class="token number">97</span><span class="token punctuation">,</span><span class="token number">106</span>
palette<span class="token operator">-</span>green<span class="token operator">=</span><span class="token number">163</span><span class="token punctuation">,</span><span class="token number">190</span><span class="token punctuation">,</span><span class="token number">140</span>
palette<span class="token operator">-</span>yellow<span class="token operator">=</span><span class="token number">235</span><span class="token punctuation">,</span><span class="token number">203</span><span class="token punctuation">,</span><span class="token number">139</span>
palette<span class="token operator">-</span>blue<span class="token operator">=</span><span class="token number">129</span><span class="token punctuation">,</span><span class="token number">161</span><span class="token punctuation">,</span><span class="token number">193</span>
palette<span class="token operator">-</span>magenta<span class="token operator">=</span><span class="token number">180</span><span class="token punctuation">,</span><span class="token number">142</span><span class="token punctuation">,</span><span class="token number">173</span>
palette<span class="token operator">-</span>cyan<span class="token operator">=</span><span class="token number">136</span><span class="token punctuation">,</span><span class="token number">192</span><span class="token punctuation">,</span><span class="token number">208</span>
palette<span class="token operator">-</span>light<span class="token operator">-</span>grey<span class="token operator">=</span><span class="token number">229</span><span class="token punctuation">,</span><span class="token number">233</span><span class="token punctuation">,</span><span class="token number">240</span>

palette<span class="token operator">-</span>foreground<span class="token operator">=</span><span class="token number">216</span><span class="token punctuation">,</span><span class="token number">222</span><span class="token punctuation">,</span><span class="token number">233</span>
palette<span class="token operator">-</span>background<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span></code></pre>
<h4>Tmux</h4>
<p>As much as distraction free writing is the goal here, it is nice to have a tiny bit of system metadata available to us from the TTY. Mainly what I'm after is time, date and battery life. As was done in the article that inspired this project, I'm going to use Tmux and it's built in status bar to accomplish this. We'll need to install Tmux, a utility called <code>acpi</code>, and <code>grep</code> to print the battery percentage in the status bar:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span> tmux acpi <span class="token function">grep</span></code></pre>
<p>Once the installation is complete, we can create a tmux config in <code>~/.tmux.conf</code> and get to work. The first thing that I'll do is move the Tmux status bar to the top of the screen so that it doesn't interfere with my Neovim status bar, and set the background color of the status bar to black:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-position <span class="token function">top</span>
set-option <span class="token parameter variable">-g</span> status-style <span class="token string">"bg=black"</span></code></pre>
<p>By default the tmux status bar displays active windows on the left side and the time and data on the right side of the status bar. All I want to do is add the battery to the right side of the status bar with the word-salad looking line that I'll explain below:</p>
<pre class="language-bash"><code class="language-bash">set-window-option <span class="token parameter variable">-g</span> status-right <span class="token string">"#[bg=green,fg=black] %a %d #[bg=blue] %H:%M #[bg=cyan] #(acpi -b | grep -m1 -o -P '.{0,2}%') "</span></code></pre>
<p><code>set-window-option -g status-right</code> tells the config that we want to edit the right side of the status bar. The sections that include <code>#[bg=X,fg=X]</code> are setting background and foreground colors based on the colors as we defined them in the kmscon config earlier. Since we previously set the status bar to black, the entire status bar will be black until we change that with: <code>#[bg=green,fg=black]</code>. The status bar will then change to a green background and black text for the command <code>%a %d</code> which displays the date and day of the week as <strong>Day 00</strong>. We then change the background the blue with <code>#[bg=blue]</code>and print the time in <strong>HH:MM</strong> format (the black text inherited from the earlier definition). Finally, we change the background to cyan and display the battery life with: <code>#(acpi -b | grep -m1 -o -P '.{0,2}%') </code>. The grep syntax is a little tricky to wrap your head around, but essentially all this is doing is removing the first part of the <code>acpi</code> command's output, and truncating it, so that all that's actually displayed is the battery percentage.</p>
<p>By default the command output would look something like: <code>Battery 0: Discharging, 4%, 00:13:11 remaining</code></p>
<img class="thirds" alt="My TMUX status bar customized." src="/essays/img/2026-10-05.jpg"/>
<p class="caption">
My TMUX status bar with the configurations detailed here. 
</p>
<p>Next, we can use Tmux to generate keybindings for brightness control, as the quickest way to run down even a good battery is by running the display at 100% when there is no reason to do so. The <code>brightnessctl</code> app in the pacman repositories will add this functionality:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span> brightnessctl</code></pre>
<p>We can then add some keybindings to our pre-existing Tmux config. By default every keybinding in tmux uses <code>Ctrl+b</code> as a leader key, but we can bypass this with the <code>-n</code> option, and have the brightness increase with <code>F9</code> and decrease with <code>F8</code>.</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> F8 run-shell <span class="token string">'brightnessctl -q set 5%-'</span>
<span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> F9 run-shell <span class="token string">'brightnessctl -q set +5%'</span></code></pre>
<p>Last but not least we need to have Tmux automatically launch when a new TTY is launched. For my use case, I don't want Tmux to spawn with every new terminal, so we have to create a more specialized if/then statement. The easiest way to do this is be editing the <code>.bashrc</code>, or other shell configuration to make this happen:</p>
<pre class="language-shell"><code class="language-shell"><span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token parameter variable">-z</span> <span class="token string">"<span class="token variable">$TMUX</span>"</span> <span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable"><span class="token variable">$(</span>loginctl show-session <span class="token string">"<span class="token environment constant">$XDG_SESSION_ID</span>"</span> <span class="token parameter variable">-p</span> Type <span class="token parameter variable">--value</span><span class="token variable">)</span></span>"</span> <span class="token operator">=</span> <span class="token string">"tty"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
    <span class="token builtin class-name">exec</span> tmux new <span class="token parameter variable">-A</span> <span class="token parameter variable">-s</span> main
<span class="token keyword">fi</span></code></pre>
<p>Adding this to the top of our shell config with check the <code>$XDG_SESSION_ID</code> to make sure that we are in a new TTY and not a virtual terminal like would be spawned when launching alacritty or foot. After this is verified, Tmux is launched with the <code>tmux new -A -s main</code> command. This will either attach to the session called <code>main</code> if it exists, or create the <code>main</code> session if it does not. For my workflow, I almost always use a main/master session in Tmux, so this will also allow me to start work in my standard userspace and pick things up where I left of in TTY3.</p>
<p>The full Tmux config that I use is below:</p>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># Setting a quick way to reload config</span>
<span class="token builtin class-name">bind</span> r source-file ~/.tmux.conf

<span class="token comment"># Allowing mouse control, moving status bar to top</span>
<span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> mouse on
<span class="token builtin class-name">set</span> <span class="token parameter variable">-s</span> escape-time <span class="token number">0</span>
<span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-position <span class="token function">top</span>

<span class="token comment"># Customizing status bar colors &amp; adding battery percentage to status bar</span>
set-option <span class="token parameter variable">-g</span> status-style <span class="token string">"bg=black"</span>
set-window-option <span class="token parameter variable">-g</span> status-right <span class="token string">"#[bg=green,fg=black] %a %d #[bg=blue] %H:%M #[bg=cyan] #(acpi -b | grep -m1 -o -P '.{0,2}%') "</span>

<span class="token comment"># Vim keys for navigating panes</span>
<span class="token builtin class-name">bind</span> h select-pane <span class="token parameter variable">-L</span>
<span class="token builtin class-name">bind</span> j select-pane <span class="token parameter variable">-D</span>
<span class="token builtin class-name">bind</span> k select-pane <span class="token parameter variable">-U</span>
<span class="token builtin class-name">bind</span> l select-pane <span class="token parameter variable">-R</span>

<span class="token comment"># Adding keybindings for adjusting brightness</span>
<span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> F8 run-shell <span class="token string">'brightnessctl -q set 5%-'</span>
<span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> F9 run-shell <span class="token string">'brightnessctl -q set +5%'</span></code></pre>
<h4>Networking and Synchronization</h4>
<p>To wrap things up, networking is probably something that will be essential for most writers. If your laptop has an ethernet port, simply installing an enabling <code>dhcpcd</code> will be more than enough, and it might be worth actively not enabling WiFi on a trail bases to restrict the writerdeck ever further. However, I'm assuming that I and just about everyone else will eventually want to enable WiFi connectivity for syncing or pushing a git repository.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span> networkmanager</code></pre>
<p>Running <code>nmtui</code> will allow us to connect to and manage WiFi networks from the TTY.</p>
<p>The final issue to solve is file back-ups and synchronization for which there are far to many options to discuss every single one. In the past I've used services like Dropbox and I know plenty of folks use Syncthing to sync files between devices. We could even set up a simple back-up script with <a href="https://makc.co/docs/simple-tailscale-setup/">rsync</a> and a passwordless SSH key. I personally opt to use good ol' fashioned git and push changes to a <a href="https://makc.co/docs/create-your-own-git-server/">private git server</a>. The only additional software required for this is a VPN, so that I can securely connect to my private server and push changes. I like using tailscale for this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-fsSL</span> https://tailscale.com/install.sh <span class="token operator">|</span> <span class="token function">sh</span></code></pre>
]]></description>
      </item>
    
      <item>
        <title>Replacing my Airtable Backend with One JSON File</title>
        <link>https://makc.co/essays/airtable-to-JSON/</link>
        <guid>https://makc.co/essays/airtable-to-JSON/</guid>
        <pubDate>Mon, 21 Sep 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A few years ago, I began the process of rebuilding my website using the static site generator <a href="https://11ty.dev">11ty</a>. My goal in doing this was to create a hyper-performant version of my site that I could proudly show off on the world wide web. But I also set some fairly ambitious goals for this site, mainly to create one hub to host not only all of the <a href="https://makc.co/downloads">content that I've made available</a> for public consumption, but also links to all the external content I've created. The most challenging part: my personal YouTube channel, and it's backlog of a little over two hundred videos when I started the project. Creating a website that links to YouTube would be hard enough, but around the time I started this rebuild of my site, I also decided that I needed to make sure that users could not only get links to my videos on YouTube, but also alternative platforms like <a href="https://invidious.io/">Invidious</a> and <a href="https://odysee.com/">Odysse</a>. One database to host video metadata like title, upload date, thumbnails, tags, categories, and links to view each video in at least three different locations, was no small undertaking, but I made the job even harder by refusing to use a proper database application or CMS solution and instead insisting on using <a href="https://airtable.com">Airtable</a> for the job.</p>
<p>My reasoning for doing this seemed sound at the time: I had been using Airtable to keep track of my video creation pipeline for the past few years, and already had a good chunk of the videos in an Airtable base, along with much of the metadata that I wanted. Unfortunately it soon became clear that Airtable simply wasn't the right tool for the job. My Airtable integration ended up making a static site that routinely built in less than one second into a convoluted mess that had to call out to Airtable's API every time I compiled the site whether I was messing with the <a href="https://makc.co/videos">video page</a> or not. Of course, Airtable as a database solution is also pretty terrible. I have used plenty of database applications in the past, and while most of them don't have the convenience of editing on a phone or in a browser like Airtable, all provide a much cleaner experience for creating and updating entries, not to mention a faster one. Airtable's website and phone apps were slow and clunky when I first started using them, but only ever got worse as Airtable, like so many others, insisted on shoving AI integration into every inch of the platform, and moved more and more features behind a paywall.</p>
<h2>JSON &amp; Airtbale</h2>
<p>So, last week I decided that I was done with Airtable, no matter the cost. The solution that I ended up wanting to try first: replacing the whole database with a single JSON file. I have plenty of familiarity with 11ty, and I knew that a JSON table would allow me to call all of the metadata that I'd need. So I created a file in <code>_data/videos.json</code>, and populated the file with a few test items:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">[</span>
    <span class="token punctuation">{</span> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Creating a Private Git Server"</span><span class="token punctuation">,</span>
      <span class="token property">"date"</span><span class="token operator">:</span> <span class="token string">"2026-05-13"</span><span class="token punctuation">,</span>
      <span class="token property">"ytid"</span><span class="token operator">:</span> <span class="token string">"HvklHoL7Ano"</span><span class="token punctuation">,</span>
      <span class="token property">"category"</span><span class="token operator">:</span> <span class="token string">"System Admin"</span><span class="token punctuation">,</span>
      <span class="token property">"odurl"</span><span class="token operator">:</span> <span class="token string">"https://odysee.com/@makc:a/creating-a-private-git-server:b"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> 

    <span class="token punctuation">{</span> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Transferring Files Remotely with SSH and a VPN Tunnel"</span><span class="token punctuation">,</span>
      <span class="token property">"date"</span><span class="token operator">:</span> <span class="token string">"2026-01-25"</span><span class="token punctuation">,</span>
      <span class="token property">"ytid"</span><span class="token operator">:</span> <span class="token string">"TQdmp2P93cM"</span><span class="token punctuation">,</span>
      <span class="token property">"category"</span><span class="token operator">:</span> <span class="token string">"System Admin"</span><span class="token punctuation">,</span>
      <span class="token property">"odurl"</span><span class="token operator">:</span> <span class="token string">"https://odysee.com/@makc:a/transferring-files-remotely-with-rsync,:6"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> 

    <span class="token punctuation">{</span> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Exporting .md to .odt with Pandoc &amp; Lua"</span><span class="token punctuation">,</span>                                       
      <span class="token property">"date"</span><span class="token operator">:</span> <span class="token string">"2026-01-18"</span><span class="token punctuation">,</span>
      <span class="token property">"ytid"</span><span class="token operator">:</span> <span class="token string">"8ORPbeFyzeU"</span><span class="token punctuation">,</span>
      <span class="token property">"category"</span><span class="token operator">:</span> <span class="token string">"CLI &amp; Scripting"</span><span class="token punctuation">,</span>
      <span class="token property">"odurl"</span><span class="token operator">:</span> <span class="token string">"https://odysee.com/@makc:a/exporting-markdown-to-openoffice-with:2"</span> <span class="token punctuation">}</span>
<span class="token punctuation">]</span></code></pre>
<p>I was then able to easily call this JSON file on the videos page of my site with liquid: <code>{% for video in videos %}</code>. I figured out that I could build (1) a link to the YouTube thumbnail, (2) a YouTube link, and (3) an Invidious link all with just a YouTube ID, so the only metadata I ended up actually needing to pull was:</p>
<ol>
<li>Video Title</li>
<li>Upload Date</li>
<li>YouTube ID</li>
<li>Category/Topic</li>
<li>Odysee URL</li>
</ol>
<p>From that point I could assemble the whole thing in a clean and succinct <em>card</em> that would automatically populate on my videos page with all that data I needed:</p>
<pre class="language-liquid"><code class="language-liquid">
<span class="token liquid language-liquid"><span class="token delimiter punctuation">{%</span> <span class="token keyword">for</span> <span class="token object">video</span> <span class="token keyword">in</span> videos <span class="token delimiter punctuation">%}</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">data-category</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{/{ video.category }}<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>video-card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://youtube.com/watch?v=<span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span>ytid <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>YouTube<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://i.ytimg.com/vi/<span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span>ytid <span class="token delimiter punctuation">}}</span></span>/maxresdefault.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Hero image<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>metadata<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span><span class="token object">date</span> <span class="token operator">|</span> <span class="token function filter">displayDate</span> <span class="token delimiter punctuation">}}</span></span> • <span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span>category <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>videos<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span>title <span class="token operator">|</span> <span class="token function filter">truncate</span><span class="token operator">:</span> <span class="token number">42</span><span class="token punctuation">,</span> <span class="token string">"..."</span> <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>platform-select<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
            Watch on:  
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://youtube.com/watch?v=<span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span>ytid <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>YouTube, <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span>odurl <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Odysee, <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span> or
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://inv.nadeko.net/watch?v=<span class="token liquid language-liquid"><span class="token delimiter punctuation">{{</span> <span class="token object">video</span><span class="token punctuation">.</span>ytid <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Invidious<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span> 
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
<span class="token liquid language-liquid"><span class="token delimiter punctuation">{%</span> <span class="token keyword">endfor</span> <span class="token delimiter punctuation">%}</span></span>
</code></pre>
<p><strong>Note</strong>: I did also create a <code>displayDate</code> filter in my Eleventy config to convert the ISO date (YYYY-MM-DD) to a more human readable format. I liked the precision of keeping the ISO format in the JSON file, even if it was just being treated as a string on build, but didn't love dates being rendered as 2026-09-05 on the live site.</p>
<pre class="language-javascript"><code class="language-javascript">eleventyConfig<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span><span class="token string">"displayDate"</span><span class="token punctuation">,</span> <span class="token parameter">dateString</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> DateTime<span class="token punctuation">.</span><span class="token function">fromISO</span><span class="token punctuation">(</span>dateString<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toFormat</span><span class="token punctuation">(</span><span class="token string">"LLLL d, yyyy"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>Creating My New Database</h2>
<p>Getting a JSON file to work with liquid and create dynamic HTML is only one part of the story, and, at the risk of being a bit dismissive, it's the least interesting part of it. After all, Eleventy was specifically designed to work with basically every file-type a web developer might want to work with, JSON included. What took far longer during this process, and what I found to be a much more interesting challenge, was the process of actually creating a single file that could list every single video that I've ever made and all the metadata associated with it.</p>
<h3>Migrating Airtable to JSON</h3>
<p>Despite the fact that I had been using my half-assed Airtable solution for at least a year, I hadn't actually put in the work to consolidate all the metadata on the platform. However, I did manage to create Airtable entries for 176 out of the 242 videos that I have uploaded to YouTube and made public. So I wasn't exactly eager to abandon my Airtable database altogether. But I needed to get the data off of Airtable and into a format that I could manipulate. Unfortunately, the only real way to do this with Airtable was by exporting the base as a CSV file.</p>
<img src="img/2026-09-21-airtable.jpg" class="half" alt="Airtable option to export CSV">
<p class="caption"> 
Exporting my Airtable base as Comma Separated Values (a CSV file)
</p>
<p>I was then able to open the CSV in Libre Office Calc and strip away all but the metadata that I actually needed. I removed all the rows containing videos that hadn't been completed yet, or that I didn't want to display on my site, and then I removed all of the columns with metadata that I didn't need. I made sure to leave the first cell in each column as a title for the field, and ended up with a clean CSV file that I could export to JSON with Python's built-in CSV library. For more details on this part, check out the <a href="http://makc.co/docs/csv-to-json/">documentation</a> that I published in tandem with this essay.</p>
<pre class="language-python"><code class="language-python">python <span class="token operator">-</span>c 
<span class="token string">'import csv,json,sys; print(json.dumps(list(csv.DictReader(open(sys.argv[1], newline="", encoding="utf-8"))), indent=2))'</span> 
AIRTABLE<span class="token punctuation">.</span>csv <span class="token operator">></span> NEW<span class="token punctuation">.</span>json</code></pre>
<img src="img/2026-09-21-csv.jpg" class="thirds" alt="My cleaned up CSV file">
<p class="caption"> 
My cleaned up CSV file
</p>
<h4>Cleaning Everything Up</h4>
<p>Of course, nothing can be as simple as it seems from the outside. I quickly noticed some weird artifacts that had made their way into my new JSON file, most notably, every <code>&quot;title&quot;:</code> field had a string of random characters (<code>&quot;\ufefftitle&quot;:</code>) in front of the field. Luckily, however a little bit of Vim magic was able to clean this all up. If you're unfamiliar with Vim's macros, one can record a macro by pressing: <code>qX</code> and then execute that macro <em>n</em> number of times with: <code>n@X</code>. I was able to record a macro with: <code>qa</code>, fix one field, and move the cursor to the next error, close the recording with: <code>q</code>, and then watch Vim fix the whole document with: <code>174@a</code>.</p>
<p>Or, if you must do things the boring way, a simple find and replace action will do the same thing:</p>
<pre class="language-vim"><code class="language-vim"><span class="token punctuation">:</span><span class="token operator">%</span>s<span class="token operator">/</span>\ufefftitle<span class="token operator">/</span><span class="token builtin">title</span><span class="token operator">/</span>g</code></pre>
<p>There were a few other miscellaneous artifacts that came from symbols being rendered incorrectly by the Python conversion. I found that virtually all of these artifacts could be located by searching for a preceding backslash, or a leading space after a quote. It took me no longer than a few minutes in Vim to manually fix all the weirdness and have a clean JSON file with roughly 70% of my content library ready to go.</p>
<h3>Google Takeout</h3>
<p>But what to do about the remaining 30% of my video catalog? I considered just opening up Creator Studio and manually updating my JSON table. After all, I am one of those weird people who finds database creation to be a pretty calming process, even if it is comically time-consuming. But before I started to do that I remembered a little project called <em>Google Takeout</em>. A few minutes of research and I had confirmed that the same tool Google made to let users export search history and YouTube watch history could also be used to export precisely the precious metadata that I needed to build out the rest of my video catalog. Unfortunately, when I first attempted to export this data, I simply selected the <em>YouTube &amp; YouTube Music</em> export option, which, when ready for export, gave me well over 200GiB worth of .zip files to download.</p>
<img src="img/2026-09-21-takeout-massive.jpg" class="half" alt="Exporting Google Takeout Data">
<p class="caption"> 
Google Takeout defaulting to exporting every single video I've ever uploaded, not just the metadata
</p>
<p>Takeout, by default, will export <em>everything</em>, including a full resolution copy of every single video I've uploaded to YouTube. This is probably a good thing, based on what the tool was designed for, but it's also far from what I wanted. Fortunately I was able to backtrack and find an option in the Takeout settings that allowed me to export only <em>video metadata</em>. A few more minutes of waiting, and I had a collection of CSV files that I was able to clean up and convert to JSON via the same Python library I employed earlier. All of the metadata that I needed ended up being in the <code>videos.csv</code> file in my takeout directory, but your mileage may vary.</p>
<pre class="language-bash"><code class="language-bash">Takeout
│
└───YouTube and YouTube Music
    │
    └───video metadata
        │
        └───video metadata
            │ video recordings<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>.csv
            │ video recordings.csv
            │ video texts<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>.csv
            │ video texts.csv
            │ videos<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>.csv
            │ videos.csv <span class="token comment"># &lt;-- Contained all the metadata I needed</span></code></pre>
<img src="img/2026-09-21-takeout-metadata.jpg" class="half" alt="Exporting Google Takeout Data only metadata">
<p class="caption"> 
The option in Google Takeout to export only video metadata
</p>
<h4>Odysee Links &amp; Final Steps</h4>
<p>The final step in the process was gathering up all of the links to the remaining 80 or so videos on the Odysee platform. Odysee does provide some metadata export tools, and there's even a <a href="https://apify.com/parseforge/odysee-channel-scraper">channel scraper</a> on Apify that can help out as well. However, I found it just as easy to just copy and paste the links that I was missing into the JSON file.</p>
<p>There was a little bit of cleanup to do after all the CSVs had been converted, and more cleanup stlll once I dropped the JSON file into my Eleventy site and started calling it on the pages where I needed to use the data. All in all, however, I ended up turning a job that I had been putting off completing for the better part of a year into something that I was able to knock out in a few hours while simultaneously being disappointed by the fourth season of <a href="https://www.youtube.com/watch?v=CAfVvSCCklw">Reacher</a>.</p>
]]></description>
      </item>
    
      <item>
        <title>Blowing Up, Just to Blow Up</title>
        <link>https://makc.co/essays/blowing-up-to-blow-up/</link>
        <guid>https://makc.co/essays/blowing-up-to-blow-up/</guid>
        <pubDate>Sun, 16 Aug 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Recently I began the process of moving all of my <a href="https://github.com/makcc/">GitHub repositories</a> to <a href="https://codeberg.org/makccr/">Codeberg</a>. I am far from the only person making this move in 2026; if anything, I'm well behind the curve. However, I did want to dedicate some time to detailing (a) how I am conducting my transition to Codeberg, and (b) the reasons why I felt that this move was necessary.</p>
<p>First, for the foreseeable future my intention is to push my public facing repositories, such as my <a href="https://codeberg.org/makccr/dot">dotfiles</a>, or my <a href="https://github.com/makccr/vim2tor">Vim2tor</a> project to both GitHub and Codeberg. Largely I view Codeberg as a <em>back-up plan</em>, as I still hope that GitHub itself can return to what it, in my view, once was: a reliably easy place to find, learn about and contribute to open-source software projects. My transition to Codeberg is something that feels <em>thrust upon me</em>, rather than a deliberate choice. However, even if GitHub returns to it's former pre-Microsoft and pre-unreliable-AI-slop-farm glory, I'll likely still push to both platforms, if for no other reason than to be in compliance with the old prepper one-liner: &quot;Two is one, and one is none.&quot;</p>
<p>The process for doing this is very simple:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> REPO

<span class="token comment"># Ensure empty repository with the same name has been made on Codeberg, and any required SSH keys have been added to Codeberg</span>

<span class="token function">git</span> remote set-url <span class="token parameter variable">--add</span> <span class="token parameter variable">--push</span> origin git@codeberg.org:<span class="token environment constant">USER</span>/REPO
<span class="token function">git</span> remote set-url <span class="token parameter variable">--add</span> <span class="token parameter variable">--push</span> origin git@github.com:<span class="token environment constant">USER</span>/REPO</code></pre>
<p>And can be confirmed with:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> remote <span class="token parameter variable">-v</span>

<span class="token comment"># Example Output</span>
orgin	git@github.com:makccr/dot <span class="token punctuation">(</span>fetch<span class="token punctuation">)</span>
origin	git@github.com:makccr/dot <span class="token punctuation">(</span>push<span class="token punctuation">)</span>
origin	git@codeberg.org:makccr/dot <span class="token punctuation">(</span>push<span class="token punctuation">)</span></code></pre>
<p>However, if you would like more details for setting this up with the best practices in mind, I'll refer any readers interested to <a href="https://schalkneethling.com/about/">Schalk Neethling's</a> <a href="https://schalkneethling.com/posts/pushing-to-github-and-codeberg-simultaneously-with-git/">dedicated guide</a>. One note I'll mention however is that Codeberg does enforce fairly <a href="https://blog.codeberg.org/new-storage-limits-on-codeberg-what-you-need-to-know.html">strict quotas</a>, so it's simply not possible for me to back-up all of my public repositories. I have at least two repos that are well over 1GiB and that is outside of the quota at the time of writing.</p>
<p>The more interesting question of course, is why the transition felt necessary now? The easiest answer is to point to the unquestionably degraded <a href="https://mrshu.github.io/github-statuses/">reliability of the GitHub platform</a>. GitHub claims to guarantee <a href="https://checkupstream.com/reliability/github/sla">99.9% uptime</a> over the course of a calendar month, and while this goal sounds awesome, GitHub hasn't actually delivered on it in years (if ever). This point alone mandates that I look into having back-up options, as much like birth control, even 95% effectiveness, feels like asking for injury. Initially this desire for alternatives took the form of me spinning up a private Git server, but recently it's occurred to me that the proper thing to do, would be to also ensure there is a back-up for the public repositories as well. Codeberg in particular seems like a great choice for this, thanks to their recent commitments to banning both <a href="https://codeberg.org/Codeberg/org/pulls/1253#issuecomment-19820434">AI generated code</a> &amp; <a href="https://codeberg.org/Codeberg/org/pulls/1254">cryptocurrency related projects</a> from their platform. Two stances that I am a big fan of, even if I'm unsure exactly how thoroughly these things can realistically be prevented from entering the platform.</p>
<p>This does bring us however to the biggest elephant in the room: the AI and big-business infestation of what used to be trustworthy and helpful projects online. The temptation to blame the constant degradation of online platforms on Artificial Intelligence in The Year of our Lord, 2026 is strong, mostly because AI is to blame for a really big chunk of it. However, there are still examples of projects going downhill for the good ol' fashioned reasons. Most notably for me is <a href="https://itsfoss.com/news/bitwarden-quiet-changes/">Bitwarden</a>, a password-manager who's troubles this year have had absolutely nothing to do with forcing AI into the platform. Rather, Bitwarden's unannounced change in leadership earlier this year, and silent fiddling with the <a href="https://davescomputertips.com/bitwardens-new-direction-concerning/">GRIT acronym</a> on their website, in a fashion much the same as Google's famous removing <a href="https://en.wikipedia.org/wiki/Don%27t_be_evil">&quot;Do No Evil&quot;</a> as their motto, was more than enough to push me to close my Bitwarden account and move to <a href="https://keepass.info/">Keepass</a> a few months ago.</p>
<p>The problem is far bigger indeed than AI: Whether it's Meta buying Instagram and forcing everyone to an algorithmic timeline when I was in high school, or Amazon buying and slowly making <a href="https://imdb.com">IMDb</a> less and less useful as time goes on, or even my beloved <a href="https://watch.plex.tv">Plex</a> service insisting on shoving their own &quot;free&quot; TV and movies down my throat, and ignoring the fact that I've already added anything and everything I'd want to watch to my own server and just user their apps as a front-end to access that content - the list of projects that have <em>blown-up</em> just to blow up is staggering. In my view it has never been more important to be aware of where the tools you are using come from, and who owns them. Of course a great place to start is trying to shift usage towards open-source software, but even this, is not the golden ticket that it once seemed to be. Increasingly then, I have found my posture forced into a position of <em>always having a way out</em>. For just about every service I use to <a href="https://makc.co/terms/">power this website</a>, or just getting through my daily life I've found myself scheming up and documenting an escape plan. My dual pushing to GitHub and Codeberg is simply the first public example of this newfound philosophy.</p>
]]></description>
      </item>
    
      <item>
        <title>On, Good Advice Delivered Poorly</title>
        <link>https://makc.co/essays/good-advice-delivered-poorly/</link>
        <guid>https://makc.co/essays/good-advice-delivered-poorly/</guid>
        <pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>I recently finished watching <a href="https://www.youtube.com/watch?v=8KQFgWdiudo">the conclusion</a> of Linus Tech Tips's second Linux challenge. Unsurprisingly, much of the series focused on criticizing the <em>overly-aggressive</em> online Linux community. This is by no means a new criticism, but it did become a very central theme of the series. All three of the challenge's participants for example, admitted to relying in LLM's for some amount of Linux trouble-shooting, specifically to avoid having to sift through forums or Reddit posts where they might run into the stereotypical linux-brained neck beard who silently shouts things like: &quot;RTFM!&quot;</p>
<p>To be clear from the start: I'm not here to make the case that the online Linux community in places like Reddit or StackOverflow is some perfect utopia, which is only toxic to Windows snowflakes who just need to <em>toughen up</em> if they're going to us the world's greatest OS. It makes all the sense in the world that a user-base that makes up roughly <a href="https://gs.statcounter.com/os-market-share/desktop/worldwide">3% of desktop market-share world wide</a>, would be full of very opinionated users. MacOS itself has just over 10% of desktop market share as of May of 2026, and that community is also known for its own collection of contrarian and sycophantic members. However, I do think that this online conversation is a great lens by which to talk about the best skill I've ever developed, and one that I think everyone could benefit from: an ability to take good advice, even when it's delivered in the worst possible way.</p>
<p>In May of 2020 I decided to catalog my own attempt to switch to Linux full-time. I had spent the last few months learning how to control my Mac from the command line, and falling in love with CLI apps like Vim. For the first time, I thought it might be advantageous for me to abandon the OSX set-up I had come to love. I made the somewhat ill-advised decision to jump straight from a proprietary child-proof OS, straight to good ol' Arch Linux. In a two-part video appropriately titled: <a href="https://youtu.be/mxTDsqcaj1o">&quot;A Clueless Arch Linux Install Guide&quot;</a> I successfully managed to get an Arch install to boot, only to read the comments below the video and realize I'd made a long list of pretty critical errors in the install process. I immediately wiped the hard drive and started over again. I say this was an ill-advised decision however, only because there wasn't anyone advising me to do it. Nearly every online listicle or thread in 2020, and in 2026 advises that new users wade into the Linux waters with something like Linux Mint or Pop!_OS rather than jumping in head first with Arch.</p>
<p>The truth however, is that jumping straight to Arch Linux was the best decision I could have made, specifically because I did it in such a public way. Over the next year I uploaded all manner of <em>clueless Linux guides</em>. I focused on everything from setting up Proton and its various forks, to hacking on X11 window mangers, to creating miscellaneous bash scripts and hyper-niche ricing projects on my new Linux install. I learned an insane amount about Linux, and computing in general, because I thrust myself into the deep-end with tools that I barely knew how to use. But also, specifically because of the Reddit-posters and YouTube comment dweller's that everyone else seemed so eager to punch down on. All the way back in 2020 (it feels as if I might as well be talking about 1920), the option wasn't available to simply use an AI agent or LLM if I found the Reddit threads or YouTube comments to be rude. If I wanted to trouble-shoot a problem, or learn how to do something new on my system I had to dive head first into the very same waters that everyone complained so much about having to occasionally dip their toes into.</p>
<p>Part of my position here has nothing to do with the &quot;Linux community&quot; itself. I have worked more than a few jobs in construction, law enforcement, retail and even a restaurant job or two when I was younger. Across all of these industries, it's very rare to have anyone sit a new employee down and give them nice and gentle instructions for doing their job well. In retail or the restaurant industry, employees learn what not to do by getting yelled at by a disgusting Karen who has nothing better to do. In construction it's a foreman or GC doing basically the same thing. During my time as a prison guard, I was equally likely to be cursed at by the inmates I was in charge of and the officers worked with. But in all of these cases, I eventually realized that most people didn't get upset for no reason whatsoever. No matter how unnecessary or ridiculous an outburst might have been, it was pretty rare that there wasn't something I could trace the anger that was being directed at me back to. It didn't take me long to learn that my best move, if I wanted to be better at my job, or just wanted to avoid having to deal with unsavory people any longer than was absolutely necessary, was to find a way to accept the truth in an insult, and forget about the insult itself.</p>
<p>Throughout my years, I have often found that the most passionate, aggressive and down-right rude comments are often the ones that I could potentially learn the most from. For example: one of the most obnoxious comments that people tend to complain about follows a very specific format: (a) Person A asks for help with Software X, (b) Person B berates Person A for using Software X in the first place, and tells them to use Software Y. Clearly this is just about the worst way for Person B to go about spreading the word about Software Y. But it occurs to me that during the height of my brief career as a Linux YouTuber this was my favorite type of comment to receive. Finding cool tools for streamlining my workflow or ricing my window manager was a big part of the difficulty in using Linux, not because cool open-source projects didn't exist, but because they didn't get a lot of clicks, and certainly weren't going to pop up at the top of any search engine results. I've lost track of the number of tools that I use on a daily basis, which I only discovered because of a snarky comment that followed this exact template: &quot;Why are you using X? Y exists and it's way better.&quot; Even as recently as last month, when I uploaded a video complaining about GitHub's increasing unreliability and lamenting the fact that I didn't know of any good alternatives, I was thrilled to find that I immediately had a comment below the video providing exactly what I had said I wanted: good alternatives to GitHub:</p>
<img src="img/2026-06-14-A.jpg"  alt="An agressive comment under a linux video" class="half">
<p class="caption">The pinned comment underneath my <a href="https://www.youtube.com/watch?v=HvklHoL7Ano" target="_blank">most recent YouTube video</a> about setting up a private git server</p>
<p>As I have learned to expect from being in situations like this many times before, both <a href="https://sourcehut.org/">SourceHut</a> and <a href="https://codeberg.org/">Codeberg</a> are much closer to the kind of alternatives to GitHub I was looking for. Even the most objectively rude parts of the comment above were also, 100% true. My proper microphone's audio was corrupted while recording (Pulse Audio weirdness), and rather than re-recording the video, I opted to clean up my laptop's on-board audio as best as I could and run with it. I also did leave in much more explanation than I normally would have in that particular video, as I was attempting to aim for a target audience that was slightly less tech-savvy than the audience that my videos usually reach. This is precisely the kind of comment that I love to get. Would it be better if it were phrased a bit kinder? I guess. But the crazy thing is, after years of conditioning myself to view every comment or piece of feedback through a lens of just extracting what's helpful for me, I barely even register the parts that are objectively insulting. I read a comment like the one above, note the resources that could be helpful for me (SourceHut and Codeberg), accept the feedback that's true (the lower than average audio quality), and ignore the parts that are irrelevant (the pacing of the video being slower than normal).</p>
<blockquote>
<p>&quot;When you wake up in the morning, tell yourself: The people I deal with today will be meddling, ungrateful, arrogant, dishonest, jealous, and surly. They are like this because they can’t tell good from evil. But I have seen the beauty of good, and the ugliness of evil, and have recognized that the wrongdoer has a nature related to my own—not of the same blood or birth, but the same mind, and possessing a share of the divine. And so none of them can hurt me. No one can implicate me in ugliness. Nor can I feel angry at my relative, or hate him. We were born to work together like feet, hands, and eyes, like the two rows of teeth, upper and lower. To obstruct each other is unnatural. To feel anger at someone, to turn your back on him: these are obstructions.&quot;</p>
</blockquote>
<p class="attr">
Marcus Aurelius<br>Meditations 2:1
</p>
<p>This is a problem as old as time, but the good news is that a fix has existed for as long as the problem has: Accept the fact that certain people - most people - are horrifically bad at articulating themselves clearly. It would be wonderful if every teacher was patient, every boss with a great leader, not just an ill-tempered martinet, and it would be great if every online commenter was empathetic and kind. But they aren't, haven't ever been, and likely won't ever be. Rather than evaluating feedback in it's totality, ask yourself: &quot;Is any part of this valuable?&quot; It's easy to waste an enormous of energy trying to improve the quality of feedback we receive, but it seems more than worth spending a bit of this energy on improving our ability to parse imperfect feedback as well. If we can learn to strip out the worthwhile content from even the most aggressive exchanges, we can become the world's best students. We don't need to have perfectly trained, well meaning instructors, we don't need instructors at all. Our desire to learn more, and willingness to accept human nature has already given us the ability to learn anything, from anyone, at any time, no matter how difficult they make it for us to do so.</p>
]]></description>
      </item>
    
      <item>
        <title>A Tale of AI Betrayal &amp; Snapshotting Victory!</title>
        <link>https://makc.co/essays/gpt-clusterfuck/</link>
        <guid>https://makc.co/essays/gpt-clusterfuck/</guid>
        <pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>At the beginning of this year, I decided to leave the quiet comforts of the <a href="https://aur.archlinux.org/">AUR</a>, Pacman and my purpose built, minimal system for a bit of <em>distro-hopping</em>. I've been using Linux full time for about five years now. That journey <a href="https://youtu.be/mxTDsqcaj1o">began</a> with, and has maintained Arch Linux as a constant for it's duration. So, as any readers familiar with the ins-and-outs of Linux distributions might have guessed, I spent a bit of time playing around with Ubuntu, found out that I quite liked Fedora, but by the beginning of March, was ready to return to good ol' Arch Linux. To keep things interesting (and because I was a bit pressed for time), I opted to test drive <a href="https://cachyos.org/">Cachy OS</a>, a relatively new Arch derivative that boasts some interesting features like a custom kernel tuned with the <a href="https://docs.kernel.org/scheduler/sched-eevdf.html">EEVDF scheduler</a>. This is how I ended up inadvertently setting up my first Linux box that featured automatic Snapshotting, a fact that will become relevant later.</p>
<p>Now for an embarrassing confession: At the beginning of this year, I also began to toy around with Artificial Intelligence a good bit. I never enjoyed using ChatGPT (my drug of choice) for anything big, anytime I did something like set-up a website with a new tool, or install Arch Linux I'd reach for proper documentation or Wikis. But I did develop a habit of using ChatGPT to find <em>quick fixes</em> when I was in a pinch, and I did have it generate one <a href="https://youtu.be/FdKy0F0sVRw">YouTube thumbnail</a> for me (I'm now refusing to change the thumbnail so I'll be reminded of how dumb an idea that was every-time I see the image on my website). The disturbing truth is that I was never even convinced of AI's <em>superior intelligence</em>. I never even thought of GPT as <em>a better Google</em>, as many less sycophantic users claim it to be. Very early on in my usage, I found that these tools couldn't write, were prone to <a href="https://openai.com/index/why-language-models-hallucinate/">hallucinations</a>, and couldn't be trusted for anything mission critical (unless that mission critical thing is <a href="https://www.theguardian.com/news/2026/mar/26/ai-got-the-blame-for-the-iran-school-bombing-the-truth-is-far-more-worrying">choosing a target for missile strikes</a>). Unfortunately, I also just happened to notice that, at least when it came to small Linux eccentricis or low-level coding questions, GPT and other models were pretty good at providing a <em>good 'nough</em> style of fix, so long as the questions were well documented elsewhere on the internet and GPT could steal the answer from Stack Overflow or others.</p>
<p>So a few weeks ago when I finally made the shift to a Wayland window manger (better late than never), and started toying around with <a href="https://https://hypr.land/">Hyprland</a>, I ended up using the GPT more than I really care to admit. To be clear, Hyprland's documentation is really solid, and I did rely on it quite heavily as well, but for anything that was an abstraction on top of Hyprland I was running to the AI for quick fixes that I knew I'd have to redo later when I learned more and realized that I'd fixed my problem in the dumbest possible way. This was precisely the kind of fix I was looking for the first time that I took my laptop to work and, upon finding my battery already run down to 17%, realized that the hypridle config I had set up was clearly not working as intended. So I ran to the AI overlords begging for a quick fix that could let me solve the problem and get on with what I had intended to use my laptop for.</p>
<p>Almost immediately I hit a wall with ChatGPT: Despite the fact that I had mentioned I was running Cachy OS with the default options selected during install, even specifically stating that I was using the Limine boot manager, I kept getting nonsensical answers that suggested I make changes to GRUB andior Systemd configs. But I was committed to the lazy solution, and after a few minutes of back and forth I was finally given an answer that sounded reasonable enough: my laptop's Nvidia GPU, and the dreaded proprietary drivers I have to run so I can use Davinci Resolve were the culprit (every Linux users favorite nemesis). As is ChatGPT's way, it also provided me with a simple one line fix to paste into my Limine config:</p>
<pre class="language-bash"><code class="language-bash">cmdline: <span class="token assign-left variable">root</span><span class="token operator">=</span>UUID<span class="token operator">=</span>xxxxx rw quiet splash <span class="token assign-left variable">nvidia.NVreg_PreserveVideoMemoryAllocations</span><span class="token operator">=</span><span class="token number">1</span> <span class="token assign-left variable">nvidia_drm.modeset</span><span class="token operator">=</span><span class="token number">1</span></code></pre>
<p>Having never actually edited this Limine config before, I had a few follow up questions for ChatGPT. Specifically I was unsure whether all of these options needed to go on one <em>cmdline:</em>, or if I should separate out the <em>PreserveVideoMemoryAllocations=1</em> to it's own line. While asking this I ended up actually copy &amp; pasting into Chat GPT my current Limine <em>cmdline:</em>, including the proper UUID identifier for my SSD:</p>
<pre class="language-bash"><code class="language-bash">cmdline: quiet nowatchdog splash rw <span class="token assign-left variable">rootflags</span><span class="token operator">=</span>subvol<span class="token operator">=</span>/0 <span class="token assign-left variable">root</span><span class="token operator">=</span>UUID<span class="token operator">=</span>8b17bdfb-efb4-41a-852342</code></pre>
<p>It was at this point that ChatGPT suggested the most feral and unhinged thing I'd ever see in my days of relying on robots to do human work: Simply append GPT's recommendation onto the end of my existing Limine <em>cmdline</em>, repeating commands like <em>rw</em>, <em>quiet</em> and <em>splash</em>, and leaving in the boilerplate: <em>root=UUID=xxxxx</em>. I can't get the screen-shot because <em>(!Spoilers!)</em> I ended up deleting my ChatGPT account, but scout's honor, no BS, this is actually what GPT recommended I paste into my config:</p>
<pre class="language-bash"><code class="language-bash">cmdline: quiet nowatchdog splash rw <span class="token assign-left variable">rootflags</span><span class="token operator">=</span>subvol<span class="token operator">=</span>/0 <span class="token assign-left variable">root</span><span class="token operator">=</span>UUID<span class="token operator">=</span>8b17bdfb-efb4-41a-852342 <span class="token assign-left variable">root</span><span class="token operator">=</span>UUID<span class="token operator">=</span>xxxxx rw quiet splash <span class="token assign-left variable">nvidia.NVreg_PreserveVideoMemoryAllocations</span><span class="token operator">=</span><span class="token number">1</span> <span class="token assign-left variable">nvidia_drm.modeset</span><span class="token operator">=</span><span class="token number">1</span></code></pre>
<p>One doesn't need to be a Linux expert to know just how stupid this suggestion was. I'll save you from all the excuses that I want to make to present myself as less stupid than I truly was in this moment, as the truth is that I put far too much trust in a fundamentally untrustworthy tool. Without even bothering to read the line that was so quickly offered up for my copy &amp; pasting needs, I dropped the line into my Limine config and had typed <em>reboot</em> into a terminal before I had time to register a second thought. The end result? For the first time in five years of using Linux full time I manged to actually trigger a straight up Linux blue screen, a feature that I actually didn't even know existed. Full on kernel panic: Cachy OS had blocked out the root user, and refused to even try to boot my OS; not surprising as I had mapped out the UUID of my boot drive as: <em>xxxxx</em>.</p>
<p>But what then is a good little fake Arch-BTW user meant to do when the AI overlords brick their computer? We go running back to AI for the fix of course. To GPT's credit (I guess), when pressed a second time and told &quot;WTF? My computer won't boot now!&quot;, it did pretty much immediately identify the problem as the fact that it had recommended the ludicrous <em>cmdline</em> than included a UUID path to nowhere. GPT quickly gave me instructions to fix the problem that included getting a Linux installer on a USB key, booting off of that, mounting my nvme drive and editing the Limine config to make the drive bootable again. But this solution was no good. I was stuck at work, alone in a guard shack with no USB drive, no way to download a Linux installer and unable to leave for another ten hours. No matter how desperately I pleaded with this serf of a demigod there was no way to solve my problem without the USB key and Linux installer that I didn't have. <strong>Fun fact</strong>: I actually did have a Tails OS USB in the glove box of my car (I always do), but I was quite sleep deprived and this fact did not enter my mind.</p>
<p>I threw my phone down on the desk in despair, and leaned back in my chair starring back at the mess I had created in front of me: The Limine boot menu with my boot drive selected, but the knowledge that if dared I press <em>enter</em> the system would just try and fail to boot once again.</p>
<p>That's when I noticed it: a submenu in the Cachy OS boot options labeled: <strong>Snapshots</strong>. As mentioned earlier, I wasn't even aware that Cachy OS turned on btrfs Snapshots by default during the installation process, but here was a potential solution starring back at me. I opened the submenu and noticed that there was a Snapshot available from only about ten minutes ago. Cachy OS automatically has btrfs create a snapshot every time a full system upgrade is triggered, and by mere coincidence I happened to have run the now life-saving <em>sudo pacman -Syu</em> command moments before starting to fiddle with the Limine config. I hit enter, and seconds later was met with the familiar Hyprland userspace I had built over the past few days. I quickly opened up the Limine config and found that it contained the garbled mess that I had pasted in minutes ago. I had found my solution: (a) fix the config, (b) reboot, (c) problem solved.</p>
<p>Like a good little drone though, I first opened up Firefox and ran the idea past my old pal ChatGPT. Sure it had screwed me once already tonight. But surely it would be able to tell me if this was the fix that I thought it would be? Once again I was hit with devastating news. ChatGPT swore up and down that this would not be the fix I was looking for. One couldn't just boot from a Snapshot, fix an error and reboot to solve a problem. GPT stuck to it's guns. The only way to solve my problem was to follow its original advice: boot off a USB, mount my boot drive and manually edit the config.</p>
<p>Something inside me stirred. What was this? A desire to thrust off the shackles placed on me by my over-reliance on this inhuman scourge on my nation's power and water resources? An inkling of courage? A returning ability to think independently? What was the point of a snapshot if I couldn't use it to fix config errors? Was this not the whole point of the feature in the first place? I gathered up all my free will and for the second fateful time this night typed those fateful letters into my terminal: <em>reboot</em>. Once again I was greeted by the Limine boot menu. I made sure that I was launching not from the snapshot of ten minutes ago, but instead from my system's boot drive. I waited, fingers crossed, breath held in anticipation until finally: success! My risky (not risky) endeavor was rewarded! Once again I could operate a system without the infernal advice of an AI agent spoon feeding me commands to paste into terminals! Free from my self-imposed, headless pillory, I was again my own man! After all is there a more masculine story than finding a way to solve problems of one's own design‽</p>
<hr>
<p><strong>A note</strong>: While the ultimate resolution of this story was 100% me setting up a DNS level block on ChatGPT's website; I do think I should say a quick word on <em>why</em>. As mentioned a few times here, AI agents are more than capable of providing quick and dirty fixes to some technical problems, and probably a good bit more if they are employed correctly. But I have come to the conclusion that the <em>reward</em> is simply not worth it. Not because these fixes are sub optimal (though they usually are), but rather, because there is great knowledge to be had through the process of trouble-shooting a process the <em>old fashioned way</em>. Much of what I have learned about any number of topics, but particularly about technical subjects has been learned because I found myself having to read <em>more</em> to solve problems. When troubleshooting this way it's far harder to find fixes that just work, without having any clue as to <em>why they work</em>. I'm not suggesting that this doesn't still happen from time to time, my personal GitHub repositories are sufficient proof that it does, but learning a solution for yourself, in my opinion, has a far better end result than relying on an AI agent to find one for you.</p>
<p>Also, in case anyone was wondering: GPT's initial diagnosis of my Nvidia GPU being the culprit was incorrect from the outset.</p>
]]></description>
      </item>
    
      <item>
        <title>Becoming an Empire</title>
        <link>https://makc.co/essays/becoming-an-empire/</link>
        <guid>https://makc.co/essays/becoming-an-empire/</guid>
        <pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A couple of days ago, Donald Trump’s administration carried out a military operation in Venezuela. A Delta Force unit was deployed to <a href="https://www.dropsitenews.com/p/united-states-venezuela-nicolas-maduro-attacks-regime-change-bombing">abduct President Nicolás Maduro</a> and his wife. The operation was accompanied by bombing inside the country, and between the airstrikes and the raid itself, roughly eighty people were killed. By all available accounts, the plan unfolded exactly as intended. Maduro was transported to New York to stand trial—though it remains unclear what, precisely, he has been charged with. Shortly thereafter, the administration announced that Venezuela’s acting leadership was being ordered to hand over up to 50 million barrels of oil to the United States. At the same time, U.S. forces <a href="https://www.dropsitenews.com/p/united-states-military-venezuela-russia-trump-administration-cabello-dhs-arrests-minneapolis">seized a Russian ship</a> that had been prevented from docking during the month-long naval blockade and bombing campaign along Venezuela’s coast.</p>
<p>This is, objectively, one of the most absurd positions the American military has ever placed itself in. However, this moment is not an aberration, rather, a logical extension of how American political elites have understood—and justified—foreign policy since at least the end of World War II, and arguably long before that.</p>
<p>I haven’t ever written about foreign policy before. That’s not because the subject hasn’t held my interest over the past decade. It’s because—embarrassingly—I bought into propaganda produced by people who claimed to understand the world better than the average American. I deferred to “serious” commentators. I trusted the experts.</p>
<p>That posture was summarized perfectly in a recent <a href="https://www.independent.co.uk/news/world/americas/us-politics/hillary-clinton-gaza-israel-tiktok-b2877089.html">interview with Hillary Clinton</a>, in which she reaffirmed her support for Israel amid its occupation and genocide of Gaza. She offered no substantive defense of her position. Instead, she scolded young people for forming opinions based on social media rather than through proper channels—traditional news outlets, which, she implied, alone possess the necessary historical context to evaluate Israel’s actions responsibly.</p>
<p>I’ve thought quite a bit about Israel for years, nearly as long as I’ve thought about politics, and I’ve always found Zionism to be an impossible position to support. But I’ve always hit a wall: commentators I genuinely respected—Sam Harris and Bill Maher, among others—insisted that any discomfort with Israel’s occupation of Palestine could be resolved by acquiring a fuller understanding of history. And I believed them.</p>
<p>Part of that was because the apparent hypocrisy seemed too extreme to actually be hypocrisy. Sam Harris, for example, is a fierce atheist who has spent years arguing <a href="https://www.youtube.com/watch?v=vln9D81eO60">against religious influence in government</a>, particularly Christian and Muslim influence in Western states. So when Harris <a href="https://www.samharris.org/blog/5-myths-about-israel-and-the-war-in-gaza">argued that Israel</a>, because of its unique history, was perhaps the only state in history justified in existing as a religiously defined, authoritarian ethnostate, I assumed I must be missing something.</p>
<p>I wasn’t.</p>
<p>What looked like a contradiction too wild to be real was exactly that. I spent years reading, listening, and actively attempting to propagandize myself into believing the Zionist position. But it was always a lie. The United States has propped up one of the most brutal state actors in the Middle East for decades, while Americans who rejected the propaganda were dismissed as ignorant or naïve—told we simply lacked sufficient historical understanding. Given that the history stretches back thousands of years, it’s a clever argument. It’s also an absurd one.</p>
<p>But this essay isn’t just about Israel.</p>
<p>It’s about American military interventionism as a whole: regime change, occupation, coercion, and influence in Korea, Vietnam, Afghanistan, Cuba, China, Russia, Ukraine—virtually every country where American boots have touched the ground since World War II, and many before that. What’s most disturbing isn’t just that the United States has behaved as an imperial power every bit as ruthless as the British Empire it once rebelled against. It’s that a political elite has spent the last century convincing ordinary Americans that criticizing these actions is ignorant, unpatriotic, or dangerous—despite dissent being the very first right enshrined in the Bill of Rights.</p>
<p>There are, of course, many reasons elites pursue war. One of the most obvious is profit. But unlike empires of old, which plundered openly, modern American capitalism has become far more adept at extracting wealth without distributing it. The Iraq War under George W. Bush—engineered by figures like Dick Cheney—made billions for defense contractors and energy interests while leaving Iraq in ruins.</p>
<p>The Trump administration’s action in Venezuela may ultimately surpass even that in its brazenness. Reports already indicate that oil executives at companies like ExxonMobil were <a href="https://www.americanprogress.org/article/trumps-military-intervention-in-venezuela-serves-big-oil-not-the-american-people/">informed of the operation in advance</a>, while opposition party leadership learned about it the same way the rest of us did—after it happened. At least Bush’s administration went through the motions of lying about weapons of mass destruction and seeking congressional authorization. This time, even the pretense has been dropped.</p>
<p>Still, there’s a deeper issue at play—one I’ve clearly been circling for years without naming. It’s the corrupted legacy of what figures like John Adams articulated as the Monroe Doctrine.</p>
<p>Originally, the doctrine wasn’t a blank check for imperial conquest. It was an acknowledgment that great powers exert influence over the territories they occupy—and a warning that European empires should stop treating the Western Hemisphere as their playground. Early American involvement in South America often aimed—however imperfectly—at undermining Spanish, French, and British imperial control. It coincided with westward expansion and the belief that a fledgling republic had the right to determine its own sphere of influence.</p>
<p>None of this is meant to romanticize early American expansion. That period included genocide against Native Americans, a war of conquest against Mexico, and countless other atrocities. Much of America’s territorial growth came not through legitimate purchase, but through violence indistinguishable from the imperialism it claimed to oppose.</p>
<p>The point is narrower than that: I don’t believe the Monroe Doctrine was conceived as a justification for indiscriminate invasion or resource theft. Early American military action was debated intensely by people who had personally experienced imperial subjugation. For a brief historical moment, the United States genuinely inspired independence movements abroad. That’s why statues of George Washington still stand in countries across South America and Europe.</p>
<p>Power corrupts even the most idealistic ideas.</p>
<p>The Monroe Doctrine, like so many philosophies before it, was stripped of its original intent and repurposed to justify exploitation. Nietzsche’s philosophy was bastardized by Ayn Rand. Jesus’ teachings mangled by the Catholic Church. And the writings of America’s founders have been contorted by generations of power-hungry political and corporate elites.</p>
<p>As much as I admire the early ideals of Adams, Jefferson, and Franklin, the nation they founded has become precisely what they once fought against. There is no hidden historical context that makes this make sense. No deeper understanding that redeems it.</p>
<p>War is America’s primary export. We no longer manufacture meaningfully. We don’t lead. We invade. We extract. We destroy. And we leave behind rubble, resentment, and mass graves.</p>
]]></description>
      </item>
    
      <item>
        <title>Custom Syntax Highlighting With 11ty</title>
        <link>https://makc.co/essays/syntax-highlighting-in-11ty/</link>
        <guid>https://makc.co/essays/syntax-highlighting-in-11ty/</guid>
        <pubDate>Wed, 31 Dec 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Unless you stalk my <a href="https://github.com/makccr">GitHub account</a> relentlessly, you are probably unaware of it, but <a href="https://makc.co">my website</a> was developed using the static site generator, <a href="https://www.11ty.dev/">Eleventy (11ty)</a>. 11ty offers incredibly easy web development, not only because of its capability to play nicely with a massive amount of different file formats, but also because of its massive library of NodeJS plugins. Throughout the process of developing and maintaining my website, I have found that adding a new feature to my site is often as simple as running <em>npm install X</em> and making a small change to the <em>eleventy.config.js</em> file in my root directory. Earlier this week, when I decided that I wanted to add syntax highlighting to HTML code blocks on my site, the process looked to be just that simple.</p>
<p>The most complicated part of this process is solved by 11ty itself. Typically, any page that I can get away with not writing custom HTML for, I simply write in the Markdown format. Using 11ty, however, this is no problem. Once the site is compiled, Markdown is automatically converted to HTML, and I can easily style any text that was marked as code in Markdown as a class separate from the site’s normal text. Even when attempting to take the next step and add individual syntax highlighting to these code blocks, I quickly came across a tool called <a href="https://prismjs.com/">PrismJS</a> that seemed to do exactly what I needed it to. Simply running one command in the terminal and adding some text to my <em>eleventy.config.js</em> seemed like it would <em>just work</em>, just the way I’ve become accustomed to things in the 11ty ecosystem working.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> @11ty/eleventy-plugin-syntaxhighlight --save-dev</code></pre>
<pre class="language-bash"><code class="language-bash">const syntaxHighlight <span class="token operator">=</span> require<span class="token punctuation">(</span><span class="token string">"@11ty/eleventy-plugin-syntaxhighlight"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module.exports <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>eleventyConfig<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    eleventyConfig.addPlugin<span class="token punctuation">(</span>syntaxHighlight<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token builtin class-name">return</span> eleventyConfig<span class="token punctuation">;</span> 
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>However, after hooking everything up and compiling my 11ty site, I found that nothing appeared to have changed. I did a bit of research and found a great article by Martin Gunnarsson, in which he detailed his own experience <a href="https://www.martingunnarsson.com/posts/eleventy-syntax-highlighting/">going through the same process</a>. He ran into problems when he didn’t label code blocks through Markdown as PrismJS was expecting. If you are unfamiliar with Markdown, there are three different ways to denote code in Markdown:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="token list punctuation">1.</span> Inline markdown where only <span class="token code-snippet code keyword">`a few words`</span> are denoted as code

<span class="token list punctuation">2.</span> A proper code block: 
'''
console.log("some code");
'''

<span class="token list punctuation">3.</span> A code block with a label: 
'''javascript
console.log("some code");
'''</code></pre>
<p>It turns out that the syntax highlighting plugin from 11ty only supports the third option: a full code block with a label at the top line declaring the language to which the syntax highlighting needs to be applied. Unfortunately for me, this is how I had already formatted damn near all of my code blocks site-wide. So I went ahead and took a closer look:</p>
<img src="img/2025-12-31-A.jpg" alt="Firefox inspector window proving that the plugin was working" class="thirds">
<p>After taking a look at the inspector window in Firefox, it became clear to me that the PrismJS plugin was doing quite a bit. When rendering the HTML document for my site, 11ty no longer was simply putting all of the text in a generic <em>code</em> block, but was instead creating individual CSS classes for <em>language-javascript</em>, <em>language-shell</em>, and so on - as well as breaking down the contents of the block even further into classes for <em>punctuation, string, operator, comment</em> and several other elements that made up the blocks. The only problem? The text in the code block was still the same color as generic text, no styling at all. This was odd, but it also wasn’t the worst thing in the world. Knowing myself, I knew I’d eventually want a way to customize the colors and makeup of the individual elements myself anyway, so I began the process of trying to compile a concrete list of every class that the syntax highlighting plugin would create for every single one of the nearly 300 languages that PrismJS supports -- no easy task.</p>
<p>But, before I break down how I solved that problem, it is worth noting that 11ty’s plugin should automatically add syntax highlighting when it is installed. I have since confirmed that the only reason this was not happening on my site is that I had previously added customization to the “<em>code”</em> and “<em>pre”</em> CSS classes, changing the text color. After removing that customization, the syntax highlighting worked as expected. But unfortunately, I didn’t confirm that this was the case until after I had already spent nearly a full workday figuring out a way more complex workaround, which just happens to add a higher level of customization.</p>
<p>It only took me a matter of minutes to find a way to isolate every one of these class names that PrismJS was creating. Simply opening the Console in Firefox and typing the following showed me every class being rendered on an individual page.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token punctuation">[</span><span class="token operator">...</span><span class="token keyword">new</span> <span class="token class-name">Set</span><span class="token punctuation">(</span>
  <span class="token punctuation">[</span><span class="token operator">...</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'pre code span'</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">el</span> <span class="token operator">=></span> el<span class="token punctuation">.</span>className<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">]</span>

<span class="token comment">// That returned: </span>
<span class="token function">Array</span><span class="token punctuation">(</span><span class="token number">9</span><span class="token punctuation">)</span> <span class="token punctuation">[</span> <span class="token string">"token function"</span><span class="token punctuation">,</span> <span class="token string">"token keyword"</span><span class="token punctuation">,</span> <span class="token string">"token operator"</span><span class="token punctuation">,</span> <span class="token string">"token punctuation"</span><span class="token punctuation">,</span> <span class="token string">"token string"</span><span class="token punctuation">,</span> <span class="token string">"token function-variable function"</span><span class="token punctuation">,</span> <span class="token string">"token parameter"</span><span class="token punctuation">,</span> <span class="token string">"token list punctuation"</span><span class="token punctuation">,</span> <span class="token string">"token code-snippet code keyword"</span> <span class="token punctuation">]</span></code></pre>
<p>Of course, I realized now that using this method, the only way I’d ever be able to make sure that every piece of syntax was formatted properly would be to somehow have a snippet of every single one of PrismJS’s 297 supported languages in one Markdown document. So I got to work.</p>
<p>A few hours later, I had a single Markdown file that contained a one-line snippet of all 297 languages (or pretty close to all of them), and once I compiled the site and ran my console code again, I had a <em>comprehensive enough</em> list of every class that PrismJS could potentially create on my site. Once I had that list, the process was as simple as creating a custom set of color variables at the top of my main CSS page, which I themed using the <a href="https://github.com/morhetz/gruvbox">Gruvbox</a> color scheme:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span> <span class="token comment">/* Base Settings */</span>
  <span class="token property">--code-aqua</span><span class="token punctuation">:</span> #8ec07c<span class="token punctuation">;</span>
  <span class="token property">--code-green</span><span class="token punctuation">:</span> #b8bb26<span class="token punctuation">;</span>
  <span class="token property">--code-purple</span><span class="token punctuation">:</span> #d3869b<span class="token punctuation">;</span>
  <span class="token property">--code-blue</span><span class="token punctuation">:</span> #83a598<span class="token punctuation">;</span>
  <span class="token property">--code-yellow</span><span class="token punctuation">:</span> #fabd2f<span class="token punctuation">;</span>
  <span class="token property">--code-red</span><span class="token punctuation">:</span> #fb4934<span class="token punctuation">;</span>
  <span class="token property">--code-gray</span><span class="token punctuation">:</span> #a89984<span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token comment">/* Defining color scheme */</span></code></pre>
<p>And after setting up color definitions, I began the work of applying each color to the proper CSS classes. Luckily for me, 297 different languages only generated 34 different CSS classes that I needed to assign, but once again, this process took a few hours of experimenting to get everything right. Eventually, I managed to get everything where it was meant to be and ended up with what should be proper syntax highlighting for every single one of PrismJS’s supported languages.</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* CODE COLORS */</span>
<span class="token selector">.token.comment, .token.prolog, .token.doctype, .token.cdata</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-gray<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token property">font-style</span><span class="token punctuation">:</span> italic<span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.operator, .token.assign-left, .token.builtin, .token.entity, .token.namespace</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-aqua<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.string, .token.char, .token.attr-value, .token.inserted</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-green<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.attr-name, .token.property, .token.keyword, .token.selector</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-purple<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.function</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-blue<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.number, .token.boolean, .token.variable, .token.parameter, .token.class-name, .token.type</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-yellow<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.regex, .token.symbol, .token.punctuation, .token.constant, .token.tag</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-red<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.bold</span> <span class="token punctuation">{</span> <span class="token property">font-weight</span><span class="token punctuation">:</span> bold<span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector">.token.italic</span> <span class="token punctuation">{</span> <span class="token property">font-style</span><span class="token punctuation">:</span> italic<span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector">.token.deleted</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-red<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector">.token.important</span> <span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-red<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token property">font-weight</span><span class="token punctuation">:</span> bold<span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token selector">.token</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--code-gray<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre>
<p>It does seem worth mentioning once more that this whole process is not necessary to get syntax highlighting working with 11ty. But if anyone else out there just happens to want to use a custom color scheme that isn’t supported by Prism, or finds themselves in a position where they need to customize any of the syntax highlighting that PrismJS generated, hopefully you’ll come across this essay and not waste the six hours that I wasted figuring this all out.</p>
]]></description>
      </item>
    
      <item>
        <title>The Death of the Internet</title>
        <link>https://makc.co/essays/the-death-of-the-internet-and-true-conenectivity-in-exchance-for-para-social-cyborgization-of-us-all/</link>
        <guid>https://makc.co/essays/the-death-of-the-internet-and-true-conenectivity-in-exchance-for-para-social-cyborgization-of-us-all/</guid>
        <pubDate>Wed, 24 Sep 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>I got my first computer when I was eight years old. It wasn’t anything fancy, an old Windows 95 box that barely worked. But when my parents decided to upgrade to a shiny, new, Dell Machine running Windows XP, I was more than happy to do whatever it took to get my hands on the old family PC. The deal that I struck up with them took place in early February of my seventh year. They agreed that if I kept my room clean for the remainder of the year, that come Christmas Day, the computer would be mine. Needless to say, there wasn’t so much as a sock on the floor of my room for the rest of the year. At the time I shared a room with my younger brother, and I specifically remember having to work twice as hard, cleaning up not only my own mess, but his as well. Come Christmas day though, all the work paid off and the computer was all mine.</p>
<p>If you lived through this era of computing, I probably don’t need to tell you there wasn’t a whole hell of a lot that I could actually do with the machine. A lone Windows 95 Box without an internet connection in the middle of the south Texas woods in 2005 or 2006 wasn’t exactly a portal to anything dangerous or exciting. But for an eight year old me, it was more than enough to capture my attention. I spent hours on end sitting in front of that old PC, toying around, learning everything I could about it. I spend days cataloging every CD in the house. Ripping individual CDs, manually adding all the metadata that was available to add in an early version of Windows Media Player. I toyed around with the screen savers (particularly the <a href="https://inv.nadeko.net/watch?v=oRL5durPleI">maze screen saver</a>), breaking random things, painstakingly fixing them - I even managed to convince myself that I had drawn the attention of law enforcement one night when I tried to print without a printer attached, and got the dreaded message:</p>
<img src="img/2025-09-24-A.png" alt="Windows 95 Illegal Operation Error Message" class="half">
<p class="caption">The dreaded Windows 95 <em>Illegal Operation</em> Error Message</p>
<p>Even when I managed to find some old Ethernet wire and figure out how to run myself a dial up connection under the house to my bedroom, all I ever really managed to figure out how to do was slowly download album art for my Media Player library. Still I spent days manually downloading images online, often queuing up ten or fifteen album covers to download before going to sleep and adding them to my Media Library when I woke up the next morning.</p>
<p>Eventually though, things changed. At some point the old dial-up connection was replaced with a fancy new 5Mbps internet connection. The old Windows 95 box got replaced with whatever the cheapest laptop Dell sold at the time was, and my days of cataloging old CDs were replaced by days of hanging out on sites like <a href="https://vidoecopilot.net">VideoCopilot</a>, <a href="https://https://creativecow.net/">Creative Cow</a>, Reddit, and more than anything else; YouTube. Now, my ability to learn about my new computer was virtually unlimited. There wasn’t a single thing I wanted to do that I couldn’t find a video tutorial or online article telling me how to do. These were the early days of the internet culture we find ourselves a part of now. Back before the days of hyper-monetization, algorithms and far before you heard the word “influencer” as much as you hear the word “breakfast”. Even when I did get on sites like Facebook or Twitter there was no algorithm pushing some content to the top and silencing other content. Posts were simply displayed chronologically, and seeing posts from someone you didn’t explicitly follow was almost unheard of. The content was still out there, you could still learn about any topic you wanted to, or jump down any internet rabbit hole that caught your fancy; but you did have to go find the content yourself as opposed to having a few billion dollar companies simply decide for you what you’d be viewing for the day the second you open one of their apps.</p>
<p>These were the glory days of social media. People put content online not because they thought it was a good way to make some extra money, but because they had a real desire to create and contribute to the zeitgeist. Communication was different. There were trolls and people who would say disgusting things online that they’d never say in person. Buy largely you could effectively communicate and learn from complete strangers in a way that I don’t know is possible today. I spent much of my teenage years on Twitter connecting with other aspiring filmmakers and creative-types around the world. I was able to learn a massive amount on the same platforms that I now wan’t nothing to do with simply by asking. I could message industry professionals and have a conversation with them as if I was just a co-worker, and not a thirteen year old kid. I even found my first job the same way I had learned the skills to actually do that job. I simply saw a company that was hiring a motion graphics artist, clicked on the Google Form, saw they wanted a link to a demo reel, and then spent all night putting together a <a href="https://inv.nadeko.net/watch?v=ec-SGLA0d1U">reel</a> to send them by the next morning. It wasn’t anything special, thirty seconds or so of miscellaneous designs, half of which I put together the night before, but it was enough to get me in the door.</p>
<p>By the time I was graduating high school in 2016, the seams were already more than apparent. What was once, through Twitter, a platform that allowed me to talk, learn and share my creations; became a platform that only accepted arguments and inflammatory content. It was around this time that Twitter first moved away from a chronological feed and replaced the feed with an algorithm that decided for users what they’d like to see. There was resistance to this of course. For years I continued to use Twitter and simply opted to use third-party apps that would allow you do nix the algorithm and simply use Twitter the way it was originally designed to be used. But the world had already shifted. It wasn’t long before Facebook, Instagram, YouTube, Reddit and every one else began to embrace the world of algorithms and trading in user data. It’s not hard to see why, it’s clearly profitable for the companies, but it’s also not hard to see the damage that this shift has done to our culture.</p>
<p>I, like everyone else saw the recent footage of Charlie Kirk’s murder. The difference is that while everyone in the news media and general public seemed to be shocked and appalled by the social media firestorm that followed in the immediate aftermath, I was not. Whether we’re talking about the way that people on scene immediately tried to exploit the murder and start live-streaming, being sure to tell everyone their TikTok handle Or the outrageous posts from people on the political left attempting to quietly justify Kirk’s murder Or the equally outrageous attempts from the political right to use the murder as their vehicle to advance their political agenda What’s very clear is that for a not insignificant percentage of Americans, Kirk’s assassination was not viewed as a murder at all, but rather, nothing more than an opportunity.</p>
<p>Shocking behavior? Yes. But, un-predictable? Not even close. We’ve seen a steady rise in this kind of behavior for at least the past decade. People pulling out their phones to record or live-stream a shooting or an emergency situation, as opposed to putting the phone down and actually trying to help. People competing on a daily basis to try and say the most outrageous thing that will draw eye-balls to them. There isn’t an area of our culture that hasn’t been harmed by social media algorithms. It’s in advertising, it’s in politics, it’s in daily life. Even if, like me, you’ve completely removed yourself from social media over the past few years, it’s not as if you haven’t felt the same effects as everyone else. The algorithms and constant draw of a chance to become internet-famous has infected enough of us now, that no one can escape it’s effects. Even if Facebook, X, TikTok, YouTube and every other social media platform in the world ditched their algorithms tomorrow and returned to chronological feeds, turned off monetization; it still wouldn’t solve the problem. The damage has been done. We’ve already been trained to think like an algorithm even if what we’re saying isn’t being fed into one.</p>
<p>The clear irony of course is that this reality is precisely the opposite of the one made by companies on the frontiers of the internet. Online communities were meant to be decentralized, free from corporate or government control. And in the early days, this was more or less true. There weren’t five websites that made up 90% of internet traffic, rather millions of individual sites created by passionate people who were interested solely in sharing their ideas with the world. But where organizations like Alphabet and Meta originally claimed to be an answer to monolithic control and the stifling of communication, they’ve simply become exactly the thing they were designed to replace, but with a much a higher degree of efficiency.</p>
<p>Now, we’re left to ask the question; is there something we can do to fix this? It’s not as if there aren’t corners of the internet free from the effects of algorithms, monetization and “influencers”. Much of what I outlined as being a positive part of the early days of the internet still exists, it’s just buried under a mountain of click-bait thumbnails and outrageous tweets. But there isn’t any real reason why everyone couldn’t simply abandon the platforms that have ruined us tomorrow, delete their accounts, set up a DNS filter to block the sites from tracking us, and return to a simpler time. But it would require a good deal of sacrifice. The issue however isn’t a technological one, it’s a human problem.</p>
<p>Years ago some internet dwellers coined the term <em>Endless September</em>. This was way back in the days of dial-up or nothing being the standard level of internet connectivity. Back then the way that most people gained access to the world wide web was through a university. Every September a new group of students would be turned loose on the websites and forums of old, annoying veteran users with the way that they broke community norms, violated forum rules, and polluted what was, for most of the year, a source of intriguing discussion for most members. Then, as utility companies caught up with the time and Megabit or better internet connections became the standard, the internet experienced a September influx that never ended, and a part of internet culture died.</p>
<p>Back before anyone could get online by puling a computer out of their pocket, it took real effort to get onto the internet. In a lot of cases there was a massive premium to be paid for a suitable internet connection, but even when there wasn’t, the level of technological knowledge required to set up your home network alone was enough to filter out most potential users. Those who did manage to get online, not only almost always had a basic level of understanding about the technology they were using, but also knew what was required to protect these new technologies from corruption. Early history of computing and networking is full of individuals who saw deep into the future, and understood the consequences of exactly the problem we’re dealing with today, before it was even created.</p>
<p>But this is the problem that comes with any new technology. At first it’s just a few users, and users who truly understand the limitations and capabilities of the machine. There weren’t car accidents in the early history of automobiles - at least not ones caused by the things that cause accidents today. In the super early days of cars, owning one meant you probably built it yourself. But even after that, simply starting a car required a great deal more knowledge of the machine than just pushing a button. To get into a position where you could successfully be driving down the road, the end-user had to have a great deal more knowledge than modern drivers could ever dream of possessing about their own vehicles. But, of course, slowly, over time, the technology became better, the price of entry become lower, and the required knowledge became nearly zero. Before you know it, we arrived in a world where there are a <a href="https://d2law.com/texting-and-driving-accident-and-death-statistics-in-2024/">million and a half car crashes a year</a> caused by people looking at their phone instead of the road, just in the US.</p>
<p>The problem isn’t unique to the internet, or to computing, but it is greatly exacerbated by the incredible speed in which technological advances in these areas can be made. There is good news here though; it’s not all that difficult to actually break free almost entirely from the world of algorithms. An increasingly reasonable way to break free form Microsoft, or Apple is to make the swap to Linux. I did this years ago, and have since converted at least twenty friends and family members over as well. Services like <a href="https://inv.nadeko.net">Invidious</a> and <a href="https://freetubeapp.io/">FreeTube</a> make it possible to watch any YouTube video without dealing with YouTube’s tracking or algorithm. And while there are some promising alternatives to the social media sites that capture most of our attention, it’s just as easy to simply stop using social media all together, and just talk to people. A quick profile set up with <a href="https://nextdns.io/">NextDNS</a> and a simple configuration of a router will block out all of these sites in their entirety. At that point the only thing left to lock down is a cell phone, which, admittedly is the one area where there isn’t an amazing or easy solution available yet. There are custom ROMs that can run on Android devices and “De-Google” your device, but these come with their own problems.</p>
<p>The technological problem, isn’t the hard one to solve though. It requires effort and social-consciousness to break free from all of this. The biggest hurdle: simply convincing people that there is a problem. Being willing to go through the effort to consciously disassociate from platforms that do us harm, requires individuals either realizing the problem for themselves, or being convinced of the problem. Either way, the process can only happen one person at a time. It’s a painstakingly slow speed, especially when the enemy that we’re attempting to break free from is one that can move at the speed of the internet. But it’s not impossible.</p>
<p>However, there is one last point worth mentioning here. The frontier-era of the internet is over, almost certainly dead and gone for good. There isn’t a reality in which suddenly 90% of the world once again doesn’t have access to the internet. Part of what we are now experiencing is an unavoidable consequence of human nature, and that’s not something that can simply be negotiated away. We can embrace alternatives to social media, sites like Mastodon or run to even further de-centralized corners of the internet, IRC chats and the like. But at some point we have to accept that the same pressures that led Twitter, Facbeook, YouTube, Google, Microsoft and all the rest to become the antithesis of what they once claimed to be, will exist with any platform that rises up to take their place. Like it or not, this is the world that we live in now. Steps can be taken to mitigate it’s effects on us, but there is no removing it entirely.</p>
]]></description>
      </item>
    
      <item>
        <title>Consequences</title>
        <link>https://makc.co/essays/consequences/</link>
        <guid>https://makc.co/essays/consequences/</guid>
        <pubDate>Tue, 22 Jul 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>What would have happened if, when Rosa Parks made her protest and sat at the front of the bus, no one tried to stop her? No police. No outrage. Just silence. Would we still remember her name? More importantly, would the decades-long civil rights battle that her quiet act helped ignite have ever begun?</p>
<p>There are plenty of absurd laws still technically on the books in America—and in every other country. Remnants from another time, they persist, yet no one thinks of enforcing them. But there is no natural expiration date for a law, no guarantee that it will quietly vanish just because it’s unjust, irrelevant, or plainly stupid. Even today, despite forty states legalizing marijuana for medical use, the drug remains classified as Schedule I on the federal level. That contradiction alone proves that injustice can sit in plain sight, uncorrected, indefinitely.</p>
<p>The strange and unsettling truth is this: the reaction to a protest is often more important than the protest itself. That may sound cynical—but history offers few counterexamples.</p>
<p>Why do we remember Jesus? Why Socrates? They weren’t the first prophets or philosophers. Neither did they make any effort to preserve their own legacy. Jesus may have been illiterate. Socrates deliberately avoided writing his teachings down. And yet both men inspired students and followers who documented their words, spread their ideas, and endured persecution on their behalf. What turned them into symbols wasn’t the originality of their ideas; it was the injustice of their punishment. Without the crucifixion or the hemlock, we might know nothing of either man.</p>
<p>Their teachings were shared by others before and after. What immortalized them was the reaction. Their deaths exposed the fragile cruelty of the systems that killed them. The same has been true of almost every transformative figure or movement since.</p>
<p>But what happens when power doesn’t react? When it refuses to acknowledge a protest at all?</p>
<p>This, too, is a tactic—one often more chilling than suppression. A protest that provokes no backlash, no commentary, no visible consequence, is at risk of simply disappearing. Without confrontation, the best we can hope for is a law’s slow fade into irrelevance. But even then, it’s not truly dead, just dormant. And what lies dormant can always be reawakened. Worse, it can be used selectively, quietly, to punish those whom power deems inconvenient. Its obscurity becomes its strength. The public pays it no mind, while it becomes a surgical instrument of control.</p>
<p>We’ve seen this play out in dozens of contexts. Consider Gandhi leading the Salt March, Hypatia’s execution by an early Christian mob, or the Stonewall Riots. In each case, the protest alone did not make history—it was the overreaction, the visible persecution, that forced the world to pay attention. That moment of disproportionality is what flips the moral balance and changes the public narrative.</p>
<p>Even in recent history, the same dynamic persists. The first accusations against Bill Cosby came three decades before he was brought to justice. It wasn’t until Hannibal Buress launched a public reaction with a comedy routine that the former star was held accountable for years of abuse. Edward Snowden wasn’t the first NSA employee to blow the whistle on surveillance abuses made possible under the Patriot Act. It wasn’t until the state tried to make an example of him—charging him with espionage—that the country was forced to pay attention.</p>
<p>Some regimes have mastered the weaponization of silence itself. Stalinist Russia didn’t crush dissent with drama—it erased it with cold efficiency. Potential dissidents simply disappeared. No spectacle. No martyrdom. No memory. China’s censorship model is even more refined: protests aren’t just suppressed—they’re ignored out of existence. They aren’t acknowledged by the state or shown to the public. This deliberate absence confuses the public, demoralizes activists, and dissolves clarity. No reaction means no symbol. No martyr. No spark.</p>
<p>Many readers may remember hearing, as children: “If you’re going to break the rules, be prepared to face the consequences.” But this isn’t about justice. It’s not a moral appeal. It’s a reflection of how change happens. Not from the act of defiance, but from the consequences it provokes. Martyrdom is what transforms a simple protest into a legacy.</p>
<p>Unjust laws don’t fall because they should. They fall because someone breaks them, and power punishes them, and in doing so reveals the law’s own ugliness. The paradox of progress is that systems of power destroy themselves not when laws are broken, but when they try to punish those who dare to break them.</p>
<p>Power doesn’t fear disobedience; it fears being seen as punishing it. That’s when the mask slips. That’s when history begins to shift.</p>
]]></description>
      </item>
    
      <item>
        <title>My Two Million Dollar Spending Spree</title>
        <link>https://makc.co/essays/1-million-dollar-spending-spree/</link>
        <guid>https://makc.co/essays/1-million-dollar-spending-spree/</guid>
        <pubDate>Fri, 18 Jul 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Over the past few weeks, I’ve been laser-focused on getting my finances in order. Like many Americans over eighteen, I’ve accumulated more than my fair share of debt—along with the bad spending habits that come with it. Credit cards, a car loan that’s longer than it should be—everything short of payday loans and collections—I’ve got it.</p>
<p>Fortunately, I caught the problem early enough to turn it around. I did what every financial advisor suggests: built a strict budget that includes only essentials. To my surprise, I found myself in a decent position—able to put at least $500 a month toward debt. In short, six months of disciplined effort could wipe out the worst of it, and another six to eight could have me completely debt-free, car and all.</p>
<p>Everything was going smoothly—until last week.</p>
<p>On an ordinary Wednesday, I woke up feeling proud of the discipline I’d shown over the past two weeks. I had already saved enough for a one-month emergency fund and was ready to start chipping away at my debt. That morning, I noticed an email from Chick-fil-A offering me a free birthday milkshake.</p>
<p>Thirty-five seconds later, I had downloaded the app, added the milkshake to my cart, and thrown in a chicken sandwich meal for good measure. I told myself it was a small celebration.</p>
<p>You can probably guess where this goes.</p>
<p>An hour and a half later, I was staring at a passenger seat full of fast food bags, two empty cans of Monster, a bag of Hot Cheetos I don’t remember grabbing, and a pack of cigarettes I had bought at the gas station. In total, I’d spent just under $50. But the real damage wasn’t financial—it was emotional. I threw away most of the food and the cigarettes in disgust and sat there feeling completely deflated.</p>
<p>Then a thought struck me: what if I calculated not just what I spent, but the actual cost—the opportunity cost—of this slip?</p>
<p>I had spent $46.29. If that amount became a recurring habit (which, let’s be honest, it has before), it would cost me more than just cash. At the pace I had set today, my habit would end up costing me an absolutely staggering amount: $16,895.85.</p>
<p>But the real kicker came when I factored in investing. Assuming an average annual return of about 8% (adjusted for inflation, roughly the historical S&amp;P 500 average), that same spending habit could cost me:</p>
<ul>
<li>Over $250,000 in 10 years</li>
<li>Nearly $1 million in 20</li>
<li>More than $2.2 million in 30</li>
</ul>
<p>Of course, I can’t afford to invest $45 every single day—not yet. But this exercise gave me a crystal-clear picture of just how damaging small, impulsive purchases can be over time.</p>
<p>Since that day, I’ve been using this method every time I’m tempted to spend unnecessarily. So far, it’s worked almost every time. The one exception? I bought a can of white Monster while filling up my gas tank. But even then, I sat in the car, did the math, and realized I had just bought a $200,000 energy drink. And I have to admit—it was the most satisfying can of Monster I’ve ever had. I savored every drop.</p>
<p>At first, I did these calculations by hand, which I actually recommend. Something is grounding about manually working out the numbers. But as I started using the method more frequently, I built a <a href="https://w8.makc.co">simple website to do the math for me</a> automatically. It’s made the process much quicker.</p>
<p>If you’re dealing with similar habits, feel free to use the site—but I also encourage you to try doing the math yourself, at least once or twice. The impact hits a little harder that way.</p>
]]></description>
      </item>
    
      <item>
        <title>Barge Haulers on the Volga</title>
        <link>https://makc.co/essays/barge-haulers/</link>
        <guid>https://makc.co/essays/barge-haulers/</guid>
        <pubDate>Thu, 17 Jul 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<img src="img/2025-07-17-A.jpg" alt="Ilya Repin's: Barge Haulers on The Volga" class="full">
<p class="caption">Ilya Repin's "Barge Haulers on The Volga</p>
<p>Above is, without question, my favorite painting of all time. <em>Barge Haulers on the Volga</em> is a nineteenth-century work by Russian painter <a href="https://www.britannica.com/biography/Ilya-Yefimovich-Repin">Ilya Repin</a>. It comes as no surprise to me that I was immediately drawn to it, as, like so many others, I’ve long been captivated by the cultural output of the Russian Empire and its many political incarnations: the Soviet Union, the Russian Federation, the RSFSR. Whether it’s Dostoevsky, Tolstoy, Rachmaninoff, or even Solzhenitsyn’s bleak documentation of the Soviet-era prison system in <em>The Gulag Archipelago</em>, Russian art has been some of the most intellectually and emotionally formative material I’ve encountered in recent years. Repin’s painting more than earns its spot in that canon: deeply human, brutally honest, and impossible to forget.</p>
<p>What initially fascinated me about Barge Haulers wasn’t just the painting itself, but the unexpected historical rabbit hole it sent me down. After admiring the image online, I wound up on its Wikipedia page (pro tip: Wikipedia is actually one of the best sources I’ve found for high-resolution artwork). A few links later, I was reading about the Burlaks—a class of Russian laborers whose job was to haul ships upstream, often by literally dragging them along the banks of the Volga.</p>
<p>Most people today don’t realize how ineffective traditional sailing was before the 20th century. Before the advent of efficient hull designs that allowed boats to sail effectively across the wind or even at a close reach against the wind, if you weren't going downwind, you weren’t going. Without engines or favorable currents, rowing or hauling vessels manually was often the only option. This latter is the raw, backbreaking labor that Repin captures with unnerving precision.</p>
<p>But while the history behind the painting adds depth, what struck me most were the eleven men depicted—specifically, the contrast between ten of them and the one at the center. These ten figures are darkened by grime, fatigue, and shadows so heavy they almost obscure their ethnicity. At a glance, their appearances suggest they've become indistinguishable from the filth and hardship of their lives. They are bent, broken, trudging forward with a resigned stoicism that anyone who has done hard manual labor will instantly recognize. They’re not protesting. They’re not posturing. They're simply enduring.</p>
<p>And then there is the man at the center—lit as if by a spotlight, his features cleaner, his shadows gentler, his clothes no less ragged, but his posture unmistakably different. While the others lean into the weight of the ship, he resists it. His hands don’t even allow the rope to press against his chest. He leans backward and avoids the task at hand in a silent, but no less clear protest. His presence is jarring, not because he breaks from the group physically, but because he breaks from them spiritually. He refuses.</p>
<p>What Repin meant to convey with this contrast is open to interpretation. Maybe it’s a statement on the quiet heroism of resistance—of refusing to yield to an unjust reality. Maybe it’s the opposite: a reminder of how useless dissent can be in the face of overwhelming necessity. Or perhaps it’s not a judgment at all, but a question. Do we admire the stoic or the rebel? Is there honor in compliance, or only in protest?</p>
<p>Whatever the answer, the painting lingers. It reminds me of moments in my own life—at work, at school—when pushing against the grain came at a cost, even when I believed I was right. And that’s what great art does: it doesn’t just reflect history or beauty; it reflects us.</p>
]]></description>
      </item>
    
      <item>
        <title>The Art of Virtue</title>
        <link>https://makc.co/essays/the-art-of-virtue/</link>
        <guid>https://makc.co/essays/the-art-of-virtue/</guid>
        <pubDate>Mon, 14 Jul 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Without a doubt, the best book I’ve read this year is <em>The Autobiography of Benjamin Franklin</em>. I only opened it because it leads off The Harvard Classics—a 51‑volume anthology I describe in a separate essay—but I was pleasantly surprised by how readable, engaging, and practical it is.</p>
<p>The most discussed section is The Art of Virtue, where Franklin lays out 13 qualities he believed make a “good human specimen”:</p>
<ol>
<li>Temperance – Eat not to dullness; drink not to elevation.</li>
<li>Silence – Speak only when it benefits yourself or others. Avoid trivial talk.</li>
<li>Order – Let all things have their place; every task its time.</li>
<li>Resolution – Do what you ought; follow through without fail.</li>
<li>Frugality – Spend only to benefit yourself or others. Waste nothing.</li>
<li>Industry – Always be doing something useful. Cut out the unnecessary.</li>
<li>Sincerity – Use no deceit. Think justly, speak accordingly.</li>
<li>Justice – Do no harm, whether by action or neglect.</li>
<li>Moderation – Avoid extremes; don’t overreact to injury.</li>
<li>Cleanliness – Keep yourself, your clothes, and your home clean.</li>
<li>Tranquility – Don’t be disturbed by small things or accidents.</li>
<li>Chastity – Use sex only for health or offspring, never to excess or harm.</li>
<li>Humility – Imitate Jesus and Socrates.</li>
</ol>
<p>Two things grabbed me right away:</p>
<ol>
<li>The Comparison Between Jesus and Socrates. Franklin isn’t the only Founder to pair them; Jefferson does the same in <em><a href="https://thejeffersonbible.com/">The Life and Morals of Jesus of Nazareth</a></em>, his cut-and-paste iterative isolation of just the story of Jesus and philosophy contained in The New Testament Gospels.</li>
<li>The idea of tracking virtues, not habits. Franklin built a weekly chart (see below) and marked a box whenever he failed a virtue. The goal was character, not routine.</li>
</ol>
<img src="img/2025-07-14-B.jpg" alt="An estimation of what this tracking system might have looked like" class="half">
<p class="caption">A sketch of Franklin's tracking diagram</p>
<p>That distinction solved a problem I’ve long had with habit trackers. My lists balloon—work out, read, Duolingo, create something—until the whole system implodes. Franklin’s approach refocuses on principles I actually value. Habits follow naturally.</p>
<p>He also advanced one virtue at a time. Week 1: Temperance. Week 2: add Silence. And so on. Tracking all 13 gives honest data; concentrating on one prevents overload.</p>
<p>In practice, I’ve slimmed Franklin’s list to seven virtues that group naturally for me—e.g., I can’t picture Order, Industry, and Cleanliness separately. Your mileage may vary.</p>
<img src="img/2025-07-14-A.png" alt="My virtue tracking spreadsheet" class="thirds">
<p class="caption">A modern interpretatoin of Franklin's chart using Excel</p>
<p>Paper worked fine, but I wanted weekly grades for quick comparison, so I built a spreadsheet. Each day, I type “X” under any virtue I feel I have succeeded in representing, and the sheet totals my slips, converting them to a letter grade:</p>
<pre class="language-xlsx"><code class="language-xlsx"><span class="token operator">=</span><span class="token function-name builtin">COUNTIF</span><span class="token punctuation">(</span><span class="token range selector"><span class="token column">START</span><span class="token operator">:</span><span class="token column">END</span></span><span class="token operator">,</span><span class="token string">"*X*"</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">2.04</span>

<span class="token operator">=</span><span class="token function-name builtin">LOOKUP</span><span class="token punctuation">(</span> GRADE<span class="token operator">-</span>CELL<span class="token operator">,</span>  
<span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">;</span><span class="token number">60</span><span class="token punctuation">;</span><span class="token number">63</span><span class="token punctuation">;</span><span class="token number">67</span><span class="token punctuation">;</span><span class="token number">70</span><span class="token punctuation">;</span><span class="token number">73</span><span class="token punctuation">;</span><span class="token number">77</span><span class="token punctuation">;</span><span class="token number">80</span><span class="token punctuation">;</span><span class="token number">83</span><span class="token punctuation">;</span><span class="token number">87</span><span class="token punctuation">;</span><span class="token number">90</span><span class="token punctuation">;</span><span class="token number">93</span><span class="token punctuation">;</span><span class="token number">97</span><span class="token punctuation">}</span><span class="token operator">,</span>  
<span class="token punctuation">{</span><span class="token string">"F"</span><span class="token punctuation">;</span><span class="token string">"D-"</span><span class="token punctuation">;</span><span class="token string">"D"</span><span class="token punctuation">;</span><span class="token string">"D+"</span><span class="token punctuation">;</span><span class="token string">"C-"</span><span class="token punctuation">;</span><span class="token string">"C"</span><span class="token punctuation">;</span><span class="token string">"C+"</span><span class="token punctuation">;</span><span class="token string">"B-"</span><span class="token punctuation">;</span><span class="token string">"B"</span><span class="token punctuation">;</span><span class="token string">"B+"</span><span class="token punctuation">;</span><span class="token string">"A-"</span><span class="token punctuation">;</span><span class="token string">"A"</span><span class="token punctuation">;</span><span class="token string">"A+"</span><span class="token punctuation">}</span> <span class="token punctuation">)</span></code></pre>
<p>Franklin’s simple chart still feels modern: measure what matters, focus narrowly, improve steadily. It’s habit‑tracking with a soul.</p>
]]></description>
      </item>
    
      <item>
        <title>John Woolman was Kind of a Dick</title>
        <link>https://makc.co/essays/woolman-was-a-dick/</link>
        <guid>https://makc.co/essays/woolman-was-a-dick/</guid>
        <pubDate>Tue, 01 Apr 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Having just finished reading <em>The Journal of John Woolman</em>, I was struck by a few different things, the foremost: just how early an anti-slavery campaigner Woolman was. I knew that the guy wrote about the evils of slavery around the time of the American Revolution. What I did not realize is that Woolman actually died in 1774, 2 years before <em>The Declaration of Independence</em> was even signed. To say that the guy was &quot;ahead of his time&quot; is one of the most egregious understatements in history. At a time when virtually everyone owned slaves, including virtually all of our first presidents - this guy was actually running around successfully convincing people, on an individual level, to just straight up grant freedom to slaves they had already paid for.</p>
<p>This does beg a bit of an interesting question: Why, if Woolman was running around as a powerful anti-slavery advocate, well before our nation was even formed, did it take so damn long for us to outlaw slavery?</p>
<p><em>One possible theory:</em> Woolman was a bit of a dick.</p>
<p>None of what I'm about to say should take away from his contributions to history, but having just read the guy's personal journals, I can pretty soundly say that this is not exactly the type of guy that I'd want to be listening to lectures from. Is this surprising? A Quaker coming off as a bit preachy -- yeah, kind of.</p>
<p>Maybe the weirdest thing about Woolman's early campaigning was the way in which he decided to make his case. In the early days, Woolman was something akin to a notary, or maybe just someone who could write properly (the difference between the two in 1740 isn't as big as you'd think). As a result, Woolman often had requests to write up wills or miscellaneous contracts, which, given the fact that this was eighteenth century colonial America, often facilitated the transfer of ownership of slaves. Rather than tell his peers outright that he wasn't willing to actually do this service for them; he would invite them to his house, write up the entirety of the contract, save for the bit about the slaves; and then proceed to lecture them about the ownership of slaves until they either gave up and bailed, or gave up and agreed to set the slaves free.</p>
<p>Now, it's hard to take shots at the guy for doing something that, according to his journals, did free at least two or three slaves from captivity. But just imagine if this were any other type of transaction. Let's say you're selling an iPhone. I see your ad, but I'm an Android guy (newly converted too), and the last thing in the world I want is an iPhone. But rather than just ignore the ad like a normal person, I invite you to my house under the pretense of promising to buy the phone; but when you get there, I proceed to lecture you for hours on end until you agree to just take the iPhone outback and put it down <em>Old Yeller style</em>.</p>
<p>Again, this criticism feels kind of gross even as I'm writing it, because the guy was using deceptive marketing to argue on behalf of freeing slaves -- so I'll just move on to another example:</p>
<p>In Chapter IX of his journal, Woolman goes into great detail about his account of the time that he stood outside a magic show, pestering patrons for an hour, before feeling satisfied enough that he could leave.</p>
<p>The story goes that Woolman was traveling around, going to various Quaker meetings and spreading his anti-slavery message, or at least that's what he was meant to be doing. He also just mentions several times over and over again that he gets to some meeting and just sits there without saying anything at all the entire time, so make of that what you will. Anyway, while traveling around, Woolman spots what I'm guessing by his description is a kind of pop-up magic show. He specifically asks the sleight of hand artist there if it's cool if he hangs out; the artist says yes. At which point Woolman proceeds to stand outside of the show, preaching the evils of magicians, pestering everyone who thought about coming to the show, until finally:</p>
<blockquote>
<p>&quot;So, having spent about an Hour amongst them, and feeling my Mind easy, I departed.&quot;</p>
</blockquote>
<p class="attr">
John Woolman<br>
The Journal of John Woolman
</p>
<hr>
<p>I could go on and on about the various dickish pursuits of John Woolman, but unfortunately, I'd just be making stuff up. I read the whole journal, and I could only find two moments that I could twist into something negative. Of course, the reality is that Woolman was a trailblazer of early American politics and deserves a huge amount of respect, even if he won't let you go to a magic show. I just thought this might be a fun essay to read, if you happen to actually come across it on the day that it's published:</p>
<h2>Happy April Fool’s Day!</h2>
]]></description>
      </item>
    
      <item>
        <title>The Only Way Out is Through</title>
        <link>https://makc.co/essays/the-only-way-out-is-through/</link>
        <guid>https://makc.co/essays/the-only-way-out-is-through/</guid>
        <pubDate>Sun, 30 Mar 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>The first exposure I had to this line of thinking, at least as far as I can remember, was in Nikos Kazantzakis' novel: <em>Zorba the Greek</em> (which, interestingly enough, might have since become my favorite book). The original quote is said by Zorba to the protagonist: &quot;How do you expect to get the better of a devil ... if you don't turn into a devil and a half yourself?&quot; Kazantzakis is far from the only one to make this philosophical suggestion, but this was my first exposure to the idea, and thus, I think it always sort of rings the loudest in my head when I am contemplating the idea.</p>
<p>Embracing evil with full force, as opposed to running from evil, is the only true way that I have found to mitigate the effects of the evil we all must contend with. The interesting thing, however, is that it seems not to matter what form the evil is taking. When we phrase things in nigh apocalyptic terms, we tend to think of the application of this kind of logic on a massive scale. Clearly, this logic works here: If you want to avoid falling prey to mass societal acceptances of evil, you have no chance of being able to do so if you can not identify the evil, and even less of a shot if you simply dig your head in the sand and pretend that you simply are not capable of doing an evil thing. That last idea: <em>pretending that you simply are not capable of true evil</em>, is one of the most pervasive bad ideas that society has ever developed. If you are not capable of evil, then by definition, everything you do will <em>not be evil</em>. You'll find yourself personally filling the chamber with <em>Zyklon B</em>, all the while still telling yourself that pervasive lie: &quot;I'm simply not capable of that kind of evil.”</p>
<p>But, importantly, embracing evil can be used to defeat even the smallest forms of evil, the ones that are annoying more than murderous. In particular, I'm referring to vice, but I do not want to limit the train of thought. Interestingly enough, the thing that finally got me to quit smoking cigarettes was smoking more than I had ever smoked in my entire life. I had a job where I smoked damn near a pack of cigarettes a day, if not more. If I were driving, it would be impossible that I didn't have a cigarette in my mouth. If I were on break, same situation. Even when getting home from a sixteen-hour shift, I could not go inside and go to sleep until I smoked a cigarette. I became so reliant on nicotine that I was smoking cigarettes with nicotine pouches in my mouth and nicotine patches on my arm (only a mild exaggeration).</p>
<p>Then I simply decided I wanted to stop. Being now immensely familiar with the perks and downsides of the drug, I was able to make that decision with more clarity than I could have had before. I took a break for a very short amount of time, maybe a week and a half, maybe two weeks, and then attempted to smoke again. The nicotine had no effect whatsoever, save for putting a disgusting chemical taste in my mouth; and when I forced myself to smoke another cigarette (for science, I guess), I instantly got a headache that lasted the rest of the day. Even the Zyn pouches that I used to pop like candy were absolutely useless to me. I went from being fully addicted to nicotine to a nicotine non-responder in the space of a fortnight.</p>
<p>Of course, it does seem like this kind of thing is possible to do as a thought exercise; there's no need to actually destroy your lungs just to stop a casual habit. It would be a wild world indeed if, for example, the only way humans could get rid of a desire to murder was to go out and physically murder so many people that they were simply bored with it. But, luckily, that is not the world that we live in. It seems more than possible to simply imagine the depths of evil that you might descend into, as a safeguard against ever actually going there unconsciously.</p>
]]></description>
      </item>
    
      <item>
        <title>The Best Bargain in the History of Literature</title>
        <link>https://makc.co/essays/harvard-classics/</link>
        <guid>https://makc.co/essays/harvard-classics/</guid>
        <pubDate>Sat, 15 Mar 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>I can't remember exactly when or how I first came across <em>The Harvard Classics</em>, but ever since that day I have been on a mission to get a copy of the collection for myself. The Classics, originally published as: <em>Dr. Elliot's Five Foot Shelf of Books</em> are an absolutely massive, fifty-one volume collection of novels, poetry, history, philosophy, scientific writing, historical documents, and anything else that Dr. Elliot deemed necessary reading to establish a well-rounded liberal education. But the size of the collection isn't what's really special about it. Any fool could copy and paste 200 books into a Word Document, hit print, and self-publish in an effort to profit off of someone else's contributions to the world.</p>
<p>Fortunately, I can say with a high degree of confidence that <em>The Harvard Classics</em> is actually the most unique and complete collection of literature I've every encountered. The collection was put together by Charles William Elliot, who was the President of Harvard from 1869 to 1909; a man who established himself as a real <em>stuffy nerd</em> when he tried to ban the playing of football at Harvard, and was later quoted claiming that <a target="_blank" href="https://www.nytimes.com/1906/11/28/archives/eliot-against-basket-ball-harvard-president-says-rowing-and-tennis.html">Rowing and Tennis were the only &quot;clean sports&quot;</a>. Despite his anti-sporting rhetoric, Elliot was a prominent member of American Academia at a time when it was much better respected, and objectively more effective than it is today. Plenty of people would argue that the 1950's and 1960's were the true Golden of Age of American Academia; and while I'm not going to argue that they weren't; I will say that Eliot was one of the men best placed in history and proximity to Academia to be able to accurately make decisions about what would fit into the category of <em>required reading</em>. It honestly shouldn't be surprising at all then that this collection contain virtually all of the most influential works of the past three centuries (at least up to 1909 that is), as well as a massive amount of literature pre-dating America itself.</p>
<p>Just as a thought experiment, a few days ago I opened up the table of contents, and made a note of everything that I had either read previously and thought was valuable, or had heard of in high esteem and earmarked for future reading. That list ended up being so long that it's probably a bad idea to put it here in it's entirety; but I can think of no better way to actually prove the point that I'm trying to make:</p>
<p>The first volume alone contains:</p>
<ul>
<li><em>The Autobiography of Benjamin Franklin</em></li>
<li><em>Some Fruits of Solitude</em></li>
<li>and  <em>The Journal of John Woolman</em></li>
</ul>
<p>However, this is actually a pretty weak start - no offense to Franklin and crew, but the second volume is an absolute unit of ancient Greek philosophy, containing:</p>
<ul>
<li><em>The Apology, Phaedo,</em> and <em>Crito</em> from Plato</li>
<li>Epictetus' <em>The Golden Sayings</em>, as well as</li>
<li>Marcus Aurelius' <em>Meditations</em></li>
</ul>
<p>It's worth reminding readers here that Socrates himself didn't actually write any of his own philosophy down. Almost all of what we know of Socrates' teachings, we know because it was documented by his peers and students; the foremost of which was Plato. Therefore we're really not only getting the work of Epictetus and Plato here, but also Socrates.</p>
<p>The next three volumes are dedicated to the poetry of:</p>
<ul>
<li>John Milton</li>
<li>Ralph Waldo Emerson</li>
<li>and Robert Burns, respectively.</li>
</ul>
<p>And throughout the remaining 45 volumes, in no particular order of importance you will find:</p>
<ul>
<li><em>The Confessions</em> from Augustine of Hippo</li>
<li><em>The Imitation of Christ</em></li>
<li><em>Agamenon</em></li>
<li><em>The Wealth of Nation</em></li>
<li><em>The Origin of Species</em></li>
<li><em>Plutarch's Lives</em></li>
<li><em>The Æneid</em></li>
<li><em>Pilgrim's Progress</em></li>
<li><em>The Divine Comedy</em></li>
<li><em>Don Quixote</em></li>
<li><em>The Odyssey</em></li>
<li><em>The Voyage of the Beagle</em></li>
<li>Machiaveli's <em>The Prince</em></li>
<li><em>Beowul</em></li>
<li><em>The Song of Roland</em></li>
<li><em>The Alchemist</em></li>
<li><em>Uncle Tom's Cabin</em></li>
<li><em>Pride and Prejudice</em></li>
<li><em>The Astroleger</em></li>
<li><em>David Copperfield</em>, the Charles Dickens novel. Not a book about the <a href="https://www.businessinsider.com/jeffrey-epstein-victim-named-magician-david-copperfield-unsealed-documents-2024-1?op=1">magician who maybe or maybe wasn't hanging out on Epstein Island</a>.</li>
<li><em>The Mill on the Floss</em></li>
<li><em>The Scarlet Letter</em></li>
<li><em>The Portrait of a Lady</em></li>
<li><em>The Hunchback of Notre Dame</em></li>
<li><em>Anna Karenina</em></li>
<li><em>Ivan the Fool</em></li>
<li>and Fyodor Dostoyevsky's <em>Crime and Punishment</em> (only Crime and Punsihment though, oddly enough)</li>
</ul>
<p>The collection also includes miscellaneous writings from:</p>
<ul>
<li>John Locke</li>
<li>Berkeley</li>
<li>Hume</li>
<li>Descartes</li>
<li>Shakespeare</li>
<li>Voltaire</li>
<li>Thomas Hobbes</li>
<li>Pascal</li>
<li>Lord Byron</li>
<li>Faust</li>
<li>Euripides</li>
<li>Virgil</li>
<li>Confucius</li>
</ul>
<p>As well as fables from:</p>
<ul>
<li>Aesop</li>
<li>The Brothers's Grimm</li>
<li>Anderson</li>
</ul>
<p>Followed by a pretty massive collection of early US historical artifacts, including:</p>
<ul>
<li><em>The Declaration of Independence</em> (obviously), but also:</li>
<li><em>The Mayflower Compact</em></li>
<li><em>The First Charter of Virginia</em></li>
<li><em>Washington's Farewell Address</em></li>
<li><em>The Treaty with Great Britain</em> (the one following the Revolution and the one following the war of 1812)</li>
<li><em>The Monroe Doctrine</em></li>
<li><em>The Federalist Papers</em></li>
<li><em>The Fugitive Slave Act</em></li>
<li><em>The Emancipation Proclamation</em></li>
<li><em>The Gettysburg Address</em></li>
<li>and even <em>The Terms of Robert E. Lee's Surrender at Appomattox</em></li>
</ul>
<p>Finally, we get into the religious texts, which I was surprised to find included more than just the bog standard Judeo-Christian artifacts that you would expect to find in a collection like this:</p>
<ul>
<li><em>The Book of Job</em></li>
<li><em>Psalms</em></li>
<li><em>Ecclesiastes</em></li>
<li><em>The Gospels according to Luke</em></li>
<li><em>The Acts of the Apostles</em></li>
<li>Not all of, but several passages from <em>The Qur'an</em></li>
<li><em>The Bhagavad Gita</em> in it's entirety</li>
</ul>
<hr>
<p>I hope it's quite clear now why I was so interested in this collection. Basically if you threw in a copy of <a target="_blank" href="https://youtu.be/WNEdo_8tFN0" title="A shameless plug to one of my YouTube videos"><em>Zorba the Greek</em></a> and a couple of Cormac McCrathy novels, I would have everything that I'd really want to read for the rest of my life anyway. What I found most surprising while conducting my little experiment; was the fact that I didn't come across a single volume that looked like it had nothing to offer. Just statistically speaking, you'd think there would be a dud in a collection of fifty books, and there very well might be, but, at least it wasn't immediately obvious which one it would be.</p>
<p>As you can imagine, actually getting your hands on a physical copy of the collection is a bit of an investment. For a while I really couldn't find anywhere that would sell a full-set for less than 4 figures. Eventually however, I did find some eBay sellers who were actually willing to part with their collection for exceedingly reasonable prices. However shipping is still a bit complex, to say the least, and I'm only really wiling to drive so far to pick up some books-- none of that's really relevant though, because it turns out you can get the whole thing for 99¢.</p>
<p>I'm not even being hyperbolic: Completely by chance I was messing around on my Kobo eReader a few days ago, and searched for &quot;Harvard Classics&quot; in the store, expecting to find nothing. I found a result claiming to be the full set, but also on sale from it's normal price of $1.99, for 99¢. I bought the eBook expecting it to be, maybe the first volume, or worse, somebody writing a book about the collection. But when I opened the book, I found that I now had access not only to the original 51 volumes; but also an additional twenty volumes that were published in 1917 under the title: <em>The Harvard Classics Shelf of Fiction</em>. The latter is, exactly what it sounds like: and additional twenty volumes full of nothing but fiction reading.</p>
<p>To be clear this isn't some kind of freak deal in the <a href="https://www.kobo.com/us/en/ebook/the-complete-harvard-classics-2020-edition-all-71-volumes">Kobo Store</a>. This same collection is $1.99 if you buy if from <a href="https://www.barnesandnoble.com/w/the-complete-harvard-classics-2024-edition-newly-updated-benjamin-franklin/1144723871?ean=9782377933822">Barnes &amp; Noble</a> for a Nook, $1.99 in the <a href="https://play.google.com/store/books/details/The_Complete_Harvard_Classics_ALL_71_Volumes_The_F?id=GrfoDwAAQBAJ&amp;hl=en-US">Google Play Store</a>, $1.99 from <a href="https://books.apple.com/us/book/the-harvard-classics-all-284-works-in-71-volumes/id6532577939">Apple Books</a>, and please have enough self-respect not to buy it from Amazon -- but it's also 99¢ in the <a href="https://www.amazon.com/Complete-Harvard-Classics-2020-Volumes-ebook/dp/B08D97WFS5">Kindle Store</a>.
Don't get me wrong the collection is more than worth the five or six hundred bucks I'd have to pay to get my physical copy, and I'll probably still end up buying my own set at some point in the future. But if it's worth $500 it's definitely worth paying $2 for. I can't think of a single other place where you could acquire this quantity of literature for less than the price of a pack of a gum; and please, feel free to take that as a challenge: if you know of a better bargain now, or discover on in the future; I'll be here eagerly waiting to here about it.</p>
]]></description>
      </item>
    
      <item>
        <title>Vim: The Best Markdown Editor</title>
        <link>https://makc.co/essays/vim-the-best-markdown-editor/</link>
        <guid>https://makc.co/essays/vim-the-best-markdown-editor/</guid>
        <pubDate>Thu, 05 Dec 2019 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Before I start writing any opinions here, I’d like to take a second to talk about why I think I’m qualified to say what the best markdown editor is, and what my criteria in choosing a markdown editor looks like. There is no shortage of blogs and articles out there that seek to answer this very question, so I wouldn’t expect anyone to just be taking my word for it, without knowing if my word has any value to you, or the way that you want to work.</p>
<p>Also, if you’re not familiar with Markdown, the rest of this will make no sense whatsoever. Sorry.</p>
<h2>Who Am I To Judge</h2>
<p>I write a lot of text. Which, I know, alone isn’t really a qualification. What I do think is a qualification, however, is the diversity of text that I work with, basically every day. I write an insane amount of text in this format. I routinely work with books, blog posts like this, notes, recipe cards, and even my to-do list (when I use it) is a markdown file. I manipulate all of these types of markdown files daily. This is important for two reasons:</p>
<ol>
<li><em>Exporting:</em> I’m writing a lot of different things in Markdown, with the intention of exporting them to other formats. So I’m not just writing markdown. I routinely need to get markdown files formatted as HTML, PDFs, Word documents, and only occasionally (personal notes &amp; GitHub) is markdown the final product. Of course, that’s sort of one of the pillars of the language, so I think it’s important to be familiar with the way that this process works.</li>
<li><em>Stress Testing:</em> It’s very easy for me to tell, immediately, if a markdown editor is not up to snuff. I often have multiple files open at one time, and I routinely open up very large files. I have four or five documents that are somewhere between 100,000 and 200,000 words, which I need to open up every day. A lot of markdown editors, especially new ones, just won’t do this for me. They’ll freeze up, crash, chug, or find other more unique ways to make their usage miserable.</li>
</ol>
<p>On a final note: I have tried a ridiculous amount of Markdown apps on every platform. Whether it be iOS, OSX, Linux, Windows, or BSD. Really, the only platform on which I haven’t done enough experimentation to really say anything confidently is Android. I don’t have anything against Android; it’s just that I sort of hate phones, and I don’t want to spend a ton of time on them. I have an iPhone just because that was the first smartphone that I bought, and I haven’t really messed around too much since then.</p>
<h2>My Grading Criteria</h2>
<h3>Power</h3>
<p>I think that I have pretty high expectations for Markdown. I didn’t try to develop a stressful test to put editors through. I just have a pretty heavy workload, as a result of the way that I like to work. Even before I started using Markdown, I was hitting Microsoft Word or Google Sheets with very heavy workloads.</p>
<p>But this is, essentially, the criteria by which I grade a Markdown app. I like really nice-looking, beautiful apps, but I’m totally fine using something a bit more ugly if it does the job better. I think most people feel the same way, but in my case, I’m pretty good at figuring out quickly whether or not the pretty apps are even worth wasting my time with.</p>
<h3>Cross-platform Compatibility</h3>
<p>I also really care about an app being cross-platform. I really want to be able to use an app on at least MacOS and Linux; ideally, Windows as well.</p>
<h3>Feature Set (Export, Templates, ext.)</h3>
<p>I overlooked this for a really long time. But being able to export quickly and easily, to a lot of different formats, is sort of a must for a good markdown editor. More than just being able to export to PDF or Word, I’m also looking for editors that will support a lot of customization in this department as well. Being able to browse a lot of pre-made templates and being able to create custom templates myself is a huge bonus.</p>
<h3>Speed</h3>
<p>There’s not a lot to say about his category. I want to be able to write and manipulate text quickly. If that requires some additional learning (keyboard shortcuts and whatnot), that’s totally fine.</p>
<h3>Launch Time</h3>
<p>Finally, I’ve taken into account the launch time. Not just for the app itself, but for the opening of files once the application is open, and the switching between files. This, I think, should be pretty self-explanatory. Obviously, Photoshop is the gold standard for photo editing, but it takes so long to launch, in comparison to other apps, that I’m probably not opening up Photoshop just to crop an image. Being able to just launch an app when you launch your computer and leave it open is one thing. But that’s not really a good way to get around a slow launch time. A slow launch time for the app almost always means a slow loading time when opening files. Also, apps crash, and sometimes you have to restart, and I don’t want to be sitting around waiting for Photoshop to open when I can rotate an image from Finder.</p>
<h2>Vim</h2>
<p>There are two apps that I want to talk about today, and the first of which is Vim. This is a very old app, originally released in 1991, and based on an even older text editor, Vi, that’s been around since the mid-seventies. I’m going to wager that a good chunk of the markdown crowd has never heard of Vim, and that’s because, despite being a wickedly fast and powerful text editor, Vim has never really caught on with a lot of writing communities. Vim really thrives, almost exclusively, in the Linux and BSD world, which is where I found it.</p>
<p>So the first thing to mention is that Vim checks four of the five boxes on my criteria better than any app I have ever used. I’m not trying to be hyperbolic, I’m being honest. The launch time, both for files and the app itself, is instant. Again, it may sound like I’m being hyperbolic, but I’m 100% serious. It doesn’t matter whether I’m opening up a list of 10 names or a 150,000-word novel, Vim launches immediately. This is because of the fact that Vim, at least in the form that I use it and recommend people use it, is a command-line application.</p>
<p>This is likely why Vim has yet to take off, among writers, the way that I believe it should have. Vim has a learning curve, and a lot of mythology around that learning curve. People who use Vim daily will ward off new users, because it’s not for <em>just anyone</em>. This mythology is the main reason why I stayed away from Vim for as long as I did. I was constantly being told by people who used Vim every day not to mess around with it unless you were already a senior developer capable of writing your own text editor over a weekend.</p>
<p>But the truth is, Vim’s brutal learning curve is conquered in about a week, mostly a weekend in my case. But you will actually be slower in Vim when you first start. Which is a very odd and off-putting thing. Your typical learning curve for any piece of software is a nice, gentle upward curve. But when using Vim, you will quite literally start typing at a fairly normal speed, get way slower for a brief period of time, and then become faster in Vim than any other application you’ve ever used. This is mostly because Vim does not utilize your computer’s mouse. Vim, or at least Vi, was built at a time before the mouse was really a thing. Everything was then, and is still now, done via the keyboard.</p>
<p>This is Vim’s real strength. If you think about it, you can only ever type in any application as fast as your fingers will go. If I type 60 WPM in Microsoft Word, I won’t type 120 WPM in Vim. The real strength of any text editor is its ability to manipulate text. Markdown takes care of a lot of the basic formatting for us, which is why writing Markdown probably already feels fast and more efficient than anything else. But if Markdown handles formatting, Vim handles everything else. You can correct words, spell check, delete, re-write, change, and manipulate text in a matter of seconds using Vim. Vim has movement controls to navigate by line, paragraph, sentence, word, and all the way down to the individual characters, and it’s all done without ever moving your fingers from the home row. Most Vim users I know of actually disable arrow keys to force themselves to use Vim’s alternative arrow keys: h,j,k,l (the real pros even disable the backspace).</p>
<img class="thirds" src="img/2019-12-05-B.jpg" alt="a graph i made">
<p class="caption">
    A very rough depiction of Vim's "learning curve".
</p>
<h2>Typora</h2>
<p>However, as much as I like Vim, and I really do use Vim for basically everything, the app does have a glaring weakness. Vim is an app that is only interested in being one thing: a text editor. It’s designed to allow you to write and manipulate whatever kind of text you need to work with, as quickly and efficiently as possible. Even Vim’s closest competitor, Emacs, does much, much more out of the box than Vim. By default in Vim, something as basic as spell checking is turned off. Vim writes and edits text. That’s all.</p>
<p>So, in some ways, it really shouldn’t even be considered a contender for the top spot on my list of Markdown editors, as it miserably fails on one point of criteria. Vim does not export to anything. You have to find an alternative solution for exporting Markdown. But this should say a lot about the quality of the text editing experience that you’ll get with Vim. It just straight up doesn’t even try to work with Markdown, and yet I still call it the best text editor for Markdown. I hope this makes sense to people who’ve never used Vim before, as that’s the target audience for this post.</p>
<p>Anyway, I recommend Typora as an alternative tool for exporting Markdown to other formats. It’s not as pretty as something like Ulysses or IA Writer, but it does pretty much all the same stuff, and it runs on more than just MacOS. Typora runs on Linux, MacOS, Windows, and is really easy to install. (That might not make sense if you run MacOS or Windows, but any Linux users will know that, depending on your distribution, some apps can actually be pretty difficult to install.)</p>
<p>Typora is a really good graphical editor. I’ve never had much of an issue in terms of reliability, and it handles my 100,000+ word documents absolutely fine. So if you’re not going to use Vim, and you’re not on MacOS, Typora really is the way to go. But even if you’re using Vim as your primary editor, Typora will be very helpful for you.</p>
<p>First of all, Typora will actually allow you to export to alternative formats. HTML, PDF, Word, Epub, ext, you can convert Markdown to anything else you might need very quickly and easily. But where I really think Typora stands out are in two areas:</p>
<ol>
<li><strong>Simplicity</strong>: Like Vim, Typora does seem to be built to do one thing, and do it better than anything else. Typora is a markdown editor, and it’s built to edit markdown as quickly as possible. There aren’t any crazy UI elements that get in the way; it has a very simple layout. Of course, the simplicity is sort of misleading, because it’s very powerful under the hood, as you might expect, and maintains the useful feature sets of any other Markdown editors I’ve used.</li>
<li><strong>Customization</strong>: Not only does Typora have its own Theme Gallery that allows user submission, but it also allows for easy, custom theming. Typora’s themes are powered by CSS, a language that is easy to learn, if you don’t already know it, and even easier to manipulate, even if you don’t. I imagine it would be very easy for a committed tinkerer to create custom Typora themes for their projects without ever intentionally setting out to learn CSS.</li>
</ol>
<p>Also, it’s free!</p>
]]></description>
      </item>
    
      <item>
        <title>The Crusade for a Better Dark Mode</title>
        <link>https://makc.co/essays/the-crusade-for-a-better-darkmode/</link>
        <guid>https://makc.co/essays/the-crusade-for-a-better-darkmode/</guid>
        <pubDate>Tue, 02 Jul 2019 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>I couldn’t exactly tell you why, but I care a great deal about the progress of dark modes on modern software platforms. But I kind of always have hated the way that this feature is baked into software and websites. Almost always, you open a new app or website, and at some point, you realize you’d like to go into dark mode; so you fish around in the settings and menus looking for a button to do just that for you, or maybe you even get desperate and just Google the answer — the point is, it’s a frustrating process. Every app, every website, every OS has a manual button that you, the user, have to go and track down. Of course, that’s just the process for enabling the dark mode; turning it back off is equally annoying.</p>
<p>Apple actually did make a really great move back with the release of macOS Whatever It Was, when they first implemented a system-wide dark mode. They baked in one button that would change the theming for every system app and every little piece of UI (with a few tiny exceptions). Then Apple did the really smart thing, and gave developers a quick and easy way to sort of tap into that theme switching within their apps (and seriously, it actually is really easy, I’ve only ever used Xcode in short spurts, working on iOS apps, but even I feel confident after reading the Documentation that I could get this working).</p>
<p>Then, after a little delay, Apple also handed this feature over to web developers. If you’re interested,d you can build a website that will follow the system-wide theme without any need to reload the page, or enable any weird features, it just works.</p>
<p>The biggest issue with this feature is that for it to just work, a lot of conditions have to be met. You have to be on a supported operating system (that means just MacOS), using a supported browser (for the longest time, that was just Safari), and actually be a user who was running the latest software, and taking advantage of the new dark mode feature. And yet, when it did work, this was, dare I say it, the absolute best implementation of any dark mode; no need to fish around for settings, no need to even care to pay attention to what was going on, if you were the end user, it just worked.</p>
<p>But now, a little over a year later, this functionality is becoming quite a bit more widely supported. The biggest sign of this is that both a new, system-wide dark mode, as well as support for websites following the system dark mode, is being added to the upcoming iOS 13, but that’s far from the only support that’s been added. Mozilla added this functionality to Firefox relatively recently, and while Google Chrome still doesn’t actually support this theme switching in their official release of the browser, both Chromium and Chrome Canary support this feature in their most recent updates, so I’ve got to assume that this is coming to Chrome at some point in the near future.</p>
<p>This really kind of leaves it up to Microsoft, whose Windows 10 also now has a system-wide dark mode, to either develop a similar system for web developers, or just join in with Apple, and the vast majority of web users will be able to get the most no-nonsense Dark Mode the world has ever known. I’m not ignoring Android and Linux users; I just think that Android will almost certainly implement this feature soon, and there are way too many Linux distributions for me to keep track of, and most people just use Firefox or Chrome anyway.</p>
<p>But for now, we’re stuck in a relative corner where our new, amazing, dark mode only works in a few places. So the way I see it, there are two options:</p>
<ol>
<li>Go ahead and enable a dark mode the old-fashioned way with a button and a bit of JavaScript. This isn’t a terrible solution; you can actually enable both the manual dark mode with JavaScript and the automatic with media queries, and that will work fine.</li>
<li>The second option is basically to rely 100% on Apple’s solution, and then just serve up a very specific version of your website on the platforms that don’t support theme switching.</li>
</ol>
<p>I’m actually going with the second option for now, while working on the redesign of my personal website. I might end up actually adding a manual dark mode switcher, button thing, before the site goes live, but at least for now, I’m kind of happy with the way that things work. The way that I have this set up is to serve, by default, not the standard light mode of my site, but the dark mode version. Anyone on an unsupported platform will just see the dark version of my site. But when a user moves over to a supported browser, my site will follow the preference set by the OS. Setting this up is fairly straightforward.</p>
<p>The first step is to decide what version of your site you want users to see by default, before you ever start writing it. This is important because whatever classes you set up in your CSS document at the top will be what users in an unsupported browser will see.</p>
<p>So in my case, since I want to serve the dark mode by default, I would design my site in dark mode by default, dark backgrounds, light text, that type of thing:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">html, body</span> <span class="token punctuation">{</span>
	<span class="token property">background-color</span><span class="token punctuation">:</span> black<span class="token punctuation">;</span> 
	<span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> 
<span class="token punctuation">}</span></code></pre>
<p>Then we can use media queries to enable theme-switching, which works in one of two ways:</p>
<pre class="language-css"><code class="language-css">@media <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">)</span> <span class="token comment">/* Which will only trigger when a user is in light mode on their system. */</span>

@media <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span> <span class="token comment">/* Which will only trigger when a user is in dark mode on their system. */</span></code></pre>
<p>In my case, I want to enable a dark mode by default, and then use media queries to enable a light mode, so I’ll want to trigger the changes when a user is in light mode, which would look like this:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
	<span class="token selector">body, html</span> <span class="token punctuation">{</span>
		<span class="token property">background-color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> black<span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Then, if we want to put it all together:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">html, body</span> <span class="token punctuation">{</span>
  <span class="token property">background-color</span><span class="token punctuation">:</span> black<span class="token punctuation">;</span> 
  <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> 
<span class="token punctuation">}</span> <span class="token comment">/* Made dark mode color settings */</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
	<span class="token selector">body, html</span> <span class="token punctuation">{</span>
		<span class="token property">background-color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> 
		<span class="token property">color</span><span class="token punctuation">:</span> black<span class="token punctuation">;</span> 
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token comment">/* Switching to light color scheme when enabled on supported system */</span></code></pre>
]]></description>
      </item>
    
      <item>
        <title>Watch “The Incredible Hulk” Before “Avengers: Endgame”</title>
        <link>https://makc.co/essays/why-you-should-watch-the-incredible-hulk-before-you-watch-avengers-endgame/</link>
        <guid>https://makc.co/essays/why-you-should-watch-the-incredible-hulk-before-you-watch-avengers-endgame/</guid>
        <pubDate>Tue, 30 Apr 2019 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>I just got back from Avengers: Endgame, and it was pretty incredible. It took advantage of its three-hour run time in a way I never expected, and formed itself into a quiet contemplation of the MCU’s past events. But what surprised me about the film more than anything else was the way that no single character felt short shrifted in the film. Of course, the biggest complaint that I heard about the penultimate film, “Avengers: Infinity War”, was how the film moved quickly and glossed over important character moments. But somehow, in a film with perhaps more characters in the script than any other film in history, this was not the case with “Avengers: Endgame”.</p>
<p>You could make a case that the bulk of the characters that came back after Hulk’s snap weren’t given enough time to shine, but at least the main characters, the original six Avengers, all had very complete story arcs. Whether it was Iron Man’s journey from loss to security, to risk, and ultimately his decision to sacrifice himself; or Black Widow’s very similar journey; or Captain America’s journey to be done with the world, and go back in time to live out his days with Peggy Carter, everyone seemed to have a fully fleshed out story. What I found most odd about these character arcs is that, rather than rebuild a narrative arc to fit their film, Markus &amp; McFeely instead built on what was already there in previous films, no matter how deeply it was hidden.</p>
<p>Of course, the best example, or at least the most obvious example of this, was the way that the film heavily leaned on “Thor: The Dark World”, an almost universally despised part of the MCU. There was plenty of material to work with from Thor’s past experiences, most notably in his most recent solo outing, “Thor: Ragnarok”, however, the screenwriters seemed happy to give themselves a bigger challenge. But I don’t want to talk about that; plenty of other people already are. I want to talk about Hulk.</p>
<p>Almost by accident, I watched Edward Norton’s “The Incredible Hulk”, right before I saw “Avengers: Endgame”. I’ve held the opinion for a while now that “The Incredible Hulk” is the MCU’s most underrated film, more on that later. But I was surprised to see that this didn’t stop the “Avengers: Endgame” screenwriters from dropping easter eggs for fans of “The Incredible Hulk”.</p>
<p>About 2/3 of the way through “Avengers: Endgame”, the team finishes their time heist and gets their hands on all six Infinity Stones. Very quickly, the team starts nobly arguing over who should wield the new Infinity Gauntlet and risk dying to bring back all their old teammates. The bickering goes on for a minute until Bruce Banner shuts everyone up and makes it clear that he will be the one performing the un-snappening. The way he does this, though, threw me off for a second, until I re-watched Joss Whedon’s original “The Avengers” film, that is. Bruce Banner convinces everyone he can handle the force of the Infinity Gauntlet by saying that the force coming from the stones “is mostly gamma anyways”.</p>
<p>It’s an obvious allusion to Bruce Banner/ Hulk’s well-known origin story, but it’s still an odd thing to say, until you realize it’s actually a more direct allusion to Joss Whedon’s film. Early on in that film, Erik Selvig is examining the Tesseract (ie, the Space Stone), and comments to Nick Fury that there is radiation coming off of the stone, which he describes as “mostly harmless, low-level gamma”, and of course Nick Fury gets the killer line, “that can be dangerous.</p>
<p>But that’s beside the point; we’re already building Hulk’s character arc. He’s a character that’s mostly ignored in the MCU, until the rest of the Avengers need a tank to deal with some big enemy in the middle of a fight. But Markus &amp; McFeely went out of their way to pay attention to what little backstory Bruce Banner did have. But of course, this little “easter egg” pales in comparison to the second reference we get in “Avengers: Endgame”:</p>
<p>At the very beginning of the film, right after the time jump, we’re introduced to our characters as they’ll exist for the rest of the film. Tony Stark the dad, Black Widow the project manager, fat Thor, and most impressively, Bruce Banner’s new persona: He’s no longer doing the whole Jekyll-Hyde thing and transforming into a green monster at increasingly less well defined times, but has rather leaned fully into the Hulk side of the, uh, personality, we’ll say — much like what we saw in “Thor: Ragnarok”. This time around, however, we get to see what I’m dubbing: “The Hanna Montana Hulk” (the best of both worlds). Basicall,y Hulk’s body and Banner’s brain.</p>
<p>What’s interesting about this transformation isn’t so much the fact that it exists, but how Banner explains it came about. Bruce Banner explains his current existence is only possible because he “stopped trying to treat The Hulk like a disease” and, rather than trying to cure it, decided to attempt a kind of coexistence. But when did we ever see Mark Ruffalo’s Bruce Banner trying to cure The Hulk? He was tepidly comfortable with the Jekyll-Hyde setup in “The Avengers”, lost control of The Hulk completely in “Avengers: Age of Ultron”, was completely consumed by The Hulk in “Thor: Ragnarok”, and spent the entirety of “Avengers: Infinity War” trying, unsuccessfully, to coax The Hulk out. Mark Ruffalo’s Hulk has, at least from my reading of the films, viewed The Hulk as a tool. He’s lost control of it from time to time, and certainly isn’t happy about being The Hulk, but we’ve never once seen him try to cure The Hulk.</p>
<p>Everyone that I talked to about this bit of the film seemed happy to just write this off as something that happened during the five-year time gap. Many seem to be going as far as demanding to see a solo film based on Hulk’s journey during that five-year gap. While I’d certainly be the first to get my tickets to that movie, I actually had a different read on the scene. It certainly could be like Hawkeye &amp; Black Widow’s throw-away reference to Budapest throughout the franchise. But I think it’s much more likely a reference to “The Incredible Hulk”. A film that focuses entirely on Bruce Banner’s effort to cure himself and get rid of The Hulk. It’s the inciting incident that causes him to head to America; it’s the reason the film’s villain comes to exist; Bruce Banner’s drive to find a cure is what makes “The Incredible Hulk” work.</p>
<p>There’s a pretty good chance that I’m wrong, but even if I am, the connection is there. “Avengers: Endgame” is a film that rewards long-time viewers of the MCU. Most of the films in the MCU tend to pretend like inconvenient, or less revered films, just don’t exist. Tony Stark blew up all of his suits in “Iron Man 3” only to build more than ever by the time “Avengers: Age of Ultron” started rolling. Jane Foster has been almost completely erased from the MCU because Natalie Portman refused to come back. But of course, “The Incredible Hulk” is probably the best example of this. Bruce Banner travels to Canada at the end of “The Incredible Hulk”, only to be picked up by Black Widow in Calcutta at the start of “The Avengers”, Bruce Banner was recast, Abomination is never mentioned again, and Liv Tyler went the way of Jane Foster. But then, whether intentional or not, Markus and McFeely acknowledged “The Incredible Hulk”. I think that’s pretty awesome.</p>
<img src="img/2019-04-30-B.jpg" alt="Screenshot from The Incredible Hulk" class="full">
<p class="caption">Screenshot from "The Incredible Hulk"</p>
<h3>This next bit is pretty off topic, so just quit reading if you’re not interested.</h3>
<p>I’m not one of those “The Incredible Hulk” fans that pretends like the movie is perfect or anything. It’s not even close to my favorite film in the MCU. There are 22 films to choose from there, and 4 of them are directed by Joe &amp; Anthony Russo, so there’s really not much of a contest.</p>
<p>However, I think the biggest issue with “The Incredible Hulk”, at least in terms of audience reception, is the timing. I’d posit that if “The Incredible Hulk” were released today, rather than back in 2008, it would be received by the audience with open arms.</p>
<p>“The Incredible Hulk” came out in 2008, a few short months after “Iron Man”. Compare a fun, sarcastic super-hero who announces his secret identity at the end of the film just so that he can bask in the glory of being Iron Man; to the much more deliberate, at points just straight-up morose, film focused on contemplating the tragedy that being a super-hero is… I mean… is it any surprise that “Iron Man” tripled “The Incredible Hulk’s” box office numbers?</p>
<p>Of course, following “The Incredible Hulk” we get “Iron Man 2” in 2010, another film about the same cocky, fun superhero who’s having a blast getting to save the world. Then, like a one-two punch, we get “Thor” in 2011, and are introduced to another hero who loves being a hero. Of course, later that year, we got “Captain America: The First Avenger”, and with that, a hero who’s a bit more stoic, and doesn’t love 100% of his time as Captain America. But I’d still argue he’s living out his dreams for at least 70% of the film.</p>
<p>But even if “Captain America: The First Avenger” was half as morose and brooding as “The Incredible Hulk”, it’s only a year later that we get Joss Weadon’s “Avengers”. A film that’s so fun, and groundbreaking (at the time) that we forget forever that super-hero’s are allowed be self-aware.</p>
<p>But then something odd happens. Shane Black takes over the reins of the Iron Man franchise and starts to steer the story in a kind of “The Incredible Hulk” direction. Tony Stark spends the biggest chunk of “Iron Man 3” fighting PTSD that he’s developed as a result of the last film, “Avengers”. He loses all interest in being a hero anymore and blows up all of his suits at the end of the film.</p>
<p>Then Joe &amp; Anthony Russo get brought in to direct “Captain America: The Winter Soldier”, and they start to steer things in the same direction again. Captain America spends that whole movie fighting against S.H.I.E.L.D., the organization that tracked him down, and gave him a reason to be alive in between “Captain America: The First Avenger” &amp; “Avengers”. It’s starting to feel like maybe the filmmakers behind the movies are having more fun getting to write and direct superheroes that are, at least somewhat, self-aware.</p>
]]></description>
      </item>
    
      <item>
        <title>Is “The Prisoner of Azkaban” the Best “Harry Potter” Movie</title>
        <link>https://makc.co/essays/why-the-prisoner-of-azkaban-is-the-worst-movie/</link>
        <guid>https://makc.co/essays/why-the-prisoner-of-azkaban-is-the-worst-movie/</guid>
        <pubDate>Fri, 19 Oct 2018 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>If you have read any reviews for the Harry Potter films online, you probably know that Harry Potter and the Prisoner of Azkaban is widely considered to be the best of the films. The praise for this movie is off the charts. It has 91% on Rotten Tomatoes (a score beaten only by The Deathly Hallows Pt.2, at 94%), Roger Ebert gave it 3.5/4 starts, the film has high reviews on basically every movie rating platform out there, MetaCritic, IMDb, ext. If you google the movie, the top voted tags, are “magical”, “must watch”, “clever”, and “intense”; one of my favorite YouTuber’s, the Nerd Writer even made a <a href="https://www.youtube.com/watch?v=3hZ_ZyzCO24">video</a> about why he believes The Prisoner of Azkaban to be the best movie.</p>
<p>I’m coming at this piece with a lot of opposition, so let me explain a few things. First of all, I am writing this as a fan of the books first, who is also a pretty big fan of the movies. Anytime I need a distraction or white noise, Harry Potter is my go-to. I’ve seen all of the Harry Potter movies, as many times as a Netflix addict has seen all the episodes of The Office. I do like all of the movies. I also like all of the books, though I have a much harder time ranking them. Technically speaking, The Prisoner of Azkaban is a very well-made movie. If I were rating the movies on that principle alone, I think I would likely put it at the top of my list. But I’m not. I also don’t know that I think The Prisoner of Azkaban is actually “the worst movie” in the series. I just don’t think it’s the best. I actually think it should be pretty low in the list. Perhaps not #8, but certainly no higher than #5; if you’re looking for a numeric breakdown.</p>
<p>So, what follows is, simply put, a breakdown of a few of my biggest issues with the film. These issues don’t really ruin the movie for me; I still get excited when it’s time for movie three, in my never-ending binge-watching sessions. But I think that has more to do with what the movie does for the series as a whole, re-casting some essential characters, and largely changing the tone of the movie to make them dramatically more interesting and fun to watch, than it has to do with the movie itself.</p>
<h2>Fred &amp; George</h2>
<p>Fred &amp; George are largely absent from the Prisoner of Azkaban. This isn’t saying a lot, because Fred &amp; George are characters who, much like Tonks, or Ginny, or Lupin, are hugely important in the books, but had their roles severely butchered on their way to the big screen. It’s a sad fact of the movie franchise in general, but it doesn’t say anything about The Prisoner of Azkaban in particular. That being said, we have, essentially, two notable scenes with Fred &amp; George in this movie; the first is Fred &amp; George giving Harry the Marauder’s Map, pure plot, nothing really to reflect on there, and the second, I’ll get to in a moment. Before that, however, I think it would be beneficial to quickly break down the character traits that drive the book versions of Fred &amp; George.</p>
<p>When we first meet Fred &amp; George in The Sorcerer’s Stone, they are introduced as comic relief. That’s not meant to degrade them; they help turn what could have been a nightmarish scene (as anyone who has ever had trouble finding a bus) at Platform Nine and Three Quarters, into a happy and quirky second introduction into the wizarding world for Harry. But later in this very book, we first get a real glimpse at the true nature of Fred &amp; George.</p>
<p>On Christmas Day, the four Weasley brothers at Hogwarts, along with Harry, receive some lovely hand-made sweaters from Molly Weasley. Ron and Percy both react to the gift the way that most would, either begrudgingly putting it on and hiding in others’ shadows, or, in the case of Percy, refusing to put it on entirely. But Fred &amp; George have the complete opposite reaction. They show up in the Gryffindor common room proudly sporting their new sweaters and making sure that their brothers are doing the same. In the case of Percy, literally forcing him into the sweater. This is a striking scene, and admittedly an unexpected one. Molly is nowhere to be found; there’s no chance that she could be offended by her sons’ refusal to wear the sweaters she worked so hard on. But that never matters to Fred &amp; George. They’re proud to be Weasley’s, are happy to show it, and are incapable of understanding why their brothers wouldn’t feel the same way.</p>
<p>The brothers continue to define themselves as perhaps the most loving members of the Weasley family throughout the books. Maybe the most interesting thing about the Weasley twins is that we never really witness them complaining about the financial status of the family. At least for the boys in the family, this is really saying something. Ron can be found feeling sorry for his lack of money off and on throughout the series, most notably in The Goblet of Fire. Percy disowns the family for two whole books, and it’s quite clear that this has as much to do with his disdain for the family’s lifestyle as it has to do with his dedication to his job at the Ministry of Magic. But Fred &amp; George never seem to have this problem. They have the perfect mix of pride, intelligence, bravado, and love that force them to realize how lucky they are to be in such a great family. In short, they grew up in a loving family, well taken care of, but appreciate it like people who didn’t.</p>
<p>So, when we first meet Fred &amp; George in The Prisoner of Azkaban, I was shocked at what I saw. Harry arrives at The Leaky Cauldron, quickly followed by Hermione and the entire Weasley family. Ron wastes no time in showing Harry a newspaper clipping from the family’s vacation to Egypt, which they took after winning prize money from The Daily Prophet. Fred &amp; George then enter the scene, immediately rip the newspaper clipping from Ron’s hands, and proceed to berate him for showing anyone the clipping in the first place. I have no doubt in saying that book Fred &amp; George would have been the first ones to show off the newspaper clipping, and would have never considered for a moment being embarrassed by their summer with the family in Egypt. Cuarón has Fred &amp; George behaving as many fifteen-year-old boys probably would. This wouldn’t be a problem, except for the fact that it totally contradicts the characters that exist in the book world.</p>
<h2>Dumbledore</h2>
<p>At the end of The Prisoner of Azkaban, Harry, Ron, and Hermione discover the truth about Sirius. But before this can happen, Ron is bitten and has his leg broken by Sirius, in dog form, and is then dragged all the way through the secret passage under the Whomping Willow and through the preceding tunnel, into the Shrieking Shack. Even after he gets away from Sirius, Ron is forced to walk around on a broken leg, as the trio escapes from Professor Lupin, now transformed into a werewolf, and attacking any living thing in sight. It’s after all of this that we catch up with Ron again, enjoying some well-deserved peace in the hospital wing. He’s resting as peacefully as you can with a broken leg, when Dumbledore makes his way into the room, and immediately breaks into a monologue, recapping the previous scenes’ events, and telling Harry and Hermione to go back in time to save Sirius and Buckbeak. At some point in this monologue, Dumbledore makes his way over to Ron and does a little more than pat Ron on his broken leg. It’s quite clear in the movie that he’s actually causing Ron a good deal of pain.</p>
<p>This is another huge violation of the character traits established in the books. You certainly could make the case that Dumbledore is more flippant with the safety of his students than he should be. We get our first example of this in the first book, when he allows Harry to chase the Sorcerer’s Stone and face Voldemort on his own at eleven-years-old. But the idea of Dumbledore physically causing anyone pain is a foreign concept to anyone who’s read the books. The only example I can think of of Dumbledore physically harming anyone in the books is when he hexes an Auror in the Order of the Phoenix, so that he can avoid being taken to Azkaban. Even when confronted with the certainty of his own death in The Half-Blood Prince, Dumbledore has a calm and collected conversation with the Death Eaters closing in on him, as he awaits Snape to finish the job.</p>
<p>What’s most annoying about this little gaff is the fact that it seems to happen mostly for comic relief. This is probably why this moment in the film has become one of the most criticized by book lovers, but that doesn’t make the criticism any less valid.</p>
<h2>Wizarding Law</h2>
<p>Perhaps the best-established piece of the Wizarding World is the Ministry of Magic. This is probably the single most notable reason why the Harry Potter universe feels more realistic than other magical universes. The bureaucracy of the world grounds the characters and adds age to the world as a whole, making it feel refined. Perhaps the most well-understood of all the various Ministry regulations is the “Decree for the Reasonable Restriction of Underage Sorcery. Three times Harry is caught playing fast and loose with this law, once in The Chamber of Secrets (though technically it was Dobby’s fault), again in The Order of the Phoenix, and finally, later in The Prisoner of Azkaban itself. This couldn’t be clearer. Until a witch or wizard turns seventeen, they are not allowed to use magic outside of school.</p>
<p>So, when I turn on The Prisoner of Azkaban, and the first thing I see is Harry casting Lumos under his bedsheets, with no consequences; I’m understandably irked by the scene. Much like the scene with Dumbledore, I’m flustered by the fact that this scene happens for no real reason at all. The Lumos scene happens as part of the opening credits. The light from the spell is the transition to reveal the title of the movie. Cuarón broke the most basic, most well-understood rule of the universe for spectacle. This is a cardinal sin in filmmaking, and deservedly so.</p>
<hr>
<p>Now that I’ve thoroughly trashed the film for seven minutes, it seems like a good idea to repeat the fact that, buying large, I like this movie quite a bit. I highly recommend you watch the video linked above. This is because I don’t think any amount of criticism of the film should outweigh its well-deserved praise. The movie is important in the series, not only because it changes the tone of the films and enables the films that came after it, but also because it really is spectacular in its own right. The movie is consistent in its themes, more so than any of the others. While it deviates from the book more than the previous two films, it does so with the best of intentions. Largely, this movie first sets up the dynamic of the movies and the books as two entities to be enjoyed separately.</p>
]]></description>
      </item>
    

    
      <item>
        <title>Tmux for Lazy Folks</title>
        <link>https://makc.co/docs/tmux-for-lazy-folks/</link>
        <guid>https://makc.co/docs/tmux-for-lazy-folks/</guid>
        <pubDate>Wed, 30 Sep 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Some basic tmux commands, as well as some shortcuts for people who just want a quick multiplexer, but don't want to think about it to much.</p>
<h2>Ensure Tmux is Installed</h2>
<table>
<thead>
<tr>
<th style="text-align:right">OS</th>
<th>Install Python</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:right"><strong>Arch Linux</strong></td>
<td><code>sudo pacman -S tmux</code></td>
</tr>
<tr>
<td style="text-align:right"><strong>Ubuntu/Debian</strong></td>
<td><code>sudo apt install tmux</code></td>
</tr>
<tr>
<td style="text-align:right"><strong>Fedora</strong></td>
<td><code>sudo dnf install tmux</code></td>
</tr>
<tr>
<td style="text-align:right"><strong>macOS + Homebrew</strong></td>
<td><code>brew install tmux</code></td>
</tr>
<tr>
<td style="text-align:right"><strong>FreeBSD</strong></td>
<td><code>su &amp;&amp; pkg install tmux</code></td>
</tr>
</tbody>
</table>
<h3>Create a Main/Master Session andior Connect to It</h3>
<p>There are many benefits to using tmux, but perhaps the most salient is the ability to attach and detach from the same terminal session over several machines or several different points in time. For this reason, it's common to create a master or main session in which a user can always connect to. The standard syntax for creating a new tmux session is:</p>
<pre class="language-bash"><code class="language-bash">tmux new <span class="token parameter variable">-s</span> main</code></pre>
<p>However we can also use the command below to either:</p>
<ol>
<li>Create a session called <code>main</code>, or</li>
<li>Attach to a previously existing session with the name: <code>main</code>.</li>
</ol>
<pre class="language-bash"><code class="language-bash">tmux new <span class="token parameter variable">-A</span> <span class="token parameter variable">-s</span> main</code></pre>
<p class="caption">
If one desired to do so, this would be a pretty great usecase for a bash or <a href="https://youtube.com/watch?v=KBh8lM3jeeE" target="_blank">zsh alias.</a>
</p>
<p>The command above (<code>new -A -s main</code>) can be used to connect to a pre-existing connection, but it should also be noted that the standard syntax for doing this is:</p>
<pre class="language-bash"><code class="language-bash">tmux attach <span class="token parameter variable">-t</span> main</code></pre>
<h3>Disconnecting from Main/Master Session</h3>
<p><code>Ctrl+b</code>, then <code>d</code>.</p>
<p>Tmux will keep sessions alive if a SSH session disconnects (another feature), but it's not great practice to just kill a terminal window with a tmux session running.</p>
<h3>Some Movements and Tiling Stuffs</h3>
<table>
<thead>
<tr>
<th>Keybinding</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Ctrl+b</code>, then <code>arrow keys</code></td>
<td>Navigate up, down, left and right.</td>
</tr>
<tr>
<td><code>Ctrl+b</code>, then <code>%</code></td>
<td>Tile session vertically.</td>
</tr>
<tr>
<td><code>Ctrl+b</code>, then <code>&quot;</code></td>
<td>Tile session horizontaly.</td>
</tr>
<tr>
<td><code>Ctrl+b</code>, then <code>x</code></td>
<td>Close selection pane.</td>
</tr>
<tr>
<td><code>Ctrl+b</code>, then <code>c</code></td>
<td>New window.</td>
</tr>
<tr>
<td><code>Ctrl+b</code>, then <code>n</code></td>
<td>Cycle windows.</td>
</tr>
<tr>
<td><code>Ctrl+b</code>, then <code>&amp;</code></td>
<td>Close current window.</td>
</tr>
<tr>
<td><code>Ctrl+b</code>, then <code>d</code></td>
<td>Fully detach tmux session.</td>
</tr>
</tbody>
</table>
<p>In case you didn't figure it out, <code>Ctrl+b</code> is the leader for just about every keybinding in tmux.</p>
]]></description>
      </item>
    
      <item>
        <title>Convert a CSV file to JSON</title>
        <link>https://makc.co/docs/csv-to-json/</link>
        <guid>https://makc.co/docs/csv-to-json/</guid>
        <pubDate>Mon, 21 Sep 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A guide to converting a CSV (Comma Separated Value) file to a properly formatted JSON file with python, and cleaning up any artifacts that might come up in the process.</p>
<h2>Ensure Python is Installed</h2>
<table>
<thead>
<tr>
<th style="text-align:right">OS</th>
<th>Install Python</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:right"><strong>Arch Linux</strong></td>
<td><code>sudo pacman -S python</code></td>
</tr>
<tr>
<td style="text-align:right"><strong>Ubuntu/Debian</strong></td>
<td><code>sudo apt install python3</code></td>
</tr>
<tr>
<td style="text-align:right"><strong>Fedora</strong></td>
<td><code>sudo dnf install python3</code></td>
</tr>
<tr>
<td style="text-align:right"><strong>macOS + Homebrew</strong></td>
<td><code>brew install python</code></td>
</tr>
</tbody>
</table>
<h3>Clean up CSV</h3>
<p>If necessary, use a tool like Libre Office Calc or Microsoft Excel to clean up the <code>.csv</code> file, as long as the spreadsheet app can export to CSV, use whatever you like. Remove any columns that contain unnecessary information, as we only want to convert data that we want to end up in the JSON file. Importantly: ensure that the top cell in each column is what you want to become the title of a JSON field. For example, a spreadsheet that looks like:</p>
<table>
<thead>
<tr>
<th>title</th>
<th>date</th>
<th>link</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cool video thang</td>
<td>YYYY-MM-DD</td>
<td>makc.co/vid1.mov</td>
</tr>
<tr>
<td>Antoher vid</td>
<td>YYYY-MM-DD</td>
<td>makc.co/vid2.mov</td>
</tr>
</tbody>
</table>
<p>Will become a JSON file that looks like:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
        <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Cool video thang"</span><span class="token punctuation">,</span> 
        <span class="token property">"date"</span><span class="token operator">:</span> <span class="token string">"YYYY-MM-DD"</span><span class="token punctuation">,</span>
        <span class="token property">"link"</span><span class="token operator">:</span> <span class="token string">"makc.co/vid1.mov"</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
        <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Another vid"</span><span class="token punctuation">,</span> 
        <span class="token property">"date"</span><span class="token operator">:</span> <span class="token string">"YYYY-MM-DD"</span><span class="token punctuation">,</span>
        <span class="token property">"link"</span><span class="token operator">:</span> <span class="token string">"makc.co/vid2.mov"</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">]</span></code></pre>
<h2>Convert to JSON</h2>
<p>Modern versions of Python come with a CSV library built in. We can use this library to convert our <code>.csv</code> file to a <code>.json</code> database with the proper formatting:</p>
<pre class="language-python"><code class="language-python">python <span class="token operator">-</span>c <span class="token string">'import csv,json,sys; print(json.dumps(list(csv.DictReader(open(sys.argv[1], newline="", encoding="utf-8"))), indent=4))'</span> OLD<span class="token punctuation">.</span>csv <span class="token operator">></span> NEW<span class="token punctuation">.</span>json</code></pre>
<p class="caption">
<strong>Note:</strong> This process is very fast, almost immediate on my test CSV file with 176 rows and 5 columns. If the process is taking more than a second or two, there is probably something wrong.
</p>
<h3>Clean up JSON</h3>
<p>It may also be necessary to clean up some artifacts from the conversion process. I've found that if an artifact is going to be created, it will almost always be prefaced by either a backslash or a leading space where there shouldn't be one. Using a tool like Vim and searching for patterns like <code>/\</code>, <code>/ &quot;</code>or <code>/&quot; </code> make finding and fixing these errors very easy.</p>
<p>On my test conversion I also found that the first field in each JSON entry was mangled. The field should have been called: <code>&quot;title&quot;:</code>, but in every instance, what was actually rendered was: <code>&quot;\ufefftitle&quot;:</code>. One again however, a simple Vim command was able to clean this up nearly instantly: <code>:%s/\ufefftitle/title/g</code>.</p>
]]></description>
      </item>
    
      <item>
        <title>Powershell and Bash</title>
        <link>https://makc.co/docs/powershell-and-bash/</link>
        <guid>https://makc.co/docs/powershell-and-bash/</guid>
        <pubDate>Mon, 07 Sep 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Some basic tips and rules for navigating Windows and Linux with powershell and bash. This was made for my own personal use and does assume that any reader is pretty familiar with bash commands and mainly interested in learning how to transfer these same skills to navigating a Windows system via powershell.</p>
<h2>Powershell eccentricitys</h2>
<ul>
<li>Get-Help (linux of -h or --help or man x)
<ul>
<li><code>Get-Help ls</code> or <code>Get-Help ls --full</code></li>
<li><code>-Force</code> on ls is like <code>ls -a</code>
<ul>
<li>example: <code>ls -Force C:\</code> to see <code>ProgramData</code></li>
</ul>
</li>
</ul>
</li>
<li>As a general rule just change forward slash to backslash in Powershell and things just kind of work as you'd expect. This is because while powershell commands are weird, but have a ton of aliases that are mapped to the UNIX equivalents.</li>
<li>Some exceptions to the rule:
<ul>
<li><code>-f</code> becomes <code>-Force</code></li>
<li><code>-r</code> becomes <code>-Recurse</code>
<ul>
<li>Often arguments needs to be at the end of a command, ex: <code>rm -r FILE -Recurse</code></li>
</ul>
</li>
</ul>
</li>
<li>Powershell doesn't have <code>less</code> command, just the old bash <code>more</code>, which does still work.</li>
<li><code>Select-String</code> - ps version of <code>grep</code></li>
<li><code>-Filter</code> lets you search for files in a directory, ex: <code>ls C:\ -Recurese - Filter *.exe</code> will give you so much data that you won't be able to use it for anything if you don't pipe it through <code>more</code>.</li>
</ul>
<h3>Escape Characters</h3>
<ol>
<li>In Linux <code>\</code> is the escape character.<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> My<span class="token punctuation">\</span> Folder</code></pre>
</li>
<li>Windows in it's normal weird fashion uses a backtick `<pre class="language-powershell"><code class="language-powershell">mkdir My` Folder</code></pre>
</li>
</ol>
<ul>
<li><code>history</code> Ctrl+R allows searching through recent command history in Powershell and bash.</li>
<li>wildcard * is handy as hell, cool example:</li>
</ul>
<pre class="language-bash"><code class="language-bash"><span class="token function">cp</span> *.jpg ~/Dowloads/

<span class="token comment"># Copies all jpegs from working directory to ~/Downloads</span></code></pre>
<h2>Linux stuff</h2>
<ul>
<li>/bin - stores apps</li>
<li>/etc - system configs</li>
<li>/home - duh</li>
<li>/proc - currently running process</li>
<li>/usr - usr intalled files</li>
<li>/var - temp stuff (sort of)</li>
</ul>
<h3><code>ls -l</code> broken down</h3>
<table>
<thead>
<tr>
<th>permissions</th>
<th># of links</th>
<th>file owner group</th>
<th>size</th>
<th>date of modification</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>drwxr-xr-x</td>
<td>2</td>
<td>makc makc</td>
<td>3</td>
<td>Sep  3 19:00</td>
<td>Desktop</td>
</tr>
<tr>
<td>drwxr-xr-x</td>
<td>13</td>
<td>makc makc</td>
<td>14</td>
<td>Sep  6 17:49</td>
<td>Documents</td>
</tr>
<tr>
<td>drwxr-xr-x</td>
<td>4</td>
<td>makc makc</td>
<td>5</td>
<td>Sep  6 21:22</td>
<td>Downloads</td>
</tr>
</tbody>
</table>
<h3><code>ls -l permissions</code> broken down</h3>
<table>
<thead>
<tr>
<th>d</th>
<th>rwx</th>
<th>r-x</th>
<th>r-x</th>
</tr>
</thead>
<tbody>
<tr>
<td>filetype</td>
<td>owner rights</td>
<td>group rights</td>
<td>everyone elses rights</td>
</tr>
</tbody>
</table>
<p>The first letter in the string (<code>d</code> in the above example) indicates the type of file that is being listed.</p>
<ul>
<li><code>d</code> = directory</li>
<li><code>-</code> = regular file</li>
<li><code>l</code> = symbolic link</li>
</ul>
<p>The next nine characters are broken into three categories, permissions for the:</p>
<ol>
<li>file owner</li>
<li>group</li>
<li>other users</li>
</ol>
<ul>
<li><code>r</code> = read</li>
<li><code>w</code> = write</li>
<li><code>x</code> = execute</li>
</ul>
<p><code>-</code> indicates that a permission does not exist, for example: <code>r-x</code> would indicate rights to read and execute, but not to write. In the example above (<code>drwxr-xr-x</code>), we can learn that:</p>
<ol>
<li>The file is a directory.</li>
<li>The owner has full rights: read, write, execute</li>
<li>The group has read and execution rights, not write rights.</li>
<li>All other users also have read and execution rights, not write rights.</li>
</ol>
<h3>Viewing Files</h3>
<ul>
<li><code>cat</code> - prints full file</li>
<li><code>head</code> - first 10 lines
<ul>
<li><code>head -n 1 FILE</code> - prints specific number of lines (10 is just a default)</li>
<li><code>head --lines 1 FILE</code> - same thing</li>
</ul>
</li>
<li><code>tail</code> - last 10 lines
<ul>
<li><code>tail -n 1 FILE</code> - prints specific number of lines (10 is just a default)</li>
<li><code>tail --lines 1 FILE</code> - same thing</li>
</ul>
</li>
<li><code>less</code> - fills window with text file
<ul>
<li>Bash also has the legacy <code>more</code> command, but why bother if you don't have to?</li>
</ul>
</li>
</ul>
<h3>Grep and His Powerful Friends</h3>
<p>Way better than the powershell set-up (or at least less weird). Some examples follow:</p>
<ul>
<li><code>grep &quot;America is doomed&quot; Documents/journal/*.md</code> - finds every-time I wrote a journal entry after watching the news in 2026.
<ul>
<li><code>grep &quot;America is doomed&quot; Documents/journal/*.md | less</code> - give me an output that I can actually parse easily.</li>
<li><code>grep -c &quot;America is doomed&quot; Documents/journal/*.md</code> - would just brake down how mnay times I've used the phrase in a journal.
<ul>
<li>Example Output:</li>
</ul>
<pre class="language-bash"><code class="language-bash">Documents/journal/volume-03.md:20
Documents/journal/volume-04.md:122
Documents/journal/volume-05.md:36
Documents/journal/volume-06.md:79
Documents/journal/volume-07.md:22</code></pre>
</li>
<li><code>grep -i</code> - case insensitive</li>
<li><code>grep -n pattern</code> - show line numbers with output</li>
<li><code>grep -w</code> - only returns if pattern matches exactly
<ul>
<li><code>grep -w data app.log</code> wouldn't return the word &quot;database&quot; as a result</li>
</ul>
</li>
<li>Showing lines before or after pattern is found:
<ul>
<li><strong>Before</strong>: <code>grep -B 2 &quot;data&quot; app.log</code> - shows two lines before &quot;data&quot;</li>
<li><strong>After</strong>: <code>grep -A 85 &quot;data&quot; app.log</code> - shows eighty-five lines before &quot;data&quot;</li>
</ul>
</li>
<li><code>grep &quot;America is doomed | sort --unique</code> - filters out anytime I've just timed &quot;America is doomed&quot; and ended the entry there.</li>
</ul>
</li>
</ul>
]]></description>
      </item>
    
      <item>
        <title>Creating a New Branch</title>
        <link>https://makc.co/docs/new-git-branch/</link>
        <guid>https://makc.co/docs/new-git-branch/</guid>
        <pubDate>Sun, 16 Aug 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A guide for creating a new branch in a git repository.</p>
<h2>Creating a New Branch</h2>
<ol>
<li>Navigate to git repo:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> REPO</code></pre>
<ol start="2">
<li>Create new branch:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> checkout <span class="token parameter variable">-b</span> NEW-BRANCH</code></pre>
<ol start="3">
<li>Push new branch:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> push origin NEW-BRANCH</code></pre>
<h3>(Optionally) Create a Branch from a Specific Commit</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> checkout <span class="token parameter variable">-b</span> NEW-BRANCH HASH</code></pre>
<h4>Swap Between Branches</h4>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> checkout BRANCH-NAME</code></pre>
<h2>Merge Changes to master/main Branch</h2>
<ol>
<li>Change back to main branch:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> checkout master</code></pre>
<ol start="2">
<li>Merge it:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> merge branchNAME</code></pre>
]]></description>
      </item>
    
      <item>
        <title>Downloading a Video</title>
        <link>https://makc.co/docs/video-download/</link>
        <guid>https://makc.co/docs/video-download/</guid>
        <pubDate>Fri, 07 Aug 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A quick guide to downloading videos that are harder than they should be to download.</p>
<h2>Locating a File to Download</h2>
<ol>
<li>Toggle <em>Developer Mode</em> in any modern full-featured browser. Developer mode can be triggered in Chromium or Firefox by pressing <em>F12</em>.</li>
<li>Swap to the <em>Network</em> tab.</li>
<li>Reload web page if necessary and ensure video playback has been triggered.</li>
<li>Monitor for any video files, using URL filters andior the Media category in the Developer menu.</li>
<li>Select individual requests and view the headers until a url with a video file is included, the video file will often be obscured, as in the following example: <code>https://site.com/control.php?file=filename.mp4&amp;acctoken=token</code></li>
</ol>
<img class="thirds" src="img/2026-08-07.jpg" alt="Using the Network section to find a URL">
<p class="caption">
An example using the network tab in Firefox's Dev Tools to located a segmented video stream when a video starts playing on an example website.
</p>
<h2>Downloading the Video File</h2>
<ol>
<li>Using either <em>curl</em> or <em>wget</em> use the full URL to download the video file.</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-L</span> <span class="token parameter variable">-O</span> <span class="token string">"https://video.com/something.php?file=title.mp4&amp;token=token"</span></code></pre>
<p class="caption">or</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">wget</span> --content-disposition <span class="token string">"https://video.com/something.php?file=title.mp4&amp;token=token"</span> </code></pre>
<p class="caption"><em>ensure quotes around the video file URL are in place</em></p>
<ol start="2">
<li>Multiple attempts may be neccasary if their are multiple video files located (ads in front of video, <em>et cetera</em>), however once the curl or wget process has started the file-size is usually a pretty good giveaway (18MB vs 250MB is a pretty clear difference and easy to discriminate against).</li>
<li>When the download is complete, often times you will be left with something other than a video file, example: <em>remote_control.php</em> would most likely be the filename if downloading a video with the previously referenced sample URL. The file-size here is once again a pretty clear give-away, as most .php files won't be several hundred megabytes.</li>
<li>Change the file-name and extension as you will, ensuring that the .php extension is changed to a video container. I have found that <em>.mp4</em> or <em>.mov</em> will almost always result in a playable video, but depending on the format of the original file, a few tries might be necessary. In any case, simply matching the file extension of the original file should always work.</li>
</ol>
<h3>Sequenced Videos</h3>
<p>Some websites don't list a full video file in the Network tab of the Developer Menu. Rather than hosting a full file, it's only possible to access segmented video clips. This however does not mean that we cannot still download a full video.</p>
<ol>
<li>First find the sequence of video clips, for example the following URL might show up: <code>https://video.net/video.mp4/seg-01-av.ts</code></li>
<li>Create a list of all segmented files, by first downloading an individual segment:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-L</span> <span class="token parameter variable">-O</span> <span class="token string">"https://video.net/video.mp4/seg-01-av.ts"</span></code></pre>
<ol start="3">
<li>Once you have one video segment, you can calculate or roughly guess the video segment in question. For example if the first segment was roughly two seconds, and the video was about thirty minutes long, we can reasonably guess that there would be about nine hundred individual segments.</li>
<li>Create a bash script to download all of the segments:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/usr/bin/env bash</span>

<span class="token assign-left variable">BASE_URL</span><span class="token operator">=</span><span class="token string">"https://video.net/video.mp4"</span>

<span class="token keyword">for</span> <span class="token for-or-select variable">i</span> <span class="token keyword">in</span> <span class="token variable"><span class="token variable">$(</span><span class="token function">seq</span> <span class="token parameter variable">-w</span> <span class="token number">1</span> <span class="token number">900</span><span class="token variable">)</span></span><span class="token punctuation">;</span> <span class="token keyword">do</span>
    <span class="token function">curl</span> <span class="token parameter variable">-L</span> <span class="token parameter variable">-O</span> <span class="token string">"<span class="token variable">$BASE_URL</span>/seg-<span class="token variable">$i</span>--av.ts"</span>
<span class="token keyword">done</span></code></pre>
<ol start="5">
<li>Run the script.</li>
<li>Add all the downloaded files into a properly organized text document. This function will organize all the files, regardless of whether or not they are numbered with proceeding zeros:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">printf</span> <span class="token string">'%s\n'</span> seg-*-av.ts <span class="token operator">|</span> <span class="token function">sort</span> <span class="token parameter variable">-V</span> <span class="token operator">|</span> <span class="token function">sed</span> <span class="token string">"s/^/file '/; s/$/'/"</span> <span class="token operator">></span> files.txt</code></pre>
<ol start="7">
<li>Combine all the files into one with <em>ffmpeg</em>:</li>
</ol>
<pre class="language-bash"><code class="language-bash">ffmpeg <span class="token parameter variable">-f</span> concat <span class="token parameter variable">-safe</span> <span class="token number">0</span> <span class="token parameter variable">-i</span> files.txt <span class="token parameter variable">-c</span> copy output.mp4</code></pre>
]]></description>
      </item>
    
      <item>
        <title>Setting up a Calibre-web Server on Linux</title>
        <link>https://makc.co/docs/calibre-web-docker/</link>
        <guid>https://makc.co/docs/calibre-web-docker/</guid>
        <pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<h2>Install Docker Engine / Docker CE</h2>
<ol>
<li>Follow <a href="https://docs.docker.com/engine/install/#get-started">the Docker website's documentation</a>, it's solid &amp; also includes instructions to remove previous iterations of Docker on all the other supported distributions.</li>
</ol>
<h2>Install Calibre-web via the <a href="https://github.com/linuxserver/docker-calibre-web">Docker Image</a></h2>
<ul>
<li><strong>Note</strong>: <a href="https://github.com/janeczku/calibre-web">Calibre-web</a> can be installed via a python pip package, however the Docker image is easier to set up and run as a background process on a Linux server.</li>
</ul>
<ol>
<li>Create a new directory for the calibre-web app to live in:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> Docker/claibre-web
<span class="token builtin class-name">cd</span> Docker/claibre-web</code></pre>
<ol start="2">
<li>Set up a docker-compose.yml file in app directory:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> docker-compose.yml</code></pre>
<ol start="3">
<li>Ensure to make required changes to the <em>volumes:</em> section of the default docker-compose.yml:</li>
</ol>
<pre class="language-bash"><code class="language-bash">name: calibre-web

---
services:
  calibre-web:
    image: lscr.io/linuxserver/calibre-web:latest
    container_name: calibre-web
    environment:
      - <span class="token assign-left variable">PUID</span><span class="token operator">=</span><span class="token number">1000</span>
      - <span class="token assign-left variable">PGID</span><span class="token operator">=</span><span class="token number">1000</span>
      - <span class="token assign-left variable">TZ</span><span class="token operator">=</span>Etc/UTC
      - <span class="token assign-left variable">DOCKER_MODS</span><span class="token operator">=</span>linuxserver/mods:universal-calibre
    volumes:
      - /home/<span class="token environment constant">USER</span>/Docker/calibre-web/config:/config
<span class="token comment"># ~/Docker/calibre-web/ will map the Calibre config directory to /config in the app directory</span>
      - /home/<span class="token environment constant">USER</span>/Documents/Books:/books 
<span class="token comment"># ~/Documents/Books = Pre-existing Calibre libary directory including metadata.db file</span>
    ports:
      - <span class="token number">8083</span>:8083
    restart: unless-stopped</code></pre>
<ol start="4">
<li>Start the Calibre server:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> compose up <span class="token parameter variable">-d</span></code></pre>
<h3>Set-up the Calibre Library</h3>
<ol>
<li>From inside the server's LAN access the server via: <strong>http://serverIP:8083</strong></li>
<li>Login with the default credentials:</li>
</ol>
<pre class="language-bash"><code class="language-bash">Username: admin
Password: admin123</code></pre>
<ol start="3">
<li>When prompted set the Calibre Database Location to <strong>/books</strong>. Link to a pre-existing calibre libary in the <strong>docker-compose.yml</strong>, in the above example: <strong>/home/USER/Documents/Books</strong> is mapped to <strong>/books</strong>.</li>
</ol>
<img src="img/2026-06-14-A.png"  alt="Database config setup in calibre-web" class="thirds">
<p class="caption">
Databse config setup in the Calibre Web UI
</p>
<h4>Access the Calibre Server Remotely</h4>
<p>The easiest &amp; most secure way to access the calibre-server remotely is via a VPN. <a href="https://www.wireguard.com/">Wireguard</a> is very good for this, &amp; <a href="https://tailscale.com/">Tailscale</a> is an even simpler abstraction that uses Wireguard as a dependency. Full instructions for installing and configuring Tailscale can be found <a href="https://makc.co/docs/simple-tailscale-setup/">here</a>.</p>
<h2>Using the Calibre-web OPDS Server</h2>
<p>Maybe the best thing about the calibre-web server is the fact that it spins up an OPDS server alongside the web UI that can be accessed via many Andoid and iOS eReader apps. This server can be accessed in a browser or in an application by navigating to: <strong>http://serverIP:8083/opds</strong></p>
<img src="img/2026-06-14-B.png"  alt="The calibre-web OPDS server linked up in the Yomu app on iOS" class="half">
<p class="caption">The <a href="https://www.yomu-reader.com/" target="_blank">Yomu app</a> on iOS with the Calibre-web OPDS server linked via a Tailscale VPN connection.</p>
<p>OPDS makes it incredibly easy to browse and download a user's full Calibre library of eBooks in any number of OPDS apps across all platforms. Some of the most popular apps are <a href="https://apps.apple.com/us/app/fbreader-epub-and-fb2-reader/id1067172178">FBReader</a> on iOS, <a href="https://play.google.com/store/apps/details?id=com.flyersoft.moonreader">Moon+ Reader</a> for Android &amp; <a href="https://thorium.edrlab.org/en/">Thorium Reader</a> for Linux, Windows and Apple Silicon Macs. A much larger list of OPDS applications can be found on the <a href="https://github.com/opds-community/awesome-opds">Awesome OPDS GitHub page</a>.</p>
<img src="img/2026-06-14-c.png"  alt="My calibre library accessed in Yomu" class="half">
<p class="caption">My personal calibre library being accessed via the OPDS server in Yomu</p>
]]></description>
      </item>
    
      <item>
        <title>Setting up a Private Immich Server</title>
        <link>https://makc.co/docs/immich/</link>
        <guid>https://makc.co/docs/immich/</guid>
        <pubDate>Wed, 10 Jun 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<h2>Install Docker Engine / Docker CE</h2>
<ol>
<li><strong>Note</strong>: <em>Docker Engine</em> is not the same thing that would generally be installed via a standard linux package manager. To remove a previously installed iteration of Docker (at least on Ubuntu), use the command below:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> remove <span class="token variable"><span class="token variable">$(</span>dpkg --get-selections docker.io <span class="token function">docker-compose</span> docker-compose-v2 docker-doc podman-docker containerd runc <span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-f1</span><span class="token variable">)</span></span></code></pre>
<ol start="2">
<li>Follow <a href="https://docs.docker.com/engine/install/#get-started">the Docker website's documentation</a>, it's solid &amp; also includes instructions to remove previous iterations of Docker on all the other supported distributions.</li>
</ol>
<h2>Install Immich</h2>
<ol>
<li>Use <a href="https://docs.immich.app/install/docker-compose">the Immich website's documentation</a> for installing and running with Docker Compose; once again, this documentation is more than good enough.</li>
</ol>
<h3>Configure Immich</h3>
<ol>
<li>Visit port 2283 on the machine that Immich was installed on: <em>http://SERVER.IP.ADDRESS:2283</em>.</li>
<li>Follow the prompts to set up an admin &amp; some user accounts &amp; use <a href="https://docs.immich.app/install/post-install">Immich's documentation</a> if stuck.</li>
<li>There are a few different ways to remotely access an Immich server, but one of the easiest &amp; most secure is <a href="https://makc.co/docs/create-your-own-git-server/">via a VPN</a> - just do that unless there's a really good reason not to.</li>
</ol>
]]></description>
      </item>
    
      <item>
        <title>Copying a ISO Image to a USB Drive</title>
        <link>https://makc.co/docs/iso-2-usb-with-pv/</link>
        <guid>https://makc.co/docs/iso-2-usb-with-pv/</guid>
        <pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<h2>Required Items</h2>
<ul>
<li><a href="https://linux.die.net/man/1/pv">pv</a>. Don't use <a href="https://linux.die.net/man/1/dd">dd</a>. While it will often work, dd is a 50+ year old command, uses archaic syntax and provides little to no feedback on what it is doing.</li>
<li>An <a href="https://archlinux.org/download/">ISO image</a> (usually <em>.iso</em>, but the process will also work for <em>.raw.xz</em>, and some other archived images).</li>
</ul>
<h2>Steps</h2>
<ol>
<li>Insert a removable USB drive, and ensure that there is at least one partition large enough for the ISO image.</li>
<li>Use <a href="https://linux.die.net/man/8/lsblk">lsblk</a> to check the drive name &amp; partitions (very often a removable drive will be something like <em>/dev/sda</em> or <em>/dev/sdb</em>).</li>
</ol>
<pre class="language-bash"><code class="language-bash">lsblk <span class="token parameter variable">-f</span> <span class="token comment">## List block devices</span>

<span class="token comment">## Look for something like the partition tree below</span>

NAME        FSTYPE FSVER LABEL UUID  
sda                                   
└─sda1                                                                             </code></pre>
<ol start="3">
<li>If the drive has no partition, <a href="https://linux.die.net/man/8/cfdisk">cfdisk</a> is a user-friendly TUI tool that can create drive partitions.</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">cfdisk</span> /dev/sda
<span class="token comment">## Ensure changes are written before exiting the application</span></code></pre>
<ol start="4">
<li>Write the ISO image to a partition on the drive:</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">pv</span> arch.iso <span class="token parameter variable">-o</span> /dev/sda1</code></pre>
<h3>Optional Improvements</h3>
<ul>
<li><strong>--sync</strong> or <strong>-Y</strong> will use <a href="https://linux.die.net/man/2/fdatasync">fdatasync</a> to synchronize the buffer cache after every write operation. This will sometimes improve the accuracy of pv's progress bar when writing data to a slow disk (like a USB stick).</li>
</ul>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">pv</span> arch.iso <span class="token parameter variable">--sync</span> <span class="token parameter variable">-o</span> /dev/sda1

<span class="token comment">## or</span>

<span class="token function">sudo</span> <span class="token function">pv</span> arch.iso <span class="token parameter variable">-Y</span> <span class="token parameter variable">-o</span> /dev/sda1</code></pre>
<ul>
<li><strong>--stats</strong> or <strong>-v</strong> will show the transfer rate in bytes per second.</li>
</ul>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">pv</span> arch.ios <span class="token parameter variable">--sync</span> <span class="token parameter variable">--stats</span> <span class="token parameter variable">-o</span> /dev/sda1</code></pre>
]]></description>
      </item>
    
      <item>
        <title>Creating a Private Git Server</title>
        <link>https://makc.co/docs/create-your-own-git-server/</link>
        <guid>https://makc.co/docs/create-your-own-git-server/</guid>
        <pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>As <a href="https://github.com">GitHub</a> becomes <a href="https://mrshu.github.io/github-statuses/">increasingly less reliable</a> (not to mention riddled with AI slop), many are looking for alternatives. This documentation will walk through the process of setting up a private Git server for any non public-facing repositories.</p>
<h2>Required Hardware</h2>
<ul>
<li>A server. The required software is available on virtually every OS and should run on any modern CPU architecture. This documentation provides instructions for a POSIX-style shell.</li>
<li>A client machine to <em>push</em> to the server, once again, virtually any OS or CPU architecture will work.</li>
</ul>
<h2>Required Software</h2>
<ul>
<li><a href="https://www.openssh.org/">Open SSH</a></li>
<li><a href="https://git-scm.com/">Git</a></li>
<li>A VPN software, this guide uses: <a href="https://tailscale.com/">Tailscale</a></li>
</ul>
<h3>Required set-up for both devices</h3>
<p>On both the server and any machine you intend to push changes from, you will need to set-up a VPN connection and install OpenSSH, as well as an <em>optional</em> SSH key. The process is identical for each system:</p>
<h4>Install Tailscale</h4>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-fsSL</span> https://tailscale.com/install.sh <span class="token operator">|</span> <span class="token function">sh</span></code></pre>
<p>Authenticate your user account when prompted, or set up a new account on first use.</p>
<h4>Install OpenSSH</h4>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> pacman <span class="token parameter variable">-S</span> openssh  <span class="token comment"># Arch Linux</span>

<span class="token function">sudo</span> <span class="token function">apt</span> update <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> openssh-server <span class="token comment"># Debian &amp; Ubuntu</span>

<span class="token function">sudo</span> dnf <span class="token function">install</span> openssh-server  <span class="token comment"># Fedora</span>

<span class="token function">sudo</span> pkg <span class="token function">install</span> openssh  <span class="token comment"># FreeBSD</span>

brew <span class="token function">install</span> openssh  <span class="token comment"># Homebrew</span>

nix profile <span class="token function">install</span> nixpkgs<span class="token comment">#openssh  # Nix</span>
</code></pre>
<h3>Server Set-up</h3>
<p>Remote into the server using SSH and the server's Tailscale generated IP address. This documentation will use the user name: &quot;user&quot; &amp; a fake IP address of &quot;100.27&quot;.</p>
<pre class="language-bash"><code class="language-bash">tailscale status  <span class="token comment"># To locate the server's IP</span>

<span class="token function">ssh</span> user@100.27</code></pre>
<ol>
<li>Set up a location in which to store Git repositories</li>
<li>Create a directory with the name of the repository</li>
<li>Initiate a bare git repository</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> Repos <span class="token operator">&amp;&amp;</span> <span class="token builtin class-name">cd</span> Repos
<span class="token function">mkdir</span> <span class="token string">"REPO-NAME"</span> <span class="token operator">&amp;&amp;</span> <span class="token builtin class-name">cd</span> <span class="token string">"REPO-NAME"</span>

<span class="token function">git</span> init <span class="token parameter variable">--bare</span></code></pre>
<h3>Client Set-up</h3>
<p>Create an empty directory with the same name (REPO-NAME) anywhere on the client system.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">mkdir</span> REPO-NAME <span class="token operator">&amp;&amp;</span> <span class="token builtin class-name">cd</span> REPO-NAME</code></pre>
<p>Once inside of that directory run the following commands:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> init  <span class="token comment"># Initiates the new repository</span>
<span class="token function">git</span> <span class="token function">add</span> <span class="token parameter variable">-A</span>   <span class="token comment"># Stages all files</span>
<span class="token function">git</span> commit <span class="token parameter variable">-m</span> <span class="token string">"Initial commit"</span>  <span class="token comment"># Commit's those changes</span>

<span class="token function">git</span> remote <span class="token function">add</span> origin user@100.27:/home/user/Repos/REPO-NAME/

<span class="token function">git</span> branch  <span class="token comment"># Depending on Git version, changes might be commited to the "main" or "master" branch by default. Note which branch this repository defaulted to.</span>

<span class="token function">git</span> push <span class="token parameter variable">-u</span> origin master 
<span class="token comment"># or</span>
<span class="token function">git</span> push <span class="token parameter variable">-u</span> origin main</code></pre>
<p>After this set-up any new changes can be pushed to the private server with the standard <em>git push</em> command, with no additional flags or options required.</p>
<p><strong>Note</strong>: Git will only accept one origin to push to. If a typo or other error is made while running <em>remote add origin</em>, this can be fixed by running:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> remote set-url origin user@server:/home/user/Repo/REPO-NAME/

<span class="token function">git</span> remote <span class="token parameter variable">-v</span>  <span class="token comment"># Can be used to confirm that a typo was made upon initial set-up as well</span></code></pre>
<h3>Migrating an Existing Repository to Server</h3>
<ul>
<li>Change the remote origin</li>
<li>Ensure the primary branch names match up</li>
<li>Push to server</li>
</ul>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> remote set-url origin user@server:/home/user/Repo/EXISTING-REPO/
<span class="token function">git</span> remote <span class="token parameter variable">-v</span>  <span class="token comment"># To check url &amp; confirm before pushing</span>

<span class="token function">git</span> branch  <span class="token comment"># To check primary branch name, almost always "master" or "main" </span>

<span class="token comment"># The primary branch names must line up, to change run</span>
<span class="token function">git</span> branch <span class="token parameter variable">-m</span> BRANCH-NAME

<span class="token function">git</span> push <span class="token parameter variable">-u</span> origin BRANCH-NAME
</code></pre>
<h3>Cloning Repo on a New Client</h3>
<p>Ensure SSH and Tailscale are installed and configured on any new machine, and then run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> clone user@100.27:/home/user/Repos/REPO-NAME/</code></pre>
<hr>
<h4>Authentication with a SSH key</h4>
<p>Authenticating transfers and remote operation via SSH keys is not only much more secure than a standard password, but also much better for any sort of CI, CD or cron job automation that a user might want to set up in the future. Luckily the <em>ssh-keygen</em> utility is packaged with <em>OpenSSH</em> upon install. This makes it very easy to create a SSH key on both the server and client.</p>
<pre class="language-bash"><code class="language-bash">ssh-keygen <span class="token parameter variable">-t</span> ed25519 <span class="token parameter variable">-a</span> <span class="token number">100</span> <span class="token parameter variable">-C</span> <span class="token string">"you@mail.com"</span>

<span class="token comment"># If you are using a legacy system that does not support the Ed25519 hash algorithm use rsa</span>
ssh-keygen <span class="token parameter variable">-t</span> rsa <span class="token parameter variable">-b</span> <span class="token number">4096</span> <span class="token parameter variable">-a</span> <span class="token number">100</span> <span class="token parameter variable">-o</span> <span class="token parameter variable">-C</span> <span class="token string">"you@mail.com"</span>

<span class="token comment"># If you want to use a hardware key for 2FA</span>
ssh-keygen <span class="token parameter variable">-t</span> ed25519-sk <span class="token parameter variable">-C</span> <span class="token string">"you@mail.com"</span></code></pre>
<p>After creating the key, add the client's public SSH key to the server's list of authorized keys. This can be done by manually copying and pasting the full public key, but OpenSSH has a built in utility that makes the process much easier.</p>
<pre class="language-bash"><code class="language-bash">ssh-copy-id user@100.27</code></pre>
<p><strong>Note</strong>: If a repository was initially cloned or initiated using HTTPS, it might be necessary to add the <em>ssh://</em> prefix to the URL. This is not necessary when using the instructions provided here and pushing to origin via a VPN connection, but still helpful information to have access to.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> remote set-url origin ssh://makc@100.27:/home/user/Repo/REPO-NAME/</code></pre>
]]></description>
      </item>
    
      <item>
        <title>Launch Any Package via Dmenu</title>
        <link>https://makc.co/docs/flatpak-to-dmenu/</link>
        <guid>https://makc.co/docs/flatpak-to-dmenu/</guid>
        <pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Documentation for setting up Flatpack, Snap and other packages installed to non-standard locations to launch via dmenu and other minimal application launchers.</p>
<h2>Issue</h2>
<p>By default demnu (and most other minimal app launchers) look(s) for packages installed in <em>/usr/bin</em>. This is where most packages installed via <em>apt</em>, <em>pacman</em> or other package managers generally write to. However when installing apps with services like Flatpak or Canonical's Snap Store, the appImage is often placed elsewhere.</p>
<p><strong>Note</strong>: I have found that Snap packages specifically often do launch via dmenu without issue, though not always. This fix will apply to any application that is installed to a non-standard location.</p>
<h2>Steps</h2>
<h4>Locate your newly installed package:</h4>
<ul>
<li><strong>Flatpak</strong> installs to <em>/var/lib/flatpak/exports/bin</em></li>
<li><strong>Snap Store</strong> typically installs to either <em>/var/lib/snapd/snap</em> or <em>/snap</em></li>
<li><strong>Blackmagic</strong> installs Davinci Resolve in <em>/opt/resolve/bin</em></li>
</ul>
<h4>Create a symlink to <em>/usr/bin</em>, where dmenu can find it:</h4>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">ln</span> <span class="token parameter variable">-s</span> /var/lib/flatpak/exports/bin/com.app.NAME /usr/bin/appNAME</code></pre>
<p>Replace <em>appNAME</em> with whatever you want to type into dmenu to launch the app.</p>
<h2>Examples</h2>
<h4>PlexAMP (Flatpak)</h4>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">ln</span> <span class="token parameter variable">-s</span> /var/lib/flatpak/exports/bin/com.plexamp.Plexamp /usr/bin/plexAMP</code></pre>
<h4>Davinci Resolve (Blackmagic installer)</h4>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">ln</span> <span class="token parameter variable">-s</span> /opt/resolve/bin/resolve /usr/bin/resolve</code></pre>
<h2>Source</h2>
<p>I found the original fix from <a href="https://gist.github.com/curioswati/668e9e120ddd4b6f8d07dc28b5780d22">a gist</a> written by <a href="https://github.com/curioswati">Swati Jaiswal</a>.</p>
]]></description>
      </item>
    
      <item>
        <title>Install Davinci Resolve on Linux</title>
        <link>https://makc.co/docs/install-resolve-on-linux/</link>
        <guid>https://makc.co/docs/install-resolve-on-linux/</guid>
        <pubDate>Fri, 23 Jan 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Documentation for installing Black Magic Design's Davinci Resolve or Davinci Resolve Studio on Linux.</p>
<h2>Required Software</h2>
<ul>
<li><a href="https://www.blackmagicdesign.com/products/davinciresolve">Davinci Resolve</a></li>
<li>Proprietary Nvidia, or AMD Drivers</li>
<li>unzip</li>
</ul>
<p><strong>Important Note</strong>: Davinci Resolve requires a GPU with OpenCL or CUDA support. Full compatibility information can be found on the <a href="https://www.khronos.org/conformance/adopters/conformant-products#opencl">Khronos Group's list</a>, but running Resolve on Linux will likely require a discrete GPU &amp; may require configuring a laptop with hybrid graphics to run using only the GPU for display output.</p>
<p><strong>Important Note:</strong> Resolve is only supported running with proprietary Nvidia drivers. Resolve will not run with <a href="https://nouveau.freedesktop.org/">Nouveau</a> drivers installed.</p>
<p><strong>Important Note</strong>: While Resolve included full Wayland support in Resolve 19, many users have reported issues. This documentation covers installing Resolve in an X11 environment.</p>
<h2>Installation</h2>
<ol>
<li>Ensure that the appropriate display driver's &amp; unzip are installed.</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># Arch (Nvidia)</span>
<span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span> nvidia nvidia-utils cuda opencl-nvidia <span class="token function">unzip</span>

<span class="token comment"># Arch (AMD)</span>
<span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span> rocm-opencl-runtime rocm-hip-runtime <span class="token function">unzip</span>

<span class="token comment"># Fedora (Nvida)</span>
<span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token parameter variable">-y</span> <span class="token punctuation">\</span>
https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-<span class="token variable"><span class="token variable">$(</span><span class="token function">rpm</span> <span class="token parameter variable">-E</span> %fedora<span class="token variable">)</span></span>.noarch.rpm <span class="token punctuation">\</span>
https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-<span class="token variable"><span class="token variable">$(</span><span class="token function">rpm</span> <span class="token parameter variable">-E</span> %fedora<span class="token variable">)</span></span>.noarch.rpm

<span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token parameter variable">-y</span> akmod-nvidia xorg-x11-drv-nvidia-cuda

<span class="token comment"># Fedora (AMD)</span>
<span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token parameter variable">-y</span> rocm-opencl rocm-hip
<span class="token function">sudo</span> <span class="token function">usermod</span> <span class="token parameter variable">-aG</span> render,video <span class="token environment constant">$USER</span>

<span class="token comment"># Ubuntu (Nvida)</span>
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> nvidia-driver-550 nvidia-cuda-toolkit

<span class="token comment"># Ubuntu (AMD)</span>
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> rocm-opencl-runtime rocm-hip-runtime</code></pre>
<ol start="2">
<li>Download <a href="https://www.blackmagicdesign.com/products/davinciresolve">Davinci Resolve Linux</a> from Black Magic Design's website.</li>
</ol>
<p><strong>Note</strong>: The process is identical for the free version of Davinci Resole and Davinci Resolve Studio.</p>
<ol start="3">
<li>Extract the installer &amp; make it executable.</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> ~/Downloads/ <span class="token comment"># Navigating to Downloads</span>
<span class="token function">unzip</span> DaVinci_Resolve_*_Linux.zip <span class="token comment"># Unzipping zip file</span>
<span class="token function">chmod</span> +x DaVinci_Resolve_*_Linux.run <span class="token comment"># Making install script executable</span></code></pre>
<ol start="4">
<li>Install.</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token assign-left variable">SKIP_PACKAGE_CHECK</span><span class="token operator">=</span><span class="token number">1</span> ./DaVinci_Resolve_*_Linux.run</code></pre>
<h3>Post Install Fixes</h3>
<h4>Fedora Libraries Fix</h4>
<p>If installing on Fedora Linux it may be necessary to replace older libraries that conflict with Fedora workstation:</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> /opt/resolve/libs
<span class="token function">sudo</span> <span class="token function">mkdir</span> disabled-libraries
<span class="token function">sudo</span> <span class="token function">mv</span> libglib* libgio* libgmodule* disabled-libraries <span class="token comment"># Move outdated libraries to disabled-libraries</span>

<span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token parameter variable">-y</span> libxcrypt-compat <span class="token comment"># Instal updated libraries</span></code></pre>
<h4>Display Scaling Workaround</h4>
<p>If using Davinci Resolve on a HiDPI display, scaling issues may be encountered. This is due to the fact that Resolve uses <a href="https://www.qt.io/">QT's GUI toolkit</a>, but often ignores userspace environment variables to control QT apps. A quick workaround is to create a wrapper script that will change the display scaling when launching Resolve, and revert the scaling when Resolve is closed:</p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span>
xrandr <span class="token parameter variable">--output</span> DP-2 <span class="token parameter variable">--scale</span> <span class="token number">0</span>.5x0.5 <span class="token comment"># Scale screen down by 2x</span>
/opt/resolve/bin/resolve <span class="token comment"># Launch Resolve</span>
xrandr <span class="token parameter variable">--output</span> DP-2 <span class="token parameter variable">--scale</span> 1x1 <span class="token comment"># Restore native scale</span></code></pre>
<h2>Launching</h2>
<p>When installing Davinci Resolve in a full Desktop environment, Resolve will most likely not launch. The easiest solution is to <a href="http://makc.co/docs/flatpak-to-dmenu/">add an entry</a> in <em>/usr/bin</em> for Resolve.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">ln</span> <span class="token parameter variable">-s</span> /opt/resolve/bin/resolve /usr/bin/resolve</code></pre>
]]></description>
      </item>
    
      <item>
        <title>Recording &amp; Transcoding 2:1 Screen-caps on Linux</title>
        <link>https://makc.co/docs/yt-pipeline/</link>
        <guid>https://makc.co/docs/yt-pipeline/</guid>
        <pubDate>Thu, 22 Jan 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Some notes on setting up a custom display resolution on X11, transcoding H.264 media using ffmpeg, editing in Davinci Resolve, and then transcoding screen capture videos to the VP9 codec for delivery to YouTube &amp; other video platforms.</p>
<h2>Required Software</h2>
<ul>
<li><a href="https://ffmpeg.org/">ffmpeg</a>: For transcoding media</li>
<li><a href="https://www.x.org/wiki/Projects/XRandR/">xrandr</a>: For setting a custom display resolution on X11</li>
<li><a href="https://github.com/emersion/wlr-randr">wlr-randr</a>: For setting a custom display resolution on Wayland</li>
</ul>
<h4>Optional</h4>
<ul>
<li><a href="https://obsproject.com/">OBS</a>: For recording screen</li>
<li><a href="https://www.blackmagicdesign.com/products/davinciresolve">Davinci Resolve</a>: An all around awesome NLE that runs on Linux, proprietary</li>
<li><a href="https://kdenlive.org/">Kdenlive</a>: A less than ideal, but usable NLE that is open-source</li>
</ul>
<h2>Installation</h2>
<p><strong>Note</strong>: Installing Davinci Resolve on Linux requires a fairly specific process, documentation for installing can be found <a href="https://makc.co/docs/install-resolve-on-linux/">here</a>.</p>
<p><strong>Note</strong>: <em>cvt</em> is used in this documentation and installed with the X11 utilities</p>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># Arch Linux</span>
<span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span>
<span class="token function">sudo</span> pacman <span class="token parameter variable">-S</span> obs-studio kdenlive ffmpeg xorg-xrandr

<span class="token comment"># Fedora </span>
<span class="token function">sudo</span> dnf upgrade <span class="token parameter variable">--refresh</span> <span class="token parameter variable">-y</span>
<span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token parameter variable">-y</span> <span class="token punctuation">\</span>
https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-<span class="token variable"><span class="token variable">$(</span><span class="token function">rpm</span> <span class="token parameter variable">-E</span> %fedora<span class="token variable">)</span></span>.noarch.rpm <span class="token punctuation">\</span>
https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-<span class="token variable"><span class="token variable">$(</span><span class="token function">rpm</span> <span class="token parameter variable">-E</span> %fedora<span class="token variable">)</span></span>.noarch.rpm 

<span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token parameter variable">-y</span> obs-studio kdenlive ffmpeg xorg-x11-server-utils


<span class="token comment"># Ubuntu</span>
<span class="token function">sudo</span> add-apt-repository universe
<span class="token function">sudo</span> <span class="token function">apt</span> update <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> <span class="token function">apt</span> upgrade <span class="token parameter variable">-y</span>
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> obs-studio kdenlive ffmpeg x11-xserver-utils</code></pre>
<h2>Changing A Display's Native Resolution</h2>
<p>Depending on the display, it is sometimes possible to use the <em>cvt</em> command to generate a <em>modeline</em>, and then use <em>xrandr</em>'s <em>--addmode</em> flag to create a fully custom display resolution. Basic steps can be found below, or more detailed instructions can be viewed <a href="https://youtu.be/IzFdqELyZbA">here</a>:</p>
<pre class="language-bash"><code class="language-bash">cvt <span class="token number">3840</span> <span class="token number">1920</span> <span class="token number">60</span> <span class="token comment"># Generating Modeline at 3840x1920 &amp; 60hz</span>
<span class="token comment"># 3840x1920 59.97 Hz (CVT) hsync: 119.27 kHz; pclk: 629.75 MHz</span>
Modeline <span class="token string">"3840x1920_60.00"</span>  <span class="token number">629.75</span>  <span class="token number">3840</span> <span class="token number">4144</span> <span class="token number">4560</span> <span class="token number">5280</span>  <span class="token number">1920</span> <span class="token number">1923</span> <span class="token number">1933</span> <span class="token number">1989</span> <span class="token parameter variable">-hsync</span> +vsync

xrandr <span class="token parameter variable">--output</span> DP-2 <span class="token parameter variable">--addmode</span> <span class="token string">"3840x1920_60.00"</span>  <span class="token punctuation">\</span> <span class="token comment"># Adding output to X11 display</span>
<span class="token number">629.75</span>  <span class="token number">3840</span> <span class="token number">4144</span> <span class="token number">4560</span> <span class="token number">5280</span>  <span class="token number">1920</span> <span class="token number">1923</span> <span class="token number">1933</span> <span class="token number">1989</span> <span class="token parameter variable">-hsync</span> +vsync </code></pre>
<p>However, if using a display port connection, or a modern laptop that uses an eDP display, it is very often not possible to set a fully custom resolution. However, X11's tools for display scaling can be exploited to achieve virtually the same thing. In the case of the Thinkpad P1 that was used for this documentation, the native resolution is 3840x2160. The desired output video resolution is at a 2:1 aspect ratio, 3840x1920. Dividing the desired vertical resolution by the original vertical resolution, <em>1920 ÷ 2160</em>, gives a result of <em>0.888888888889</em>, which can be used as the vertical scale factor in <em>xrandr</em>.</p>
<pre class="language-bash"><code class="language-bash">xrandr <span class="token parameter variable">--output</span> DP-2 <span class="token parameter variable">--mode</span> 3840x2160 <span class="token parameter variable">--scale</span> 1x0.8889</code></pre>
<p><strong>Notes</strong>:</p>
<ul>
<li>The <em>wlr-randr</em> command functions as a clone of xrandr for Wayland, and will accomplish the same goal in Wayland window managers or desktop environments.</li>
<li>The scale factor will have to be reset after any screen recording.</li>
</ul>
<pre class="language-bash"><code class="language-bash">xrandr <span class="token parameter variable">--output</span> DP-2 <span class="token parameter variable">--mode</span> 3840x2160 <span class="token parameter variable">--scale</span> 1x1</code></pre>
<h2>Transcoding H.264</h2>
<p>Unless using a <a href="https://obsproject.com/forum/threads/full-custom-ffmpeg-output.99064/">custom <em>ffmpeg</em> output</a>, OBS will only output video using the H.264 codec. This is a problem for a number of reasons when building a video creation pipeline on Linux:</p>
<ol>
<li>Fedora, Ubuntu &amp; many other Linux distributions do not ship an iteration of <em>ffmpeg</em> that supports H.264 libraries.</li>
<li>Kdenlive will not allow users to edit H.264 video without a transcode.</li>
<li>The free edition of Davinci Resolve will not work with H.264 at all when running on Linux.</li>
</ol>
<p>Much of this is due to a <a href="https://www.zdnet.com/article/a-closer-look-at-the-costs-and-fine-print-of-h-264-licenses/">licensing fee structure</a> implemented by <a href="https://via-la.com/">Via-LA</a>-- but the end result is that, the first step in any editing pipeline has to be a transcode to escape the limitations of the H.264 codec. NLE friendly alternative codecs include, but are not limited to, AVID's <a href="https://en.wikipedia.org/wiki/DNxHR_codec">DNxHR</a>, GoPro's <a href="https://gopro.github.io/cineform-sdk/">CineForm</a> and Apple's <a href="https://support.apple.com/en-us/102207">ProRes</a>. All three have varying degrees of support across Linux distributions, but ProRes typically works <em>out of the box</em> and is what this documentation will recommend.</p>
<pre class="language-bash"><code class="language-bash">ffmpeg <span class="token parameter variable">-i</span> INPUT.MOV <span class="token punctuation">\</span> <span class="token comment"># Calls ffmpeg and selects file to transcode </span>
<span class="token parameter variable">-c:v</span> prores_ks <span class="token punctuation">\</span> <span class="token comment"># "c:v" = codec for video, "prores_ks" = ProRes Codec</span>
<span class="token parameter variable">-profile:v</span> <span class="token number">0</span> <span class="token punctuation">\</span> <span class="token comment"># How ProRes sets quality 0=proxy, 1=low, 2=high, 3=lossless</span>
<span class="token parameter variable">-c:a</span> pcm_s16le <span class="token punctuation">\</span> <span class="token comment"># "c:a" = codec for audio, "pcm_s16le" is selecting 16-bit audio</span>
OUTPUT.md <span class="token comment"># Gives a name for output</span>

ffmpeg <span class="token parameter variable">-i</span> INPUT.MOV <span class="token parameter variable">-c:v</span> prores_ks <span class="token parameter variable">-profile:v</span> <span class="token number">0</span> <span class="token parameter variable">-c:a</span> pcm_s16le OUTPUT.MOV <span class="token comment"># Sample ProRes</span>
ffmpeg <span class="token parameter variable">-i</span> INPUT.MOV <span class="token parameter variable">-c:v</span> dnxhr <span class="token parameter variable">-profile:v</span> dnxhr_lb <span class="token parameter variable">-c:a</span> pcm_s16le OUTPUT.MOV <span class="token comment"># Sample DNXHQ</span>
ffmpeg <span class="token parameter variable">-i</span> INPUT.MOV <span class="token parameter variable">-c:v</span> cfhd <span class="token parameter variable">-quality</span> medium <span class="token parameter variable">-c:a</span> pcm_s16le OUTPUT.MOV <span class="token comment"># Sample Cineform </span></code></pre>
<p>Depending on use case, it is sometimes worth creating a bash script to handle this process. If this is desired, the use of input variables will make a script run more efficiently:</p>
<pre class="language-bash"><code class="language-bash">ffmpeg <span class="token parameter variable">-i</span> <span class="token variable">$1</span> <span class="token parameter variable">-c:v</span> prores_ks <span class="token parameter variable">-profile:v</span> <span class="token number">0</span> <span class="token parameter variable">-c:a</span> pcm_s16le <span class="token string">"prefix-<span class="token variable"><span class="token variable">$(</span><span class="token function">basename</span> <span class="token string">"%1"</span><span class="token variable">)</span></span>"</span>

<span class="token comment"># Usage</span>
./scriptname.sh FILENAME.mov <span class="token comment"># Outputs a transcoded file named, prefix-FILENAME.mov</span></code></pre>
<h4>Bonus: Quick Audio Cleanup in Resolve</h4>
<ol>
<li>Drag audio onto timeline, select <em>Fairlight</em> panel.</li>
<li>Right-click audio track, select <em>Clip Options</em>, then <em>Normalize Audio</em>.</li>
<li>When the pop-up window appears, normalize audio to -1db.</li>
<li>Navigate to the <em>Mixer Panel</em>, select the <em>Plus sign</em> to add and effect, <em>EQ</em> menu, select <em>Fairlight EQ</em>.</li>
<li>To set up a basic noise gate: mute or reduce audio output on Band 1 &amp; Band 2, which control 100hz &amp; 120hz wavelengths respectively.</li>
<li>If there is any hissing or popping in narration, slightly lower the decibel level of Band 6.</li>
</ol>
<h2>Transcoding for Delivery</h2>
<p>When exporting from Davinci Resolve, once again we run into issues, as Resolve does not support exporting to the H.264 codec. Luckily, Google's <a href="https://www.webmproject.org/vp9/">VP9</a> codecs has nearly all the same compression advantages as H.264 and none of the licensing issues. Unfortunately, Resolve also can't export to the VP9 codec. The easiest workaround is to export ProRes from Resolve and transcode to VP9 using <em>ffmpeg</em> for final delivery to a video sharing site:</p>
<pre class="language-bash"><code class="language-bash">ffmpeg <span class="token parameter variable">-i</span> INPUT.mov <span class="token parameter variable">-c:v</span> libvpx-vp9 <span class="token parameter variable">-crf</span> <span class="token number">35</span> <span class="token parameter variable">-b:v</span> <span class="token number">0</span> <span class="token parameter variable">-c:a</span> libopus <span class="token parameter variable">-b:a</span> 512k OUTPUT.webm

<span class="token comment"># The syntax for using an input variable is slightly different here, due to the changing file extension</span>
ffmpeg <span class="token parameter variable">-i</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token parameter variable">-c:v</span> libvpx-vp9 <span class="token parameter variable">-crf</span> <span class="token number">35</span> <span class="token parameter variable">-b:v</span> <span class="token number">0</span> <span class="token parameter variable">-c:a</span> libopus <span class="token parameter variable">-b:a</span> 512k <span class="token string">"prefix-<span class="token variable"><span class="token variable">$(</span><span class="token function">basename</span> <span class="token string">"<span class="token variable">$1</span>"</span> .mov<span class="token variable">)</span></span>.webm"</span></code></pre>
]]></description>
      </item>
    
      <item>
        <title>Converting a Markdown File to a Manuscript</title>
        <link>https://makc.co/docs/markdown-to-manscript/</link>
        <guid>https://makc.co/docs/markdown-to-manscript/</guid>
        <pubDate>Sat, 17 Jan 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A guide for exporting Markdown <em>(.md)</em> files to Open Office <em>(.odt)</em> files, with <a href="https://en.wikipedia.org/w/index.php?title=Standard_manuscript_format">standard manuscript formatting</a>.</p>
<h2>Background</h2>
<p>Last year I began writing my first novel. My preferred method of writing is to use the <a href="https://daringfireball.net/projects/markdown/">Markdown format</a>, and <a href="https://neovim.io/">Neovim</a>. This setup is great for writing efficiently and accessing my work on any device. However, I have run into two issues:</p>
<ol>
<li>I prefer to edit with pen &amp; paper. Markdown is not a printer friendly format.</li>
<li>If I ever do try to publish my work, no publisher in the world will accept a <em>.md</em> file as a manuscript.</li>
</ol>
<p>Both of these problems can be solved by periodically converting the Markdown files into an Office format. I prefer the Open Office format <em>(.odt)</em>, but this process is nearly identical with Microsoft Word <em>(.docx)</em> or Google Doc <em>(.gdoc)</em> formats.</p>
<h2>Required Software</h2>
<ul>
<li><a href="https://pandoc.org/">Pandoc</a>: A tool that will convert Markdown files to Open Office files.</li>
<li><a href="https://www.libreoffice.org/">Libre Office</a>: My office software of choice. <a href="https://www.openoffice.org/">Open Office</a> will work just as well.</li>
</ul>
<h4>Optional</h4>
<ul>
<li>My <a href="https://makc.co/downloads/reference.zip">Open Office Template</a></li>
<li>My <a href="https://github.com/makccr/dot/blob/master/.scripts/rule-to-scene-break.lua"><em>.lua</em> filter</a>, available as part of my <a href="https://github.com/makccr/dot">dotfiles</a>.</li>
</ul>
<h3>Installation</h3>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># Arch</span>
<span class="token function">sudo</span> pacman -Syu<span class="token punctuation">;</span> <span class="token function">sudo</span> pacman <span class="token parameter variable">-S</span> libreoffice-still pandoc

<span class="token comment"># Fedora</span>
<span class="token function">sudo</span> dnf upgrade <span class="token parameter variable">--refresh</span> <span class="token parameter variable">-y</span> <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token parameter variable">-y</span> libreoffice pandoc

<span class="token comment"># Ubuntu </span>
<span class="token function">sudo</span> <span class="token function">apt</span> update <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> <span class="token function">apt</span> upgrade <span class="token parameter variable">-y</span> <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> libreoffice pandoc

<span class="token comment"># FreeBSD</span>
<span class="token function">su</span> <span class="token operator">&amp;&amp;</span> pkg update <span class="token operator">&amp;&amp;</span> pkg upgrade <span class="token parameter variable">-y</span> <span class="token operator">&amp;&amp;</span> pkg <span class="token function">install</span> <span class="token parameter variable">-y</span> libreoffice pandoc

<span class="token comment"># MacOS (via Homebrew)</span>
brew update <span class="token operator">&amp;&amp;</span> brew upgrade <span class="token operator">&amp;&amp;</span> brew <span class="token function">install</span> pandoc libreoffice</code></pre>
<h2>Procedure</h2>
<h3>Open Office Template</h3>
<p>The first step is to create an Open Office template, or, what Pandoc will refer to as a: <em>reference-doc</em>. To do this, run an existing Markdown project through Pandoc to generate an Open Office file with all of the Markdown elements.</p>
<pre class="language-bash"><code class="language-bash">pandoc INPUT.md <span class="token parameter variable">-o</span> OUTPUT.odt </code></pre>
<p>Open the exported <em>.odt</em> file in Libre Office and edit each paragraph style that is utilized. This can be done by opening the paragraph style menu, and using the drop down menu to edit each style individually. I have made the template I use <a href="https://makc.co/downloads/reference.zip">available for download</a>.</p>
<p>Store this <em>reference.odt</em> file in an easy to access location, I put a copy of the template in the root directory of any writing project I'm embark on.</p>
<img src="img/2026-01-17-A.jpg" alt="Libre Office Style's List" class="half">
<p class="caption">
Libre Office's style's list
</p>
<h3>.lua Filter</h3>
<p>A problem I encountered early on involved my use of Markdown’s horizontal rule feature. When exporting to an Office format, Pandoc, quite reasonably, renders the horizontal rule as a visible line. However, in my document this element is intended to mark a scene break, which follows specific formatting conventions in a manuscript.</p>
<img src="img/2026-01-17-B.jpg" alt="Libre Office Style's List" class="full" >
<p class="caption">
A before and after of the horizontal rule conversion via the lua filter
</p>
<p>To avoid making changes to my Markdown documents, I created a <em>.lua</em> filter that can be run with Pandoc, and will convert the horizonal rule <em>(---)</em> in Markdown, to a <em>#</em> symbol, when exporting. In order for this filter to work properly, it is necessary to create a <em>SceneBreak</em> style in the <em>reference.odt</em> template.</p>
<pre class="language-lua"><code class="language-lua"><span class="token comment">-- rule-to-scene-break.lua</span>

<span class="token keyword">function</span> <span class="token function">HorizontalRule</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">local</span> para <span class="token operator">=</span> pandoc<span class="token punctuation">.</span><span class="token function">Para</span><span class="token punctuation">(</span><span class="token punctuation">{</span>pandoc<span class="token punctuation">.</span><span class="token function">Str</span><span class="token punctuation">(</span><span class="token string">"#"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> pandoc<span class="token punctuation">.</span><span class="token function">Div</span><span class="token punctuation">(</span><span class="token punctuation">{</span>para<span class="token punctuation">}</span><span class="token punctuation">,</span> pandoc<span class="token punctuation">.</span><span class="token function">Attr</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">[</span><span class="token string">"custom-style"</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token string">"SceneBreak"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">end</span></code></pre>
<h3>Creating the Final Manuscript</h3>
<p>Using the Open Office template and <em>.lua</em> filter, convert the Markdown file to an Open Office file. In order to do this, use the standard Pandoc syntax, but provide a link to the <em>reference.odt</em> template, with the <em>--reference-doc=</em> flag, and the <em>.lua</em> filter, using the <em>--lua-filter=</em> flag.</p>
<pre class="language-bash"><code class="language-bash">pandoc INPUT.md <span class="token parameter variable">-o</span> OUTPUT.odt <span class="token punctuation">\</span>
--reference-doc<span class="token operator">=</span>reference.odt <span class="token punctuation">\</span>
--lua-filter<span class="token operator">=</span>/home/makc/.scripts/rule-to-scene-break.lua</code></pre>
<h4>Converting to PDF</h4>
<p>To create a PDF from the command line, we can use Libre Office in a headless manner. After creating the Open Office Document with Pandoc, run:</p>
<pre class="language-bash"><code class="language-bash">libreoffice <span class="token parameter variable">--headless</span> --convert-to pdf FILENAME.odt</code></pre>
<p>Or, simply create one bash script to handle everything in one motion.</p>
<pre class="language-bash"><code class="language-bash"><span class="token shebang important">#!/usr/bin/env bash</span>
<span class="token builtin class-name">set</span> <span class="token parameter variable">-euo</span> pipefail

<span class="token comment"># Resources</span>
<span class="token assign-left variable">SCRIPTS_DIR</span><span class="token operator">=</span><span class="token string">"<span class="token environment constant">$HOME</span>/.scripts"</span>
<span class="token assign-left variable">REFERENCE_DOC</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$SCRIPTS_DIR</span>/reference.odt"</span>
<span class="token assign-left variable">LUA_FILTER</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$SCRIPTS_DIR</span>/rule-to-scene-break.lua"</span>

<span class="token comment"># Arg check</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">$#</span> <span class="token parameter variable">-ne</span> <span class="token number">1</span> <span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
    <span class="token builtin class-name">echo</span> <span class="token string">"Usage: <span class="token variable"><span class="token variable">$(</span><span class="token function">basename</span> <span class="token string">"<span class="token variable">$0</span>"</span><span class="token variable">)</span></span> FILE.md"</span>
    <span class="token builtin class-name">exit</span> <span class="token number">1</span>
<span class="token keyword">fi</span>

<span class="token assign-left variable">INPUT</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$1</span>"</span>

<span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token operator">!</span> <span class="token parameter variable">-f</span> <span class="token string">"<span class="token variable">$INPUT</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
    <span class="token builtin class-name">echo</span> <span class="token string">"Error: File not found: <span class="token variable">$INPUT</span>"</span>
    <span class="token builtin class-name">exit</span> <span class="token number">1</span>
<span class="token keyword">fi</span>

<span class="token comment"># Paths</span>
<span class="token assign-left variable">INPUT_ABS</span><span class="token operator">=</span><span class="token string">"<span class="token variable"><span class="token variable">$(</span>realpath <span class="token string">"<span class="token variable">$INPUT</span>"</span><span class="token variable">)</span></span>"</span>
<span class="token assign-left variable">BASENAME</span><span class="token operator">=</span><span class="token string">"<span class="token variable"><span class="token variable">$(</span><span class="token function">basename</span> <span class="token string">"<span class="token variable">$INPUT_ABS</span>"</span> .md<span class="token variable">)</span></span>"</span>
<span class="token assign-left variable">WORKDIR</span><span class="token operator">=</span><span class="token string">"<span class="token variable"><span class="token variable">$(</span><span class="token function">dirname</span> <span class="token string">"<span class="token variable">$INPUT_ABS</span>"</span><span class="token variable">)</span></span>"</span>

<span class="token assign-left variable">ODT</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$WORKDIR</span>/<span class="token variable">$BASENAME</span>.odt"</span>
<span class="token assign-left variable">PDF</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$WORKDIR</span>/<span class="token variable">$BASENAME</span>.pdf"</span>

<span class="token comment"># Converting and print to terminal</span>
<span class="token builtin class-name">echo</span> <span class="token string">"→ Converting Markdown to ODT..."</span>
pandoc <span class="token string">"<span class="token variable">$INPUT_ABS</span>"</span> <span class="token punctuation">\</span>
    <span class="token parameter variable">-o</span> <span class="token string">"<span class="token variable">$ODT</span>"</span> <span class="token punctuation">\</span>
    --reference-doc<span class="token operator">=</span><span class="token string">"<span class="token variable">$REFERENCE_DOC</span>"</span> <span class="token punctuation">\</span>
    --lua-filter<span class="token operator">=</span><span class="token string">"<span class="token variable">$LUA_FILTER</span>"</span>

<span class="token builtin class-name">echo</span> <span class="token string">"→ Converting ODT to PDF..."</span>
libreoffice <span class="token punctuation">\</span>
    <span class="token parameter variable">--headless</span> <span class="token punctuation">\</span>
    --convert-to pdf <span class="token punctuation">\</span>
    <span class="token parameter variable">--outdir</span> <span class="token string">"<span class="token variable">$WORKDIR</span>"</span> <span class="token punctuation">\</span>
    <span class="token string">"<span class="token variable">$ODT</span>"</span>

<span class="token builtin class-name">echo</span> <span class="token string">"✓ Done: <span class="token variable">$PDF</span>"</span></code></pre>
]]></description>
      </item>
    
      <item>
        <title>Remotely Accesing a File Server with Tailscale</title>
        <link>https://makc.co/docs/simple-tailscale-setup/</link>
        <guid>https://makc.co/docs/simple-tailscale-setup/</guid>
        <pubDate>Fri, 16 Jan 2026 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>Set up a free and simple VPN tunnel to access and modify a remote file server in the most secure way possible, wile still being pretty lazy.</p>
<h2>Background</h2>
<p>The goal is to be able to access and add files to what was originally set up as a local file server, from outside of the LAN. In order to add an additional layer of security, this documentation assumes the use of an intermediate system. That is, a third system to remotely connect to, on the same LAN as the file server, that can then be used to access and manipulate the file server, without having to leave an open VPN connection on the file server itself.</p>
<img src="img/2026-01-16.png"  alt="Map of devices invovled" class="thirds">
<p class="caption">A map of my basic jumpHost set-up
</p>
<h3>Required Software</h3>
<ul>
<li><a href="https://tailscale.com/">Tailscale</a>: A very simple VPN service, with great apps for MacOS, Windows, Linux,
BSD, Android and iOS.</li>
<li>OpenSSH: For remote access.</li>
<li>Scp, rSync or both: To remotely move files and directories.</li>
<li>Curl: Required for installing Tailscale on Linux.</li>
</ul>
<h3>Optional Software</h3>
<ul>
<li><a href="https://termius.com/">Terminus</a>: An iOS &amp; Android solution that allows users to save ssh information for easy remote connection.</li>
</ul>
<h2>Install Instructions</h2>
<h3>Dependencies (SSH, SCP, rSync &amp; Curl)</h3>
<pre class="language-bash"><code class="language-bash"><span class="token comment">## Ubuntu</span>
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> openssh-server <span class="token function">curl</span> <span class="token function">rsync</span> <span class="token operator">&amp;&amp;</span>
<span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> <span class="token parameter variable">--now</span> sshd

<span class="token comment">## Arch</span>
<span class="token function">sudo</span> pacman <span class="token parameter variable">-S</span> openssh <span class="token function">scp</span> <span class="token function">curl</span> <span class="token function">rsync</span> <span class="token operator">&amp;&amp;</span> 
<span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> <span class="token parameter variable">--now</span> sshd

<span class="token comment">## Fedora</span>
<span class="token function">sudo</span> dnf <span class="token function">install</span> openssh-server <span class="token function">scp</span> <span class="token function">curl</span> <span class="token function">rsync</span> <span class="token operator">&amp;&amp;</span>
<span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> <span class="token parameter variable">--now</span> sshd

<span class="token comment">## FreeBSD</span>
<span class="token function">sudo</span> pkg <span class="token function">install</span> openssh <span class="token function">scp</span> <span class="token function">curl</span> rysync <span class="token punctuation">;</span> 
<span class="token function">sudo</span> kidload fuse <span class="token operator">&amp;&amp;</span> 
<span class="token function">sudo</span> sysrc <span class="token assign-left variable">sshd_enable</span><span class="token operator">=</span><span class="token string">"YES"</span> <span class="token operator">&amp;&amp;</span>
<span class="token function">sudo</span> <span class="token function">service</span> sshd start

<span class="token comment">## MacOS (Via Homebrew)</span>
brew <span class="token function">install</span> openssh <span class="token function">scp</span> <span class="token function">curl</span> <span class="token function">rsync</span> <span class="token operator">&amp;&amp;</span>
brew <span class="token function">install</span> <span class="token parameter variable">--cask</span> macfuse</code></pre>
<h3>Tailscale</h3>
<h4>Linux</h4>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-fsSL</span> https://tailscale.com/install.sh <span class="token operator">|</span> <span class="token function">sh</span></code></pre>
<h4>Android or iOS</h4>
<p>Install the Tailscale app from the <a href="https://play.google.com/store/apps/details?id=com.tailscale.ipn">Google Play Store</a> or <a href="https://apps.apple.com/us/app/tailscale/id1470499037">iOS App Store</a> and login to a Tailscale account, accept any necessary permissions.</p>
<h4>MacOS</h4>
<pre class="language-bash"><code class="language-bash">brew <span class="token function">install</span> tailscale</code></pre>
<h2>Starting Tailscale</h2>
<h3>Desktop (Linux, MacOS, Powershell)</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> tailscale up</code></pre>
<p>After starting the Tailscale process, an authentication link will be printed to the terminal. Follow the URL, sign in to a Tailscale account.</p>
<p>To stop an active VPN tunnel, run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> Tailscale down</code></pre>
<h4>A Note on Unattended Use</h4>
<p>By default a Tailscale connection will stay open until a user closes the process. Tailscale can also be set to automatically start back up upon a system restart with:</p>
<pre class="language-bash"><code class="language-bash">tailscale up <span class="token parameter variable">--unattended</span><span class="token operator">=</span>true</code></pre>
<p>The best resource for use case specific information is Tailscale's documentation, particularly the <a href="https://tailscale.com/kb/1088/run-unattended">page on unattended use</a>.</p>
<h3>Android or iOS</h3>
<p>Tailscale is toggled on an off via the respective phone application. Much the same as on desktop, once the VPN tunnel is open, by default it will remain open indefinitely.</p>
<h2>Access</h2>
<p>In order to connect to a device remotely all that is required is an open Tailscale tunnel on each device.</p>
<h3>SSH Access</h3>
<p>Visiting <a href="https://login.tailscale.com/">https://login.tailscale.com</a> on any device that you are logged in on, will provide a rundown of all machines authenticated with a given Tailscale account. Here you can easily find IP addresses for remote connection, and copy them to the clipboard. Additionally you can access the IP addresses by opening any Tailscale app, and if running on MacOS, FreBSD or Linux, by running:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> tailscale status</code></pre>
<p>Once you have the IP address, the SSH syntax is the same as it would be if you were remotely accessing a device on LAN. By default Tailscale uses port 22 for SSH. It is important to know the username for the device you are accessing via Tailscale's service. However, per standard SSH operation the username part of the equation is irrelevant if you use the same username across machines.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">ssh</span> <span class="token environment constant">USER</span>@100.XXX.XX.XX <span class="token comment"># If username is different between the two machines</span>
<span class="token function">ssh</span> <span class="token number">100</span>.XXX.XX.XX <span class="token comment"># If username is the same</span></code></pre>
<h4>A Note on Best Practices &amp; Security</h4>
<p>This guide focuses primarily on using an intermediate box to download items via a jump host or intermediate box &amp; then transfer them to a secondary host. This is done out of convenience as much as out of security, as the LAN connection of a stationary server is likely far faster than a remote device that a user can connect with. However using a jump host is also a valid security practice &amp; and OpenSSH has built in functionality to make this process easier and more secure, if a user is attempting to connect to a device like the File Server mentioned in the image at the top of this documentation.</p>
<p>From a remote device, it is generally best practice to use the <em>-J</em> flag in SSH to simply make one connection that connects a user from their remote machine, to the File Server, using the jump host as an intermediate connection point:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">ssh</span> <span class="token parameter variable">-J</span> user@jumpHost user@fileServer

<span class="token comment">## Or, if using custom ports: </span>

<span class="token function">ssh</span> <span class="token parameter variable">-J</span> user@jumpHost:port user@fileServer <span class="token parameter variable">-p</span> port</code></pre>
<h2>Moving Files</h2>
<p>Both <em>Scp</em> and <em>rSync</em> work to move files from a remote access machine, to a file server. rSync is typically considered more robust, and will work faster for large file transfers. Tailscale also has a file sharing protocol of their own in the Alpha stage of development called, <a href="https://tailscale.com/kb/1106/taildrop">Taildrop</a>.</p>
<h3>Scp</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">scp</span> /path/to/local/file <span class="token environment constant">USER</span>@REMOTE:/path/to/remote/directory/ <span class="token comment"># Basic Syntax</span></code></pre>
<p>Moving a single file is great for a simple test, but most use cases require moving an entire directory structure. This can be accomplished by adding the <strong>-r</strong> flag, for recursive transfer, to the beginning of the <em>Scp</em> command:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">scp</span> <span class="token parameter variable">-r</span> /path/to/local/directory <span class="token environment constant">USER</span>@REMOTE:/path/to/remote/directory/ <span class="token comment"># Basic Syntax</span></code></pre>
<h3>rSync</h3>
<p>rSync, for most use cases, is a far better option for remote file transfers. The syntax however is nearly identical to using Scp. The only change is that rSync requires the use of a <strong>-a</strong> flag, for <em>archive</em> transfer, in order to transfer a directory &amp; it's contents.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">rsync</span> <span class="token parameter variable">-a</span> /path/to/local/directory <span class="token environment constant">USER</span>@REMOTE:/path/to/remote/directory/</code></pre>
<p>However, where rSync becomes a much more powerful tool, is in the additional functionality it provides. A few of the most helpful options are:</p>
<ul>
<li><strong>-a</strong>: To move a directory &amp; it's contents</li>
<li><strong>-v</strong>: Verbose (to have the command tell you what it's doing)</li>
<li><strong>-z</strong>: To apply compression to a transfer</li>
<li><strong>--progress</strong>: To show a progress bar for longer transfers.</li>
<li><strong>--partial</strong>: To continue a file transfer that failed.</li>
</ul>
<p>If a transfer does fail, it can restarted by running rSync with the same parameters and the <strong>--partial</strong> flag.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">rsync</span> <span class="token parameter variable">-av</span> /path/to/local/directory <span class="token environment constant">USER</span>@REMOTE:/path/to/remote/directory/

<span class="token comment"># Transfer fails :(</span>

<span class="token function">rsync</span> <span class="token parameter variable">-av</span> <span class="token parameter variable">--partial</span> /path/to/local/directory <span class="token environment constant">USER</span>@REMOTE:/path/to/remote/directory/</code></pre>
<h3>Important Note</h3>
<p>Using both Scp and Rsync, pay very close attention to the placement of forward slashes. In a case like this where the goal is to move the entire directory structure of <em>path/to/local/directory</em> no slash should be included:</p>
<p><em>/path/to/local/directory</em> <strong>not</strong> <s><em>/path/to/local/directory/</em></s>.</p>
<p>However, in order to ensure the directory structure ends up in <em>/path/to/remote/directory/</em>, this location should include a trailing forward slash:</p>
<p><em>/path/to/remote/directory/</em> <strong>not</strong> <s><em>/path/to/remote/directory</em></s>.</p>
<h2>Getting Setup to Download Linux ISOs ;)</h2>
<p>As a bonus round this documentation will walk through set-up and use of a command line instance of <a href="https://transmissionbt.com/">Transmission</a>, as downloading new Linux ISOs via the BitTorrent protocol and storing them on a file server seems to a pretty common use for these servers.</p>
<h3>Installation</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> transmission-cli <span class="token comment"># Ubuntu</span>
<span class="token function">sudo</span> dnf <span class="token function">install</span> transmission-cli <span class="token comment"># Fedora </span>
<span class="token function">sudo</span> pacman <span class="token parameter variable">-S</span> transmission-cli <span class="token comment"># Arch</span>
brew <span class="token function">install</span> transmission-cli <span class="token comment"># MacOS (vai Homebrew)</span></code></pre>
<p>After installation, start the Transmission Daemon.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> systemctl <span class="token builtin class-name">enable</span> <span class="token parameter variable">--now</span> transmission-daemon</code></pre>
<h3>Usage</h3>
<p><strong>Important Note</strong>: This documentation will not provide full usage instructions for Transmission, just enough to get started. More complete documentation can be found on the <a href="https://man.archlinux.org/man/extra/transmission-cli/transmission-remote.1.en">Arch wiki</a>.</p>
<p>It's helpful to change the default download directory before downloading any torrents:</p>
<pre class="language-bash"><code class="language-bash">transmission-remote <span class="token parameter variable">-w</span> ~/Downloads</code></pre>
<p>Transmission can now download torrent via a .torrent file or a magnet link using the <strong>transmission-cli</strong> command:</p>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># With a .torrent file</span>
transmission-cli <span class="token parameter variable">-a</span> /path/to/file

<span class="token comment"># With a magnet link</span>
transmission-cli <span class="token parameter variable">-a</span> <span class="token string">"MAGNET LINK"</span></code></pre>
]]></description>
      </item>
    
      <item>
        <title>Setting up BSPWM on Fedora Workstation</title>
        <link>https://makc.co/docs/fedora-bspwm/</link>
        <guid>https://makc.co/docs/fedora-bspwm/</guid>
        <pubDate>Mon, 29 Dec 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A guide for setting up a working instance of <a href="https://github.com/makccr/dot">my BSPWM configuration</a> on a clean install of <a href="https://fedoraproject.org/workstation/download">Fedora Workstation</a>. Note: <em>I have only tested this on version 43 and above on x86 platforms.</em></p>
<h2>Install Required Packages</h2>
<p>We need to first install all packages that are required by BSPWM and are referenced in my BSPWM configuration. Most packages can be installed via the dnf package manger:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> dnf upgrade <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> dnf <span class="token function">install</span> xorg-x11-server-Xorg polybar dunst sxhkd bspwm picom feh flameshot redshift xrandr udiskie alacritty pavucontrol brightnessctl xbindkeys</code></pre>
<p><strong>Note</strong>: The only packages that are required for BSPWM to function are:</p>
<ul>
<li>bspwm</li>
<li>sxhkd</li>
<li>xrandr</li>
<li>X11-server</li>
</ul>
<p>My polybar configuration will also use a few different scripts that will require API keys stored in a Dropbox folder. Installing and logging into the default Dropbox client is required for some polybar modules to work. In order to install dropbox, you must first download the appropriate rpm package from the <a href="https://linux.dropbox.com/packages/fedora/">Dropbox website</a> &amp; then install with:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> dnf <span class="token function">install</span> ~/Downloads/DROPBOX-VERSION.rpm</code></pre>
<p>Finally, BSPWM works a lot better with an application launcher of some sort. My configuration is set up to use a <a href="https://github.com/makccr/suckless/tree/main/dmenu">custom build of dmenu</a> that will need to be installed. To install we will need to clone the repo via either HTTPS or SSH and then build the application.</p>
<pre class="language-shell"><code class="language-shell"><span class="token builtin class-name">cd</span> ~/Documents<span class="token punctuation">;</span> <span class="token function">git</span> clone git@github.com:makccr/suckless<span class="token punctuation">;</span> <span class="token builtin class-name">cd</span> suckless
<span class="token comment"># or</span>
<span class="token builtin class-name">cd</span> ~/Documents<span class="token punctuation">;</span> <span class="token function">git</span> clone https://github.com/makccr/suckless.git<span class="token punctuation">;</span> <span class="token builtin class-name">cd</span> suckless</code></pre>
<p>Once the repository is cloned, there is an install script located in the root folder, which needs to be run, however in order to build dmenu some X11 development headers to be installed on the system in question:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> dnf <span class="token function">install</span> libX11-devel libXinerama-devel libXft-devel</code></pre>
<pre class="language-shell"><code class="language-shell">./install.sh</code></pre>
<p><strong>Note</strong>: It is not necessary to install a custom iteration of demnu. Some amount of time and effort could be saved by simply downloading and installing the dmenu packages in dnf package manger:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> dnf <span class="token function">install</span> dmenu</code></pre>
<h2>Install Configuration Files &amp; Fonts</h2>
<p>First use git to clone my dot files repository and create symbolic links for my config and script directories.</p>
<pre class="language-shell"><code class="language-shell"><span class="token builtin class-name">cd</span> ~/Documents<span class="token punctuation">;</span> <span class="token function">git</span> clone git@github.com:makccr/dot<span class="token punctuation">;</span> <span class="token builtin class-name">cd</span>
<span class="token comment"># or</span>
<span class="token builtin class-name">cd</span> ~/Documents<span class="token punctuation">;</span> <span class="token function">git</span> clone https://github.com/makccr/dot.git<span class="token punctuation">;</span> <span class="token builtin class-name">cd</span></code></pre>
<p>There is a setup script in the <em>.scripts/</em> folder of this repository as well. It should be noted however, that on a clean install of Fedora Workstation the <em>~/.config</em> folder will be populated. It is necessary to move these default configuration files somewhere before running the install script.</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">mv</span> ~/.config ~/Documents/</code></pre>
<p>At this point we can now run the <em>setup.sh</em> script, which will create symbolic links from the <em>~/Documents/dot</em> directory to the respective locations for all configs. <strong>Important</strong>: This script must be run from the users home directory (~/).</p>
<pre class="language-shell"><code class="language-shell">./~/Documents/dot/.scripts/setup.sh</code></pre>
<p>At this point we can now add the default Fedora configs back into the <em>~/.config</em> folder:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">mv</span> ~/Documents/.config/<span class="token punctuation">{</span>*,.*<span class="token punctuation">}</span> ~/.config</code></pre>
<p>Lastly we need to install some fonts that polybar will be looking for in it's configuration. The easiest way to do this would be to simply install two font packages from dnf.</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> dnf <span class="token function">install</span> julietaula-montserrat-fonts nerd-fonts-hack</code></pre>
<p>However, this is also a good opportunity to go ahead an install my full collection of fonts:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">git</span> clone git@github.com:makccr/fonts<span class="token punctuation">;</span> <span class="token builtin class-name">cd</span> fonts<span class="token punctuation">;</span> <span class="token function">sudo</span> <span class="token function">cp</span> <span class="token parameter variable">-r</span> makc-fonts/ /usr/share/fonts/</code></pre>
<h2>Testing</h2>
<p>One of the few issues I have run into, is the <em>bspwm.desktop</em> file not being created and put into <em>/usr/share/xessions</em>. This will result in a situation where BSPWM can not be launched from the default login manger. In order to fix this we simply need to create a <em>bspwm.desktop</em> file in <em>/usr/share/xsessions</em>.</p>
<p><strong>Note</strong>: Ensuring that <em>xorg-x11-server-Xorg</em> and <em>bspwm</em> packages are installed should prevent this problem from ever taking place, but it's still helpful to know how to fix the issue.</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> <span class="token function">nano</span> /usr/share/xsessions/bspwm.desktop</code></pre>
<p>Add the following text to the <em>.dekstop</em> file and save changes.</p>
<pre class="language-shell"><code class="language-shell"><span class="token punctuation">[</span>Desktop Entry<span class="token punctuation">]</span>
<span class="token assign-left variable">Name</span><span class="token operator">=</span>BSPWM
<span class="token assign-left variable">Comment</span><span class="token operator">=</span>Binary Space Partitioning Window Manager
<span class="token assign-left variable">Exec</span><span class="token operator">=</span>/usr/bin/bspwm
<span class="token assign-left variable">Type</span><span class="token operator">=</span>Application</code></pre>
]]></description>
      </item>
    
      <item>
        <title>Cloning This Webiste</title>
        <link>https://makc.co/docs/cloning-this-site/</link>
        <guid>https://makc.co/docs/cloning-this-site/</guid>
        <pubDate>Mon, 29 Dec 2025 00:00:00 +0000</pubDate>
        <description><![CDATA[<p>A quick guide for cloning this site's repository via git &amp; getting set up for development on a new system. I use <a href="https://github.com/11ty/eleventy/">Eleventy (11ty)</a> as a static site generator &amp; <a href="https://airtable.com/">Airtable</a> as a <em>sort of a CMS solution</em>. This documentation will walk through cloning this website's repository from <a href="https://github.com/makccr/makccr.github.io">GitHub</a>, installing all required dependencies, insuring all NPM plugins are installed, and testing to ensure that the new machine is ready for further development.</p>
<h2>Install Required Packages</h2>
<pre class="language-shell"><code class="language-shell"><span class="token comment">#### Arch</span>
<span class="token function">sudo</span> pacman <span class="token parameter variable">-Syu</span> <span class="token function">git</span> nodejs <span class="token function">npm</span>

<span class="token comment">#### Fedora</span>
<span class="token function">sudo</span> dnf upgrade <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> dnf <span class="token function">install</span> <span class="token function">git</span> nodejs <span class="token function">npm</span>

<span class="token comment">#### Ubuntu</span>
<span class="token function">sudo</span> <span class="token function">apt</span> update <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> <span class="token function">apt</span> upgrade<span class="token punctuation">;</span> <span class="token function">curl</span> <span class="token parameter variable">-fsSL</span> https://deb.nodesource.com/setup_current.x <span class="token operator">|</span> <span class="token function">sudo</span> <span class="token parameter variable">-E</span> <span class="token function">bash</span> - <span class="token operator">&amp;&amp;</span> <span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> <span class="token parameter variable">-y</span> nodejs<span class="token punctuation">;</span> <span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> <span class="token function">git</span> <span class="token function">npm</span>

<span class="token comment">#### Free BSD</span>
<span class="token function">su</span> <span class="token parameter variable">-c</span> <span class="token string">"pkg update &amp;&amp; pkg upgrade &amp;&amp; pkg install git node npm"</span></code></pre>
<h2>Clone the Site's Repository</h2>
<ol>
<li>Navigate to a suitable location, typically I will use a <em>~/Documents</em> folder</li>
<li>Clone the site from GitHub either via HTTPS or SSH:</li>
</ol>
<pre class="language-shell"><code class="language-shell"><span class="token function">git</span> clone git@github.com:makccr/makc.co
or
<span class="token function">git</span> clone https://github.com/makccr/makc.co</code></pre>
<h3>Install 11ty &amp; Required Plugins</h3>
<pre class="language-shell"><code class="language-shell"><span class="token function">npm</span> <span class="token function">install</span></code></pre>
<p>Or, to install the specific versions currently in use, stored in `package-lock.json':</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">npm</span> ci</code></pre>
<h3>Verify the New Instance is Ready for Development</h3>
<p>The easiest way to verify that everything is working properly, is simply to attempt to compile and serve the eleventy site:</p>
<pre class="language-shell"><code class="language-shell">npx @11ty/eleventy <span class="token parameter variable">--serve</span></code></pre>
<p>If all went well, a local version of the site can now be reached at <em>http://localhost:8080</em> in any web browser. So long as this command is running the site will continually recompile as changes are made. You can also manually compile the site with:</p>
<pre class="language-shell"><code class="language-shell">npx @11ty/eleventy</code></pre>
<p>When ready to commit changes to the site, just use the normal git process for doing so. The site is hosted on Netlify &amp; any changes pushed to the <em>master</em> branch will go live immediately.</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">git</span> <span class="token function">add</span> -A<span class="token punctuation">;</span> <span class="token function">git</span> commit <span class="token parameter variable">-m</span> <span class="token string">"message"</span><span class="token punctuation">;</span> <span class="token function">git</span> push</code></pre>
<h2>Updating NPM Packages</h2>
<p>Obviously keeping packages up to date is a pretty important security practice. Luckily if packages are installed via <code>npm</code>, we can check for and install updates to 11ty packages very easily:</p>
<pre class="language-bash"><code class="language-bash">npx npm-check-updates <span class="token parameter variable">-u</span> <span class="token comment"># Checks for updates </span>

<span class="token function">npm</span> <span class="token function">install</span> <span class="token comment"># Installs updates </span>
<span class="token function">npm</span> i <span class="token comment"># Shorthand for npm install</span></code></pre>
<p>It may also be necessary to use the <code>npm audit</code> command to fix any issues that arise in the update process.</p>
<hr>
<h2>Helpful Formatting Blocks</h2>
<h3>New Essay Header</h3>
<pre class="language-markdown"><code class="language-markdown"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml">title: "TITLE TEXT"
layout: essay.html
date: YYYY-MM-DD
tags: essay 
subject: ["subject1", "subject2"]
description: "WORDS"</span>
<span class="token punctuation">---</span></span></code></pre>
<h3>New Doc Header</h3>
<pre class="language-markdown"><code class="language-markdown"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml">title: "TITLE"
layout: docs.html
date: YYYY-MM-DD
updated: YYYY-MM-DD
tags: docs
subject: ["subject1", "subject2"]
description: "WORDS"</span>
<span class="token punctuation">---</span></span></code></pre>
<h3>Generic New Page</h3>
<pre class="language-markdown"><code class="language-markdown"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml">includes: main.html 
title: "TITLE"
description: "WORDS"</span>
<span class="token punctuation">---</span></span></code></pre>
]]></description>
      </item>
    

  </channel>
</rss>
