<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2026-06-06T17:24:52-07:00</updated><id>/feed.xml</id><title type="html">Ryan Young</title><subtitle>The personal website of Ryan Young.</subtitle><entry><title type="html">My New Laptop Is a Lenovo Legion Go</title><link href="/2026/my-new-laptop-is-a-lenovo-legion-go/" rel="alternate" type="text/html" title="My New Laptop Is a Lenovo Legion Go" /><published>2026-06-06T00:00:00-07:00</published><updated>2026-06-06T00:00:00-07:00</updated><id>/2026/my-new-laptop-is-a-lenovo-legion-go</id><content type="html" xml:base="/2026/my-new-laptop-is-a-lenovo-legion-go/"><![CDATA[<p>Netbooks? Remember those? Sub-10-inch, horribly underpowered, miniature laptops? Asus Eee PC? One Laptop Per Child? Netbooks were all the rage in the late 2000’s. And then, like Zune, the Windows Phone, and the Avatar, they disappeared.</p>

<p>Let’s be clear: The netbooks of yesteryear were pieces of utter garbage. Their under-specced innards could barely keep pace with the copies of Windows XP they shipped with; tech journalists dissed these things like automotive journalists mock the Chevy Spark. But folks, I submit to you—we didn’t know how good we had it.</p>

<p>See, when I go shopping for a laptop, I have a checklist:</p>

<ul>
  <li>Size: Compact, ideally with a screen size 11 inches or smaller. A portable computer should fit inside my backpack, not <em>define</em> my backpack.</li>
  <li>Compatibility: Needs to run PC software; needs to run Linux. Software development is important to me, and you simply can’t do that on Android or iOS.</li>
  <li>Battery life: As long as possible, obviously.</li>
  <li>Performance: Here’s the compromise! I really just need to be able to browse the web, run some programming tools, and maybe do some lightweight gaming (think <em>Phoenix Wright Ace Attorney</em>, not <em>Microsoft Flight Simulator</em>).</li>
  <li>Thermals: As cool and quiet as possible. I mean, I’m not asking for a lot of horsepower here…</li>
  <li>Affordability: My price ceiling for a laptop has been roughly $500 for a long time now. If I wanted to drop $2k on a computer, which is what many high-end ultrabooks are priced at nowadays, I’d rather put the money into a desktop, and get far more performance per dollar.</li>
</ul>

<p>I have room for exactly three computers in my life. My smartphone fits in my pocket and can place calls and pay my grocery bill. My desktop computer sports a graphics card, a mechanical keyboard, and a set of triple monitors. I want my laptop to be as cheap and as portable as possible—I am not interested in spending a bunch of money on a big, bulky machine that could never hope to compete with the desktop I already own.</p>

<p>Well, I must really be wishing for a unicorn, because no device in the world checks all my boxes!</p>

<h3 id="laptops-how-i-loathe-thee">Laptops, How I Loathe Thee</h3>

<p>In 2009, my parents bought me this hulking tank of a Toshiba laptop with a first-generation Intel Core processor and a dedicated Nvidia graphics card. It breathed fire out of its exhaust outlets, it sounded about as loud as a commercial airliner at takeoff (even as an avid player of FlightGear Flight Simulator, I did not appreciate this kind of “immersion”), and it was so heavy that I could incorporate it into a weight-lifting regime. Today’s ultrabooks, marvels of thinness that they are, aren’t <em>quite</em> as easy to criticize as my piece-of-Toshit-ba, but they haven’t exactly advanced as much as I’d hoped, either. Every Intel or AMD laptop today still aims a jet of hot air into your crotch to spin up a Firefox or Windows Explorer process, and that battery never lasts quite as much as you’d like, especially if you want to actually—you know—use the device, and not just stare at Bliss dot jpeg all day.</p>

<p>Then there’s the screen sizes. Browse the laptop section of your local box store and you’ll see my problem right away: Today’s laptops are <em>huge</em>. Like the unbroken lineup of SUV’s and pickups at your local “car” dealership, laptop manufacturers have somehow got it in their heads that all anybody wants is a big, chunky 15-inch or 17-inch display. A 13-inch screen, as featured on the recently-released Macbook Neo and Framework 13, is what counts for “compact” nowadays, and my ideal preference is for something much smaller. For whatever reason—whether it’s the search for higher margins, or the chase for consumers that are always demanding bigger and better—compact laptops have seemingly gone the <a href="https://www.spacebar.news/cant-go-back-to-small-phones/">way</a> of compact smartphones. Gone. Extinct. Dodo.</p>

<h3 id="never-send-a-tablet-to-do-a-pcs-job">Never Send a Tablet to Do a PC’s Job</h3>

<p>Since I can’t stand modern laptops, I really want to like tablets. They’re small, they’re quiet, and damned if that ChromeOS tablet I daily-drove for about a year didn’t get the best battery life out of any mobile device I’ve ever owned. But the locked-down software every tablet ships with, pinned tightly as it is under the iron grip of Big Tech, is fundamentally unfitting for a computer to me. In 2009, the notion that all your software would soon come exclusively from a walled-off app store, one that you had to pay $100 a year just to write a single line of code for, was novel—and to the thinking computer user, maybe even a little offensive. Well, here we are in 2026, and a whole generation of digital natives has been raised wearing Apple and Google-brand straightjackets. They don’t know anything else. “An iPad replaced my laptop!” shout the tech journos.</p>

<p>We’re the frogs, guys, and the water is just about boiling.</p>

<p>Take the iPad. Paper-thin and paper-light, holding one really does feel like holding the future of computing. But with all software gated behind that App Store license, few open-source developers can justify the annual fees and the mandatory Apple hardware purchase. I mean, I’m all for supporting indie developers, but <a href="https://blink.sh">paying</a> $20/year to license a basic SSH client—something that’s available for free on any other operating system—just rubs me the wrong way. And I’m not sure how it’s managed to escape the scrutiny of the tech press for so long, but Cupertino still dictates to this day that all iOS apps must use Apple’s own Safari browser engine. Many websites do not function correctly if not used in Chrome—thanks to lazy web developers and Google’s near-absolute monopoly on the space—so being unable to run the Chrome engine is, sad to say, a dealbreaker for me.</p>

<p>Things are only slightly better in Mountain View. I’m keeping tabs on the Android Terminal project, which promises to marry the attractive qualities of Android hardware with the capability of a virtual machine running Debian Linux, but as of early 2026, the Internet consensus seems to be “Nice idea, but with all these bugs and crashes, it’s not ready for primetime.” And I had to ditch that ChromeOS tablet after Google <a href="https://chromeunboxed.com/google-is-shutting-down-lacros-the-standalone-browser-for-chromebooks/">removed</a> support for multiple browser profiles from the ChromeOS version of Chrome. Here’s why this is such a big deal to me—so as not to serve up my browsing history to Alphabet and Meta on a silver platter, I always <a href="https://www.fastcompany.com/90311396/incognito-mode-wont-keep-you-private-try-browser-compartmentalization">compartmentalize</a> my browsing into two independent sessions. One stays signed in to services that I use a lot. The other doesn’t sign into a damn thing. There are exactly two ways to achieve my setup: Use two browser profiles, or use two different browsers. And if you need a secondary browser, don’t even give ChromeOS any further thought. Like, you could theoretically do it by running the second browser inside the Linux virtual machine, but because browsers do so much for us nowadays, the performance will never be satisfactory. The only real way to run a web browser is to do so natively, on a supported platform.</p>

<h3 id="o-netbooks-where-art-thou">O Netbooks, Where Art Thou?</h3>

<p>Back to netbooks. Where are they today? Well, if you’re an executive in charge of product development for a PC manufacturer, you’re <a href="https://www.theguardian.com/technology/2012/dec/31/netbooks-dead-2013">not</a> exactly going to be thrilled by the prospect of making pennies on the dollar for a product that retails for maybe $300, and that you still have to pay Microsoft for a Windows license for. And then Steve Jobs’ 800-pound gorilla, the iPad, buried the concept for good—suddenly, anybody on the market for a computer in this price range had a competitor that could stay unplugged for much longer, that was much easier to use, and that could also keep the kids busy playing <em>Angry Birds</em>.</p>

<p>It’s just tragic, because today’s computing landscape ought to be tilted even more in the netbook’s favor. Intel’s low-end Atom processors now pack some serious punch, desktop Linux is more viable than ever (in no small part thanks to SteamOS), and our digital lives are more reliant on cloud computing than ever before. The concept was sound. It still makes for a great pitch today. For somebody like me, who just needs to run the Firefox browser and Visual Studio Code on the go, a cheap, compact netbook would be the computing equivalent of a Smartcar: Snug. Cozy. Practical. Yet here we are, with the netbook extinct, and the great masses carrying locked-down iPads and Galaxy tablets through the Best Buy checkout line—soon to mount their F-150’s and Surburbans for the drive home.</p>

<h3 id="okay-so-heres-what-i-did-settle-on">Okay, so Here’s What I Did Settle On…</h3>

<p>The good news is that the story doesn’t end there. Because Valve has brought the netbook back, and it’s here in the form of the Steam Deck.</p>

<p>I mean, no, the Steam Deck is not shaped anything like a laptop (the cottage industry for 3D-printed cases and the tech journalist thought <a href="https://www.techradar.com/news/can-i-really-ditch-my-work-laptop-for-a-steam-deck">pieces</a> notwithstanding). First of all, it has no built-in keyboard—and its low-resolution, 720p display, optimized for low-spec gaming, is far too undersized to get any productive work done. For something designed as a portable game console, all this is fine! The Deck isn’t a laptop, and it’s not trying to be one.</p>

<p>Some of the Steam Deck’s competitors, however, have made some different—and very interesting—design choices. I’m talking about Lenovo’s Legion Go, which sports detachable controllers (like those on a Nintendo Switch) and a screen that is both larger and that has a much greater pixel count than the Steam Deck’s. These features make the Legion Go totally viable as an ultraportable PC! You can take the controllers off to make the device even more compact (and SFW) and the luxuriously-sized display boasts enough screen estate to comfortably fit a code or spreadsheet editor.</p>

<p>So, take the Legion Go, add a 3D-printed magnetic keyboard <a href="https://www.etsy.com/listing/1897686079/lenovo-legion-go-1-keyboard-attachment">attachment</a>, replace the stock Windows operating system with a fully-customizable Linux distribution, and voila! A modern, gaming-capable netbook.</p>

<p><img src="/assets/posts/2026-06-06-legion-go.jpg" alt="Image of my Legion Go" /></p>

<p>Running Linux on the Legion Go isn’t difficult, because the hardware is fully functional in recent kernels, but I still needed to tinker a fair amount to make the device behave exactly as I wanted it to. You see, Linux generally assumes that the Legion Go is just another desktop or laptop, when in actuality, it’s a multi-mode tablet that may or may not have a keyboard present at all. The Gnome desktop, in particular, does not enable its screen auto-rotate or touch keyboard features out of the box—the only way to tell Gnome “Yes, this is a tablet, so turn on your modes that are appropriate for tablets” is to have one of the input drivers compiled into the Linux kernel transmit a set of special input codes. Since nobody else on the planet has thought of using the Legion Go as a tablet, Linux, quite naturally, does not do that.</p>

<p>Fortunately, it’s possible for a userspace program to do the job instead, so I learned some Rust and wrote just such a service. It uses the presence of a Bluetooth or USB keyboard to determine when to enter or leave tablet mode. (Keyboard present, not a tablet—no keyboard, time to be a tablet. Nice and simple.) In addition to writing that code, I also had to make a bunch of miscellaneous fixes: disabling Wi-Fi power-saving in NetworkManager, adding custom overrides to the hwdb and libinput databases, and even recompiling Mutter with a yet-to-be released fix for screen orientation.</p>

<p>If you’ve purchased a Legion Go and are interested in replicating my setup, you can find all my documentation and source code on <a href="https://github.com/YoRyan/legion-go-umpc">GitHub</a>.</p>

<p>I still wouldn’t call the Legion Go my perfect portable computer. Using my checklist, I’d give it an A for size, a C for battery life (that gaming processor is thirsty), an A for performance, a B for thermals, an A for compatibility, and a B for affordability (retail price is quite high, but the Legion Go is easily found for much lower prices on the secondhand and open box markets; I did not have to break my $500 price barrier to score mine). I still have to make some compromises, but overall, I’m quite happy with the end result. And it’s all the more satisfying having gotten the chance to write some code in the process.</p>

<p>What’s my biggest annoyance? Honestly, it’s when I unpack my quirky little device at work to try to get something productive done—and the heads begin to turn and everybody starts asking me questions about it.</p>]]></content><author><name>Ryan Young</name></author><category term="tech" /><summary type="html"><![CDATA[Netbooks? Remember those? Sub-10-inch, horribly underpowered, miniature laptops? Asus Eee PC? One Laptop Per Child? Netbooks were all the rage in the late 2000’s. And then, like Zune, the Windows Phone, and the Avatar, they disappeared. Let’s be clear: The netbooks of yesteryear were pieces of utter garbage. Their under-specced innards could barely keep pace with the copies of Windows XP they shipped with; tech journalists dissed these things like automotive journalists mock the Chevy Spark. But folks, I submit to you—we didn’t know how good we had it. See, when I go shopping for a laptop, I have a checklist: Size: Compact, ideally with a screen size 11 inches or smaller. A portable computer should fit inside my backpack, not define my backpack. Compatibility: Needs to run PC software; needs to run Linux. Software development is important to me, and you simply can’t do that on Android or iOS. Battery life: As long as possible, obviously. Performance: Here’s the compromise! I really just need to be able to browse the web, run some programming tools, and maybe do some lightweight gaming (think Phoenix Wright Ace Attorney, not Microsoft Flight Simulator). Thermals: As cool and quiet as possible. I mean, I’m not asking for a lot of horsepower here… Affordability: My price ceiling for a laptop has been roughly $500 for a long time now. If I wanted to drop $2k on a computer, which is what many high-end ultrabooks are priced at nowadays, I’d rather put the money into a desktop, and get far more performance per dollar. I have room for exactly three computers in my life. My smartphone fits in my pocket and can place calls and pay my grocery bill. My desktop computer sports a graphics card, a mechanical keyboard, and a set of triple monitors. I want my laptop to be as cheap and as portable as possible—I am not interested in spending a bunch of money on a big, bulky machine that could never hope to compete with the desktop I already own. Well, I must really be wishing for a unicorn, because no device in the world checks all my boxes! Laptops, How I Loathe Thee In 2009, my parents bought me this hulking tank of a Toshiba laptop with a first-generation Intel Core processor and a dedicated Nvidia graphics card. It breathed fire out of its exhaust outlets, it sounded about as loud as a commercial airliner at takeoff (even as an avid player of FlightGear Flight Simulator, I did not appreciate this kind of “immersion”), and it was so heavy that I could incorporate it into a weight-lifting regime. Today’s ultrabooks, marvels of thinness that they are, aren’t quite as easy to criticize as my piece-of-Toshit-ba, but they haven’t exactly advanced as much as I’d hoped, either. Every Intel or AMD laptop today still aims a jet of hot air into your crotch to spin up a Firefox or Windows Explorer process, and that battery never lasts quite as much as you’d like, especially if you want to actually—you know—use the device, and not just stare at Bliss dot jpeg all day. Then there’s the screen sizes. Browse the laptop section of your local box store and you’ll see my problem right away: Today’s laptops are huge. Like the unbroken lineup of SUV’s and pickups at your local “car” dealership, laptop manufacturers have somehow got it in their heads that all anybody wants is a big, chunky 15-inch or 17-inch display. A 13-inch screen, as featured on the recently-released Macbook Neo and Framework 13, is what counts for “compact” nowadays, and my ideal preference is for something much smaller. For whatever reason—whether it’s the search for higher margins, or the chase for consumers that are always demanding bigger and better—compact laptops have seemingly gone the way of compact smartphones. Gone. Extinct. Dodo. Never Send a Tablet to Do a PC’s Job Since I can’t stand modern laptops, I really want to like tablets. They’re small, they’re quiet, and damned if that ChromeOS tablet I daily-drove for about a year didn’t get the best battery life out of any mobile device I’ve ever owned. But the locked-down software every tablet ships with, pinned tightly as it is under the iron grip of Big Tech, is fundamentally unfitting for a computer to me. In 2009, the notion that all your software would soon come exclusively from a walled-off app store, one that you had to pay $100 a year just to write a single line of code for, was novel—and to the thinking computer user, maybe even a little offensive. Well, here we are in 2026, and a whole generation of digital natives has been raised wearing Apple and Google-brand straightjackets. They don’t know anything else. “An iPad replaced my laptop!” shout the tech journos. We’re the frogs, guys, and the water is just about boiling. Take the iPad. Paper-thin and paper-light, holding one really does feel like holding the future of computing. But with all software gated behind that App Store license, few open-source developers can justify the annual fees and the mandatory Apple hardware purchase. I mean, I’m all for supporting indie developers, but paying $20/year to license a basic SSH client—something that’s available for free on any other operating system—just rubs me the wrong way. And I’m not sure how it’s managed to escape the scrutiny of the tech press for so long, but Cupertino still dictates to this day that all iOS apps must use Apple’s own Safari browser engine. Many websites do not function correctly if not used in Chrome—thanks to lazy web developers and Google’s near-absolute monopoly on the space—so being unable to run the Chrome engine is, sad to say, a dealbreaker for me. Things are only slightly better in Mountain View. I’m keeping tabs on the Android Terminal project, which promises to marry the attractive qualities of Android hardware with the capability of a virtual machine running Debian Linux, but as of early 2026, the Internet consensus seems to be “Nice idea, but with all these bugs and crashes, it’s not ready for primetime.” And I had to ditch that ChromeOS tablet after Google removed support for multiple browser profiles from the ChromeOS version of Chrome. Here’s why this is such a big deal to me—so as not to serve up my browsing history to Alphabet and Meta on a silver platter, I always compartmentalize my browsing into two independent sessions. One stays signed in to services that I use a lot. The other doesn’t sign into a damn thing. There are exactly two ways to achieve my setup: Use two browser profiles, or use two different browsers. And if you need a secondary browser, don’t even give ChromeOS any further thought. Like, you could theoretically do it by running the second browser inside the Linux virtual machine, but because browsers do so much for us nowadays, the performance will never be satisfactory. The only real way to run a web browser is to do so natively, on a supported platform. O Netbooks, Where Art Thou? Back to netbooks. Where are they today? Well, if you’re an executive in charge of product development for a PC manufacturer, you’re not exactly going to be thrilled by the prospect of making pennies on the dollar for a product that retails for maybe $300, and that you still have to pay Microsoft for a Windows license for. And then Steve Jobs’ 800-pound gorilla, the iPad, buried the concept for good—suddenly, anybody on the market for a computer in this price range had a competitor that could stay unplugged for much longer, that was much easier to use, and that could also keep the kids busy playing Angry Birds. It’s just tragic, because today’s computing landscape ought to be tilted even more in the netbook’s favor. Intel’s low-end Atom processors now pack some serious punch, desktop Linux is more viable than ever (in no small part thanks to SteamOS), and our digital lives are more reliant on cloud computing than ever before. The concept was sound. It still makes for a great pitch today. For somebody like me, who just needs to run the Firefox browser and Visual Studio Code on the go, a cheap, compact netbook would be the computing equivalent of a Smartcar: Snug. Cozy. Practical. Yet here we are, with the netbook extinct, and the great masses carrying locked-down iPads and Galaxy tablets through the Best Buy checkout line—soon to mount their F-150’s and Surburbans for the drive home. Okay, so Here’s What I Did Settle On… The good news is that the story doesn’t end there. Because Valve has brought the netbook back, and it’s here in the form of the Steam Deck. I mean, no, the Steam Deck is not shaped anything like a laptop (the cottage industry for 3D-printed cases and the tech journalist thought pieces notwithstanding). First of all, it has no built-in keyboard—and its low-resolution, 720p display, optimized for low-spec gaming, is far too undersized to get any productive work done. For something designed as a portable game console, all this is fine! The Deck isn’t a laptop, and it’s not trying to be one. Some of the Steam Deck’s competitors, however, have made some different—and very interesting—design choices. I’m talking about Lenovo’s Legion Go, which sports detachable controllers (like those on a Nintendo Switch) and a screen that is both larger and that has a much greater pixel count than the Steam Deck’s. These features make the Legion Go totally viable as an ultraportable PC! You can take the controllers off to make the device even more compact (and SFW) and the luxuriously-sized display boasts enough screen estate to comfortably fit a code or spreadsheet editor. So, take the Legion Go, add a 3D-printed magnetic keyboard attachment, replace the stock Windows operating system with a fully-customizable Linux distribution, and voila! A modern, gaming-capable netbook. Running Linux on the Legion Go isn’t difficult, because the hardware is fully functional in recent kernels, but I still needed to tinker a fair amount to make the device behave exactly as I wanted it to. You see, Linux generally assumes that the Legion Go is just another desktop or laptop, when in actuality, it’s a multi-mode tablet that may or may not have a keyboard present at all. The Gnome desktop, in particular, does not enable its screen auto-rotate or touch keyboard features out of the box—the only way to tell Gnome “Yes, this is a tablet, so turn on your modes that are appropriate for tablets” is to have one of the input drivers compiled into the Linux kernel transmit a set of special input codes. Since nobody else on the planet has thought of using the Legion Go as a tablet, Linux, quite naturally, does not do that. Fortunately, it’s possible for a userspace program to do the job instead, so I learned some Rust and wrote just such a service. It uses the presence of a Bluetooth or USB keyboard to determine when to enter or leave tablet mode. (Keyboard present, not a tablet—no keyboard, time to be a tablet. Nice and simple.) In addition to writing that code, I also had to make a bunch of miscellaneous fixes: disabling Wi-Fi power-saving in NetworkManager, adding custom overrides to the hwdb and libinput databases, and even recompiling Mutter with a yet-to-be released fix for screen orientation. If you’ve purchased a Legion Go and are interested in replicating my setup, you can find all my documentation and source code on GitHub. I still wouldn’t call the Legion Go my perfect portable computer. Using my checklist, I’d give it an A for size, a C for battery life (that gaming processor is thirsty), an A for performance, a B for thermals, an A for compatibility, and a B for affordability (retail price is quite high, but the Legion Go is easily found for much lower prices on the secondhand and open box markets; I did not have to break my $500 price barrier to score mine). I still have to make some compromises, but overall, I’m quite happy with the end result. And it’s all the more satisfying having gotten the chance to write some code in the process. What’s my biggest annoyance? Honestly, it’s when I unpack my quirky little device at work to try to get something productive done—and the heads begin to turn and everybody starts asking me questions about it.]]></summary></entry><entry><title type="html">Check Emails From Other Accounts in Gmail—Briskly—With Go-Getmail</title><link href="/2024/check-emails-from-gmail-briskly-go-getmail/" rel="alternate" type="text/html" title="Check Emails From Other Accounts in Gmail—Briskly—With Go-Getmail" /><published>2024-05-19T00:00:00-07:00</published><updated>2024-05-19T00:00:00-07:00</updated><id>/2024/check-emails-from-gmail-briskly-go-getmail</id><content type="html" xml:base="/2024/check-emails-from-gmail-briskly-go-getmail/"><![CDATA[<blockquote>
  <p>TL;DR Use <a href="https://github.com/mback2k/go-getmail">go-getmail</a> to sync your indie, IMAP inbox to another IMAP inbox supported by Gmailify like Outlook.com in combination with Gmailify to achieve a (nearly) perfect Gmail setup.</p>
</blockquote>

<p>For as long as I can remember being a sentient Internet user, I have called myself a Gmail addict. I remember chatting with online buddies in the embedded Google Talk widget. I remember claiming two extra gigs of storage by enabling 2-factor authentication when it first became available for Google accounts. I remember when the Internet was going all goo-ga for Google Inbox.</p>

<p>I love Gmail because it integrates so well with the rest of the Google ecosystem: your contacts, your calendar events, your Drive attachments. Microsoft and Apple’s offerings are catching up, but they’re still grappling with the Web 2.0 concept of the web browser as a first-rate client, something Google has been mastering since the turn of the century. Add to that synergy the rest of Gmail’s power features—automatic categorization for incoming messages, precision spam detection, a world-class search engine with custom operators, among various other goodies—and I’m honestly not sure if I could ever possibly leave Gmail. I’m hooked, no matter how many privacy scandals Google keeps embroiling itself in.</p>

<p>I love Gmail so much that I refuse to use any other webmail platform to interact with my email. That includes my secondary addresses I have with other providers (shout-out to <a href="https://purelymail.com/">Purelymail</a>) and the public address that I publish on my website and social profiles. I mean, reading and writing mail <em>without</em> feasting my eyes on Google’s beautiful material design language? The horror!</p>

<p>So I insist on importing all my mail into my Gmail inbox. Seems pretty easy, right? After all, Google even offers some officially supported <a href="https://support.google.com/mail/answer/21289?hl=en">methods</a>. Well, as anyone who has ever looked at this problem has doubtlessly figured out, it is in fact <em>not</em> so simple. It turns out there is no obvious way to connect Gmail with an external inbox in such a way as to accomplish all of the following at the same time:</p>

<ul>
  <li>Little-to-no delay in delivery time</li>
  <li>Reliable delivery of all messages, including ones that look like spam</li>
  <li>Support for Gmail’s automated filters and classifiers</li>
</ul>

<p>Call it the holy grail—or whatever faith-agnostic metaphor you prefer—of Gmail integration, but whatever it is, it has always been seemingly unattainable. Until now.</p>

<p>First, I’ll explain what doesn’t work, and then I’ll explain the solution that I finally arrived at.</p>

<h2 id="just-use-forwarding">Just use forwarding!</h2>

<p>Setting your external inbox to forward everything to your Gmail address is the most obvious technique. At first glance, this works alright. Every email provider offers forwarding, it’s nearly instantaneous, and Gmail runs all its magic classifiers on the forwarded messages.</p>

<p>The problem is when your external address receives mail that is spam, or even looks vaguely like spam.</p>

<p>The email protocol is such that it appears to Gmail your external email provider—not the true sender of the message—is the entity who “sent” the spam. And when Gmail receives too many spam-y messages (forwarded or not) from a single server, it gets a little antsy about accepting any more, and simply blackholes <em>any</em> further messages (whether they look like spam or not) from that server for several hours. I emphasize that this isn’t just a little delay in the delivery timeline, nor just a one-way trip to your spam folder, but poof, gonzo, into the digital void your messages go—for hours at a time. And neither the forwarding server, nor the original sender of your email, receives <em>any</em> notification that this is what’s happening to your precious messages.</p>

<p>Now, <em>you</em> might think <em>your</em> email address is squeaky clean and would only receive a miniscule amount of spam, but you have to multiply this particular scenario by everyone <em>else</em> using your particular provider to forward their messages to Gmail. You see, <em>their</em> spam also tests Gmail’s tolerance. So, when you rely on email forwarding from an indie provider like Purelymail, it’ll work great most of the time, but every few days, you’ll have several hours of downtime when Gmail will go all Quiet Place on your communications. And your senders won’t know a thing.</p>

<p>Not good. Onto the next solution.</p>

<h2 id="what-about-gmailify">What about Gmailify?</h2>

<p>Should forwarding not meet your needs, Gmail offers an official product, called “<a href="https://support.google.com/mail/answer/6304825">Gmailify</a>,” to import mail from an external inbox. It’s easy to use and it does exactly what it says on the tin. The problem is that this service is only offered for well-known webmail providers, which presently includes Yahoo, AOL, Hotmail, and Outlook—probably companies Google was able to work out some kind of API access arrangement with.</p>

<p>By contrast, if you want to connect a mailbox that isn’t from one of the big providers, Google allows this, but only through a POP3 import job that needs to be run on a regular basis. You don’t receive your mail until Gmail does its thing. And, there’s the rub: Google schedules these jobs at <em>their</em> convenience, not yours.</p>

<p>(POP3, for some background, is an ancient protocol used to retrieve mail from an email inbox. It’s simple to implement, but it’s a <em>polling</em> protocol, which means you need to initiate a new connection with the email server and start from scratch every time you want to check for new messages. It’s the digital equivalent of having to drive to the post office to see if you have any mail, every single time. Apparently, this is a not-insignificant drain on Google’s resources, because they schedule checks of your inbox on intervals as infrequently as up to an hour apart. It’s worse than waiting for a city bus.)</p>

<p>The exact algorithm used to determine how frequently to poll your inbox, as with so many things Google, is not publicly known, and is subject to much speculation by desperate users trapped in the depths of Google’s support forums. The Internet’s best guess is that it has something to do with how frequently your external inbox has emails to retrieve when Gmail checks it. Cue the people who have taken to <a href="https://rakowski.pro/how-to-force-gmail-to-check-your-pop3-account-as-often-as-possible/">sending</a> automated emails to themselves to trick Google into thinking their inbox is more popular than it really is. This is, clearly, quite the hack, and I have seen reports that even this trick does not work consistently.</p>

<p>If you use a well-known webmail service like Yahoo or Outlook, the Gmailify experience is excellent—emails arrive within just a couple of minutes of being received upstream. It is only when using an indie provider that you get downgraded to the “delivered, whenever, maybe even slower than the actual post office” tier.</p>

<p>I spent years living with Gmail’s sloth of a POP3 importer, mostly being annoyed by the delay in receiving my mail, which could be anywhere from 15 to 60 minutes, depending on Google’s mood. It wasn’t so bad for bank statements and marketing emails, but for 2FA exchanges and conversations with real live humans, the limitations were… painfully apparent.</p>

<p>On Gmail’s desktop interface, at least, I could request a manual POP3 refresh by navigating through a maze of menus and finding the particular link. Clicking the refresh button is supposed to do this too, but in true Google fashion, that feature has been <a href="https://support.google.com/mail/thread/130772496">broken</a> since forever, with no sign of a fix. (I cannot vouch for any of the paid Chrome extensions.) On the Android app, big surprise, I was screwed: No button to request a POP3 refresh to be found anywhere. By logging into Purelymail’s web interface, I could at least read the messages, even if they hadn’t yet been retrieved by Gmail. But what if I needed to reply to one? Could I really be expected to do that outside of Gmail? The horror!</p>

<h2 id="my-first-forwarder-turbogmailify">My first forwarder: Turbogmailify</h2>

<p>Not being satisfied with either of the official methods, I wondered if I might be able to build a better mail forwarder by writing some code. Gmail, after all, still has a publicly accessible API—a relic from the days when Google was built by and for nerds. The <a href="https://developers.google.com/gmail/api/reference/rest/v1/users.messages/import">user.messages.import</a> call quickly caught my eye, and it seemed to be exactly what I was looking for: a way to import messages into my account on my own terms. To quote the description:</p>

<blockquote>
  <p>Imports a message into only this user’s mailbox, with standard email delivery scanning and classification similar to receiving via SMTP.</p>
</blockquote>

<p>It would have been a piece of cake to retrieve messages from my Purelymail inbox with POP3, just as Google does, except, obviously, more frequently. But I wasn’t going to settle for that—I wanted <em>instant</em> delivery of new messages. Enter the IMAP protocol, the successor to POP3 in all but name, which includes many more features—among them, the ability to maintain an indefinite connection to an IMAP server and get notified when new messages arrive in a mailbox. (The digital equivalent of having the post office call you when it’s time to go retrieve your mail.)</p>

<p>So the task was to log in to my IMAP account, wait for notifications of new messages, copy the messages to Gmail, and then delete the messages from the original server. After a few hours hacking together some Go code, I had a working prototype. I call the result “<a href="https://github.com/YoRyan/turbogmailify/">turbogmailify</a>.”</p>

<p>Sadly, it was after all this work that I learned that Google’s definition of “classification” does not include Gmail’s automatic labeling features. The import call simply dumps messages into the All Mail folder and you must explicitly specify the labels you want to apply to each message; there is no way to instruct Gmail to work its magic and decide for itself which labels to apply.</p>

<p>This limitation might be acceptable if all you wanted to do was import an archive of your previous mail—as, coincidentally, an official Google Python <a href="https://github.com/google/import-mailbox-to-gmail">script</a> does—but it’s no good for receiving real, live mail. I had to look for another solution.</p>

<h2 id="final-answer-go-getmail">Final answer: go-getmail</h2>

<p>The IMAP protocol has another useful feature: Not only can you download email from an IMAP server, you can also upload it—just as you might upload files to Google Drive for storage and eventual retrieval. This capability is how you can sign into multiple accounts with a desktop email client and drag and drop emails between accounts.</p>

<p>Gmail, like most other webmail providers, has support for IMAP. A thought occurred to me: What if we had a real-time, IMAP to IMAP forwarder? And there just so happens to be a little Go program, <a href="https://github.com/mback2k/go-getmail">go-getmail</a>, that is exactly that. It retrieves mail from one IMAP server and uploads it to another, while also deleting the mail from the original server.</p>

<p>At first, this approach doesn’t look to be any more promising than using the Gmail API, because once again, there’s no way to ask Gmail to classify the mail that you insert for you. Gmail exposes all its labels as IMAP folders, and dumping messages into the “Inbox” folder merely appends them without any labels. There’s no IMAP command to ask Gmail to apply its magic labels.</p>

<p>Then I wondered: What if I used, as my IMAP destination, an inbox that Gmail supported with Gmailify? Maybe, I crossed my fingers, Gmail wouldn’t perceive any difference between the synchronized messages and real “live” messages, and would import and classify them accordingly? And maybe, just maybe, Gmail would fetch these emails far more frequently, with Gmailify briskness rather than POP3 lethargy?</p>

<p>Noting that my Outlook.com inbox has support for both IMAP access and Gmailify, I quickly created a go-getmail configuration file and gave this harebrained scheme a shot: Purelymail to Outlook.com to Gmail.</p>

<p>And it works! And it’s—almost—perfect! Fast delivery, no spam black hole, automatic classification. The Gmail holy grail!</p>

<p>With one exception: Gmail perceives imported emails from “sensitive” senders (think banks, and for some reason, the USPS) as suspicious and automatically marks them as spam. No big deal—I’ll simply provide my real Gmail address to senders that Gmail consistently flags. Otherwise, this setup is close enough to perfect that I’m more than happy to live with this quirk.</p>

<h2 id="summary">Summary</h2>

<p>At long last, I’ve arrived at a Gmail-with-external-inboxes setup that seems as close to perfect as possible.</p>

<table>
  <thead>
    <tr>
      <th>Method</th>
      <th>Latency</th>
      <th>Reliability</th>
      <th>Gmail Magic</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Forwarding</td>
      <td>🚀</td>
      <td>❌</td>
      <td>🚀</td>
    </tr>
    <tr>
      <td>Check with POP3</td>
      <td>👎</td>
      <td>🚀</td>
      <td>🚀</td>
    </tr>
    <tr>
      <td>Turbogmailify</td>
      <td>🚀</td>
      <td>👍</td>
      <td>❌</td>
    </tr>
    <tr>
      <td>Go-getmail + Gmailify</td>
      <td>👍</td>
      <td>👍</td>
      <td>🚀</td>
    </tr>
  </tbody>
</table>

<p>To run go-getmail 24/7, I’ve deployed it as a Docker container inside my home server, while retaining Gmail’s POP3 importer as a backup for when my homelab goes down. Go-getmail, just like my own turbogmailify, takes advantage of IMAP’s real-time mailbox notifications, so it swipes the emails from Purelymail before Gmail’s lethargic importer ever bothers to look.</p>

<p>In the future, I want to extend go-getmail so that I can selectively choose messages not to forward. That way, if I still have problems with Gmail marking certain messages as spam, I can leave them for Gmail to import on its own. (Yes, I promise to upstream my code.)</p>

<p>I have a love/hate relationship with Google. Picture this: You’re going on a nice jog, enjoying all of this truly outstanding technology, but when something goes wrong, or you brush up against the limitations, it feels like encountering a locked gate in the middle of your walking path—you <em>should</em> be able to get through there, but you can’t, for the most arbitrary and opaque of reasons. In a sense, maybe this is only fair. If a service is free, then you are the product—or at least shouldn’t feel entitled to some good old-fashioned customer service from a faceless Big Tech behemoth.</p>

<p>But, just this once, I’m happy I get to say: Sorry, Big Tech. Score one for the nerds.</p>

<h2 id="postscript-my-homelab-configs">Postscript: My homelab configs</h2>

<p>My Docker Compose snippets for go-getmail:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
  <span class="na">go-getmail</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">ghcr.io/mback2k/go-getmail</span>
    <span class="na">container_name</span><span class="pi">:</span> <span class="s">go-getmail</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
    <span class="na">configs</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">source</span><span class="pi">:</span> <span class="s">go-getmail.yaml</span>
        <span class="na">target</span><span class="pi">:</span> <span class="s">/etc/go-getmail/go-getmail.yaml</span>

<span class="na">configs</span><span class="pi">:</span>
  <span class="na">go-getmail.yaml</span><span class="pi">:</span>
    <span class="na">content</span><span class="pi">:</span> <span class="pi">|</span>
      <span class="s">Accounts:</span>
        <span class="s">- Name: Purelymail to Outlook</span>
          <span class="s">Source:</span>
            <span class="s">IMAP:</span>
              <span class="s">Server: imap.purelymail.com:993</span>
              <span class="s">Username: bob@example.com</span>
              <span class="s">Password: supersecret</span>
              <span class="s">Mailbox: INBOX</span>
          <span class="s">Target:</span>
            <span class="s">IMAP:</span>
              <span class="s">Server: outlook.office365.com:993</span>
              <span class="s">Username: bill@outlook.com</span>
              <span class="s">Password: hunter2</span>
              <span class="s">Mailbox: INBOX</span>
      <span class="s">Logging:</span>
        <span class="s">Level: warn</span>
</code></pre></div></div>

<p>My custom Dockerfile for go-getmail (because you really should build your own image for something so sensitive that it handles your <em>email</em>, and I like to save space with <a href="https://hub.docker.com/_/scratch">scratch</a> images):</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="w"> </span><span class="s">golang:1-alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">build</span>
<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> ca-certificates
<span class="k">WORKDIR</span><span class="s"> /src</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">RUN </span>go mod download
<span class="k">ENV</span><span class="s"> CGO_ENABLED=0 GOOS=linux</span>
<span class="k">RUN </span>go build <span class="nt">-a</span> <span class="nt">-installsuffix</span> cgo <span class="nt">-o</span> ./out/go-getmail .

<span class="k">FROM</span><span class="s"> scratch</span>
<span class="k">COPY</span><span class="s"> --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/</span>
<span class="k">COPY</span><span class="s"> --from=build /src/out/go-getmail /main</span>
<span class="k">USER</span><span class="s"> 10001</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["/main"]</span>
</code></pre></div></div>]]></content><author><name>Ryan Young</name></author><category term="tech" /><summary type="html"><![CDATA[TL;DR Use go-getmail to sync your indie, IMAP inbox to another IMAP inbox supported by Gmailify like Outlook.com in combination with Gmailify to achieve a (nearly) perfect Gmail setup. For as long as I can remember being a sentient Internet user, I have called myself a Gmail addict. I remember chatting with online buddies in the embedded Google Talk widget. I remember claiming two extra gigs of storage by enabling 2-factor authentication when it first became available for Google accounts. I remember when the Internet was going all goo-ga for Google Inbox. I love Gmail because it integrates so well with the rest of the Google ecosystem: your contacts, your calendar events, your Drive attachments. Microsoft and Apple’s offerings are catching up, but they’re still grappling with the Web 2.0 concept of the web browser as a first-rate client, something Google has been mastering since the turn of the century. Add to that synergy the rest of Gmail’s power features—automatic categorization for incoming messages, precision spam detection, a world-class search engine with custom operators, among various other goodies—and I’m honestly not sure if I could ever possibly leave Gmail. I’m hooked, no matter how many privacy scandals Google keeps embroiling itself in. I love Gmail so much that I refuse to use any other webmail platform to interact with my email. That includes my secondary addresses I have with other providers (shout-out to Purelymail) and the public address that I publish on my website and social profiles. I mean, reading and writing mail without feasting my eyes on Google’s beautiful material design language? The horror! So I insist on importing all my mail into my Gmail inbox. Seems pretty easy, right? After all, Google even offers some officially supported methods. Well, as anyone who has ever looked at this problem has doubtlessly figured out, it is in fact not so simple. It turns out there is no obvious way to connect Gmail with an external inbox in such a way as to accomplish all of the following at the same time: Little-to-no delay in delivery time Reliable delivery of all messages, including ones that look like spam Support for Gmail’s automated filters and classifiers Call it the holy grail—or whatever faith-agnostic metaphor you prefer—of Gmail integration, but whatever it is, it has always been seemingly unattainable. Until now. First, I’ll explain what doesn’t work, and then I’ll explain the solution that I finally arrived at. Just use forwarding! Setting your external inbox to forward everything to your Gmail address is the most obvious technique. At first glance, this works alright. Every email provider offers forwarding, it’s nearly instantaneous, and Gmail runs all its magic classifiers on the forwarded messages. The problem is when your external address receives mail that is spam, or even looks vaguely like spam. The email protocol is such that it appears to Gmail your external email provider—not the true sender of the message—is the entity who “sent” the spam. And when Gmail receives too many spam-y messages (forwarded or not) from a single server, it gets a little antsy about accepting any more, and simply blackholes any further messages (whether they look like spam or not) from that server for several hours. I emphasize that this isn’t just a little delay in the delivery timeline, nor just a one-way trip to your spam folder, but poof, gonzo, into the digital void your messages go—for hours at a time. And neither the forwarding server, nor the original sender of your email, receives any notification that this is what’s happening to your precious messages. Now, you might think your email address is squeaky clean and would only receive a miniscule amount of spam, but you have to multiply this particular scenario by everyone else using your particular provider to forward their messages to Gmail. You see, their spam also tests Gmail’s tolerance. So, when you rely on email forwarding from an indie provider like Purelymail, it’ll work great most of the time, but every few days, you’ll have several hours of downtime when Gmail will go all Quiet Place on your communications. And your senders won’t know a thing. Not good. Onto the next solution. What about Gmailify? Should forwarding not meet your needs, Gmail offers an official product, called “Gmailify,” to import mail from an external inbox. It’s easy to use and it does exactly what it says on the tin. The problem is that this service is only offered for well-known webmail providers, which presently includes Yahoo, AOL, Hotmail, and Outlook—probably companies Google was able to work out some kind of API access arrangement with. By contrast, if you want to connect a mailbox that isn’t from one of the big providers, Google allows this, but only through a POP3 import job that needs to be run on a regular basis. You don’t receive your mail until Gmail does its thing. And, there’s the rub: Google schedules these jobs at their convenience, not yours. (POP3, for some background, is an ancient protocol used to retrieve mail from an email inbox. It’s simple to implement, but it’s a polling protocol, which means you need to initiate a new connection with the email server and start from scratch every time you want to check for new messages. It’s the digital equivalent of having to drive to the post office to see if you have any mail, every single time. Apparently, this is a not-insignificant drain on Google’s resources, because they schedule checks of your inbox on intervals as infrequently as up to an hour apart. It’s worse than waiting for a city bus.) The exact algorithm used to determine how frequently to poll your inbox, as with so many things Google, is not publicly known, and is subject to much speculation by desperate users trapped in the depths of Google’s support forums. The Internet’s best guess is that it has something to do with how frequently your external inbox has emails to retrieve when Gmail checks it. Cue the people who have taken to sending automated emails to themselves to trick Google into thinking their inbox is more popular than it really is. This is, clearly, quite the hack, and I have seen reports that even this trick does not work consistently. If you use a well-known webmail service like Yahoo or Outlook, the Gmailify experience is excellent—emails arrive within just a couple of minutes of being received upstream. It is only when using an indie provider that you get downgraded to the “delivered, whenever, maybe even slower than the actual post office” tier. I spent years living with Gmail’s sloth of a POP3 importer, mostly being annoyed by the delay in receiving my mail, which could be anywhere from 15 to 60 minutes, depending on Google’s mood. It wasn’t so bad for bank statements and marketing emails, but for 2FA exchanges and conversations with real live humans, the limitations were… painfully apparent. On Gmail’s desktop interface, at least, I could request a manual POP3 refresh by navigating through a maze of menus and finding the particular link. Clicking the refresh button is supposed to do this too, but in true Google fashion, that feature has been broken since forever, with no sign of a fix. (I cannot vouch for any of the paid Chrome extensions.) On the Android app, big surprise, I was screwed: No button to request a POP3 refresh to be found anywhere. By logging into Purelymail’s web interface, I could at least read the messages, even if they hadn’t yet been retrieved by Gmail. But what if I needed to reply to one? Could I really be expected to do that outside of Gmail? The horror! My first forwarder: Turbogmailify Not being satisfied with either of the official methods, I wondered if I might be able to build a better mail forwarder by writing some code. Gmail, after all, still has a publicly accessible API—a relic from the days when Google was built by and for nerds. The user.messages.import call quickly caught my eye, and it seemed to be exactly what I was looking for: a way to import messages into my account on my own terms. To quote the description: Imports a message into only this user’s mailbox, with standard email delivery scanning and classification similar to receiving via SMTP. It would have been a piece of cake to retrieve messages from my Purelymail inbox with POP3, just as Google does, except, obviously, more frequently. But I wasn’t going to settle for that—I wanted instant delivery of new messages. Enter the IMAP protocol, the successor to POP3 in all but name, which includes many more features—among them, the ability to maintain an indefinite connection to an IMAP server and get notified when new messages arrive in a mailbox. (The digital equivalent of having the post office call you when it’s time to go retrieve your mail.) So the task was to log in to my IMAP account, wait for notifications of new messages, copy the messages to Gmail, and then delete the messages from the original server. After a few hours hacking together some Go code, I had a working prototype. I call the result “turbogmailify.” Sadly, it was after all this work that I learned that Google’s definition of “classification” does not include Gmail’s automatic labeling features. The import call simply dumps messages into the All Mail folder and you must explicitly specify the labels you want to apply to each message; there is no way to instruct Gmail to work its magic and decide for itself which labels to apply. This limitation might be acceptable if all you wanted to do was import an archive of your previous mail—as, coincidentally, an official Google Python script does—but it’s no good for receiving real, live mail. I had to look for another solution. Final answer: go-getmail The IMAP protocol has another useful feature: Not only can you download email from an IMAP server, you can also upload it—just as you might upload files to Google Drive for storage and eventual retrieval. This capability is how you can sign into multiple accounts with a desktop email client and drag and drop emails between accounts. Gmail, like most other webmail providers, has support for IMAP. A thought occurred to me: What if we had a real-time, IMAP to IMAP forwarder? And there just so happens to be a little Go program, go-getmail, that is exactly that. It retrieves mail from one IMAP server and uploads it to another, while also deleting the mail from the original server. At first, this approach doesn’t look to be any more promising than using the Gmail API, because once again, there’s no way to ask Gmail to classify the mail that you insert for you. Gmail exposes all its labels as IMAP folders, and dumping messages into the “Inbox” folder merely appends them without any labels. There’s no IMAP command to ask Gmail to apply its magic labels. Then I wondered: What if I used, as my IMAP destination, an inbox that Gmail supported with Gmailify? Maybe, I crossed my fingers, Gmail wouldn’t perceive any difference between the synchronized messages and real “live” messages, and would import and classify them accordingly? And maybe, just maybe, Gmail would fetch these emails far more frequently, with Gmailify briskness rather than POP3 lethargy? Noting that my Outlook.com inbox has support for both IMAP access and Gmailify, I quickly created a go-getmail configuration file and gave this harebrained scheme a shot: Purelymail to Outlook.com to Gmail. And it works! And it’s—almost—perfect! Fast delivery, no spam black hole, automatic classification. The Gmail holy grail! With one exception: Gmail perceives imported emails from “sensitive” senders (think banks, and for some reason, the USPS) as suspicious and automatically marks them as spam. No big deal—I’ll simply provide my real Gmail address to senders that Gmail consistently flags. Otherwise, this setup is close enough to perfect that I’m more than happy to live with this quirk. Summary At long last, I’ve arrived at a Gmail-with-external-inboxes setup that seems as close to perfect as possible. Method Latency Reliability Gmail Magic Forwarding 🚀 ❌ 🚀 Check with POP3 👎 🚀 🚀 Turbogmailify 🚀 👍 ❌ Go-getmail + Gmailify 👍 👍 🚀 To run go-getmail 24/7, I’ve deployed it as a Docker container inside my home server, while retaining Gmail’s POP3 importer as a backup for when my homelab goes down. Go-getmail, just like my own turbogmailify, takes advantage of IMAP’s real-time mailbox notifications, so it swipes the emails from Purelymail before Gmail’s lethargic importer ever bothers to look. In the future, I want to extend go-getmail so that I can selectively choose messages not to forward. That way, if I still have problems with Gmail marking certain messages as spam, I can leave them for Gmail to import on its own. (Yes, I promise to upstream my code.) I have a love/hate relationship with Google. Picture this: You’re going on a nice jog, enjoying all of this truly outstanding technology, but when something goes wrong, or you brush up against the limitations, it feels like encountering a locked gate in the middle of your walking path—you should be able to get through there, but you can’t, for the most arbitrary and opaque of reasons. In a sense, maybe this is only fair. If a service is free, then you are the product—or at least shouldn’t feel entitled to some good old-fashioned customer service from a faceless Big Tech behemoth. But, just this once, I’m happy I get to say: Sorry, Big Tech. Score one for the nerds. Postscript: My homelab configs My Docker Compose snippets for go-getmail: services: go-getmail: image: ghcr.io/mback2k/go-getmail container_name: go-getmail restart: unless-stopped configs: - source: go-getmail.yaml target: /etc/go-getmail/go-getmail.yaml configs: go-getmail.yaml: content: | Accounts: - Name: Purelymail to Outlook Source: IMAP: Server: imap.purelymail.com:993 Username: bob@example.com Password: supersecret Mailbox: INBOX Target: IMAP: Server: outlook.office365.com:993 Username: bill@outlook.com Password: hunter2 Mailbox: INBOX Logging: Level: warn My custom Dockerfile for go-getmail (because you really should build your own image for something so sensitive that it handles your email, and I like to save space with scratch images): FROM golang:1-alpine AS build RUN apk add --no-cache ca-certificates WORKDIR /src COPY . . RUN go mod download ENV CGO_ENABLED=0 GOOS=linux RUN go build -a -installsuffix cgo -o ./out/go-getmail . FROM scratch COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=build /src/out/go-getmail /main USER 10001 ENTRYPOINT ["/main"]]]></summary></entry><entry><title type="html">Build Your Own GitHub Codespaces With the Windows OpenSSH Server</title><link href="/2022/build-your-own-github-codespaces/" rel="alternate" type="text/html" title="Build Your Own GitHub Codespaces With the Windows OpenSSH Server" /><published>2022-02-09T00:00:00-08:00</published><updated>2022-02-09T00:00:00-08:00</updated><id>/2022/build-your-own-github-codespaces</id><content type="html" xml:base="/2022/build-your-own-github-codespaces/"><![CDATA[<p>GitHub recently made Codespaces available to every GitHub user, and they’ve been getting rave reviews. Like a digital petting zoo in the cloud, a Codespace is a virtual machine built just for programming that you can access through any ordinary web browser. Code on your ultrabook, your iPad, even your phone; it’s easy to see the appeal. The promise of Codespaces is the promise of game streaming–work or play from any device, anytime, anywhere.</p>

<p>But why should you have to rent a computer from GitHub to get any work done? Didn’t you just drop $2000, plus Windows license, on that hexacore, liquid-cooled ultimate gaming rig sitting on top of your desk? And you’re <em>still</em> going to swipe your credit card for every hour you spend staring blankly at your rented GitHub terminal? Boy, don’t you feel like a chump. Your desktop computer is equipped with all the resources you need to program, and then some; the only catch is that you need to be physically at your desk–or put up with the clumsiness of a remote desktop session–to make use of them.</p>

<figure class="figure  figure--center"><img class="image" src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/78/Gaming_computers_%281%29.jpg/1280px-Gaming_computers_%281%29.jpg" alt="Gaming computers on a Las Vegas showfloor." width="1280" height="853" /><figcaption class="caption"><p>Only the best will do to browse Google Chrome and play indie sidescroller games! 4K, 60FPS! (Credit: <a href="https://commons.wikimedia.org/wiki/File:Gaming_computers_(1).jpg">Notdjey, Wikimedia</a>)</p>
</figcaption></figure>

<p>Well, what if you could turn your gaming setup into your own personal Codespaces host? Thanks to a slate of Microsoft’s newest toys, it can be done!</p>

<p>On your monster gaming rig, the stack includes:</p>
<ul>
  <li>Windows Subsystem for Linux</li>
  <li>Docker for Linux</li>
  <li>Windows OpenSSH server</li>
</ul>

<p>On your lightweight Windows/Linux/macOS PC of choice:</p>
<ul>
  <li>Visual Studio Code</li>
  <li>Remote SSH extension</li>
  <li>Dev Containers extension</li>
</ul>

<p>Basically, you write your code in Visual Studio Code. Code uses the development containers extension to connect to a container running inside a Docker daemon. The Docker daemon runs inside of Windows Subsystem for Linux, which is itself just a fancy name for a virtual machine. Why run Linux inside WSL? Well, if you value your time spent troubleshooting complicated compatibility technologies like Wine, Proton, or VFIO above <em>zero</em>, you should already be booting Windows bare metal to play games. Phoronix recently <a href="https://www.phoronix.com/review/windows11-wsl2-good">clocked</a> WSL at about 94% of the speed of a native Ubuntu install, so with WSL you’re sacrificing very little performance, while gaining a whole lot of convenience.</p>

<p>To run Docker on WSL, I opt for the headless Docker Engine daemon over Docker Desktop. The special Linux distribution that comes with Docker Desktop isn’t meant to be interacted with outside of the GUI, so it’s far more logical to SSH into a standard Linux installation that accepts ordinary shell commands. (Some people also have a problem with Docker Desktop’s new freemium model, which using Docker Engine neatly bypasses.) If you’re still interested in a GUI, Visual Studio Code’s <a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker">Docker extension</a> is a worthy substitute for Docker Desktop’s niceties.</p>

<h2 id="step-1-linux-setup-with-windows-subsystem-for-linux">Step 1: Linux setup with Windows Subsystem for Linux</h2>

<p>As of 2023, WSL is now distributed via the Microsoft Store, but installing WSL that way breaks compatibility with the OpenSSH server, among other things. It’s a <a href="https://github.com/microsoft/WSL/issues/9231">known bug</a>, and Microsoft is working on a fix. In the meantime, you should <a href="https://devblogs.microsoft.com/commandline/the-windows-subsystem-for-linux-in-the-microsoft-store-is-now-generally-available-on-windows-10-and-11/#the-store-version-of-wsl-is-now-the-default-version-of-wsl">install</a> WSL using the Windows component instead:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; wsl.exe --install --inbox
</code></pre></div></div>

<p>I recommend <a href="https://apps.microsoft.com/store/detail/debian/9MSVKQC78PK6">Debian</a> as your WSL distribution of choice. It’s officially supported and has fewer moving parts than Ubuntu, the next best alternative. Once you have your Debian installation up and running, simply follow the official <a href="https://docs.docker.com/engine/install/debian/">instructions</a> to install Docker Engine. One gotcha is that Docker Engine’s default nftables backend is currently <a href="https://github.com/docker/for-linux/issues/1406">broken</a> on WSL, so you’ll have to switch to the iptables-legacy backend with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># update-alternatives --config iptables
</code></pre></div></div>

<p>WSL does not include systemd, so services do not autostart. To start Docker Engine at boot, you’ll have to create a new file at <code class="language-plaintext highlighter-rouge">/etc/wsl.conf</code> with the following contents:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[boot]</span><span class="w">
</span><span class="py">command</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">service docker start</span>
</code></pre></div></div>

<h2 id="step-2-openssh-server-setup">Step 2: OpenSSH server setup</h2>

<p>Windows now ships with an optional OpenSSH server. (Never did I imagine I would ever write that sentence.) We can use that server to facilitate remote access to the WSL virtual machine. Trust me–this method is far easier than the alternative, which is to run an SSH server inside the VM and somehow instruct Windows to forward traffic to it (IP addresses aren’t stable in WSL), a mad act that Scott Hanselman has compared to “trying to ice skate up hill.”</p>

<p>Start by <a href="https://www.hanselman.com/blog/the-easy-way-how-to-ssh-into-bash-and-wsl2-on-windows-10-from-an-external-machine">enabling</a> the OpenSSH server and <a href="https://www.hanselman.com/blog/the-easy-way-how-to-ssh-into-bash-and-wsl2-on-windows-10-from-an-external-machine">setting</a> the default shell to WSL2. Now, you can point your SSH client at your Windows desktop on port 22, and you’ll get a Bash shell inside your WSL instance! If you are a Windows administrator (as you probably are), you should insert your public keys into the file at <code class="language-plaintext highlighter-rouge">C:\ProgramData\ssh\administrators_authorized_keys</code>.</p>

<p>Finally, you’ll have to make port 22 on your Windows desktop reachable from the Internet. How you do this depends, of course, on your own networking setup. Personally, I’m fortunate enough to receive native IPv6 connectivity at home, so I simply open a pinhole in my firewall and populate an AAAA record for my desktop. If I need to connect from a location that doesn’t support IPv6, or that blocks SSH connections, I tunnel my traffic through my trusty Mozilla VPN app, which is one of the few VPN products that supports IPv6.</p>

<h2 id="step-3-visual-studio-code-setup">Step 3: Visual Studio Code setup</h2>

<p>Install Visual Studio Code on your PC of choice and install the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh">Remote - SSH</a> and <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers">Dev Containers</a> extensions. Use the “Remote-SSH: Connect to Host” command to initiate an SSH connection to your Windows desktop.</p>

<p>On your very first attempt, you’ll likely receive a broken pipe error–when Visual Studio Code encounters a Windows SSH server, it expects a PowerShell prompt, not Bash. To fix this, <a href="https://code.visualstudio.com/docs/remote/ssh#_connect-to-a-remote-host">use</a> the <code class="language-plaintext highlighter-rouge">remote.SSH.remotePlatform</code> preference to map your Windows desktop’s hostname to “Linux”.</p>

<p>You’ll also have to set the <code class="language-plaintext highlighter-rouge">remote.SSH.localServerDownload</code> preference to “off”, and install curl or wget inside your WSL machine. By default, Visual Studio Code attempts to copy resources to the remote host via scp, which will fail with this configuration, since the Windows OpenSSH server only provides dumb shell access.</p>

<p>Once you’ve successfully established an SSH connection to the WSL machine, Visual Studio Code lets you browse files and run shell commands on it. You can even browse the Windows filesystem through the <code class="language-plaintext highlighter-rouge">/mnt/c</code> directory and, if you have the Docker extension installed, interact with the Docker daemon. But the real magic happens with the Dev Container extension–navigate to a directory with a <code class="language-plaintext highlighter-rouge">.devcontainer/devcontainer.json</code> definition, and Visual Studio Code will offer you the option to spin up and enter a development container, all over SSH.</p>

<h2 id="step-4-done">Step 4: Done!</h2>

<p>And there we have it. You get the killer features of GitHub Codespaces–templated environments, dedicated hardware, and remote access from anywhere on the Internet–for free, using a computer you probably already own.</p>

<p>The major downside of this setup is that you must use the <em>desktop</em> version of Visual Studio Code on the client, which means that client must be running an officially supported version of Windows, Linux, or macOS. (Sorry–no iPads, Chromebooks, or smart refrigerators.) As of 2023, the remote development extensions cannot be used with <a href="https://code.visualstudio.com/blogs/2021/10/20/vscode-dev">VSCode.dev</a>, nor with the official <a href="https://code.visualstudio.com/docs/remote/vscode-server">Visual Studio Code Server</a>, nor with open-source derivatives like VSCodium, so for the moment, you have no real alternative here.</p>

<p>But for me, that’s an insignificant price to pay for such a powerful, yet convenient, programming experience. My current laptop for on-the-go computing is a Lenovo Yoga Flex, which I really like; it’s thin, light, and quiet, and it gets amazing battery life. But like every laptop, it would not be so quiet or long-lasting if I instructed it to compile a very large codebase, or execute a very long test suite. Development containers hosted on my desktop give me the best of both worlds: a powerful mothership to work on and a comfortable client to type into. And since my laptop need only be a glorified netbook capable of running an Electron app, as opposed to a monster machine that can drag race its way through a few thousand unit tests, I’m free to skimp on my next laptop purchase, redirecting the savings toward my desktop for the best programming-plus-gaming machine for my dollar.</p>

<p>I cannot stress how brilliant development containers are as a concept. They give me the power to switch between programming stacks at the push of a button–from Python 3 for maintaining Mailrise, to Python 2 for contributing patches to Apprise, to Node/TypeScript for working on TypeScriptToLua. Instead of burning hours setting up the Linux tooling for each context switch, I can check out, test, and ship a new PR in under an hour, which is exactly the kind of efficiency I need to be productive on the run.</p>

<p>The experience remains impressively good over a mobile data connection, too–say, while on a city bus–thanks in large part to Visual Studio Code’s predictive terminal echoing, a feature that anticipates the characters that should appear on your terminal before they actually get transmitted back by the remote host. And since the remote connection consists primarily of shell commands, it doesn’t use that much data, either! I count just a couple of megabytes per hour of work.</p>

<p>Given all these advantages, it’s no wonder GitHub <a href="https://github.blog/2021-08-11-githubs-engineering-team-moved-codespaces/">migrated</a> all their engineering teams to Codespaces. If you use Visual Studio Code as your IDE and your projects fit into development containers, it’s a no-brainer for you, too. And with my way, you won’t owe GitHub a cent, or a credit card number.</p>]]></content><author><name>Ryan Young</name></author><category term="tech" /><summary type="html"><![CDATA[GitHub recently made Codespaces available to every GitHub user, and they’ve been getting rave reviews. Like a digital petting zoo in the cloud, a Codespace is a virtual machine built just for programming that you can access through any ordinary web browser. Code on your ultrabook, your iPad, even your phone; it’s easy to see the appeal. The promise of Codespaces is the promise of game streaming–work or play from any device, anytime, anywhere. But why should you have to rent a computer from GitHub to get any work done? Didn’t you just drop $2000, plus Windows license, on that hexacore, liquid-cooled ultimate gaming rig sitting on top of your desk? And you’re still going to swipe your credit card for every hour you spend staring blankly at your rented GitHub terminal? Boy, don’t you feel like a chump. Your desktop computer is equipped with all the resources you need to program, and then some; the only catch is that you need to be physically at your desk–or put up with the clumsiness of a remote desktop session–to make use of them. Only the best will do to browse Google Chrome and play indie sidescroller games! 4K, 60FPS! (Credit: Notdjey, Wikimedia) Well, what if you could turn your gaming setup into your own personal Codespaces host? Thanks to a slate of Microsoft’s newest toys, it can be done! On your monster gaming rig, the stack includes: Windows Subsystem for Linux Docker for Linux Windows OpenSSH server On your lightweight Windows/Linux/macOS PC of choice: Visual Studio Code Remote SSH extension Dev Containers extension Basically, you write your code in Visual Studio Code. Code uses the development containers extension to connect to a container running inside a Docker daemon. The Docker daemon runs inside of Windows Subsystem for Linux, which is itself just a fancy name for a virtual machine. Why run Linux inside WSL? Well, if you value your time spent troubleshooting complicated compatibility technologies like Wine, Proton, or VFIO above zero, you should already be booting Windows bare metal to play games. Phoronix recently clocked WSL at about 94% of the speed of a native Ubuntu install, so with WSL you’re sacrificing very little performance, while gaining a whole lot of convenience. To run Docker on WSL, I opt for the headless Docker Engine daemon over Docker Desktop. The special Linux distribution that comes with Docker Desktop isn’t meant to be interacted with outside of the GUI, so it’s far more logical to SSH into a standard Linux installation that accepts ordinary shell commands. (Some people also have a problem with Docker Desktop’s new freemium model, which using Docker Engine neatly bypasses.) If you’re still interested in a GUI, Visual Studio Code’s Docker extension is a worthy substitute for Docker Desktop’s niceties. Step 1: Linux setup with Windows Subsystem for Linux As of 2023, WSL is now distributed via the Microsoft Store, but installing WSL that way breaks compatibility with the OpenSSH server, among other things. It’s a known bug, and Microsoft is working on a fix. In the meantime, you should install WSL using the Windows component instead: &gt; wsl.exe --install --inbox I recommend Debian as your WSL distribution of choice. It’s officially supported and has fewer moving parts than Ubuntu, the next best alternative. Once you have your Debian installation up and running, simply follow the official instructions to install Docker Engine. One gotcha is that Docker Engine’s default nftables backend is currently broken on WSL, so you’ll have to switch to the iptables-legacy backend with: # update-alternatives --config iptables WSL does not include systemd, so services do not autostart. To start Docker Engine at boot, you’ll have to create a new file at /etc/wsl.conf with the following contents: [boot] command = service docker start Step 2: OpenSSH server setup Windows now ships with an optional OpenSSH server. (Never did I imagine I would ever write that sentence.) We can use that server to facilitate remote access to the WSL virtual machine. Trust me–this method is far easier than the alternative, which is to run an SSH server inside the VM and somehow instruct Windows to forward traffic to it (IP addresses aren’t stable in WSL), a mad act that Scott Hanselman has compared to “trying to ice skate up hill.” Start by enabling the OpenSSH server and setting the default shell to WSL2. Now, you can point your SSH client at your Windows desktop on port 22, and you’ll get a Bash shell inside your WSL instance! If you are a Windows administrator (as you probably are), you should insert your public keys into the file at C:\ProgramData\ssh\administrators_authorized_keys. Finally, you’ll have to make port 22 on your Windows desktop reachable from the Internet. How you do this depends, of course, on your own networking setup. Personally, I’m fortunate enough to receive native IPv6 connectivity at home, so I simply open a pinhole in my firewall and populate an AAAA record for my desktop. If I need to connect from a location that doesn’t support IPv6, or that blocks SSH connections, I tunnel my traffic through my trusty Mozilla VPN app, which is one of the few VPN products that supports IPv6. Step 3: Visual Studio Code setup Install Visual Studio Code on your PC of choice and install the Remote - SSH and Dev Containers extensions. Use the “Remote-SSH: Connect to Host” command to initiate an SSH connection to your Windows desktop. On your very first attempt, you’ll likely receive a broken pipe error–when Visual Studio Code encounters a Windows SSH server, it expects a PowerShell prompt, not Bash. To fix this, use the remote.SSH.remotePlatform preference to map your Windows desktop’s hostname to “Linux”. You’ll also have to set the remote.SSH.localServerDownload preference to “off”, and install curl or wget inside your WSL machine. By default, Visual Studio Code attempts to copy resources to the remote host via scp, which will fail with this configuration, since the Windows OpenSSH server only provides dumb shell access. Once you’ve successfully established an SSH connection to the WSL machine, Visual Studio Code lets you browse files and run shell commands on it. You can even browse the Windows filesystem through the /mnt/c directory and, if you have the Docker extension installed, interact with the Docker daemon. But the real magic happens with the Dev Container extension–navigate to a directory with a .devcontainer/devcontainer.json definition, and Visual Studio Code will offer you the option to spin up and enter a development container, all over SSH. Step 4: Done! And there we have it. You get the killer features of GitHub Codespaces–templated environments, dedicated hardware, and remote access from anywhere on the Internet–for free, using a computer you probably already own. The major downside of this setup is that you must use the desktop version of Visual Studio Code on the client, which means that client must be running an officially supported version of Windows, Linux, or macOS. (Sorry–no iPads, Chromebooks, or smart refrigerators.) As of 2023, the remote development extensions cannot be used with VSCode.dev, nor with the official Visual Studio Code Server, nor with open-source derivatives like VSCodium, so for the moment, you have no real alternative here. But for me, that’s an insignificant price to pay for such a powerful, yet convenient, programming experience. My current laptop for on-the-go computing is a Lenovo Yoga Flex, which I really like; it’s thin, light, and quiet, and it gets amazing battery life. But like every laptop, it would not be so quiet or long-lasting if I instructed it to compile a very large codebase, or execute a very long test suite. Development containers hosted on my desktop give me the best of both worlds: a powerful mothership to work on and a comfortable client to type into. And since my laptop need only be a glorified netbook capable of running an Electron app, as opposed to a monster machine that can drag race its way through a few thousand unit tests, I’m free to skimp on my next laptop purchase, redirecting the savings toward my desktop for the best programming-plus-gaming machine for my dollar. I cannot stress how brilliant development containers are as a concept. They give me the power to switch between programming stacks at the push of a button–from Python 3 for maintaining Mailrise, to Python 2 for contributing patches to Apprise, to Node/TypeScript for working on TypeScriptToLua. Instead of burning hours setting up the Linux tooling for each context switch, I can check out, test, and ship a new PR in under an hour, which is exactly the kind of efficiency I need to be productive on the run. The experience remains impressively good over a mobile data connection, too–say, while on a city bus–thanks in large part to Visual Studio Code’s predictive terminal echoing, a feature that anticipates the characters that should appear on your terminal before they actually get transmitted back by the remote host. And since the remote connection consists primarily of shell commands, it doesn’t use that much data, either! I count just a couple of megabytes per hour of work. Given all these advantages, it’s no wonder GitHub migrated all their engineering teams to Codespaces. If you use Visual Studio Code as your IDE and your projects fit into development containers, it’s a no-brainer for you, too. And with my way, you won’t owe GitHub a cent, or a credit card number.]]></summary></entry><entry><title type="html">A Farewell to Arms: Why I’m Quitting Linux on the Desktop</title><link href="/2020/12/a-farewell-to-arms-why-im-quitting-linux-on-the-desktop/" rel="alternate" type="text/html" title="A Farewell to Arms: Why I’m Quitting Linux on the Desktop" /><published>2020-12-30T01:37:40-08:00</published><updated>2020-12-30T01:37:40-08:00</updated><id>/2020/12/a-farewell-to-arms-why-im-quitting-linux-on-the-desktop</id><content type="html" xml:base="/2020/12/a-farewell-to-arms-why-im-quitting-linux-on-the-desktop/"><![CDATA[<p>It goes without saying that 2020 has been the year of the inconceivable. And to top it all off, after daily driving Linux on my laptop for nearly a decade, I just switched back to Windows!</p>

<p>Let me explain—it’s not as if I’ve given up on the Penguin OS entirely. My servers and routers continue to run Linux, delivering funny cat pictures to myself and dispatching my own hot takes to the rest of the Internet. Without question, Linux is the perfect OS for those roles, being flexible, performant, reliable, and cheap. With LXD containers, I can spin up a fresh, isolated Linux system in seconds to try out a piece of software or run my own experimental code. And with Linux’s plethora of logical volume and filesystem options, I can build any conceivable kind of software-defined storage solution. My personal trio of LUKS, Bcache, and Btrfs, for example, allows me to keep my home server fully encrypted, with a 256GB SSD-backed cache and fully automated incremental backups.</p>

<p>This kind of flexibility would not be possible on any other OS, let alone at no cost whatsoever. But it was when I would close out my terminal window and switch back to my desktop that the appeal of Linux would begin to diminish—because if you spend any amount of time working in both “houses” of Linux, it becomes obvious which side receives the lion’s share of the community’s development efforts and resources. While server-side Linux is backed by the engineering might of Big Tech, from Red Hat to Novell to Microsoft to the NSA, desktop-side Linux is maintained largely by the goodwill of open-source volunteers. The Linux ecosystem, quite simply, resembles a tale of two cities.</p>

<p>Building and maintaining a fully open-source desktop environment, like KDE or GNOME, is no small feat, and I still harbor immense respect for the programmers, testers, and managers who donate hours of their time to keep these projects going. But even so, it’s clear to me that volunteer labor has its limits.</p>

<p>In short, here’s why I made the switch to Windows.</p>

<h2 id="linux-on-the-desktop-is-kind-of-slow-and-mediocre">Linux on the desktop is kind of slow and mediocre.</h2>

<p>Conventional wisdom holds that if you want to do your computing on a low-end system like a Windows XP laptop or Raspberry Pi, that installing and using a lightweight Linux distribution is your best bet. So, how can I justify my assessment? While it’s true that a stripped-down Linux install with something like LXDE or Openbox will feel “lighter” than a Windows shell and generally consume less resources, it is not a given that it will perform any faster, nor even that it will save any energy.</p>

<p>Take web browsing—in today’s digital landscape, I spend at least 90% of my time on the computer using some online application or service that runs in a web browser, which I always keep open and permanently pinned to my taskbar. So, as you can imagine, it’s quite critical that my web browsing experience be as zippy and responsive as possible. Unfortunately, a smooth web browsing experience continues to elude me on Linux. The Linux versions of Firefox and Chromium are noticeably sluggish compared to their Windows counterparts running on identical hardware; it only takes a few hours of uptime for the telltale signs of “browser rot” to emerge, from slow page rendering to irritating freezes to snowballing memory consumption numbers. Inevitably, the situation becomes untenable and I have to close and restart my browser—something I’m forced to do at least once, sometimes even multiple times per day. This has been a consistent issue throughout my decade-long journey with Linux, from Ubuntu to Arch to Fedora.</p>

<p>If I had to speculate, I’d hazard to guess that the Linux’s GPU acceleration is not up to the task and, in some manner, causes browser engines to exhibit degraded performance and persistent memory leakage. It’s well-known that graphics acceleration in Linux can be a complete mess to develop for, but it’s not like I’m playing with exotic hardware here—just the Intel HD Graphics chip that comes standard with every el cheapo laptop on the market. In the geeky, nuts-and-bolts side of the tech press, you hear all the time about how the Linux kernel is pushing the boundaries of computer graphics through GPU-accelerated virtual machines, optimizations for TensorFlow, and other applications that sound super useful on a server or mainframe. But all of that means diddly squat to Joe Average desktop user, who just wants a good experience browsing the web at 60 fps. Once again, solutions aplenty for server users, but crumbs for desktop users.</p>

<p>Power consumption is another one of Linux’s sticking points. Browsing on Linux is not just slower compared to browsing on Windows; it also tends to run your system hotter and spin up its fans more frequently. On a form factor and energy-constrained laptop, that matters, a lot. Playing a YouTube video? On Windows, web browsers can use GPU-accelerated video decoding, which maximizes energy efficiency. But on Linux, this is basically impossible, so your battery gets sapped like the fuel tank of a Hummer and your fans get pegged at Boeing 747-like decibel levels. And don’t get me started on the more powerful laptops with AMD and Nvidia graphics cards in on-the-fly switching configurations, which are basically not supported by Linux, period.</p>

<p>None of this is to place the blame squarely on the Linux community or Linus Torvalds. Look, I get it—It’s extremely difficult to program graphics drivers, particularly when the vendors conceal their secret sauce and developers must reverse-engineer their products. But when your operating system performs so miserably on modern hardware, you have to ask yourself whether using it is really a badge of honor—a manifestation of your “1337 Unix skills”—or a questionable alternative to the factory-provided operating system that supports everything out of the box, and does that operating system-type stuff pretty well.</p>

<h2 id="windows-is-an-impressive-product-these-days">Windows is an impressive product these days!</h2>

<p>I jumped ship to Ubuntu during the Windows Vista era. Remember what a debacle Vista was? The awful performance; the buggy drivers; the nanny-like User Account Control; that Vista bomb defusal skit on <em>The IT Crowd</em>?</p>

<p>As we all know, Windows 7 righted many of these wrongs, but I felt it was still a markedly inferior choice compared to using any Linux distribution. First of all, Windows Update <em>sucked</em>—waiting for it to work its magic entailed monk-like patience, and God help you if you had to reinstall Windows 7 from scratch and thereby install the complete catalog of updates released by Microsoft. Second, hardware autodetection also sucked. If you moved a Windows 7 installation to a new system, or made a major change to your hardware configuration, there was a pretty good chance that your system would stop booting entirely, forcing you to reinstall. Third, if you were a programmer, Linux was unquestionably the kind of environment you had to have, with software compilers and interpreters at the tips of your terminal-savvy fingers.</p>

<p>And then Windows 8 came out, which made the case for switching to Linux even stronger.</p>

<p>But fast forward to 2020, and Windows 10 is… actually a significantly improved and more usable operating system. Windows Update is speedy (okay, <em>maybe</em> not quite as zippy as it could be) and unobtrusive, and it downloads, installs, and updates hardware drivers automatically. Gimmicks that were once Linux exclusives, like multiple desktops and indexed file searching, come standard in the Windows 10 shell.</p>

<p>Not to mention, stuff <em>just works</em>. Like my Firefox browser, and my network printers, and the extremely useful circular scrolling motion on my Synaptics touchpad (which I believe was dropped from the Linux driver years ago). And have you seen the swanky new <a href="https://docs.microsoft.com/en-us/windows/terminal/">Windows Terminal</a> app? It’s easily on par with any desktop Linux console.</p>

<h2 id="everything-that-i-needed-on-linux-is-now-available-on-windows">Everything that I needed on Linux is now available on Windows.</h2>

<p>One of Linux’s killer features used to be the package manager in the form of APT, Yum, pacman, etc. It was so much easier to download and update software from a centrally managed repository rather than acquiring it individually from the Internet, especially when you consider that every program on Windows has to maintain its own background updater service—if it even has an automatic updater at all.</p>

<p>But now you can get that experience on Windows, too.</p>

<p>The Chocolatey package manager, which is an awesome project, lets you search, install, and update Windows software from a curated community repository—just as you can do with APT, Yum, pacman, etc., right down to the <code class="language-plaintext highlighter-rouge">choco install</code> and <code class="language-plaintext highlighter-rouge">choco upgrade</code> commands, which mirror their Linux counterparts.</p>

<p>To run many developer tools, like Ruby and Apache, you’ll still need a working copy of Linux. But now, those binaries can run on Windows, too, thanks to Microsoft’s Windows Subsystem for Linux initiative. WSL version 1 facilitates near-seamless interoperability between Linux binaries and the Windows kernel, filesystem, and network stack, which means I can run my developer tooling without any of the overhead of a full Linux virtual machine.</p>

<p>For me, this was the game changer that led me to contemplate switching back to Windows—being a Linux programmer is no longer synonymous with actually having to run Linux. WSL really does feel like magic, and I hope Microsoft upholds their promise to maintain it in spite of their current development focus on WSL version 2. (Version 2 swaps the novel system call translation employed by WSL version 1 for an ordinary virtual machine, making it significantly, in my opinion and in <a href="https://github.com/microsoft/WSL/discussions/4022">the opinions of many others</a>, less interesting.)</p>

<h2 id="conclusionet-tu-brute">Conclusion—Et tu, Brute?</h2>

<p>Linux, we’ve had some good times together. Like schooling Java-averse classmates in Data Structures 314, or pimping out the desktop with “1337 h4x0r” Conky monitors, or laughing in the face of Lenovo’s Superfish debacle.</p>

<p>But when I can accomplish my programming (and non-programming) work faster and more efficiently on Windows, that means it’s time to make the switch. It’s only the rational thing to do.</p>]]></content><author><name>Ryan Young</name></author><category term="tech" /><category term="linux" /><category term="windows" /><summary type="html"><![CDATA[It goes without saying that 2020 has been the year of the inconceivable. And to top it all off, after daily driving Linux on my laptop for nearly a decade, I just switched back to Windows! Let me explain—it’s not as if I’ve given up on the Penguin OS entirely. My servers and routers continue to run Linux, delivering funny cat pictures to myself and dispatching my own hot takes to the rest of the Internet. Without question, Linux is the perfect OS for those roles, being flexible, performant, reliable, and cheap. With LXD containers, I can spin up a fresh, isolated Linux system in seconds to try out a piece of software or run my own experimental code. And with Linux’s plethora of logical volume and filesystem options, I can build any conceivable kind of software-defined storage solution. My personal trio of LUKS, Bcache, and Btrfs, for example, allows me to keep my home server fully encrypted, with a 256GB SSD-backed cache and fully automated incremental backups. This kind of flexibility would not be possible on any other OS, let alone at no cost whatsoever. But it was when I would close out my terminal window and switch back to my desktop that the appeal of Linux would begin to diminish—because if you spend any amount of time working in both “houses” of Linux, it becomes obvious which side receives the lion’s share of the community’s development efforts and resources. While server-side Linux is backed by the engineering might of Big Tech, from Red Hat to Novell to Microsoft to the NSA, desktop-side Linux is maintained largely by the goodwill of open-source volunteers. The Linux ecosystem, quite simply, resembles a tale of two cities. Building and maintaining a fully open-source desktop environment, like KDE or GNOME, is no small feat, and I still harbor immense respect for the programmers, testers, and managers who donate hours of their time to keep these projects going. But even so, it’s clear to me that volunteer labor has its limits. In short, here’s why I made the switch to Windows. Linux on the desktop is kind of slow and mediocre. Conventional wisdom holds that if you want to do your computing on a low-end system like a Windows XP laptop or Raspberry Pi, that installing and using a lightweight Linux distribution is your best bet. So, how can I justify my assessment? While it’s true that a stripped-down Linux install with something like LXDE or Openbox will feel “lighter” than a Windows shell and generally consume less resources, it is not a given that it will perform any faster, nor even that it will save any energy. Take web browsing—in today’s digital landscape, I spend at least 90% of my time on the computer using some online application or service that runs in a web browser, which I always keep open and permanently pinned to my taskbar. So, as you can imagine, it’s quite critical that my web browsing experience be as zippy and responsive as possible. Unfortunately, a smooth web browsing experience continues to elude me on Linux. The Linux versions of Firefox and Chromium are noticeably sluggish compared to their Windows counterparts running on identical hardware; it only takes a few hours of uptime for the telltale signs of “browser rot” to emerge, from slow page rendering to irritating freezes to snowballing memory consumption numbers. Inevitably, the situation becomes untenable and I have to close and restart my browser—something I’m forced to do at least once, sometimes even multiple times per day. This has been a consistent issue throughout my decade-long journey with Linux, from Ubuntu to Arch to Fedora. If I had to speculate, I’d hazard to guess that the Linux’s GPU acceleration is not up to the task and, in some manner, causes browser engines to exhibit degraded performance and persistent memory leakage. It’s well-known that graphics acceleration in Linux can be a complete mess to develop for, but it’s not like I’m playing with exotic hardware here—just the Intel HD Graphics chip that comes standard with every el cheapo laptop on the market. In the geeky, nuts-and-bolts side of the tech press, you hear all the time about how the Linux kernel is pushing the boundaries of computer graphics through GPU-accelerated virtual machines, optimizations for TensorFlow, and other applications that sound super useful on a server or mainframe. But all of that means diddly squat to Joe Average desktop user, who just wants a good experience browsing the web at 60 fps. Once again, solutions aplenty for server users, but crumbs for desktop users. Power consumption is another one of Linux’s sticking points. Browsing on Linux is not just slower compared to browsing on Windows; it also tends to run your system hotter and spin up its fans more frequently. On a form factor and energy-constrained laptop, that matters, a lot. Playing a YouTube video? On Windows, web browsers can use GPU-accelerated video decoding, which maximizes energy efficiency. But on Linux, this is basically impossible, so your battery gets sapped like the fuel tank of a Hummer and your fans get pegged at Boeing 747-like decibel levels. And don’t get me started on the more powerful laptops with AMD and Nvidia graphics cards in on-the-fly switching configurations, which are basically not supported by Linux, period. None of this is to place the blame squarely on the Linux community or Linus Torvalds. Look, I get it—It’s extremely difficult to program graphics drivers, particularly when the vendors conceal their secret sauce and developers must reverse-engineer their products. But when your operating system performs so miserably on modern hardware, you have to ask yourself whether using it is really a badge of honor—a manifestation of your “1337 Unix skills”—or a questionable alternative to the factory-provided operating system that supports everything out of the box, and does that operating system-type stuff pretty well. Windows is an impressive product these days! I jumped ship to Ubuntu during the Windows Vista era. Remember what a debacle Vista was? The awful performance; the buggy drivers; the nanny-like User Account Control; that Vista bomb defusal skit on The IT Crowd? As we all know, Windows 7 righted many of these wrongs, but I felt it was still a markedly inferior choice compared to using any Linux distribution. First of all, Windows Update sucked—waiting for it to work its magic entailed monk-like patience, and God help you if you had to reinstall Windows 7 from scratch and thereby install the complete catalog of updates released by Microsoft. Second, hardware autodetection also sucked. If you moved a Windows 7 installation to a new system, or made a major change to your hardware configuration, there was a pretty good chance that your system would stop booting entirely, forcing you to reinstall. Third, if you were a programmer, Linux was unquestionably the kind of environment you had to have, with software compilers and interpreters at the tips of your terminal-savvy fingers. And then Windows 8 came out, which made the case for switching to Linux even stronger. But fast forward to 2020, and Windows 10 is… actually a significantly improved and more usable operating system. Windows Update is speedy (okay, maybe not quite as zippy as it could be) and unobtrusive, and it downloads, installs, and updates hardware drivers automatically. Gimmicks that were once Linux exclusives, like multiple desktops and indexed file searching, come standard in the Windows 10 shell. Not to mention, stuff just works. Like my Firefox browser, and my network printers, and the extremely useful circular scrolling motion on my Synaptics touchpad (which I believe was dropped from the Linux driver years ago). And have you seen the swanky new Windows Terminal app? It’s easily on par with any desktop Linux console. Everything that I needed on Linux is now available on Windows. One of Linux’s killer features used to be the package manager in the form of APT, Yum, pacman, etc. It was so much easier to download and update software from a centrally managed repository rather than acquiring it individually from the Internet, especially when you consider that every program on Windows has to maintain its own background updater service—if it even has an automatic updater at all. But now you can get that experience on Windows, too. The Chocolatey package manager, which is an awesome project, lets you search, install, and update Windows software from a curated community repository—just as you can do with APT, Yum, pacman, etc., right down to the choco install and choco upgrade commands, which mirror their Linux counterparts. To run many developer tools, like Ruby and Apache, you’ll still need a working copy of Linux. But now, those binaries can run on Windows, too, thanks to Microsoft’s Windows Subsystem for Linux initiative. WSL version 1 facilitates near-seamless interoperability between Linux binaries and the Windows kernel, filesystem, and network stack, which means I can run my developer tooling without any of the overhead of a full Linux virtual machine. For me, this was the game changer that led me to contemplate switching back to Windows—being a Linux programmer is no longer synonymous with actually having to run Linux. WSL really does feel like magic, and I hope Microsoft upholds their promise to maintain it in spite of their current development focus on WSL version 2. (Version 2 swaps the novel system call translation employed by WSL version 1 for an ordinary virtual machine, making it significantly, in my opinion and in the opinions of many others, less interesting.) Conclusion—Et tu, Brute? Linux, we’ve had some good times together. Like schooling Java-averse classmates in Data Structures 314, or pimping out the desktop with “1337 h4x0r” Conky monitors, or laughing in the face of Lenovo’s Superfish debacle. But when I can accomplish my programming (and non-programming) work faster and more efficiently on Windows, that means it’s time to make the switch. It’s only the rational thing to do.]]></summary></entry><entry><title type="html">How to Fix Grandma’s Network on Verizon FiOS</title><link href="/2019/12/fix-grandmas-network-on-verizon-fios/" rel="alternate" type="text/html" title="How to Fix Grandma’s Network on Verizon FiOS" /><published>2019-12-13T19:40:38-08:00</published><updated>2019-12-13T19:40:38-08:00</updated><id>/2019/12/fix-grandmas-network-on-verizon-fios</id><content type="html" xml:base="/2019/12/fix-grandmas-network-on-verizon-fios/"><![CDATA[<p>In my family, the person with the fastest Internet connection is… Grandma, a Vietnam War refugee who has never used a computer in her life. This is by virtue of her residence on a main road in the great state of Delaware, which gets fiber TV and Internet service through Verizon FiOS. She subscribes to the cheapest Internet plan so that the grandkids can tap away at their tablets during family gatherings. And on FiOS, the “lowest tier” is a blazing-fast symmetric connection: 100 Mbps down, 100 Mbps up.</p>

<p>It really isn’t fair, is it?</p>

<p>Grandma’s 20th-century tract home, like Grandma herself, was thrust only reluctantly into the digital age. It has no data cabling whatsoever besides two landlines and two coax ports, which, naturally, are both located on the extreme corners of the house—the worst possible positions to place a Wi-Fi access point. So for many years, the family ISP shitbox sat on one end of the house or the other, saddling the opposite side with all of the classic symptoms of crappy Wi-Fi coverage: buffering videos, sluggish webpages, frequent disassociations, and frustrated kids. This fall, I discovered one of my uncles (bless his heart) had attempted to cover the dead spot with a cheap access point and powerline networking kit from TP-Link. Immediately, my heart sank—powerline networking is almost always bad news. I marshaled together two laptops and ran iperf to test the performance of the link. It was a bottleneck… to put it mildly. On a network with a 100 Mbps uplink, the powerline connection achieved a whopping 12 Mbps.</p>

<figure class="figure  figure--center"><img class="image" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Actiontec_MI424WR_Verizon_front.png/283px-Actiontec_MI424WR_Verizon_front.png" alt="The prehistoric MI-424WR is a dime a dozen on the Philadelphia-area Craigslist." width="" height="" /><figcaption class="caption">The prehistoric MI-424WR is a dime a dozen on the Philadelphia-area Craigslist.</figcaption></figure>

<p>Right then and there, I decided it was time to blow up Grandma’s home network and start over. It had to go, <em>all of it</em>—the extra AP; the powerline adapters; even the Verizon router itself, a venerable Actiontec MI-424WR that hasn’t received a security patch in over a decade. My plan was to junk everything and install a whole-home Wi-Fi mesh system using Ethernet-over-coax (MoCA) technology for reliable backhaul. (A cross between “option 9” and “option 10,” for those of you who made their way here from DSL Reports’ <a href="https://www.dslreports.com/faq/verizonfios/3.1_General_Networking">FiOS guide</a>.) We’re talking 802.11ac, dual-band, wired backbone, baby. At first, I set my sights on Google Wifi, but I found the price tag of Linksys Velop—the economy dual-band model can be had in 2-packs for just $100-150 total—a little more palatable. I wasn’t worried about cheaping out because, thanks to the MoCA backbone, I wouldn’t be relying on the performance (or lack thereof) of Velop’s wireless repeating.</p>

<p>I consider Belkin a third-rate brand, but I have to hand it to them for the job they’ve done on their Velop product. For example, if you run a home network with multiple access points, it’s important that they support roaming assistance—the 802.11k, v, and r standards—without which Wi-Fi clients tend to “stick” to the first AP they see and refuse to switch to another station, even if the-signal-quality-is-garbage-and-another-AP-is-_right-there_-so-why-the-hell-wouldn’t-you-switch-god-damnit. In the consumer space, basically nothing supports roaming assistance except for whole-home mesh systems—including, of course, Linksys Velop. Velop also autodetects the presence of an Ethernet connection between its nodes, and makes use of it for backhaul. (Some contemporary mesh systems, unbelievably, lack Ethernet ports altogether!) And bonus points for Velop’s online management interface; it’s refreshing to be able to manage a network without installing yet another smartphone app.</p>

<p>For my MoCA adapters, I cheaped out and bought a pair of <a href="https://www.actiontec.com/products/home-networking/wcb3000n/">Actiontec WCB3000N</a>‘s, which regularly go for $20 each, used. Testing with iperf clocked their maximum speed at about 100 Mbps. (The newest stuff on the market can exceed gigabit speeds, but I couldn’t justify paying triple the cost for speeds nobody in Grandma’s house would ever need or use.) Each WCB3000N comes with a pair of very outdated 802.11n Wi-Fi radios, the idea being that the device can act as a coax-backed “Wi-Fi extender.” Um, thanks, but no thanks; I’d just like the MoCA part, please. <em>But what’s this? No web interface option to disable Wi-Fi? WTF?!</em> Hilariously, there is indeed a control—it’s just hidden by a little bit of CSS.</p>

<figure class="figure  figure--center"><img class="image" src="/assets/posts/wp-uploads/2019/12/FiOS_SpectrumWCB3000.png" alt="And for my next trick, I shall make the &#8220;Wireless Radio&#8221; checkbox disappear!" width="" height="" /><figcaption class="caption">And for my next trick, I shall make the &#8220;Wireless Radio&#8221; checkbox disappear!</figcaption></figure>

<p>You see, WCB3000N’s are so cheap because the market is flooded with examples that were handed out by ISP’s. Mine came from Spectrum, who apparently removed the ability to disable Wi-Fi to make their product “idiot-proof.” One <a href="https://github.com/Saturn49/wecb">brave soul</a> on GitHub got the GPL source to compile and released a custom build that restores the missing control. Unfortunately, there’s one bug still unresolved: The setting to disable the 2.4GHz radio doesn’t stick after a reboot. Pooey. I turned off SSID broadcast on that network and called it a day.</p>

<p>There are some special considerations to mind when working on a FiOS network. First, the cable boxes require an IP connection to download TV guide data from Verizon. Although some models (including the one my Grandma has) have Ethernet ports, they are not activated, and the connection has to be made using their builtin MoCA adapters. Fortunately, the boxes can link with any commodity MoCA adapter—including the WCB3000N I was using to network the Velops. Second, the remote DVR and on-screen caller ID features won’t work if a Verizon router isn’t the gateway. In my case, the loss of neither of these mattered to Grandma…</p>

<p>Another complication is the connection from the router (my base Velop node) to the Optical Network Terminal on the side of the house, which can be made via either MoCA or Ethernet. Contemporary installs use Ethernet, but older FiOS installs—including, you guessed it, Grandma’s—used coax, probably so the installers could spare themselves the trouble of running a new Ethernet line. The coax connector on a FiOS-branded router conceals two MoCA adapters: the “LAN-side” one, which runs on channel D1 and connects to the cable boxes, and the “WAN-side” one, which runs on the less-common channel C4 and connects to the ONT. The use of differing frequencies keeps both MoCA network segments logically separate.</p>

<table>
  <thead>
    <tr>
      <th>Network Segment</th>
      <th>MoCA Frequency</th>
      <th>Connected Devices</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>WAN</td>
      <td>C4</td>
      <td>Router; ONT</td>
    </tr>
    <tr>
      <td>LAN</td>
      <td>D1</td>
      <td>Router; cable boxes; Wi-Fi; Ethernet</td>
    </tr>
  </tbody>
</table>

<p>Running a new Ethernet line wasn’t an option—Grandma would’ve strangled me if I broke out the drill and started punching holes in her precious house. So, I needed a MoCA adapter that could operate on channel C4 and talk to the ONT. Turns out these adapters have gone nearly extinct! The <a href="https://www.ebay.com/p/15015261176">Arris MEB1100</a>, which Verizon distributes to FiOS customers, seems to be the only one still in production. But I wondered if it was possible to dodge this purchase by placing Grandma’s existing FiOS router into bridge mode. The Actiontec web interface has no obvious option to do this; but as it turns out, it <em>is</em> indeed possible!</p>

<p>The key is the “Network Connections” screen, which allows you to modify the router’s internal network topology to your heart’s content. You can accomplish a bridging configuration by detaching the “Ethernet/Coax” interface from the “Network” bridge and bridging it with the “Broadband Connection” interface. Unfortunately, if your model lacks the ability to separate the Ethernet and coax interfaces, you’ll have to disable the LAN-side MoCA adapter.</p>

<figure class="figure  figure--center"><img class="image" src="https://web.archive.org/web/20200212202803if_/https://vrzn.i.lithium.com/t5/image/serverpage/image-id/13739iBA528BD6ECE93A8E" alt="(A screenshot found on Google Images. Not my actual configuration!)" width="" height="" /><figcaption class="caption">(A screenshot found on Google Images. Not my actual configuration!)</figcaption></figure>

<p>By doing this, you’ll lose all access to the web interface except through the Wi-Fi hotspot, which will become its own, isolated network segment. Just set the SSID to something unique, like <code class="language-plaintext highlighter-rouge">MI424WR_Admin</code>, and use the same WPA password printed on the unit so that it’s easy to remember in the event you need to access the configuration screen again. (It’s not a security concern to keep an Actiontec router in service like this, because the web interface is not accessible from the Internet—or even your own LAN.) Then, when you plug the WAN port of your own router into one of the LAN ports on the Actiontec, your router will receive a public IP address, and you’ll be off to the races.</p>

<p>So, several equipment overhauls and a few coax splitters later, Grandma’s network went from this:</p>

<p><img src="/assets/posts/wp-uploads/2019/12/FiOS_Ba1.png" alt="" /></p>

<p>To <em>this</em>:</p>

<p><img src="/assets/posts/wp-uploads/2019/12/FiOS_Ba2.png" alt="" /></p>

<p>Did I over-engineer the crap out of it? Probably. But at least you can start a download anywhere in Grandma’s house and get the full 100 Mbps.</p>]]></content><author><name>Ryan Young</name></author><category term="tech" /><category term="internet" /><category term="isp" /><category term="networking" /><summary type="html"><![CDATA[In my family, the person with the fastest Internet connection is… Grandma, a Vietnam War refugee who has never used a computer in her life. This is by virtue of her residence on a main road in the great state of Delaware, which gets fiber TV and Internet service through Verizon FiOS. She subscribes to the cheapest Internet plan so that the grandkids can tap away at their tablets during family gatherings. And on FiOS, the “lowest tier” is a blazing-fast symmetric connection: 100 Mbps down, 100 Mbps up. It really isn’t fair, is it? Grandma’s 20th-century tract home, like Grandma herself, was thrust only reluctantly into the digital age. It has no data cabling whatsoever besides two landlines and two coax ports, which, naturally, are both located on the extreme corners of the house—the worst possible positions to place a Wi-Fi access point. So for many years, the family ISP shitbox sat on one end of the house or the other, saddling the opposite side with all of the classic symptoms of crappy Wi-Fi coverage: buffering videos, sluggish webpages, frequent disassociations, and frustrated kids. This fall, I discovered one of my uncles (bless his heart) had attempted to cover the dead spot with a cheap access point and powerline networking kit from TP-Link. Immediately, my heart sank—powerline networking is almost always bad news. I marshaled together two laptops and ran iperf to test the performance of the link. It was a bottleneck… to put it mildly. On a network with a 100 Mbps uplink, the powerline connection achieved a whopping 12 Mbps. The prehistoric MI-424WR is a dime a dozen on the Philadelphia-area Craigslist. Right then and there, I decided it was time to blow up Grandma’s home network and start over. It had to go, all of it—the extra AP; the powerline adapters; even the Verizon router itself, a venerable Actiontec MI-424WR that hasn’t received a security patch in over a decade. My plan was to junk everything and install a whole-home Wi-Fi mesh system using Ethernet-over-coax (MoCA) technology for reliable backhaul. (A cross between “option 9” and “option 10,” for those of you who made their way here from DSL Reports’ FiOS guide.) We’re talking 802.11ac, dual-band, wired backbone, baby. At first, I set my sights on Google Wifi, but I found the price tag of Linksys Velop—the economy dual-band model can be had in 2-packs for just $100-150 total—a little more palatable. I wasn’t worried about cheaping out because, thanks to the MoCA backbone, I wouldn’t be relying on the performance (or lack thereof) of Velop’s wireless repeating. I consider Belkin a third-rate brand, but I have to hand it to them for the job they’ve done on their Velop product. For example, if you run a home network with multiple access points, it’s important that they support roaming assistance—the 802.11k, v, and r standards—without which Wi-Fi clients tend to “stick” to the first AP they see and refuse to switch to another station, even if the-signal-quality-is-garbage-and-another-AP-is-_right-there_-so-why-the-hell-wouldn’t-you-switch-god-damnit. In the consumer space, basically nothing supports roaming assistance except for whole-home mesh systems—including, of course, Linksys Velop. Velop also autodetects the presence of an Ethernet connection between its nodes, and makes use of it for backhaul. (Some contemporary mesh systems, unbelievably, lack Ethernet ports altogether!) And bonus points for Velop’s online management interface; it’s refreshing to be able to manage a network without installing yet another smartphone app. For my MoCA adapters, I cheaped out and bought a pair of Actiontec WCB3000N‘s, which regularly go for $20 each, used. Testing with iperf clocked their maximum speed at about 100 Mbps. (The newest stuff on the market can exceed gigabit speeds, but I couldn’t justify paying triple the cost for speeds nobody in Grandma’s house would ever need or use.) Each WCB3000N comes with a pair of very outdated 802.11n Wi-Fi radios, the idea being that the device can act as a coax-backed “Wi-Fi extender.” Um, thanks, but no thanks; I’d just like the MoCA part, please. But what’s this? No web interface option to disable Wi-Fi? WTF?! Hilariously, there is indeed a control—it’s just hidden by a little bit of CSS. And for my next trick, I shall make the &#8220;Wireless Radio&#8221; checkbox disappear! You see, WCB3000N’s are so cheap because the market is flooded with examples that were handed out by ISP’s. Mine came from Spectrum, who apparently removed the ability to disable Wi-Fi to make their product “idiot-proof.” One brave soul on GitHub got the GPL source to compile and released a custom build that restores the missing control. Unfortunately, there’s one bug still unresolved: The setting to disable the 2.4GHz radio doesn’t stick after a reboot. Pooey. I turned off SSID broadcast on that network and called it a day. There are some special considerations to mind when working on a FiOS network. First, the cable boxes require an IP connection to download TV guide data from Verizon. Although some models (including the one my Grandma has) have Ethernet ports, they are not activated, and the connection has to be made using their builtin MoCA adapters. Fortunately, the boxes can link with any commodity MoCA adapter—including the WCB3000N I was using to network the Velops. Second, the remote DVR and on-screen caller ID features won’t work if a Verizon router isn’t the gateway. In my case, the loss of neither of these mattered to Grandma… Another complication is the connection from the router (my base Velop node) to the Optical Network Terminal on the side of the house, which can be made via either MoCA or Ethernet. Contemporary installs use Ethernet, but older FiOS installs—including, you guessed it, Grandma’s—used coax, probably so the installers could spare themselves the trouble of running a new Ethernet line. The coax connector on a FiOS-branded router conceals two MoCA adapters: the “LAN-side” one, which runs on channel D1 and connects to the cable boxes, and the “WAN-side” one, which runs on the less-common channel C4 and connects to the ONT. The use of differing frequencies keeps both MoCA network segments logically separate. Network Segment MoCA Frequency Connected Devices WAN C4 Router; ONT LAN D1 Router; cable boxes; Wi-Fi; Ethernet Running a new Ethernet line wasn’t an option—Grandma would’ve strangled me if I broke out the drill and started punching holes in her precious house. So, I needed a MoCA adapter that could operate on channel C4 and talk to the ONT. Turns out these adapters have gone nearly extinct! The Arris MEB1100, which Verizon distributes to FiOS customers, seems to be the only one still in production. But I wondered if it was possible to dodge this purchase by placing Grandma’s existing FiOS router into bridge mode. The Actiontec web interface has no obvious option to do this; but as it turns out, it is indeed possible! The key is the “Network Connections” screen, which allows you to modify the router’s internal network topology to your heart’s content. You can accomplish a bridging configuration by detaching the “Ethernet/Coax” interface from the “Network” bridge and bridging it with the “Broadband Connection” interface. Unfortunately, if your model lacks the ability to separate the Ethernet and coax interfaces, you’ll have to disable the LAN-side MoCA adapter. (A screenshot found on Google Images. Not my actual configuration!) By doing this, you’ll lose all access to the web interface except through the Wi-Fi hotspot, which will become its own, isolated network segment. Just set the SSID to something unique, like MI424WR_Admin, and use the same WPA password printed on the unit so that it’s easy to remember in the event you need to access the configuration screen again. (It’s not a security concern to keep an Actiontec router in service like this, because the web interface is not accessible from the Internet—or even your own LAN.) Then, when you plug the WAN port of your own router into one of the LAN ports on the Actiontec, your router will receive a public IP address, and you’ll be off to the races. So, several equipment overhauls and a few coax splitters later, Grandma’s network went from this: To this: Did I over-engineer the crap out of it? Probably. But at least you can start a download anywhere in Grandma’s house and get the full 100 Mbps.]]></summary></entry><entry><title type="html">The 30-Second WebRTC Guide</title><link href="/2019/11/the-30-second-webrtc-guide/" rel="alternate" type="text/html" title="The 30-Second WebRTC Guide" /><published>2019-11-25T17:16:43-08:00</published><updated>2019-11-25T17:16:43-08:00</updated><id>/2019/11/the-30-second-webrtc-guide</id><content type="html" xml:base="/2019/11/the-30-second-webrtc-guide/"><![CDATA[<p>(<em>Web technology changes fast! Mind the date this post was written, which was November 2019.)</em></p>

<p>I get the feeling nobody uses WebRTC in the real world, since all of the tutorials use the same toy examples that don’t involve any actual network connectivity. That’s a shame, because WebRTC makes peer-to-peer communication a cakewalk. Somewhere in our imaginations, there’s a whole category of decentralized web apps, just waiting to get written!</p>

<p>Anyway, this post serves as a quick, practical guide to WebRTC. The first thing to realize is that it’s not just another web API that’s ready to go out of the box—WebRTC requires three distinct services to work its magic. Fortunately, the browser handles much of the communication behind the scenes, so you don’t need to worry about all of the nitty-gritty details.<figure class="wp-block-image size-large"></figure></p>

<figure class="figure  figure--center"><img class="image" src="/assets/posts/wp-uploads/2019/11/rtc_diagram.png" alt="A network diagram illustrating the relationships between signalling, STUN, and TURN servers and browsers." width="1285" height="695" /><figcaption class="caption"><p>The relationships between browsers and servers in WebRTC. Diagram courtesy of <a href="https://draw.io">draw.io</a>.</p>
</figcaption></figure>

<pre class="wp-block-code"><code>let me = { isInitiatingEnd: () =&gt; { ... },
           sendToOtherEnd: (type, data) =&gt; { ... } };</code></pre>

<p>To use WebRTC, you need some kind of out-of-band signalling system—in other words, a middleman—to deliver messages between the browsers. This is how they exchange the networking information necessary to negotiate a direct connection. Obviously, if they could deliver it directly, then they would have no need for WebRTC!</p>

<p>The design of the signalling system itself is left <em>entirely</em> up to you. Choose any technology you please—WebSocket, QR code, email, carrier pigeon. As we will see, WebRTC provides the necessary hooks to abstract over the underlying technology.</p>

<pre class="wp-block-code"><code>const STUN_SERVERS = { urls: ["stun:stun.l.google.com:19302"] },
      TURN_SERVERS = { urls: "stun:stun.example.com", username: ..., credential: ... };
let rtc = new RTCPeerConnection({ iceServers: [STUN_SERVERS, TURN_SERVERS]});</code></pre>

<p>If you expect your WebRTC session to traverse different networks, your clients will also need access to a <strong>Session Traversal Utilities for NAT</strong> (STUN) server. This is a service that informs browsers of their public IP address and port number, which can only be determined from a host on the public Internet. (STUN servers consume very little resources, so there are many that are freely available.)</p>

<p>Sometimes, despite the browsers’ best efforts, the network topology is too restrictive to achieve a direct connection. When this happens, WebRTC can fallback to a <strong>Traversal Using Relays around NAT</strong> (TURN) server, which is another middleman that can forward network traffic between clients. It’s like your signalling server, except it uses a standardized protocol explicitly designed for high-bandwidth streams. The more clients need such a middleman, the more bandwidth the TURN server will consume; therefore, if you want one, you will most likely need to run your own.</p>

<pre class="wp-block-code"><code>if (me.isInitiatingEnd())
        rtc.addEventListener("negotiationneeded", async (event) =&gt; {
                await sdpOffer = await rtc.createOffer();
                await rtc.setLocalDescription(sdpOffer);
                me.sendToOtherEnd("SDP-OFFER", sdpOffer);
        });
rtc.addEventListener("icecandidate", async (event) =&gt; {
        if (event.candidate)
                me.sendToOtherEnd("ICE-CAND", event.candidate);
});

me.receiveFromOtherEnd = async (type, data) =&gt; {
        switch (type) {
        case "SDP-OFFER":
                await rtc.setRemoteDescription(data);
                const sdpAnswer = await rtc.createAnswer();
                await rtc.setLocalDescription(sdpAnswer);
                me.sendToOtherEnd("SDP-ANSWER", sdpAnswer);
                break;
        case "SDP-ANSWER":
                await rtc.setRemoteDescription(data);
                break;
        case "ICE-CAND":
                await rtc.addIceCandidate(data);
                break;
        }
};</code></pre>

<p>Okay, this is the big one—here, the browsers use your signalling system to perform a two-phase pairing operation. First, in the <strong>Session Description Protocol</strong> (SDP) phase, they share information about audio, video, and data streams and their corresponding metadata; second, in the <strong>Interactive Connectivity Establishment</strong> (ICE) phase, they exchange IP addresses and port numbers and attempt to punch holes in each other’s firewalls.</p>

<p>WebRTC provides the <code class="language-plaintext highlighter-rouge">negotiationneeded</code> and <code class="language-plaintext highlighter-rouge">icecandidate</code> events to abstract over your signalling system. The <code class="language-plaintext highlighter-rouge">RTCPeerConnection</code> object fires these events whenever the browser needs to exchange SDP or ICE information (respectively), which can happen multiple times over the course of a WebRTC session as network conditions change.</p>

<p>Only the side that initiates the connection need be concerned with <code class="language-plaintext highlighter-rouge">negotiationneeded</code>. There’s a specific protocol both sides need to follow when responding to these events, or to messages from each other—it’s best to let the code speak for itself.</p>

<pre class="wp-block-code"><code>let dataChannel = rtc.createDataChannel("data", { negotiated: true, id: 0 });
dataChannel.addEventListener("open", (event) =&gt; { ... });</code></pre>

<p>Finally, set up your media and data streams. (For data channels, you can get away with a <code class="language-plaintext highlighter-rouge">negotiated</code> opening, which means the stream is pre-programmed on both ends and doesn’t require another handshake.) Wait for any <code class="language-plaintext highlighter-rouge">open</code> events to be fired.</p>

<p>You’re all done!</p>]]></content><author><name>Ryan Young</name></author><category term="tech" /><summary type="html"><![CDATA[(Web technology changes fast! Mind the date this post was written, which was November 2019.) I get the feeling nobody uses WebRTC in the real world, since all of the tutorials use the same toy examples that don’t involve any actual network connectivity. That’s a shame, because WebRTC makes peer-to-peer communication a cakewalk. Somewhere in our imaginations, there’s a whole category of decentralized web apps, just waiting to get written! Anyway, this post serves as a quick, practical guide to WebRTC. The first thing to realize is that it’s not just another web API that’s ready to go out of the box—WebRTC requires three distinct services to work its magic. Fortunately, the browser handles much of the communication behind the scenes, so you don’t need to worry about all of the nitty-gritty details. The relationships between browsers and servers in WebRTC. Diagram courtesy of draw.io. let me = { isInitiatingEnd: () =&gt; { ... }, sendToOtherEnd: (type, data) =&gt; { ... } }; To use WebRTC, you need some kind of out-of-band signalling system—in other words, a middleman—to deliver messages between the browsers. This is how they exchange the networking information necessary to negotiate a direct connection. Obviously, if they could deliver it directly, then they would have no need for WebRTC! The design of the signalling system itself is left entirely up to you. Choose any technology you please—WebSocket, QR code, email, carrier pigeon. As we will see, WebRTC provides the necessary hooks to abstract over the underlying technology. const STUN_SERVERS = { urls: ["stun:stun.l.google.com:19302"] }, TURN_SERVERS = { urls: "stun:stun.example.com", username: ..., credential: ... }; let rtc = new RTCPeerConnection({ iceServers: [STUN_SERVERS, TURN_SERVERS]}); If you expect your WebRTC session to traverse different networks, your clients will also need access to a Session Traversal Utilities for NAT (STUN) server. This is a service that informs browsers of their public IP address and port number, which can only be determined from a host on the public Internet. (STUN servers consume very little resources, so there are many that are freely available.) Sometimes, despite the browsers’ best efforts, the network topology is too restrictive to achieve a direct connection. When this happens, WebRTC can fallback to a Traversal Using Relays around NAT (TURN) server, which is another middleman that can forward network traffic between clients. It’s like your signalling server, except it uses a standardized protocol explicitly designed for high-bandwidth streams. The more clients need such a middleman, the more bandwidth the TURN server will consume; therefore, if you want one, you will most likely need to run your own. if (me.isInitiatingEnd()) rtc.addEventListener("negotiationneeded", async (event) =&gt; { await sdpOffer = await rtc.createOffer(); await rtc.setLocalDescription(sdpOffer); me.sendToOtherEnd("SDP-OFFER", sdpOffer); }); rtc.addEventListener("icecandidate", async (event) =&gt; { if (event.candidate) me.sendToOtherEnd("ICE-CAND", event.candidate); }); me.receiveFromOtherEnd = async (type, data) =&gt; { switch (type) { case "SDP-OFFER": await rtc.setRemoteDescription(data); const sdpAnswer = await rtc.createAnswer(); await rtc.setLocalDescription(sdpAnswer); me.sendToOtherEnd("SDP-ANSWER", sdpAnswer); break; case "SDP-ANSWER": await rtc.setRemoteDescription(data); break; case "ICE-CAND": await rtc.addIceCandidate(data); break; } }; Okay, this is the big one—here, the browsers use your signalling system to perform a two-phase pairing operation. First, in the Session Description Protocol (SDP) phase, they share information about audio, video, and data streams and their corresponding metadata; second, in the Interactive Connectivity Establishment (ICE) phase, they exchange IP addresses and port numbers and attempt to punch holes in each other’s firewalls. WebRTC provides the negotiationneeded and icecandidate events to abstract over your signalling system. The RTCPeerConnection object fires these events whenever the browser needs to exchange SDP or ICE information (respectively), which can happen multiple times over the course of a WebRTC session as network conditions change. Only the side that initiates the connection need be concerned with negotiationneeded. There’s a specific protocol both sides need to follow when responding to these events, or to messages from each other—it’s best to let the code speak for itself. let dataChannel = rtc.createDataChannel("data", { negotiated: true, id: 0 }); dataChannel.addEventListener("open", (event) =&gt; { ... }); Finally, set up your media and data streams. (For data channels, you can get away with a negotiated opening, which means the stream is pre-programmed on both ends and doesn’t require another handshake.) Wait for any open events to be fired. You’re all done!]]></summary></entry><entry><title type="html">For Alon Levy, the Future Is Not Retro, but It Might Still Surprise</title><link href="/2019/11/the-future-is-not-retro-but-it-might-surprise/" rel="alternate" type="text/html" title="For Alon Levy, the Future Is Not Retro, but It Might Still Surprise" /><published>2019-11-07T16:00:33-08:00</published><updated>2019-11-07T16:00:33-08:00</updated><id>/2019/11/the-future-is-not-retro-but-it-might-surprise</id><content type="html" xml:base="/2019/11/the-future-is-not-retro-but-it-might-surprise/"><![CDATA[<p>“The Future is not Retro,” declares a recent <em>Pedestrian Observation</em> <a href="https://pedestrianobservations.com/2019/09/08/the-future-is-not-retro/">post</a> that has been my metaphorical pea under the mattress for the last several weeks. Its tone is so bombastic and cavalier that the piece is difficult to take entirely seriously—much like another Alon Levy hot <a href="https://pedestrianobservations.com/2019/05/23/the-ntsb-wants-american-trains-to-be-less-safe/">take</a>, “The NTSB Wants American Trains to Be Less Safe,” or that infamous Market Urbanism <a href="https://twitter.com/marketurbanism/status/1117851199720574982">tweet</a> about rebuilding Notre Dame as a contemporary mixed-use skyscraper—but as an indicator of the way we urban planning nerds think and talk about cities, it should be taken very seriously, indeed.</p>

<p>Much of the post details Levy’s vision for the future of urban development, which goes something like this: In a few decades from now, the cities of the West—the largest of them, anyway—will look increasingly like the crowded, transit-oriented metropolises of East Asia. They will be crisscrossed by driver-less metros, whose stations will be surrounded by clusters of high-rise offices and residential towers, and be linked together by high-speed rail for zero-emissions, long-distance travel. Vacations will entail a bullet train ride to San Francisco or Miami instead of a road trip to some bygone natural wonder. (As for mid-to-lower tier cities, and the National Park Service, the outlook is rather grim—Levy fully expects those to shrivel up and die.)</p>

<!--more-->

<figure class="figure  figure--center"><img class="image" src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Bangkok_Ratchathewi_Skytrain_view_of_a_traffic_jam.JPG/1024px-Bangkok_Ratchathewi_Skytrain_view_of_a_traffic_jam.JPG" alt="The city of the future, today, in Bangkok. (Credit: [Wikimedia](https://commons.wikimedia.org/wiki/File:Bangkok_Ratchathewi_Skytrain_view_of_a_traffic_jam.JPG))" width="" height="" /><figcaption class="caption"><p>The city of the future, today, in Bangkok. (Credit: <a href="https://commons.wikimedia.org/wiki/File:Bangkok_Ratchathewi_Skytrain_view_of_a_traffic_jam.JPG">Wikimedia</a>)</p>
</figcaption></figure>

<p>As is typical of his style, Levy, the all-knowing technocrat, boasts his thesis with an aura of smug confidence. Skyscrapers and trains are imperative, he insists, because of the economic value unlocked by greater density; the moral imperative to avert catastrophic climate change; and the cold, impersonal hand of urban economics, which points the way toward a concentrated and agglomerated future. I, like Levy, see the virtues of dense urban development and mass transit; I believe that in many ways, the world would be better off under his “regime of green prosperity.” But good government is about minding the individual needs of millions of citizens, not molding them into cartoon caricatures so you can run them over with the bulldozer. You would never know that from Levy’s reading of the historical record, which is reason to give anyone pause.</p>

<p>In “The Future is not Retro,” he compresses all urban history into an interplay between two population pools—those who live in central cities, and those who do not—whose division of the population, like a seesaw, has teetered back and forth as economic fads come and go. <em>Progress</em> is the recurring theme here. At any given moment, there are those on the <em>right</em> side of history (whom Levy does not name, but we might call them <em>urban progressives</em>) and those on the <em>wrong</em> side of history, the “traditionalists” with their ignorant, selfish, “retro attitudes” Levy reserves so much of his contempt for. It should be obvious, <em>I hope</em>, that this is an absurdly reductive lens through which to view an entire metropolitan area and all of its facets, such as industry, commerce, social networks, transportation networks, political relationships, and power dynamics, to name just a few.</p>

<p>So, for example, the exclusion of the inner-city poor from the prosperity of the suburbs due to restrictive zoning—<em>and</em> racial covenants, <em>and</em> redlining, <em>and</em> block-busting, <em>and</em> other forms of racially motivated housing discrimination—Levy codes as “early 20th century urbanites [adapting] to the reality of suburbanization a generation later.” It’s his choice to expunge all social and political struggle from the historical record, but that does not prove that it didn’t happen or that it doesn’t matter. Picture a Bronx resident in the 1950s, unable to obtain a mortgage for a suburban home because the bank doesn’t take kindly to the color of his skin, and a Beverly Hills millionaire in 2019, lobbying against an affordable housing development because of the shadow it will cast over his lawn. If there is a clear and pertinent difference between these two retrograde “traditionalists,” it eludes Levy.</p>

<p>So it’s little surprise Levy views 21st-century urbanization as yet another conflict in the same old story of the Progressives versus the Luddites. In his mind, it’s black and white: Dense urban development is culturally diverse and the way of the future, while the suburbs are outmoded, exclusive, and racist—full of “Basil Fawlty types who either can’t or won’t adapt to a world that has little use for their prejudices,” with “no racial minorities or working-class people within sight,” as he puts it. If Levy sincerely believes this is an accurate representation of our urban geography, I suggest he go for a walk in a 21st-century suburb—because in 2019, you can have your cake and eat it, too: a detached house with a yard, <em>and</em> racial minorities and working-class people!</p>

<p>It’s true, <a href="https://www.brookings.edu/assets/posts/wp-uploads/2016/06/0504_census_ethnicity_frey.pdf">according</a> to Brookings: “Minorities represent 35 percent of suburban residents, similar to their share of overall U.S. population.” In 1980, the sleepy exurb of Anaheim, California was just 17 percent Hispanic; by 2015, that percentage had <a href="https://www.latimes.com/projects/la-fi-disney-anaheim-city-council/">skyrocketed</a> to 53 percent. In 1970, 25 percent of all African Americans in metro Washington, D.C <a href="https://www.citylab.com/life/2019/11/suburbs-demographic-trends-population-data-immigration/601546/">lived</a> in the suburbs; by 2000, 78 percent of them did. Have the new wave of minority suburbanites been priced out of hopping central cities (by insufficient housing supply, no doubt), but would otherwise prefer greener, hipper apartment living? Some of them, surely, but many are proud of the communities they’ve made their own. In the suburbs of Atlanta, they <a href="https://www.governing.com/topics/transportation-infrastructure/gov-Atlanta-Gwinnett-Transit-Expansion-failed.html">voted down</a> a rapid transit expansion because they loathed a tax increase and did not want to import “city problems.” Some of them (gasp!) even <a href="https://www.nytimes.com/2018/07/02/us/colorado-sixth-district-coffman.html">vote</a> Republican.</p>

<p>This is not to say contemporary suburban life is without its own set of problems. Local municipalities, now grown-up governments with formal responsibilities instead of shadowy fiefdoms for the well-to-do, must stretch their already-thin resources to cover social services—as opposed to, say, kickback deals for Disneyland. Crime, poverty, and opioids, once considered stereotypical inner-city problems, are <a href="https://www.citylab.com/equity/2017/05/the-new-suburban-crisis/521709/">spreading</a> into the aging suburbs. Delivering healthcare, education, transit, Internet, and other services over the “last mile” to low-density households is a snowballing challenge that keeps policymakers up at night. Here’s a thought: What if instead of pretending disadvantaged suburbanites don’t exist, or denigrating their choice of housing stock, we apply our big smart urban planning brains to solving the problems they’re dealing with right now?</p>

<p>But of course, we’re planners; we’re engineers—or at least wannabe planners and engineers. We’re left-brain technocrats who believe cities are machines that can be tuned to peak efficiency; that all of governing can be boiled down to data, European best practices, and sweeping generalizations of economic history. We spend so much time arguing about such inane microcosms of urban policy as setbacks, timed transfers, floor-area ratios, cant deficiencies, zoned capacities, and parking maximums—doing battle on Twitter, and on the <em>Pedestrian Observations</em> and <em>CityLab</em> comment sections—that we’ve paid astonishingly little attention to the people who actually inhabit those cities. We ignore them at our peril. For what <em>is</em> a city if not a political construct, whose goals and priorities are decided by those who happen to be in charge? It is the leaders they elect who will put the “rubber” of theory to the “road” of practice, building the transportation systems, writing the legislature, and approving the development projects that will shape our urbanizing future.</p>

<figure class="figure  figure--center"><img class="image" src="https://upload.wikimedia.org/wikipedia/commons/f/f5/Futurama_diorama_detail.jpg" alt="In 1939, the Futurama exhibit portrayed dense urban clusters linked by a high-speed, automated highway network. Offensive to nostalgic Westerners, indeed. (Credit: [Wikimedia](https://commons.wikimedia.org/wiki/File:Futurama_diorama_detail.jpg))" width="" height="" /><figcaption class="caption"><p>In 1939, the Futurama exhibit portrayed dense urban clusters linked by a high-speed, automated highway network. Offensive to nostalgic Westerners, indeed. (Credit: <a href="https://commons.wikimedia.org/wiki/File:Futurama_diorama_detail.jpg">Wikimedia</a>)</p>
</figcaption></figure>

<p>Every age is framed by its futurists, and I give Levy credit where credit is due for committing to paper a metropolitan vision fit for the 21st century. Nevertheless, I couldn’t help but notice the similarities between his “spiky” template for transit-oriented development and GM’s Futurama diorama, from the 1939 World’s Fair—just swap the maze of elevated highways for rapid transit and high-speed rail tracks. And perhaps the most striking thing about the Futurama diorama is that we are now grateful its vision for the “city of tomorrow” never came to pass.</p>]]></content><author><name>Ryan Young</name></author><category term="writing" /><category term="cities" /><category term="urbanism" /><summary type="html"><![CDATA[“The Future is not Retro,” declares a recent Pedestrian Observation post that has been my metaphorical pea under the mattress for the last several weeks. Its tone is so bombastic and cavalier that the piece is difficult to take entirely seriously—much like another Alon Levy hot take, “The NTSB Wants American Trains to Be Less Safe,” or that infamous Market Urbanism tweet about rebuilding Notre Dame as a contemporary mixed-use skyscraper—but as an indicator of the way we urban planning nerds think and talk about cities, it should be taken very seriously, indeed. Much of the post details Levy’s vision for the future of urban development, which goes something like this: In a few decades from now, the cities of the West—the largest of them, anyway—will look increasingly like the crowded, transit-oriented metropolises of East Asia. They will be crisscrossed by driver-less metros, whose stations will be surrounded by clusters of high-rise offices and residential towers, and be linked together by high-speed rail for zero-emissions, long-distance travel. Vacations will entail a bullet train ride to San Francisco or Miami instead of a road trip to some bygone natural wonder. (As for mid-to-lower tier cities, and the National Park Service, the outlook is rather grim—Levy fully expects those to shrivel up and die.) The city of the future, today, in Bangkok. (Credit: Wikimedia) As is typical of his style, Levy, the all-knowing technocrat, boasts his thesis with an aura of smug confidence. Skyscrapers and trains are imperative, he insists, because of the economic value unlocked by greater density; the moral imperative to avert catastrophic climate change; and the cold, impersonal hand of urban economics, which points the way toward a concentrated and agglomerated future. I, like Levy, see the virtues of dense urban development and mass transit; I believe that in many ways, the world would be better off under his “regime of green prosperity.” But good government is about minding the individual needs of millions of citizens, not molding them into cartoon caricatures so you can run them over with the bulldozer. You would never know that from Levy’s reading of the historical record, which is reason to give anyone pause. In “The Future is not Retro,” he compresses all urban history into an interplay between two population pools—those who live in central cities, and those who do not—whose division of the population, like a seesaw, has teetered back and forth as economic fads come and go. Progress is the recurring theme here. At any given moment, there are those on the right side of history (whom Levy does not name, but we might call them urban progressives) and those on the wrong side of history, the “traditionalists” with their ignorant, selfish, “retro attitudes” Levy reserves so much of his contempt for. It should be obvious, I hope, that this is an absurdly reductive lens through which to view an entire metropolitan area and all of its facets, such as industry, commerce, social networks, transportation networks, political relationships, and power dynamics, to name just a few. So, for example, the exclusion of the inner-city poor from the prosperity of the suburbs due to restrictive zoning—and racial covenants, and redlining, and block-busting, and other forms of racially motivated housing discrimination—Levy codes as “early 20th century urbanites [adapting] to the reality of suburbanization a generation later.” It’s his choice to expunge all social and political struggle from the historical record, but that does not prove that it didn’t happen or that it doesn’t matter. Picture a Bronx resident in the 1950s, unable to obtain a mortgage for a suburban home because the bank doesn’t take kindly to the color of his skin, and a Beverly Hills millionaire in 2019, lobbying against an affordable housing development because of the shadow it will cast over his lawn. If there is a clear and pertinent difference between these two retrograde “traditionalists,” it eludes Levy. So it’s little surprise Levy views 21st-century urbanization as yet another conflict in the same old story of the Progressives versus the Luddites. In his mind, it’s black and white: Dense urban development is culturally diverse and the way of the future, while the suburbs are outmoded, exclusive, and racist—full of “Basil Fawlty types who either can’t or won’t adapt to a world that has little use for their prejudices,” with “no racial minorities or working-class people within sight,” as he puts it. If Levy sincerely believes this is an accurate representation of our urban geography, I suggest he go for a walk in a 21st-century suburb—because in 2019, you can have your cake and eat it, too: a detached house with a yard, and racial minorities and working-class people! It’s true, according to Brookings: “Minorities represent 35 percent of suburban residents, similar to their share of overall U.S. population.” In 1980, the sleepy exurb of Anaheim, California was just 17 percent Hispanic; by 2015, that percentage had skyrocketed to 53 percent. In 1970, 25 percent of all African Americans in metro Washington, D.C lived in the suburbs; by 2000, 78 percent of them did. Have the new wave of minority suburbanites been priced out of hopping central cities (by insufficient housing supply, no doubt), but would otherwise prefer greener, hipper apartment living? Some of them, surely, but many are proud of the communities they’ve made their own. In the suburbs of Atlanta, they voted down a rapid transit expansion because they loathed a tax increase and did not want to import “city problems.” Some of them (gasp!) even vote Republican. This is not to say contemporary suburban life is without its own set of problems. Local municipalities, now grown-up governments with formal responsibilities instead of shadowy fiefdoms for the well-to-do, must stretch their already-thin resources to cover social services—as opposed to, say, kickback deals for Disneyland. Crime, poverty, and opioids, once considered stereotypical inner-city problems, are spreading into the aging suburbs. Delivering healthcare, education, transit, Internet, and other services over the “last mile” to low-density households is a snowballing challenge that keeps policymakers up at night. Here’s a thought: What if instead of pretending disadvantaged suburbanites don’t exist, or denigrating their choice of housing stock, we apply our big smart urban planning brains to solving the problems they’re dealing with right now? But of course, we’re planners; we’re engineers—or at least wannabe planners and engineers. We’re left-brain technocrats who believe cities are machines that can be tuned to peak efficiency; that all of governing can be boiled down to data, European best practices, and sweeping generalizations of economic history. We spend so much time arguing about such inane microcosms of urban policy as setbacks, timed transfers, floor-area ratios, cant deficiencies, zoned capacities, and parking maximums—doing battle on Twitter, and on the Pedestrian Observations and CityLab comment sections—that we’ve paid astonishingly little attention to the people who actually inhabit those cities. We ignore them at our peril. For what is a city if not a political construct, whose goals and priorities are decided by those who happen to be in charge? It is the leaders they elect who will put the “rubber” of theory to the “road” of practice, building the transportation systems, writing the legislature, and approving the development projects that will shape our urbanizing future. In 1939, the Futurama exhibit portrayed dense urban clusters linked by a high-speed, automated highway network. Offensive to nostalgic Westerners, indeed. (Credit: Wikimedia) Every age is framed by its futurists, and I give Levy credit where credit is due for committing to paper a metropolitan vision fit for the 21st century. Nevertheless, I couldn’t help but notice the similarities between his “spiky” template for transit-oriented development and GM’s Futurama diorama, from the 1939 World’s Fair—just swap the maze of elevated highways for rapid transit and high-speed rail tracks. And perhaps the most striking thing about the Futurama diorama is that we are now grateful its vision for the “city of tomorrow” never came to pass.]]></summary></entry><entry><title type="html">Movie Review: Moana (2016) Was Quintessential Late-Stage Disney</title><link href="/2019/10/moana-was-quintessential-late-stage-disney/" rel="alternate" type="text/html" title="Movie Review: Moana (2016) Was Quintessential Late-Stage Disney" /><published>2019-10-30T20:00:20-07:00</published><updated>2019-10-30T20:00:20-07:00</updated><id>/2019/10/moana-was-quintessential-late-stage-disney</id><content type="html" xml:base="/2019/10/moana-was-quintessential-late-stage-disney/"><![CDATA[<p><img src="https://upload.wikimedia.org/wikipedia/en/2/26/Moana_Teaser_Poster.jpg" alt="" /></p>

<p>So far, 2019 has seen the release of the new <em>Dumbo</em>, the new <em>Aladdin</em>, and the new <em>Lion King</em>—and a new <em>Mulan</em>, by the way, is in the works, too. All-mighty Disney used to inspire kids to sing about the “circle of life”; now they’ve got critics jeering cynically about the “circle of franchise reboots.” <em>Et tu, Mickey?</em> At least, Disney partisans can reassure themselves, creative bankruptcy is a relatively new look for the studio. In 2016, a year that wasn’t so long ago (best remembered, or forgotten, for a presidential election of biblical proportions), Walt Disney Animation released the surprisingly clever <em>Zootopia</em>, a thinly-veiled allegory for present-day identity politics and the millennial urban condition. Then November saw the release of <em>Moana</em>, a slightly-less-clever movie about a South Pacific teenage girl endowed with magical powers and a destiny to save the world.</p>

<!--more-->

<p>People like me—which is to say, adult males in our early 20s—are not generally concerned with computer-animated movies made for children, much less brand-name Disney Princess flicks. In part, this is due to the widely held belief that animated films aren’t “real” movies, which I thought <em>Toy Story</em> had pretty thoroughly eviscerated over 20 years ago. Maybe anthropomorphic characters and exaggerated expressions aren’t <em>your</em> cup of tea, but animated movies still impart values and themes to millions of impressionable Western children, so they’re worth paying attention to. Personally, it’s almost for lack of a better alternative: Watching comic book heroes punch each other for three R-rated hours in post-9/11 America, fueling our collective fetish for over-the-top violence and domestic terrorism? That’s our idea of “mature” storytelling? But I digress.</p>

<p><em>Moana</em> is, unfortunately, not the strongest argument for taking family movies seriously. It’s a meticulously animated film with plenty of passion evident in its creation, but it’s one that—like that one sitcom episode you’ve sworn you’ve caught six times already—struggles to recapture the magic of a first-time viewing; a slick, ray-traced imitator standing in the shadow of something bolder and more authentic. This movie has a lot going for it, not in the least because there is something immediately appealing about a story of a confident, plucky protagonist of color charting her course in a faraway oceanic paradise; but if—as Martin Scorsese <a href="https://variety.com/2019/film/news/martin-scorsese-marvel-theme-parks-1203360075/">informs</a> us—blockbusters are like amusement parks, then <em>Moana</em> is rather like a third-rate Six Flags. It’s polished and professional and lots of fun for the locals, but it’s unmistakably playing second fiddle to bigger and meaner siblings.</p>

<figure class="figure  figure--center"><img class="image" src="/assets/posts/wp-uploads/2019/10/Moana_A330.jpg" alt="Saving Motunui from environmental devastation is the best advertisement for a carbon-belching Hawaii vacation if I&#8217;ve ever heard of one. (Credit: Hawaiian Airlines.)" width="" height="" /><figcaption class="caption">Saving Motunui from environmental devastation is the best advertisement for a carbon-belching Hawaii vacation if I&#8217;ve ever heard of one. (Credit: Hawaiian Airlines.)</figcaption></figure>

<p>A major liability is the writers’ affinity for the oh-so-postmodern notion that all stories are equally absurd, and therefore the only way to say anything original is to kick out the legs of the metaphorical stool upon which their own premise rests. As an illustrative example (heads up!—if you’re reading a review of a three year-old movie on some obscure personal blog, you probably deserve every spoiler coming to you), take Moana’s first attempt to sail past the reef. After nearly <em>drowning</em> herself, something that nearly results in her <em>death</em>, she <a href="https://youtu.be/8Iay8zCVXRc?t=2">confesses</a> to Grandma Tala that her lifelong dream to explore the world—the dream she relished every time she cut “Ruling Motonui 101” class to sneak off and go gaze at the ocean—might, for Moana’s own sake and for the sake of her subjects, need to be shelved and buried for good. “Well, <em>okay</em>!” grandma bellows, with a sly grin. “What? You’re not going to talk me out of it?” Moana replies, in mock-disbelief; the twist of her head seeming to suggest, <em>and what were</em> you <em>getting so worked up about, silly viewer?</em></p>

<p>Self-aware humor is par for the course in modern Disney, and since <em>Moana</em> is a fresh entry with no cultural baggage (unlike, say, the <em>Star Wars</em> movies), most of it works. But really, Hollywood, let’s not pretend as if deconstructing cliches from TV Tropes is some kind of elixir that turns pure formula into pure gold. Now that those elitist scriptwriters with their pretentious arts school degrees can no longer wow us with inventive stories, they poke fun at us for falling for make believe in the first place—but <em>really</em>, they’re just frustrated they’re stuck scripting a corporate softball like <em>Jurassic World</em> instead of the truly biting stuff like <em>Blazing Saddles</em> or <em>Airplane!</em>. After all, why <a href="https://www.slashfilm.com/star-wars-detours-release/">won’t</a> Disney let the world see <em>Star Wars Detours</em>? Because it risks too much collateral damage to “the brand” for the suits’ taste, even if the whole thing was fully produced and the digital computer models and voice samples are sitting idle in some Lucasfilm art vault. Yes, I’m bitter.</p>

<p><em>Mulan</em> was not perfect, but its colorful cast—Mushu, Captain Shang, Shan Yu, even the horse—more than compensated for their mild offensiveness in terms of sheer entertainment value. <em>Moana</em>, by contrast, tries to get by with just two characters: Moana and Mau’i. You can write off the rest of the humans, who are essentially cardboard cutouts and backup dancers, and the villains, who rank at the very bottom of the Disney Villain Coolness scale. Since every line of the script <a href="https://www.vanityfair.com/hollywood/2016/11/moana-oceanic-trust-disney-controversy-pacific-islanders-polynesia">was run</a> through an “Oceanic Trust” of experts in Polynesian culture, the giant crab and the lava monster are most likely sourced from real myths—which I do appreciate—but as a consumer, well, sometimes the Disneyfied version is preferable to the real thing. Was <em>Moana</em> too cautious? Rumors persist of deleted plot points featuring temper tantrums and sibling relationships. Second-guessing these decisions would be like Monday-night quarterbacking, since we will never have the full story—but I can’t help but think, that might have been cool to see, you know?</p>

<p>The best parts of <em>Moana</em> are the soundtrack—which often sounds like the product of a Pacific cargo cult that worships “Let It Go,” but preserves enough of the movie’s emotional core to make for pleasant listening—and Auli’i Cravalho, whose energetic and boisterous performance as Moana is the show-stealer that saves the entire endeavor. Looking at <em>Moana</em>‘s behind-the-scenes, one imagines those voice acting veterans, who have long since sold their souls to the Hollywood machine (yes, that goes for the Rock, too), looking rather dumbstruck as the perky teenager plucked from Hawaii’s little islands schools them on acting and singing in a musical blockbuster. It’s nice to see Cravalho staying true to her Hawaiian roots, and generally avoiding acting jobs that have anything to do with Disney.</p>

<p>Moana, by the way, is no archetypal European Disney princess—no, she’s the spunkiest strong-willed, dark-skinned, H<sub>2</sub>O-manipulating female protagonist to hit the airwaves since <em>Avatar: the Last Airbender</em>‘s Katara, voiced by an authentic Hawai’ian who speaks Hawai’ian. Clearly, this is a good thing: Henceforth, Disney movies shall no longer telegraph insensitive ethnic stereotypes. The only trouble, as the crusty old journalists and politicians like to <a href="https://www.theguardian.com/film/2017/jan/13/moana-might-be-great-for-representation-but-its-not-all-heartwarming-for-hawaii">remind</a> us, is that an animated movie with characters that look and behave like you is not quite the same thing as full economic parity with one of America’s most powerful and exploitative corporations. Double but: Any negative judgment against Disney has to be reconciled with the special Hawaiian version of <em>Moana</em>, <a href="https://www.nbcnews.com/news/asian-america/hawaiian-language-version-moana-be-distributed-schools-across-hawaii-n940411">recorded</a> by a <em>dang proud</em> Auli’i Cravalho. So make of that what you will.</p>

<figure class="figure  figure--center"><img class="image" src="/assets/posts/wp-uploads/2019/10/Moana_Katara.jpg" alt="Sorry Disney, but it looks like Nickelodeon beat you to the punch!" width="" height="" /><figcaption class="caption">Sorry Disney, but it looks like Nickelodeon beat you to the punch!</figcaption></figure>

<p>To watch <em>Moana</em> is to marvel at its hyper-photogenic world: the idyllic villages and playful wooden huts, the shafts of light darting beneath the lush palm canopies, and the warm glow of the sunset as it reflects off of the rock faces. Just keep a mindful eye out for the parts that… were apparently not motivated by story considerations. Moana’s dress, I’m <a href="https://www.vanityfair.com/hollywood/2016/11/moana-disney-review">told</a> by <em>real</em> movie critics with vastly more fashion sense than I will ever have, was specifically crafted to sell direct-from-sweatshop lookalikes at Target, and I think the same could be said for the mischievous big blue jewel she hangs around her neck. Even the South Pacific setting is, to some extent, an ad for any one of the faraway vacation resorts Disney would be more than happy to sell you reservations for.</p>

<p>So the verdict is that <em>Moana</em> is a perfectly palatable movie, even if it’s not the unabashedly creative, progressive-minded triumph we’ve come to expect from Disney’s better days. And as the latest contender in the long battle for Asian and Pacific Islander (hey, that’s me!) representation in Hollywood, you could certainly do much worse. For one thing, you could be white Katara…</p>

<p><strong>Rating: 3/4</strong></p>

<p>Rotten Tomatoes: <a href="https://www.rottentomatoes.com/m/moana_2016">96%</a><br />
Metacritic: <a href="https://www.metacritic.com/movie/moana">81</a></p>]]></content><author><name>Ryan Young</name></author><category term="writing" /><category term="disney" /><category term="movie" /><summary type="html"><![CDATA[So far, 2019 has seen the release of the new Dumbo, the new Aladdin, and the new Lion King—and a new Mulan, by the way, is in the works, too. All-mighty Disney used to inspire kids to sing about the “circle of life”; now they’ve got critics jeering cynically about the “circle of franchise reboots.” Et tu, Mickey? At least, Disney partisans can reassure themselves, creative bankruptcy is a relatively new look for the studio. In 2016, a year that wasn’t so long ago (best remembered, or forgotten, for a presidential election of biblical proportions), Walt Disney Animation released the surprisingly clever Zootopia, a thinly-veiled allegory for present-day identity politics and the millennial urban condition. Then November saw the release of Moana, a slightly-less-clever movie about a South Pacific teenage girl endowed with magical powers and a destiny to save the world. People like me—which is to say, adult males in our early 20s—are not generally concerned with computer-animated movies made for children, much less brand-name Disney Princess flicks. In part, this is due to the widely held belief that animated films aren’t “real” movies, which I thought Toy Story had pretty thoroughly eviscerated over 20 years ago. Maybe anthropomorphic characters and exaggerated expressions aren’t your cup of tea, but animated movies still impart values and themes to millions of impressionable Western children, so they’re worth paying attention to. Personally, it’s almost for lack of a better alternative: Watching comic book heroes punch each other for three R-rated hours in post-9/11 America, fueling our collective fetish for over-the-top violence and domestic terrorism? That’s our idea of “mature” storytelling? But I digress. Moana is, unfortunately, not the strongest argument for taking family movies seriously. It’s a meticulously animated film with plenty of passion evident in its creation, but it’s one that—like that one sitcom episode you’ve sworn you’ve caught six times already—struggles to recapture the magic of a first-time viewing; a slick, ray-traced imitator standing in the shadow of something bolder and more authentic. This movie has a lot going for it, not in the least because there is something immediately appealing about a story of a confident, plucky protagonist of color charting her course in a faraway oceanic paradise; but if—as Martin Scorsese informs us—blockbusters are like amusement parks, then Moana is rather like a third-rate Six Flags. It’s polished and professional and lots of fun for the locals, but it’s unmistakably playing second fiddle to bigger and meaner siblings. Saving Motunui from environmental devastation is the best advertisement for a carbon-belching Hawaii vacation if I&#8217;ve ever heard of one. (Credit: Hawaiian Airlines.) A major liability is the writers’ affinity for the oh-so-postmodern notion that all stories are equally absurd, and therefore the only way to say anything original is to kick out the legs of the metaphorical stool upon which their own premise rests. As an illustrative example (heads up!—if you’re reading a review of a three year-old movie on some obscure personal blog, you probably deserve every spoiler coming to you), take Moana’s first attempt to sail past the reef. After nearly drowning herself, something that nearly results in her death, she confesses to Grandma Tala that her lifelong dream to explore the world—the dream she relished every time she cut “Ruling Motonui 101” class to sneak off and go gaze at the ocean—might, for Moana’s own sake and for the sake of her subjects, need to be shelved and buried for good. “Well, okay!” grandma bellows, with a sly grin. “What? You’re not going to talk me out of it?” Moana replies, in mock-disbelief; the twist of her head seeming to suggest, and what were you getting so worked up about, silly viewer? Self-aware humor is par for the course in modern Disney, and since Moana is a fresh entry with no cultural baggage (unlike, say, the Star Wars movies), most of it works. But really, Hollywood, let’s not pretend as if deconstructing cliches from TV Tropes is some kind of elixir that turns pure formula into pure gold. Now that those elitist scriptwriters with their pretentious arts school degrees can no longer wow us with inventive stories, they poke fun at us for falling for make believe in the first place—but really, they’re just frustrated they’re stuck scripting a corporate softball like Jurassic World instead of the truly biting stuff like Blazing Saddles or Airplane!. After all, why won’t Disney let the world see Star Wars Detours? Because it risks too much collateral damage to “the brand” for the suits’ taste, even if the whole thing was fully produced and the digital computer models and voice samples are sitting idle in some Lucasfilm art vault. Yes, I’m bitter. Mulan was not perfect, but its colorful cast—Mushu, Captain Shang, Shan Yu, even the horse—more than compensated for their mild offensiveness in terms of sheer entertainment value. Moana, by contrast, tries to get by with just two characters: Moana and Mau’i. You can write off the rest of the humans, who are essentially cardboard cutouts and backup dancers, and the villains, who rank at the very bottom of the Disney Villain Coolness scale. Since every line of the script was run through an “Oceanic Trust” of experts in Polynesian culture, the giant crab and the lava monster are most likely sourced from real myths—which I do appreciate—but as a consumer, well, sometimes the Disneyfied version is preferable to the real thing. Was Moana too cautious? Rumors persist of deleted plot points featuring temper tantrums and sibling relationships. Second-guessing these decisions would be like Monday-night quarterbacking, since we will never have the full story—but I can’t help but think, that might have been cool to see, you know? The best parts of Moana are the soundtrack—which often sounds like the product of a Pacific cargo cult that worships “Let It Go,” but preserves enough of the movie’s emotional core to make for pleasant listening—and Auli’i Cravalho, whose energetic and boisterous performance as Moana is the show-stealer that saves the entire endeavor. Looking at Moana‘s behind-the-scenes, one imagines those voice acting veterans, who have long since sold their souls to the Hollywood machine (yes, that goes for the Rock, too), looking rather dumbstruck as the perky teenager plucked from Hawaii’s little islands schools them on acting and singing in a musical blockbuster. It’s nice to see Cravalho staying true to her Hawaiian roots, and generally avoiding acting jobs that have anything to do with Disney. Moana, by the way, is no archetypal European Disney princess—no, she’s the spunkiest strong-willed, dark-skinned, H2O-manipulating female protagonist to hit the airwaves since Avatar: the Last Airbender‘s Katara, voiced by an authentic Hawai’ian who speaks Hawai’ian. Clearly, this is a good thing: Henceforth, Disney movies shall no longer telegraph insensitive ethnic stereotypes. The only trouble, as the crusty old journalists and politicians like to remind us, is that an animated movie with characters that look and behave like you is not quite the same thing as full economic parity with one of America’s most powerful and exploitative corporations. Double but: Any negative judgment against Disney has to be reconciled with the special Hawaiian version of Moana, recorded by a dang proud Auli’i Cravalho. So make of that what you will. Sorry Disney, but it looks like Nickelodeon beat you to the punch! To watch Moana is to marvel at its hyper-photogenic world: the idyllic villages and playful wooden huts, the shafts of light darting beneath the lush palm canopies, and the warm glow of the sunset as it reflects off of the rock faces. Just keep a mindful eye out for the parts that… were apparently not motivated by story considerations. Moana’s dress, I’m told by real movie critics with vastly more fashion sense than I will ever have, was specifically crafted to sell direct-from-sweatshop lookalikes at Target, and I think the same could be said for the mischievous big blue jewel she hangs around her neck. Even the South Pacific setting is, to some extent, an ad for any one of the faraway vacation resorts Disney would be more than happy to sell you reservations for. So the verdict is that Moana is a perfectly palatable movie, even if it’s not the unabashedly creative, progressive-minded triumph we’ve come to expect from Disney’s better days. And as the latest contender in the long battle for Asian and Pacific Islander (hey, that’s me!) representation in Hollywood, you could certainly do much worse. For one thing, you could be white Katara… Rating: 3/4 Rotten Tomatoes: 96% Metacritic: 81]]></summary></entry><entry><title type="html">Introducing Sia Slice, My Absurdly Cheap Block Storage Solution</title><link href="/2019/10/introducing-sia-slice-my-absurdly-cheap-block-storage-solution/" rel="alternate" type="text/html" title="Introducing Sia Slice, My Absurdly Cheap Block Storage Solution" /><published>2019-10-28T00:23:38-07:00</published><updated>2019-10-28T00:23:38-07:00</updated><id>/2019/10/introducing-sia-slice-my-absurdly-cheap-block-storage-solution</id><content type="html" xml:base="/2019/10/introducing-sia-slice-my-absurdly-cheap-block-storage-solution/"><![CDATA[<figure class="figure  figure--center"><img class="image" src="https://raw.githubusercontent.com/wiki/YoRyan/sia-slice/transfer-screen.png" alt="Sia Slice in action. (On a remote system, with tmux.)" width="" height="" /><figcaption class="caption"><p>Sia Slice in action. (On a remote system, with tmux.)</p>
</figcaption></figure>

<p>I dabble in cryptocurrencies, occasionally. I hesitate to get too partisan on a subject the Internet takes <em>very</em> seriously, but it seems to me that the fairest judge of a coin’s value is the utility it provides to its holders. So Bitcoin is useful because everyone recognizes and accepts Bitcoin, Monero is useful because it facilitates anonymous transactions, Ethereum has that smart contracts thing going for it, and so on and so forth.</p>

<!--more-->

<p>I’m pleased to endorse <a href="https://sia.tech">Sia</a> as another rising star in the cryptocurrency world. It’s a blockchain-backed decentralized storage network that connects renters with hosts, who sell spare hard drive capacity on a globe-spanning swarm of machines that range from Raspberry Pis to university datacenters. Redundancy and encryption for your files, of course, come standard. Remember “Pied Piper,” the fictional peer-to-peer storage network from Mike Judge’s <em>Silicon Valley</em>? Well, Sia is exactly that, except it’s a real system you can store real data on.</p>

<p>Still, it is clearly a work in progress. The official Sia client nicely handles uploads and downloads, but it lacks support for automatic synchronization, making for an experience that feels especially <em>manual</em> compared to, say, OneDrive or Google Drive. There are a number of promising projects (currently in beta) poised to change this: <a href="https://bitbucket.org/blockstorage/repertory/src/master/">Repertory</a> mounts Sia storage as a local filesystem, while <a href="https://github.com/tbenz9/siasync">Siasync</a> keeps files synchronized with Sia. But for maximum flexibility, system administrators and enterprise customers would desire <em>block-level</em>—not file-level—access. Very large files, like database dumps, are poorly suited for Sia, which currently does not support partial file updates. And any file synchronization solution would fail to preserve inodes, permissions, and other extended metadata, stripping specialized filesystems like ZFS and Btrfs (both copy-on-write, with support for instant snapshots and data de-duplication) of the very features that make them useful.</p>

<p>The solution is to treat your data not as a collection of files, but as one big, mutable array of bytes. Enter Sia Slice: a small Python program that splits any large file into 100-megabyte chunks for uploading to Sia. Because Sia Slice operates at the block level, it can make 1:1 copies of block devices, disk images, database backups, and other large blobs of data. And on subsequent syncs, it can accomplish partial writes by ignoring the chunks that haven’t changed.</p>

<p>You can obtain a copy, peruse the source code, and find usage instructions on the project <a href="https://github.com/YoRyan/sia-slice">homepage</a>.</p>

<h2 id="notes-on-sia">Notes on Sia</h2>

<p>Sia is an emerging platform, so it’s not just the user interface that is rough around the edges; there is also room for improvement, in my humble opinion, in the developer API. I know the Sia developers are reading this, so I promise to be gentle. 🙂</p>

<ul>
  <li>The blockchain, which as of writing is about 17GB large, takes several <em>days</em> to sync on a mechanical hard drive, even if <a href="https://siawiki.tech/daemon/bootstrapping_the_blockchain">bootstrapped</a>. Yikes! A solid-state will bring that time down to hours. There ought to be a polite, but very visible warning on the Sia download page.</li>
  <li>The API documentation is occasionally outdated or incorrect. For example, the call to validate a SiaPath is <a href="https://sia.tech/docs/#renter-validate-siapath-post">listed</a> as <code class="language-plaintext highlighter-rouge">/renter/validate/*</code>, while the correct call <code class="language-plaintext highlighter-rouge">/renter/validatesiapath/*</code> is shown right there in the example demo!</li>
  <li>JSON timestamps for access times, modification times, etc. are not represented accurately. In the examples, the timestamps are front-loaded with an unexplained series of numbers, and they also lack quote delimiters, implying they are something other than regular JSON strings.</li>
  <li>Bindings for languages that aren’t Go are scarce. For my choice of Python, both <a href="https://github.com/jnmclarty/pysia">pysia</a> and <a href="https://github.com/lolsteve/siapy">siapy</a> are over 2 years out-of-date, and missing calls. For Sia Slice, I had to write my own bindings—something I would have had to do anyway to take advantage of Python’s asyncio features.</li>
  <li>Sia is said to be most efficient with many simultaneous uploads, but my own experience with the daemon—perhaps it’s my pokey 10Mbps residential connection—is that it generally limits itself to one upload at a time. When uploading data with <code class="language-plaintext highlighter-rouge">/renter/uploadstream</code>, it is best to use one POST request at a time; otherwise, the uploads may starve each other for computation time.</li>
  <li>Presumably due to host or network availability hiccups, Sia occasionally fails to complete an upload, leaving it stuck in a stalled state with less-than-one redundancy. If your uploads are not disk-backed—because you’ve used <code class="language-plaintext highlighter-rouge">/renter/uploadstream</code>, perhaps—Sia <a href="https://gitlab.com/NebulousLabs/Sia/blob/master/modules/renter/README.md">considers</a> that file <strong>lost</strong> and will not repair it on its own; you need to invoke <code class="language-plaintext highlighter-rouge">/renter/delete</code> and start over. This was a major pitfall that left me scratching my head for several days while the stalled uploads kept piling up. Sia Slice’s solution is to restart uploads that have not completed within 3 hours.</li>
  <li>Nitpicking here: <code class="language-plaintext highlighter-rouge">/renter/uploadstream</code> lacks a specific method for handling failed or aborted POST requests due to a disconnect, program crash, etc. So when Sia Slice uploads data to Sia, it appends a .part extension—just like your web browser or download manager—to disambiguate between successful and failed partial uploads.</li>
  <li>Some of the file attributes are difficult to understand. What is the difference between <code class="language-plaintext highlighter-rouge">available</code> and <code class="language-plaintext highlighter-rouge">recoverable</code>? Why do most of my uploads get <code class="language-plaintext highlighter-rouge">stuck</code> right out of the gate?</li>
  <li>Deleting directories with <code class="language-plaintext highlighter-rouge">/render/dir</code> is not reliable. After several unit tests failed to clean up after themselves, I simply elected to delete individual files instead.</li>
</ul>

<p>Lastly, I would suggest a new feature that may prove indispensable to end users: bandwidth throttling. Most home networks are afflicted by an insidious phenomenon <a href="https://www.bufferbloat.net/">called</a> <em>buffer bloat</em>—when the relatively slow upload pipe is completely saturated by traffic, packets from other connections stop getting through. Ping times spike. Web browsing becomes sluggish. Skype and Discord calls become pixelated, then cut out entirely. In general, buffer bloat makes the whole Internet feel unreliable and unusable. (That goes for the download direction too, because TCP ACK replies are also impacted.)</p>

<p>It’s not a concern on my own network, thanks to my high-tech home router with traffic-shaping capabilities; but my neighbor, who is very generously allowing me to “borrow” his faster connection for my oversize initial upload, is not so fortunate. For lack of a bandwidth control in Sia, I had to use an OS-level tool like <a href="https://github.com/magnific0/wondershaper">wondershaper</a> to avoid crippling his digital lifestyle.</p>

<h2 id="however-you-slice-the-problem">However you slice the problem…</h2>

<p>Some challenges and gotchas aside, building on top of Sia is totally viable; I found the API cleanly designed and intuitive to use. Sia Slice is now “in production” as part of my backup workflow—I use <a href="https://github.com/digint/btrbk">btrbk</a> to save semi-automated snapshots of all my desktops and servers to an external hard drive, and then I mirror that hard drive to the Sia cloud with Sia Slice, thus constituting a 3-2-1 backup strategy. For me, this was a fun project that involved a wide array of systems, from low-level disk access to asynchronous I/O to curses to HTTP streaming.</p>

<p>While the general “split, hash, compress, and upload” principle behind Sia Slice could be extended to other kinds of object storage, the simple fact of the matter is that no service on the horizon is nearly as affordable (or decentralized!) as the Sia network. <a href="https://www.dropbox.com/buy">Dropbox</a> and <a href="https://wasabi.com/cloud-storage-pricing/">Wasabi</a> both quote USD $12/month for the 2TB of storage I use. On Sia, I pay approximately USD $1/month.</p>

<p>Once again, what is the true purpose of cryptocurrency? To provide useful services that no other medium can—something we all tend to forget as we keep our eyeballs glued to the red and green numerals on cryptocurrency exchanges. Block storage that is a whopping 92% below market cost is certainly very useful to me. Perhaps Sia Slice will make Sia useful to you.</p>]]></content><author><name>Ryan Young</name></author><category term="tech" /><category term="cryptocurrency" /><category term="programming" /><category term="project" /><summary type="html"><![CDATA[Sia Slice in action. (On a remote system, with tmux.) I dabble in cryptocurrencies, occasionally. I hesitate to get too partisan on a subject the Internet takes very seriously, but it seems to me that the fairest judge of a coin’s value is the utility it provides to its holders. So Bitcoin is useful because everyone recognizes and accepts Bitcoin, Monero is useful because it facilitates anonymous transactions, Ethereum has that smart contracts thing going for it, and so on and so forth. I’m pleased to endorse Sia as another rising star in the cryptocurrency world. It’s a blockchain-backed decentralized storage network that connects renters with hosts, who sell spare hard drive capacity on a globe-spanning swarm of machines that range from Raspberry Pis to university datacenters. Redundancy and encryption for your files, of course, come standard. Remember “Pied Piper,” the fictional peer-to-peer storage network from Mike Judge’s Silicon Valley? Well, Sia is exactly that, except it’s a real system you can store real data on. Still, it is clearly a work in progress. The official Sia client nicely handles uploads and downloads, but it lacks support for automatic synchronization, making for an experience that feels especially manual compared to, say, OneDrive or Google Drive. There are a number of promising projects (currently in beta) poised to change this: Repertory mounts Sia storage as a local filesystem, while Siasync keeps files synchronized with Sia. But for maximum flexibility, system administrators and enterprise customers would desire block-level—not file-level—access. Very large files, like database dumps, are poorly suited for Sia, which currently does not support partial file updates. And any file synchronization solution would fail to preserve inodes, permissions, and other extended metadata, stripping specialized filesystems like ZFS and Btrfs (both copy-on-write, with support for instant snapshots and data de-duplication) of the very features that make them useful. The solution is to treat your data not as a collection of files, but as one big, mutable array of bytes. Enter Sia Slice: a small Python program that splits any large file into 100-megabyte chunks for uploading to Sia. Because Sia Slice operates at the block level, it can make 1:1 copies of block devices, disk images, database backups, and other large blobs of data. And on subsequent syncs, it can accomplish partial writes by ignoring the chunks that haven’t changed. You can obtain a copy, peruse the source code, and find usage instructions on the project homepage. Notes on Sia Sia is an emerging platform, so it’s not just the user interface that is rough around the edges; there is also room for improvement, in my humble opinion, in the developer API. I know the Sia developers are reading this, so I promise to be gentle. 🙂 The blockchain, which as of writing is about 17GB large, takes several days to sync on a mechanical hard drive, even if bootstrapped. Yikes! A solid-state will bring that time down to hours. There ought to be a polite, but very visible warning on the Sia download page. The API documentation is occasionally outdated or incorrect. For example, the call to validate a SiaPath is listed as /renter/validate/*, while the correct call /renter/validatesiapath/* is shown right there in the example demo! JSON timestamps for access times, modification times, etc. are not represented accurately. In the examples, the timestamps are front-loaded with an unexplained series of numbers, and they also lack quote delimiters, implying they are something other than regular JSON strings. Bindings for languages that aren’t Go are scarce. For my choice of Python, both pysia and siapy are over 2 years out-of-date, and missing calls. For Sia Slice, I had to write my own bindings—something I would have had to do anyway to take advantage of Python’s asyncio features. Sia is said to be most efficient with many simultaneous uploads, but my own experience with the daemon—perhaps it’s my pokey 10Mbps residential connection—is that it generally limits itself to one upload at a time. When uploading data with /renter/uploadstream, it is best to use one POST request at a time; otherwise, the uploads may starve each other for computation time. Presumably due to host or network availability hiccups, Sia occasionally fails to complete an upload, leaving it stuck in a stalled state with less-than-one redundancy. If your uploads are not disk-backed—because you’ve used /renter/uploadstream, perhaps—Sia considers that file lost and will not repair it on its own; you need to invoke /renter/delete and start over. This was a major pitfall that left me scratching my head for several days while the stalled uploads kept piling up. Sia Slice’s solution is to restart uploads that have not completed within 3 hours. Nitpicking here: /renter/uploadstream lacks a specific method for handling failed or aborted POST requests due to a disconnect, program crash, etc. So when Sia Slice uploads data to Sia, it appends a .part extension—just like your web browser or download manager—to disambiguate between successful and failed partial uploads. Some of the file attributes are difficult to understand. What is the difference between available and recoverable? Why do most of my uploads get stuck right out of the gate? Deleting directories with /render/dir is not reliable. After several unit tests failed to clean up after themselves, I simply elected to delete individual files instead. Lastly, I would suggest a new feature that may prove indispensable to end users: bandwidth throttling. Most home networks are afflicted by an insidious phenomenon called buffer bloat—when the relatively slow upload pipe is completely saturated by traffic, packets from other connections stop getting through. Ping times spike. Web browsing becomes sluggish. Skype and Discord calls become pixelated, then cut out entirely. In general, buffer bloat makes the whole Internet feel unreliable and unusable. (That goes for the download direction too, because TCP ACK replies are also impacted.) It’s not a concern on my own network, thanks to my high-tech home router with traffic-shaping capabilities; but my neighbor, who is very generously allowing me to “borrow” his faster connection for my oversize initial upload, is not so fortunate. For lack of a bandwidth control in Sia, I had to use an OS-level tool like wondershaper to avoid crippling his digital lifestyle. However you slice the problem… Some challenges and gotchas aside, building on top of Sia is totally viable; I found the API cleanly designed and intuitive to use. Sia Slice is now “in production” as part of my backup workflow—I use btrbk to save semi-automated snapshots of all my desktops and servers to an external hard drive, and then I mirror that hard drive to the Sia cloud with Sia Slice, thus constituting a 3-2-1 backup strategy. For me, this was a fun project that involved a wide array of systems, from low-level disk access to asynchronous I/O to curses to HTTP streaming. While the general “split, hash, compress, and upload” principle behind Sia Slice could be extended to other kinds of object storage, the simple fact of the matter is that no service on the horizon is nearly as affordable (or decentralized!) as the Sia network. Dropbox and Wasabi both quote USD $12/month for the 2TB of storage I use. On Sia, I pay approximately USD $1/month. Once again, what is the true purpose of cryptocurrency? To provide useful services that no other medium can—something we all tend to forget as we keep our eyeballs glued to the red and green numerals on cryptocurrency exchanges. Block storage that is a whopping 92% below market cost is certainly very useful to me. Perhaps Sia Slice will make Sia useful to you.]]></summary></entry><entry><title type="html">Movie Review: John Wick: Chapter 3 – Parabellum</title><link href="/2019/06/movie-review-john-wick-chapter-3-parabellum/" rel="alternate" type="text/html" title="Movie Review: John Wick: Chapter 3 – Parabellum" /><published>2019-06-11T18:51:36-07:00</published><updated>2019-06-11T18:51:36-07:00</updated><id>/2019/06/movie-review-john-wick-chapter-3-parabellum</id><content type="html" xml:base="/2019/06/movie-review-john-wick-chapter-3-parabellum/"><![CDATA[<p><img src="https://upload.wikimedia.org/wikipedia/en/9/94/John_Wick_Chapter_3_Parabellum.png" alt="" /></p>

<p>On the night I opened my browser to buy a ticket to see <em>John Wick: Chapter 3</em>, I had never actually seen any of the <em>John Wick</em> films before. Fortunately, the premise of this sequel-to-a-sequel isn’t terribly complicated: John Wick himself (Keanu Reeves) is a professional assassin who has run afoul of the High Table, a world-spanning secret society of assassins. Wick is a master of his occupation, which entails committing dozens upon dozens of grisly on-screen murders with improvised weapons.</p>

<p>My movie buddies–human repositories of <em>John Wick</em> lore and repeat viewers of all his films–assured me the movie’s selling point would be its fight choreography. And at first, it indeed wowed me: The action started out as quirky, exhilarating fun. But as the gun-slinging, knife-slashing power fantasy dragged on, I couldn’t escape the feeling that that initial novel spark was gradually devolving into empty spectacle.</p>

<!--more-->

<p>The film makes some fine first impressions. It starts with Wick on the run, a bounty placed on his head by the High Table, getting stalked through the seedy streets of lower Manhattan by swarms of assailants disguised in the crowds. He’s outnumbered, outgunned, and can’t trust anybody–see that raggedly dressed hobo smoking a joint behind the dumpster? Blink twice, and now he’s aiming a handgun. But all of the High Table goons in the world are no match for Wick, who dispatches them with lethal efficiency, his spur-of-the-moment weapons ranging from pistols and throwing knives to horses and library books.</p>

<p>Needless to say, this deadly ballet (the film’s metaphor, not mine; it introduces ballerina assassins) delivers all of the supremely inventive thrills action junkies could hope for, and the movie’s forward momentum is lock-step with Wick’s breakneck pace as he races through Manhattan’s neon-lit streets and alleyways. There’s even a scene in which Wick stabs his opponent through their <em>eyeball</em>–and, yes, it happens on camera. (My theater cheered.) The movie thoughtfully reveals a softer side of Wick, too–one that is paranoid, vulnerable, desperate–when he begs an old doctor friend of his, barred by the High Table from helping Wick under any circumstances, for a patch-up job for a knife fight even Wick couldn’t win unscathed.</p>

<p>I assume that conflict between friendship and duty is supposed to be a recurring theme, even if it’s difficult to tell from the way the rest of the story unfolds. You see, the Adjudicator (Asia Kate Dillon), a very emotionless, bureaucratic, private investigator of sorts, has been assigned by the High Table to punish Wick’s associates–which involves interrogations, demotions, and swords, in that order–for aiding and abetting the last-movie murder spree that soured Wick’s relationship with the High Table in the first place. But the one who gets most screen time, by far, is Sofia (Halle Berry), who tag-teams with Wick for a Saharan shootout that’s many minutes too long–not one of which explores the anticipated consequences of her actions, be they a visit from the Adjudicator or otherwise. How not very satisfying. Not to mention the moment when (mild spoiler) someone fires a bullet into Sofia’s favorite attack dog, which survives–of course–thanks to body armor, the hallmark of a crowd-pleasing movie that’s playing things too safe.</p>

<p>The remainder of this transparently it’s-for-the-fans film has little to offer besides guns. Lots of guns. Only a few stylistic embellishments offer any relief from the increasingly mind-numbing, over-the-top martial arts and automatic-fire action, featuring Wick pumping shotgun shell after shotgun shell into waves of anonymous body armor-clad soldiers. (Nobody in my theater was cheering now.) One hopes the film’s characters might infuse all of the mayhem with a sense of purpose, but the script simply doesn’t give them enough time to do so. I give Wick credit for suffering a major betrayal toward the end of the film, but it takes two compelling characters to tango, so that moment is robbed of its punch to the gut. And, let’s be honest: Any less-punch-more-talk scenes serve mostly to demonstrate that for all of Keanu Reeves’ acting talents, delivering sappy dialog isn’t one of them.</p>

<p>You still root for Wick as he shoots, stabs, and parries his way through waves of bigger and bigger baddies, even as it dawns on you that each encounter is molded in a fairly predictable pattern: Somebody identifies Wick, so they fight. Wick wins. Yet another nameless adversary drops dead. But taking on the task of saving his whole film–which is in need of a healthy dose of tension or stakes, or at least fun–proves too big a job even for this master assassin.</p>

<p><strong>Rating: 2.5/4</strong></p>

<p>Rotten Tomatoes: <a href="https://www.rottentomatoes.com/m/john_wick_chapter_3_parabellum">90%</a><br />
Metacritic: <a href="https://www.metacritic.com/movie/john-wick-chapter-3---parabellum">73</a></p>]]></content><author><name>Ryan Young</name></author><category term="writing" /><category term="movie" /><category term="review" /><summary type="html"><![CDATA[On the night I opened my browser to buy a ticket to see John Wick: Chapter 3, I had never actually seen any of the John Wick films before. Fortunately, the premise of this sequel-to-a-sequel isn’t terribly complicated: John Wick himself (Keanu Reeves) is a professional assassin who has run afoul of the High Table, a world-spanning secret society of assassins. Wick is a master of his occupation, which entails committing dozens upon dozens of grisly on-screen murders with improvised weapons. My movie buddies–human repositories of John Wick lore and repeat viewers of all his films–assured me the movie’s selling point would be its fight choreography. And at first, it indeed wowed me: The action started out as quirky, exhilarating fun. But as the gun-slinging, knife-slashing power fantasy dragged on, I couldn’t escape the feeling that that initial novel spark was gradually devolving into empty spectacle. The film makes some fine first impressions. It starts with Wick on the run, a bounty placed on his head by the High Table, getting stalked through the seedy streets of lower Manhattan by swarms of assailants disguised in the crowds. He’s outnumbered, outgunned, and can’t trust anybody–see that raggedly dressed hobo smoking a joint behind the dumpster? Blink twice, and now he’s aiming a handgun. But all of the High Table goons in the world are no match for Wick, who dispatches them with lethal efficiency, his spur-of-the-moment weapons ranging from pistols and throwing knives to horses and library books. Needless to say, this deadly ballet (the film’s metaphor, not mine; it introduces ballerina assassins) delivers all of the supremely inventive thrills action junkies could hope for, and the movie’s forward momentum is lock-step with Wick’s breakneck pace as he races through Manhattan’s neon-lit streets and alleyways. There’s even a scene in which Wick stabs his opponent through their eyeball–and, yes, it happens on camera. (My theater cheered.) The movie thoughtfully reveals a softer side of Wick, too–one that is paranoid, vulnerable, desperate–when he begs an old doctor friend of his, barred by the High Table from helping Wick under any circumstances, for a patch-up job for a knife fight even Wick couldn’t win unscathed. I assume that conflict between friendship and duty is supposed to be a recurring theme, even if it’s difficult to tell from the way the rest of the story unfolds. You see, the Adjudicator (Asia Kate Dillon), a very emotionless, bureaucratic, private investigator of sorts, has been assigned by the High Table to punish Wick’s associates–which involves interrogations, demotions, and swords, in that order–for aiding and abetting the last-movie murder spree that soured Wick’s relationship with the High Table in the first place. But the one who gets most screen time, by far, is Sofia (Halle Berry), who tag-teams with Wick for a Saharan shootout that’s many minutes too long–not one of which explores the anticipated consequences of her actions, be they a visit from the Adjudicator or otherwise. How not very satisfying. Not to mention the moment when (mild spoiler) someone fires a bullet into Sofia’s favorite attack dog, which survives–of course–thanks to body armor, the hallmark of a crowd-pleasing movie that’s playing things too safe. The remainder of this transparently it’s-for-the-fans film has little to offer besides guns. Lots of guns. Only a few stylistic embellishments offer any relief from the increasingly mind-numbing, over-the-top martial arts and automatic-fire action, featuring Wick pumping shotgun shell after shotgun shell into waves of anonymous body armor-clad soldiers. (Nobody in my theater was cheering now.) One hopes the film’s characters might infuse all of the mayhem with a sense of purpose, but the script simply doesn’t give them enough time to do so. I give Wick credit for suffering a major betrayal toward the end of the film, but it takes two compelling characters to tango, so that moment is robbed of its punch to the gut. And, let’s be honest: Any less-punch-more-talk scenes serve mostly to demonstrate that for all of Keanu Reeves’ acting talents, delivering sappy dialog isn’t one of them. You still root for Wick as he shoots, stabs, and parries his way through waves of bigger and bigger baddies, even as it dawns on you that each encounter is molded in a fairly predictable pattern: Somebody identifies Wick, so they fight. Wick wins. Yet another nameless adversary drops dead. But taking on the task of saving his whole film–which is in need of a healthy dose of tension or stakes, or at least fun–proves too big a job even for this master assassin. Rating: 2.5/4 Rotten Tomatoes: 90% Metacritic: 73]]></summary></entry></feed>