<?xml version="1.0" encoding="utf-8" ?>

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>www.benguild</title>
		<description>ベン・ギルド： Somewhere in the digital forest, making apps, video, hardware, and photographs.</description>

		<link>https://benguild.com</link>
		<atom:link href="https://benguild.com/rss" rel="self" type="application/rss+xml" />

		<lastBuildDate>Thu, 01 Jan 2026 00:23:31 -0800</lastBuildDate>
		<pubDate>Thu, 01 Jan 2026 00:23:31 -0800</pubDate>
		<ttl>1800</ttl>

		<image>
			<url>https://benguild.com/favicon-32x32.png</url>

			<title>www.benguild</title>
			<link>https://benguild.com</link>

			<width>32</width>
			<height>32</height>
		</image>

		<item>
			<title>Migrating Windows from Intel (x86) VirtualBox to ARM Windows on M1 Mac computers</title>
			<description><![CDATA[ <p>Before I switched to a <a href="https://www.amazon.com/Apple-MacBook-16-inch-10%E2%80%91core-24%E2%80%91core/dp/B09MSQX8QQ?&linkCode=ll1&tag=benguild-20&linkId=6eb1d4eeecfbd3f7e35b4325c7f36b19&language=en_US&ref_=as_li_ss_tl">2021 MacBook Pro</a> with the <a href="hhttps://en.wikipedia.org/wiki/Apple_M1#M1_Max">M1 Max</a> (<a href="https://en.wikipedia.org/wiki/Apple_silicon"><strong>Apple silicon</strong></a> using the <a href="https://en.wikipedia.org/wiki/ARM_architecture_family">ARM architecture</a>), I was on an older Intel-based (<a href="https://en.wikipedia.org/wiki/X86">x86</a>) MacBook Pro like everyone else using otherwise recent Mac hardware. One of the pieces of software I used on my machine rarely but intentionally was <a href="https://www.virtualbox.org"><strong>VirtualBox</strong></a>, a virtualization product that can be used to run additional operating systems on your Intel-based Mac (or other x86 machine) such as <a href="https://www.microsoft.com/windows">Microsoft Windows</a>.</p>

<p>However, since VirtualBox is a <a href="https://en.wikipedia.org/wiki/Hypervisor">hypervisor</a>, I couldn't simply run my x86 Virtual Machines this way anymore. The ARM architecture of Apple silicon couldn't simply be &#8220;partitioned&#8221; or shared with a virtualized software system that was expecting access to real x86 hardware.</p>

<p>I researched this situation a bit, and found that <a href="https://mac.getutm.app"><strong>UTM</strong></a> was a great way to run an ARM-supported version of Windows, but I still had the hope to access my prior Windows x86 installation from VirtualBox so that I could grab any files and software configurations that I still needed from there.</p>


<h3>Getting UTM + virtualizing &#8220;Windows on ARM&#8221;</h3>

<p><a href="https://mac.getutm.app"><strong>UTM</strong></a> (which is powered by <a href="https://en.wikipedia.org/wiki/QEMU">QEMU</a>) is a free download (although it costs something if you get it from the <a href="https://apps.apple.com/us/app/utm-virtual-machines/id1538878817?mt=12&amp;at=10l4JY">Mac App Store</a> instead of the website directly), and supports a lot more than just virtualizing macOS instances or emulating x86 architecture. It'll even let you boot older <a href="https://en.wikipedia.org/wiki/PowerPC">PowerPC</a> builds of <a href="https://en.wikipedia.org/wiki/Mac_OS_9">Mac OS 9</a>, for example.</p>

<figure>
	<img src="/upload/post/image/virtualbox-to-m1-mac-utm-arm-qemu/utm-virtualize-emulate.png" width="1042" height="764" alt="A screenshot of the UTM app for Mac kickstarting a new virtual machine, and the choice to &#8220;Virtualize&#8221; or &#8221;Emulate&#8221; depending on the host and virtual machine's hardware/software intercompatibility." />
	<figcaption>A screenshot of the UTM app for Mac kickstarting a new virtual machine, and the choice to &#8220;Virtualize&#8221; or &#8221;Emulate&#8221; depending on the host and virtual machine's hardware/software intercompatibility.</figcaption>
</figure>

<p>&#8220;<a href="https://docs.microsoft.com/windows/arm/">Windows on ARM</a>&#8221; is <strong>fast</strong> on UTM with the M1 Max. &#160;&#8230;It's a delight to use, and <a href="https://mac.getutm.app/gallery/windows-11-arm"><strong>not too difficult to set up</strong></a> either. &#160;🐇</p>


<h3>Booting VirtualBox x86 Virtual Disk Images</h3>

<p>UTM will also boot and emulate x86 virtual machines on Apple silicon, not just those running ARM-targeted operating systems. However, a more modern, demanding x86 operating system may have significantly reduced performance in comparison to native M1 performance.</p>

<p>Luckily, it <strong>may still be performant and stable enough</strong> for use in migrating files and settings to your new ARM-based Windows installation, for example, which can afterward run a lot of your x86-compiled Windows software instead.</p>

<h4>Steps I used:</h4>
<ol>
  <li>Create an additional new virtual machine in UTM (don't use your ARM Windows virtual machine for this), and choose to &#8220;<strong>Emulate</strong>&#8221; (rather than &#8220;Virtualize&#8221;) and then select the &#8220;<strong>Other</strong>&#8221; type for the system. <em>(&#8230;not the &#8220;Windows&#8221; one!)</em></li>
  <li>Select the &#8220;<strong>Skip ISO boot</strong>&#8221; option on the following screen. This will bypass the expectation that you'll install a new operating system.</li>
  <li>Once created, edit the Virtual Machine before booting it. <strong>Import your previous VirtualBox *.VDI disk image file as a new drive,</strong> which should automatically clone and convert it to the required format within UTM/QEMU without modifying the original source image.</li>
  <li>You may also need to <strong>uncheck the &#8220;UEFI Boot&#8221; option</strong> under &#8220;QEMU&#8221; settings for this virtual machine in order for it to start Windows.</li>
</ol>

<p>&#8230; Hopefully, after starting and an automatic subsequent reboot by Windows (after detecting the environment's massive hardware configuration change from when it ran under VirtualBox), you should see your Windows Desktop once again and then be able to migrate data via your host system to its new ARM-targeted virtual machine counterpart!</p>

<p>After migrating and verifying everything, I personally just shut down and deleted my x86 Windows virtual machine, because the ARM version runs so much faster natively by comparison. &#160;🐌</p>
 ]]></description>
			<link>https://benguild.com/2022/07/02/virtualbox-to-m1-mac-utm-arm-qemu/</link>
			<guid>https://benguild.com-/2022/07/02/virtualbox-to-m1-mac-utm-arm-qemu</guid>
			<pubDate>Sat, 02 Jul 2022 21:17:55 -0700</pubDate>
		</item>

		<item>
			<title>Regarding perception and dreaming…</title>
			<description><![CDATA[ <p>Excerpt from <a href="https://en.wikipedia.org/wiki/The_Four_Agreements"><strong>The Four Agreements</strong></a> by Don Miguel Ruiz:</p>
<blockquote>
  <p>What you are seeing and hearing right now is nothing but a dream. You are dreaming right now in this moment. You are dreaming with the brain awake.</p>
  <p>Dreaming is the main function of the mind, and the mind dreams twenty-four hours a day. It dreams when the brain is awake, and it also dreams when the brain is asleep. The difference is that when the brain is awake, there is a material frame that makes us perceive things in a linear way. When we go to sleep we do not have the frame, and the dream has the tendency to change constantly.</p>
  <p>Humans are dreaming all the time. Before we were born the humans before us created a big outside dream that we will call society's dream or <em>the dream of the planet</em>. The dream of the planet is the collective dream of billions of smaller, personal dreams, which together create a dream of a family, a dream of a community, a dream of a city, a dream of a country, and finally a dream of the whole humanity.</p>
</blockquote>
 ]]></description>
			<link>https://benguild.com/2021/02/27/dreams/</link>
			<guid>https://benguild.com-/2021/02/27/dreams</guid>
			<pubDate>Sat, 27 Feb 2021 21:32:25 -0800</pubDate>
		</item>

		<item>
			<title>Return anything: Amazon and eBay turn a blind-eye to “Missing Parts” or “Defective” merchandise</title>
			<description><![CDATA[ <p>Since the <a href="https://en.wikipedia.org/wiki/Dot-com_bubble">dot-com boom</a>, internet companies have driven a shift away from brick-and-mortar businesses where actual, real people would inspect returns before accepting them. I just wanted to do a quick post about the lingering return fraud in today's e-commerce industry.</p>

<p>By default, these platforms are so large and handle so much transaction volume that they tend to shift this return handling liability over to third-party sellers, since combating return fraud <strong>does not scale</strong> at a large internet business where the transaction volume per human worker is so great. Third-party sellers have little maneuverability here, as having a zero-friction resolution process would ensure that said process would be taken advantage of by dishonest buyers, dishonest sellers, or both&#8230; introducing a significant financial liability for these large-scale &#8220;middlemen on commission.&#8221;</p>

<p>But, as middlemen, these platforms have to intervene when something goes wrong. Platform success is fueled primarily by honest consumers buying product, but complaints tend to be honored without recourse, even if they are invalid or fraudulent. &#8212; <a href="https://www.reddit.com/r/UnethicalLifeProTips/comments/cu111i/ulpt_any_ebay_purchase_can_be_returned_without/">This Reddit post from 2019</a> (and the emotional responses from smaller sellers in its comments section) highlights the fact:</p>

<blockquote><p>ULPT: Any eBay purchase can be returned without penalty (including full refund of shipping both ways) by just saying it's &#8220;missing parts.&#8221; &#8212; eBay doesn't handle goods, so they will always side with the buyer. Zero exceptions. It's fully automated, and the seller has no recourse even by phone.</p></blockquote>

<p>&#8230; The major issue is that these problematic transactions tend to be infrequent or so small that they are difficult to address and handle effectively, fairly, and consistently. Platforms are fighting for customers and to retain marketshare, but they have to find a way to bear these costs and keep buyers happy, or they will have no sellers at all due to lack of buyer traffic. By shifting these troublesome transactions onto third-party sellers and treating them as liable by default, they've found a way to defer dealing with something very difficult and unappealing at scale, especially if the seller is given no recourse.</p>

<p>For larger sellers, return fraud is something they accept as an expense as a cost of participating in these platforms. Some buyers eventually get caught. For smaller sellers who just sell occasionally, it can really sting, especially on large ticket items or infrequent sales. The loss of the brick-and-mortar relationship and the ability to say &#8220;no&#8221; has left the door open to this faceless behavior, all to handle more transactions with less and less human involvement.</p>
 ]]></description>
			<link>https://benguild.com/2021/01/16/return-anything/</link>
			<guid>https://benguild.com-/2021/01/16/return-anything</guid>
			<pubDate>Sat, 16 Jan 2021 15:31:23 -0800</pubDate>
		</item>

		<item>
			<title>The first ski/snowboarding goggles I&apos;ve ever liked</title>
			<description><![CDATA[ <p>Back when I lived in <a href="/tagged/japan/" target="_self">Tokyo</a>, I used to hop on the high-speed &#8220;<a href="https://en.wikipedia.org/wiki/Shinkansen">shinkansen (新幹線)</a>&#8221; train to the mountains whenever it was forecasted to be a sunny, clear day&#8230; and ideally if there was some fresh snow scheduled to fall the night before. It was paradise, and the <a href="https://www.jre-travel.com/seasonal/ski/">JR SKISKI</a> train and lift ticket packages made it very affordable and convenient for both residents and foreigners.</p>

<p>However, this year, with <a href="https://en.wikipedia.org/wiki/Coronavirus_disease_2019"><strong>COVID-19</strong></a> still widely limiting travel worldwide, it was becoming a bit unclear how to best enjoy the winter season and participate in ski/snowboarding activities while working remotely.</p>



<h3>Remote Work &#8594; Jackson Hole, WY 🏂</h3>

<p>Given that many tech companies are offering some flexible work arrangements during the pandemic, three of us ended up renting a house in <a href="https://en.wikipedia.org/wiki/Jackson_Hole">Jackson Hole</a> for the season with a focus on isolating in a remote area.</p>

<figure>
    <img src="/upload/post/image/first-ski-goggles-liked/jackson-hole-mountain-resort.jpg" width="800" height="480" alt="A view from Jackson Hole Mountain Resort." />
    <figcaption>A view from Jackson Hole Mountain Resort.</figcaption>
</figure>

<p>We're up at a high altitude and experiencing all kinds of terrain and weather when skiing/snowboarding a few times a week. With this variability being the opposite of the sunny, clear, consistent days I'd targeted in past years in Japan, I knew the time had come to own real ski goggles for the first time in a long time.</p>

<figure>
    <img src="/upload/post/image/first-ski-goggles-liked/sunglasses-side-by-side.jpg" width="800" height="480" alt="The Smith ski goggles, beside my &#8220;all-weather&#8221; Smith sunglasses." />
    <figcaption>The Smith ski goggles, beside my &#8220;all-weather&#8221; Smith sunglasses.</figcaption>
</figure>

<p>What was I using before? <strong>Sunglasses.</strong> Why? Because in the past, I've absolutely hated ski goggles. They'd fog up, they can be super bulky, they limit your vision and get scratched&#8230; you name it. My experience with them had generally been awful.</p>



<h3>The latest goggles from Smith</h3>

<p>I swung by <a href="https://www.rei.com"><strong>REI</strong></a> before I left California and spoke to a few members of the team regarding my frustrations overall with ski goggles. Together, we looked at a few newer, high-end pairs.</p>

<figure>
    <img src="/upload/post/image/first-ski-goggles-liked/model.jpg" width="800" height="480" alt="Close-up of the lens and model." />
    <figcaption>Close-up of the lens and model.</figcaption>
</figure>

<p>The ones I ended up deciding on were the &#8220;<a href="https://www.amazon.com/Smith-Optics-Adult-Snowmobile-Goggles/dp/B07QVP7ZMH/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=benguild-20&linkId=4d451aed96cffef688a107cba5ef043e">Smith <strong>4D MAG ChromaPop</strong> Snow Goggles</a>&#8221; and so far they've been fantastic.</p>



<h4>Visibility + Light/Color</h4>

<p>One of the advantages of the 4D MAG is their reduced limitation of your peripheral vision. This is noticeably better than on their other models, and on models from other manufacturers as well.</p>

<p>In terms of color and light, I have zero complaints about the two interchangeable lenses it came with, as they offer really fantastic light transmission without feeling too &#8220;amber&#8221; or disorienting. In terms of <a href="https://en.wikipedia.org/wiki/Field_of_view">field of view</a>, I can look down or to the side without feeling like the goggles' edges are in the way, which is perfect.</p>



<h4>Anti-Fog + Fit</h4>

<p>Part of what causes goggles to fog is poor fit or seal, as this can block vents that would otherwise de-fog the lens or can trigger improper airflow due to a limited seal. Zero complaints with the 4D MAG, even while wearing a facemask.</p>

<p>They also offer a <strong>&#8220;low-bridge&#8221; fit</strong>, in case that fits your face better!</p>



<h3>Conclusion</h3>

<p>I've worn Smith sunglasses for years and <a href="https://www.amazon.com/Smith-Optics-Adult-Snowmobile-Goggles/dp/B07QVP7ZMH/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=benguild-20&linkId=4d451aed96cffef688a107cba5ef043e">these goggles</a> are great. They're not the cheapest pair in their collection (in fact, they're currently the most expensive) yet I was luckily able to pick up a pair of the same specification at a discount because they were from the prior model year. The frames had changed slightly between the two generations, but the lenses did not.</p>

<p>I unfortunately accidentally scratched one of the interchangeable lenses this past week while wiping out some ice that had built up inside, so my one word of caution with these and others is to avoid wearing them again until any ice has melted away. ⚠️</p>
 ]]></description>
			<link>https://benguild.com/2020/12/26/first-ski-goggles-liked/</link>
			<guid>https://benguild.com-/2020/12/26/first-ski-goggles-liked</guid>
			<pubDate>Sat, 26 Dec 2020 23:17:09 -0800</pubDate>
		</item>

		<item>
			<title>“Shh…” launched</title>
			<description><![CDATA[ <p><strong>UPDATE (2020/02/15):</strong> I'd initially released this project when it was only about two-thirds or so finished, but ultimately <strong>decided to shut it down</strong> due to a recent change in location and employment. &#8212; <a href="/contents/#%E2%80%9CShh%E2%80%A6%E2%80%9D" target="_self">More information <strong>about the project</strong></a> can be found on my portfolio.</p>

<p>I've been tinkering with <a href="https://shh.social"><strong>an app</strong> idea</a> in some of my free time this year, and wanted to release it before 2020. It's an <strong>end-to-end encrypted, anonymized publisher/messenger</strong>&#8230; sort of like a cross between <a href="https://signal.org">Signal</a> and <a href="https://twitter.com">Twitter</a>.</p>

<figure>
    <img src="/upload/post/image/shh-launched/shh.png" width="800" height="480" alt="The branding for &#8220;Shh&#8230;&#8221;" />
    <figcaption>The branding for &#8220;Shh&#8230;&#8221;</figcaption>
</figure>

<p>With it, you simply add one or more close friends to join a &#8220;network,&#8221; and then the devices will automatically exchange and maintain unique encryption keys to help keep your communications a secret from any possible eavesdroppers. 😃</p>

<p>It's a <strong>lightweight yet powerful</strong> concept. A few features are still missing that I'd hoped to include, but they'll perhaps show up sometime down the road.</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Tossed a little gem on the App Store 🤷🏼‍♂️ ... very V1 <a href="https://x.com/hashtag/holdmybeer?src=hash&amp;ref_src=twsrc%5Etfw">#holdmybeer</a> ... (🇺🇸 🇬🇧 🇨🇦 🇯🇵 US, UK, Canada, &amp; Japan only for now.) <a href="https://t.co/PZ1Efdtmag">https://t.co/PZ1Efdtmag</a></p>&mdash; Ben Guild ➐ (@benguild) <a href="https://x.com/benguild/status/1207485961485066240?ref_src=twsrc%5Etfw">December 19, 2019</a></blockquote>
<script async src="https://platform.x.com/widgets.js" charset="utf-8"></script>

<p>Messages from you and from others are routed to your friends and beyond, until they eventually &#8220;die out&#8221; in one of a number of different ways. You won't know who wrote what, but you can block authors (anonymously) or optionally &#8220;hide&#8221; any message&#8230; essentially removing it from your networks if your friends haven't already received it from you or somebody else. &#8212; Authors and readers alike will &#8220;<strong>level up</strong>&#8221; by participating, so it's a bit &#8220;gamified.&#8221; 🎮</p>

<p>The app's still very &#8220;V1,&#8221; but if you're interested in checking it out, it's currently on the iOS <a href="https://apps.apple.com/us/app/shh/id1450359613?mt=8"><strong>App Store</strong></a> for free. (🇺🇸 🇬🇧 🇨🇦 🇯🇵 Available in the US, UK, Canada, and Japan initially.)</p>

<p>And, yeah&#8230; it's just pronounced like the &#8220;🤫&#8221; emoji.&#160;&#160; 🤷🏼‍♂️📲</p>
 ]]></description>
			<link>https://benguild.com/2019/12/18/shh-launched/</link>
			<guid>https://benguild.com-/2019/12/18/shh-launched</guid>
			<pubDate>Wed, 18 Dec 2019 21:23:42 -0800</pubDate>
		</item>

		<item>
			<title>2.9-Inch ePaper Weather Display (Arduino powered/programmable) review and guide</title>
			<description><![CDATA[ <p>I recently relocated to Manhattan, and one of the things that I really wanted next to my apartment's front door was an <a href="https://www.amazon.com/ThingPulse-ESPaper-ESP8266-ePaper-display/dp/B075QMHTG2/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=benguild-20&linkId=07957a6e73899822fae8ffc5056ecd70&language=en_US"><strong>ePaper weather display</strong></a> with a three-hour segment forecast for the coming day. ☀️ &#8212; For those unfamiliar, &#8220;<a href="https://en.wikipedia.org/wiki/Electronic_paper">electronic paper</a>&#8221; (ePaper) is a display technology commonly seen in the <a href="https://www.amazon.com/Amazon-Kindle-Ereader-Family/b/ref=as_li_ss_tl?ie=UTF8&node=6669702011&linkCode=ll2&tag=benguild-20&linkId=e3dc0f7930d458805c7c6f861db09c97&language=en_US">Amazon Kindle</a> that mimics ink on actual paper.</p>

<p>&#8220;ePaper&#8221; uses very little power, and does not have a distracting backlight that might otherwise light up a dark apartment at night, so I figured that this might be the perfect fit for this project. At first, I actually considered <a href="https://www.galacticstudios.org/kindle-weather-display/">repurposing an old Kindle</a> for it, but ultimately decided against that for practicality's sake.</p>

<p>After looking around a bit, I found the <a href="https://www.amazon.com/ThingPulse-ESPaper-ESP8266-ePaper-display/dp/B075QMHTG2/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=benguild-20&linkId=07957a6e73899822fae8ffc5056ecd70&language=en_US">ThingPulse 2.9&quot; ESPaper Lite Kit, ESP8266 WiFi ePaper display</a> which is programmable with the Arduino IDE. &#8212; You'll probably also need:</p>

<ul>
    <li>A USB to TTL Serial Adapter, such as <a href="https://www.amazon.com/HiLetgo-CP2102-Converter-Adapter-Downloader/dp/B00LODGRV8/ref=as_li_ss_tl?keywords=CP2102&qid=1560704776&s=gateway&sr=8-3&linkCode=ll1&tag=benguild-20&linkId=a80fb8da6291f1d71226caf7f0168efc&language=en_US">this one</a>.</li>
    <li>The <a href="https://docs.thingpulse.com/guides/espaper-lite-kit/">setup guide</a> (which links to the weather software), and a computer to transfer the software with.</li>
    <li>An <a href="https://www.amazon.com/gp/product/B018RAVZ30/ref=as_li_ss_tl?ie=UTF8&psc=1&linkCode=ll1&tag=benguild-20&linkId=00a0de770f8c92d55496884f231364fa&language=en_US">inexpensive Micro-USB cable</a> to provide constant power.</li>
</ul>

<p>The ePaper board arrived pretty quickly, but the serial adapter that I ended up ordering was coming from China, so that took about a week more. &#8212; However, setup was easy since I was already familiar with the <a href="https://www.arduino.cc/en/Main/Software">Arduino IDE</a>!</p>

<figure>
	<img src="/upload/post/image/epaper-weather-display-arduino/downloading.gif" width="800" height="450" alt="Downloading the software using my Mac's Arduino IDE installation, and living on the edge by using gravity instead of solder to touch the programmer's pins to those of the main board." />
	<figcaption>Transferring the software using my Mac's Arduino IDE installation, and living on the edge by using gravity instead of solder to touch the programmer's pins to those of the main board.</figcaption>
</figure>

<p>&#8230; When it had finished downloading, it instantly booted and just worked. 🆒</p>

<p>So, I put it by my door (within range of my Wi-Fi) and connected some USB power to ensure that it'd continue to run standalone, indefinitely:</p>

<figure>
	<img src="/upload/post/image/epaper-weather-display-arduino/photo.jpg" width="800" height="600" alt="The configured board with connected Wi-Fi and USB power." />
	<figcaption>The configured board with connected Wi-Fi and USB power.</figcaption>
</figure>

<p>&#8230; <strong>Done</strong>! The two requests that I've put in to the software's developers are:</p>

<ul>
    <li><strong>Vertical display rotation.</strong> I chose to mount the board upside down so that the power cord was oriented to the left, which will require a software UI tweak to reorient the button text shown on the screen to match the actual corresponding hardware buttons. <a href="https://github.com/ThingPulse/espaper-weatherstation/issues/24">#24</a></li>
    <li><strong>Support for hiding the &#8220;Wi-Fi failure&#8221; screen and using some sort of other visual feedback instead.</strong> Occasionally, I've found the board temporarily won't reconnect to my Wi-Fi. You can manually reset it using one of the buttons on it (and it seems to retry on its own periodically), but I'd honestly prefer to only show the error after several repeat failures in order to not block the full weather report unless it's become seriously stale. <a href="https://github.com/ThingPulse/espaper-weatherstation/issues/25">#25</a></li>
</ul>

<p>&#8230; I might contribute some code to this repository in order to tweak these behaviors with new configuration options, although, because I personally haven't touched Arduino in a couple of years, it might take me a while to get around to that.</p>

<p>There are other great projects for signs/lighting for your home, too, such as the <a href="http://coldattic.info/post/98/">SF Muni LED Sign at Home with Raspberry Pi</a>, so feel free to get creative. &#8212; This ePaper display in particular can run other software that you write for it, so if you're into that, you could use it for a calendar or something else cool, too. 👍🏻</p>
 ]]></description>
			<link>https://benguild.com/2019/06/16/epaper-weather-display-arduino/</link>
			<guid>https://benguild.com-/2019/06/16/epaper-weather-display-arduino</guid>
			<pubDate>Sun, 16 Jun 2019 13:46:13 -0700</pubDate>
		</item>

		<item>
			<title>Received a cool shoutout from the Apple Security and Safari teams this week 🔐</title>
			<description><![CDATA[ <p>The iOS 12.3 update launched yesterday.</p>

<figure>
	<img src="/upload/post/image/apple-security-team-shoutout/screenshot.png" width="872" height="622" alt="A screenshot of Apple's article regarding the security content in yesterday's iOS 12.3 release." />
	<figcaption>A screenshot of the &#8220;<a href="https://support.apple.com/en-us/HT210118"><strong>About the security content of iOS 12.3</strong></a>&#8221; article on Apple's site.</figcaption>
</figure>

<p>&#8230; Thanks to both teams for the mention and their work on the patch! 🙌🏻</p>
 ]]></description>
			<link>https://benguild.com/2019/05/14/apple-security-team-shoutout/</link>
			<guid>https://benguild.com-/2019/05/14/apple-security-team-shoutout</guid>
			<pubDate>Tue, 14 May 2019 09:39:08 -0700</pubDate>
		</item>

		<item>
			<title>Set a custom, emoji-filled lock screen message on your new iPhone or iPad! (“how-to” tutorial)</title>
			<description><![CDATA[ <p>When I first fully switched to iPhone back in 2010, one of the features I immediately missed most from my <a href="/tagged/blackberry/" target="_self">BlackBerry</a> was the ability to set a <strong><a href="https://en.wikipedia.org/wiki/Lock_screen">lock screen</a> message</strong> for the device.</p>

<p>Essentially this lets you print a sentence or two of text that appears after you turn it on but before you unlock it, <strong>such as your name and an alternate phone number</strong>, in case you lose the device or if it gets mixed up with someone else's, as shown below:</p>

<figure>
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/lock-screen-message-demo.jpg" width="834" height="500" alt="Demo of the secret lock screen ability on iOS." />
	<figcaption>Demo of the secret lock screen ability on iOS.</figcaption>
</figure>

<p>&#8230; Well, there's a hidden way to have this on iOS, too! 😃</p>



<h3>Requirements and past alternatives</h3>

<p>Apple makes doing this fairly cumbersome on a personal device unless you're willing to erase it beforehand. My assumption is that this is probably to avoid upsetting the iPhone's simple visual design language&#8230; which certainly doesn't consist of random emoji-ridden text on the otherwise elegant default wallpapers that ship with each device. 😅</p>

<p>Before this was even possible on a system level, you'd previously had to actually <a href="https://benguild.com/2010/07/22/iphone-4-im-keeping-it/" target="_self">embed the text yourself into your device's wallpaper</a> (which of course you are free to customize), or just omit it altogether unless using <a href="https://en.wikipedia.org/wiki/Find_My_iPhone">Find My iPhone</a> to display a message temporarily if the phone is remotely disabled.</p>

<p>So, how do you do it? With <a href="https://en.wikipedia.org/wiki/Apple_Configurator">Apple Configurator</a>! &#8230;And, does it support emoji? <strong>Of course</strong>! <em>(Which is good, because there's limited horizontal space&#8230; depending on which device you have.)</em> &#8212; The caveat, though, is that a lock screen message can only be first added to a device that hasn't been setup yet (or that you can safely erase), and you must have a Mac that's running a fairly recent version of <a href="https://en.wikipedia.org/wiki/MacOS">macOS</a> in order to install the <a href="https://itunes.apple.com/us/app/apple-configurator-2/id1037126344?mt=12"><strong>latest</strong> Apple Configurator version</a> from the Mac App Store.</p>



<h3>Walkthrough</h3>

<p><em><strong>Warning:</strong> Proceed at your own risk. You'll need to have a new or freshly-erased iOS device that has not gone past the &#8220;Hello&#8221; screen that appears on first boot! &#8212; Yes, it is possible to restore from a backup, but once you have left this screen on the device, you'll need to erase the device again in order to proceed with this walkthrough.</em></p>

<p>On <a href="https://en.wikipedia.org/wiki/Black_Friday_(shopping)">Black Friday</a> this year, I was able to grab an <a href="https://www.amazon.com/Apple-iPad-11-inch-Wi-Fi-64GB/dp/B07K344J3N/ref=as_li_ss_tl?ie=UTF8&qid=1543585696&sr=8-3&keywords=iPad+Pro+11&linkCode=ll1&tag=benguild-20&linkId=5511af7a3257fe2935b72914f11cbe8a&language=en_US"><strong>11-inch iPad Pro</strong> (2018)</a> for a discount with the help of a friend. Glowing about the potential of being able to replace all of the paper notebooks and blah-blah pens in my work bag with a digital surface and an inkless drawing device, I'd checked out the tablet the day prior at an Apple Store, and ended up going for it. While my keyboard case still hasn't arrived yet (it's out of stock locally 😢), I still ended up writing this blog post on it without much trouble!️</p>



<h4>Step 1: Power up, but pause at &#8220;Hello&#8221; and go no further!</h4>

<p>To start, <strong>turn on</strong> your new or freshly-erased iOS device if it's powered off.</p>

<p>If you don't see the &#8220;<strong>Hello</strong>&#8221; screen that first appears when setting a device up from scratch, or if you've previously moved past this screen, you'll need to first backup your device and erase it. Proceed with caution and at your own risk.</p>

<figure>
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/ipad-unboxing-hello-konnichiwa.jpg" width="800" height="600" alt="My 2018 iPad Pro, at the &#8220;Hello&#8221; screen. (shown here displaying「こんにちは！」in Japanese)" />
	<figcaption>My 2018 iPad Pro, at the &#8220;Hello&#8221; screen. <em>(shown here displaying「こんにちは」in Japanese)</em></figcaption>
</figure>



<h4>Step 2: Connect to Mac via USB, and restore data from a backup if necessary.</h4>

<p>Once you're at the &#8220;<strong>Hello</strong>&#8221; setup screen of the iPhone or iPad, open <a href="https://itunes.apple.com/us/app/apple-configurator-2/id1037126344?mt=12">Apple Configurator</a> on your Mac with the device attached via USB.</p>

<p><strong>If you need to restore a backup using iTunes or Apple Configurator,</strong> do this from your Mac before proceeding to the next step <strong>and</strong> before moving past the &#8220;Hello&#8221; screen. &#8212; You'll probably want to quit Apple Configurator and use iTunes to restore the backup instead, because, at this time of writing, Apple Configurator will <strong>not</strong> restore your apps&#8230; but iTunes will.</p>

<p>You can quit iTunes when the restore completes and the phone is automatically rebooting, and when you restart iTunes again later, all previously synced content will begin to transfer automatically! 👍🏻</p>

<figure>
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-device-ready.jpg" width="1112" height="955" alt="An iPad, ready for configuration in Apple Configurator." />
	<figcaption>The iPad, ready for configuration in Apple Configurator.</figcaption>
</figure>



<h4>Step 3: &#8220;Prepare&#8221; and &#8220;supervise&#8221; the device using Apple Configurator</h4>

<p>Tap the button to &#8220;<strong>Prepare</strong>&#8221; the device in the toolbar at the top of Apple Configurator on your Mac, and follow the steps illustrated below to &#8220;supervise&#8221; it.</p>

<figure class="two">
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-device-supervise-1.png" width="660" height="540" alt="Supervising your iOS device (1)" />
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-device-supervise-2.png" width="660" height="540" alt="Supervising your iOS device (2)" />
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-device-supervise-3.png" width="660" height="540" alt="Supervising your iOS device (3)" />
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-device-supervise-4.png" width="660" height="540" alt="Supervising your iOS device (4)" />
</figure>



<h4>Step 4: Create your &#8220;lock screen message&#8221; profile</h4>

<p>Once supervised, in the &#8220;File&#8221; menu of Apple Configurator, select the &#8220;<strong>New Profile</strong>&#8221; option, and then give the profile a name and unique identifier:</p>

<figure>
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-profile-general.jpg" width="1038" height="974" alt="General iOS profile configuration settings in Apple Configurator." />
	<figcaption>General iOS profile configuration settings in Apple Configurator.</figcaption>
</figure>

<p>Afterward, edit the &#8220;Lock Screen Message&#8221; section as you please:</p>

<figure>
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-profile-message.jpg" width="1038" height="974" alt="Lock screen message settings in Apple Configurator." />
	<figcaption>Lock screen message settings in Apple Configurator.</figcaption>
</figure>

<p>&#8230; Finally, save the profile somewhere handy on your computer. 💾</p>



<h4>Step 5: Install the profile to your supervised device</h4>

<p>When you've finished creating the profile, just add it to your <strong>supervised iOS device</strong> from the main screen of Apple Configurator:</p>

<figure>
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-profile-add.jpg" width="1112" height="955" alt="Popover menu for adding a profile to a connected iOS device in Apple Configurator." />
	<figcaption>Popover menu for adding a profile to a connected iOS device in Apple Configurator.</figcaption>
</figure>

<p>You'll be able to add or remove any lock screen profile in the future on this device without having to erase it (since it's already supervised), in case you want to change the message later on. 👍🏻</p>

<figure>
	<img src="/upload/post/image/iphone-ipad-ios-lock-screen-message-configurator/configurator-profile-installed.jpg" width="1112" height="955" alt="Displaying an installed profile in Apple Configurator." />
	<figcaption>Displaying an installed profile in Apple Configurator.</figcaption>
</figure>

<p>&#8230; And, that's it! Enjoy being one of the few with a native lock screen message on iOS. 😄</p>
 ]]></description>
			<link>https://benguild.com/2018/12/02/iphone-ipad-ios-lock-screen-message-configurator/</link>
			<guid>https://benguild.com-/2018/12/02/iphone-ipad-ios-lock-screen-message-configurator</guid>
			<pubDate>Sun, 02 Dec 2018 18:18:58 -0800</pubDate>
		</item>

		<item>
			<title>Quickstart to a load-balanced HTTP/2 web-app with Google “K8s” Kubernetes Engine, Golang (Go), and TLS by Let&apos;s Encrypt. (Bonus: gRPC, too!)</title>
			<description><![CDATA[ <p>&#8220;<strong>Hello World.</strong>&#8221; That's what you'll be seeing soon from your <a href="https://en.wikipedia.org/wiki/Kubernetes"><strong>Kubernetes</strong></a> cluster if you follow this tutorial! 💬 &#8212; Or, alternatively, an empty &#8220;SayHello&#8221; request and response via <a href="https://en.wikipedia.org/wiki/GRPC"><strong>gRPC</strong></a>&#8230; a <a href="https://developers.google.com/protocol-buffers/docs/proto#enum">modern</a> and preferable alternative to <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">RESTful APIs</a>. (with <a href="https://en.wikipedia.org/wiki/JSON">JSON</a>, etc.)</p>

<figure>
    <img src="/upload/post/image/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/grpc-kubernetes.png" width="1200" height="800" alt="To expose gRPC on Google Kubernetes Engine via Ingress isn't currently straightforward." />
    <figcaption>To expose <strong>gRPC</strong> on Google Kubernetes Engine via Ingress isn't currently straightforward.</figcaption>
</figure>


<h3>Why not Google App Engine&#8230; or Cloud Functions?</h3>

<p>I'm a big fan of <a href="https://en.wikipedia.org/wiki/Google_App_Engine"><strong>Google App Engine</strong></a> (GAE) due to its &#8220;serverless&#8221; infrastructure and its ability to autoscale at a moment's notice to support huge surges of traffic&#8230; and, at a reasonable cost.</p>

<p>GAE even has built-in autoscaling <a href="https://en.wikipedia.org/wiki/Memcached"><strong>Memcached</strong></a> and <a href="https://en.wikipedia.org/wiki/Google_Cloud_Datastore"><strong>Datastore</strong></a>, and supports <a href="https://en.wikipedia.org/wiki/Let%27s_Encrypt"><strong>Let's Encrypt</strong></a> for no-cost, auto-renewing <a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS/TLS</a> support, as well. I've launched well-designed applications to huge sudden traffic spikes from sites like <a href="https://news.ycombinator.com">Hacker News</a> and <a href="https://www.producthunt.com">Product Hunt</a> without issue, and <a href="https://www.recode.net/2017/3/1/14661126/snap-snapchat-ipo-spending-2-billion-google-cloud">Snapchat even used GAE to scale its way to an IPO</a>. 📈 &#8212; But, it doesn't support <a href="https://developers.google.com/web/fundamentals/performance/http2/"><strong>HTTP/2</strong></a> through to the application at this time of writing! 😞 GAE is unfortunately a bit dated at this point, and based on its design it's unclear <a href="https://cloudplatform.googleblog.com/2015/10/Full-Speed-Ahead-with-HTTP2-on-Google-Cloud-Platform.html">whether it will ever fully support HTTP/2</a>.</p>

<p>By nature, HTTP/2 offers <strong>bidirectional flow of data</strong>, yet the load balancer on GAE converts all HTTP/2 traffic to unidirectional HTTP/1.1 requests. Although HTTP/1.x still dominates traditional web traffic today, modern apps and browsers are quickly adopting HTTP/2. &#8212; Also unfortunate is that <a href="https://cloud.google.com/functions/"><strong>Cloud Functions</strong></a>, a potential successor to GAE, shares a lot of GAE's underlying infrastructure currently, and therefore also does not fully support HTTP/2. &#8212; So, for this project, we're &#8220;rolling our own&#8221; solution instead! 🤷🏼‍♂️</p>

<figure>
    <img src="/upload/post/image/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/kubernetes-comparison.png" width="700" height="360" alt="Comparison of levels of control between Kubernetes Engine, App Engine, and Compute Engine on Google Cloud Platform." />
    <figcaption>Comparison of levels of control between <a href="https://cloud.google.com/kubernetes-engine/"><strong>Kubernetes Engine</strong></a> (GKE), <a href="https://cloud.google.com/appengine/"><strong>App Engine</strong></a> (GAE), and <a href="https://cloud.google.com/compute/"><strong>Compute Engine</strong></a> (GCE) on Google Cloud Platform. <em>(<a href="https://cloud.google.com/containers/">image credit</a>)</em></figcaption>
</figure>

<p>A well-configured Kubernetes setup can autoscale, although it has a higher upfront monthly cost and is less simple to configure and deploy. &#8212; If you go this route, hopefully this &#8220;quickstart&#8221; can help save you some time!</p>


<h3>The codebase I'm providing to help you</h3>

<p>There's some scaffolding you'll need to get started. I've <a href="https://github.com/benguild/gke-grpc-example">created a <strong>repository</strong></a> on GitHub that includes:</p>

<ul>
    <li>A <strong>gRPC server</strong>, and a separate <strong>plain HTTP/2 server</strong> for a &#8220;health check.&#8221; <em>(If you only want HTTP/2 not gRPC, just remove the gRPC dependencies and protos, and promote the health check server from its Go routine to replace the gRPC server that runs primarily!)</em> 👍🏻</li>
    <li>Code that generates <strong>self-signed certificates</strong> (for internal use) based on the node's IP address on launch. &#8212; Ingress will provide the client-facing TLS with a different certificate!</li>
    <li>A template &#8220;<strong>proto</strong>&#8221; and &#8220;<strong>service</strong>&#8221; for gRPC, with an empty request and response. <em>(For testing and verification from a client, but can be discarded if you're not using gRPC.)</em></li>
    <li><strong>Template deployable manifest files</strong> for the necessary additional configuration of <a href="https://cloud.google.com/kubernetes-engine/docs/concepts/ingress">Ingress on Google Cloud Platform</a>. <em>(For traffic and load balancing.)</em></li>
</ul>

<p>&#8230; This will save you some steps and trouble. 😉</p>

<p>You'll need to edit each file to adjust your project name, prefix, and probably write some scripts to help you deploy more conveniently than running commands manually each time, but by the end of the quickstart, you'll hopefully have something running that you can iterate on. 📝</p>

<p>Please feel free to provide comments or feedback below or to me directly that might help others, and I'll update this post accordingly if necessary!</p>


<h3>Let's deploy Kubernetes!</h3>

<p>First of all, clone the code repository to your local `<a href="https://github.com/golang/go/wiki/GOPATH"><strong>GOPATH</strong></a>` as a starting point: <a href="https://github.com/benguild/gke-grpc-example">https://github.com/benguild/gke-grpc-example</a> <em>(&#8230; I'm assuming that you have a local Golang environment setup already on your computer!)</em> 😅</p>

<p>Then, create a project on <a href="https://console.cloud.google.com">Google <strong>Cloud Console</strong></a> if you don't have one already to work with.</p>


<h4>Create a Kubernetes cluster</h4>

<p>For this step, visit the &#8220;<a href="https://console.cloud.google.com/kubernetes/list">Clusters</a>&#8221; tab of Kubernetes Engine on Google Cloud Console, and tap the &#8220;<strong>Create cluster</strong>&#8221; button. &#8212; You can also do this from the command-line if you want.</p>

<p>There are a lot of settings you can customize here, but since this is a &#8220;quickstart&#8221; and you'll probably need to customize these later for your application based on its specific needs, I'm going to suggest just the <strong>minimum specs</strong> here:</p>

<ul>
    <li>Choose the name &#8220;<strong>example-grpc</strong>&#8221; for your cluster to ease deployment. <em>(Once you're beyond the &#8220;Hello World&#8221; stage, you can refine the configuration and replace the cluster on your own.)</em></li>

    <li>Be sure to choose <strong>at least</strong> version &#8220;<strong>1.10.7-gke.6</strong>&#8221; or later. <em>(This will not work on older versions, as they do not support the &#8220;HTTP2&#8221; tag used in our configuration.)</em></li>

    <li>If you choose a zone aside from the default &#8220;<strong>us-central1-a</strong>&#8221; zone, you'll need to update your configuration and commands below to that zone instead, so please keep this in mind.</li>

    <li>Ensure that the &#8220;<strong>Enable HTTP load balancing</strong>&#8221; option is checked.</li>

    <li>
        If you tap the &#8220;<strong>Advanced Edit</strong>&#8221; button for your &#8220;Node pool&#8221; that's created by default, you can tweak the settings there based on what I've outlined in the screenshot below. <em>(To minimize costs to start, choose the &#8220;<strong>small</strong>&#8221; size for &#8220;Machine type&#8221; unless you're sure that your needs will exceed this right away!)</em> 💸

        <p><img src="/upload/post/image/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/node-pool-configuration.png" width="600" height="747" alt="An suggested introductory &#8220;node pool&#8221; configuation for a Kubernetes cluster." /></p>
    </li>

    <li>The other settings can be configured <strong>based on your needs</strong>. <em>(You should review and adjust them accordingly as necessary. Some of them <strong>cannot be adjusted again later</strong> without deleting and recreating your cluster and/or its node pools, so keep this in mind.)</em> ⚠️</li>
</ul>

<p>&#8230; Once your cluster is running, you'll be able to deploy a Docker image to it below! ✨</p>


<h4>Deploy the Docker image</h4>

<p>OK, so at this point you need: ☑️</p>

<ul>
    <li>Permission to modify a running Kubernetes cluster. <em>(as configured above)</em></li>

    <li>The <a href="https://github.com/benguild/gke-grpc-example"><strong>repository</strong> of code</a> and a local Golang (Go) environment.</li>

    <li>Both <a href="https://www.docker.com/products/docker-desktop">Docker</a> and the <a href="https://cloud.google.com/sdk/">Google Cloud SDK</a> both installed locally on your machine.</li>
</ul>

<p>&#8230; If you're all set, it's time to <strong>jump into the terminal</strong> to build and deploy the docker image templated out in the repository to the <a href="https://cloud.google.com/container-registry/">Google Container Registry</a> for use by your Kubernetes cluster. &#8212; I've used the project name twice here, so replace &#8220;<strong>example-grpc</strong>&#8221; with your project name:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">docker build <span class="nt">-t</span> gcr.io/example-grpc/example-grpc:demo <span class="nb">.</span>
docker push gcr.io/example-grpc/example-grpc:demo</code></pre></figure>

<p>&#8230; Then, let's run the image on the cluster:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">gcloud components <span class="nb">install </span>kubectl

gcloud auth configure-docker
gcloud container clusters get-credentials example-grpc <span class="nt">--zone</span> us-central1-a <span class="nt">--project</span><span class="o">=</span>example-grpc

<span class="c"># Format: gke_{project}_{zone}_{cluster}</span>
kubectl config use-context gke_example-grpc_us-central1-a_example-grpc
kubectl run example-grpc <span class="nt">--image</span><span class="o">=</span>gcr.io/example-grpc/example-grpc:demo
kubectl get pods</code></pre></figure>

<p>&#8230; Once that's done, you should have the image running on the cluster after a few minutes. You can monitor the status of it on the &#8220;<a href="https://console.cloud.google.com/kubernetes/list"><strong>Clusters</strong></a>&#8221; tab of Kubernetes Engine on Google Cloud Console, or via the command-line.</p>


<h4>Redeploying a new image to the cluster later</h4>

<p>If you need to replace and redeploy the image again later, just rebuild and push the image with a version name other than the original &#8220;demo&#8221; label, and then run:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">kubectl <span class="nb">set </span>image deployment/example-grpc example-grpc<span class="o">=</span>gcr.io/example-grpc/example-grpc:demo2</code></pre></figure>

<p>&#8230; That's it. ✅</p>


<h3>Configuring the Workload</h3>

<p>Once your cluster is running and the image is deployed, let's proceed to configure the &#8220;<a href="https://console.cloud.google.com/kubernetes/workload"><strong>workload</strong></a>&#8221; that has been created. &#8212; First, we'll be in the terminal, then back in Cloud Console for simplicity.</p>


<h4>Configure autoscaling</h4>

<p>On the page for your workload, in the &#8220;Actions&#8221; menu at the top, there's an &#8220;<strong>Autoscale</strong>&#8221; configuration option:</p>

<figure>
    <img src="/upload/post/image/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/workload-autoscaling.png" width="450" height="320" alt="The option to configure autoscaling for Google Kubernetes Engine (GKE) workloads." />
    <figcaption>The option to configure autoscaling for Google Kubernetes Engine (GKE) workloads.</figcaption>
</figure>

<p>&#8230; If you're going to be using your cluster in production or doing heavier testing, you'll want to configure this.</p>


<h4>Override the &#8220;health check&#8221; port and path</h4>

<p>&#8230; You can skip this step if you won't be running gRPC!</p>

<p>The tricky thing about <a href="https://cloud.google.com/kubernetes-engine/docs/concepts/ingress">Ingress</a>, which we'll need to expose our service to the internet, is that by default it will stop serving your application unless the main HTTP service responds with a successful response code (i.e. between 200 and 299, inclusive) on the &#8220;/&#8221; root path of your HTTP server. &#8212; By default, <strong>gRPC does not do this</strong>!</p>

<p>Luckily, there's a way to override it, but it's not obvious or straightforward. &#8212; You can force the health check to another port and/or path alongside your default server if you're planning on running gRPC or some service that does not respond on &#8220;/&#8221; with this addition to your workload's YAML.</p>

<p>On the Cloud Console page for your workload, click the &#8220;<strong>YAML</strong>&#8221; tab, and edit this in under the &#8220;spec&#8221; &#8594; &#8220;template&#8221; &#8594; &#8220;spec&#8221; &#8594; &#8220;containers&#8221; indentation:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml">        <span class="na">livenessProbe</span><span class="pi">:</span>
          <span class="na">httpGet</span><span class="pi">:</span>
            <span class="na">path</span><span class="pi">:</span> <span class="s">/_ah/health</span>
            <span class="na">port</span><span class="pi">:</span> <span class="m">8081</span>
            <span class="na">scheme</span><span class="pi">:</span> <span class="s">HTTPS</span>
        <span class="na">readinessProbe</span><span class="pi">:</span>
          <span class="na">httpGet</span><span class="pi">:</span>
            <span class="na">path</span><span class="pi">:</span> <span class="s">/_ah/health</span>
            <span class="na">scheme</span><span class="pi">:</span> <span class="s">HTTPS</span>
            <span class="na">port</span><span class="pi">:</span> <span class="m">8081</span>
        <span class="na">ports</span><span class="pi">:</span> <span class="c1"># Replace the existing one for this...</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8081</span>
          <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span></code></pre></figure>

<p>&#8230; That's all there is to it. 👍🏻</p>

<p>You might not need this part, but if you'll be running an unmodified gRPC server at this time of writing, your health check will definitely otherwise fail without some other hack to the server software itself.</p>


<h3>Exposing the service via Ingress and HTTP/2 + TLS</h3>

<p>OK, hopefully final steps here.</p>

<p>&#8230; Since there are <strong>multiple cluster nodes/pools/pods</strong> involved, we'll need to create an <a href="https://cloud.google.com/kubernetes-engine/docs/concepts/ingress">Ingress</a> resource, which will create a load balancer automatically that serves as a single client-facing resource. <em>(<strong>NOTE</strong>: Each <a href="https://cloud.google.com/load-balancing/">Google Load Balancer</a> currently has an unavoidable base cost of around $18 USD/month at this time of writing, even only for very light or experimental usage.)</em></p>

<p>There are a few different ways to implement Let's Encrypt when running Kubernetes, but many of them require maintaining and configuring your own load balancer. &#8212; Doing this instead via Google's own offering is fairly smooth once it's up and running, though! 🤔</p>


<h4>Create a &#8220;NodePort&#8221; for your Workload</h4>

<p>First, apply the &#8220;<a href="https://github.com/benguild/gke-grpc-example/blob/master/manifest/nodeport.yaml"><strong>manifest/nodeport.yaml</strong></a>&#8221; file from the repository to create a <a href="https://kubernetes.io/docs/concepts/services-networking/service/">NodePort</a> that our Ingress load balancer can point to:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">kubectl apply <span class="nt">-f</span> manifest/nodeport.yaml</code></pre></figure>

<p>&#8230; Note that in this YAML file, it has the same ports as defined in our &#8220;<strong>livenessProbe</strong>&#8221; and &#8220;<strong>readinessProbe</strong>&#8221; YAML addition to our workload that we set above. <em>(These need to match what we've setup.)</em> &#8212; If you skipped that step because you're not serving gRPC, tweak this file to only have the one port you're serving on before applying it.</p>


<h4>If you'll be using Let's Encrypt, generate a throwaway, self-signed TLS certificate as a placeholder for Ingress</h4>

<p>&#8230; If you aren't going to use <a href="https://en.wikipedia.org/wiki/Let%27s_Encrypt"><strong>Let's Encrypt</strong></a>, you can skip this step.</p>

<p>Since HTTP/2 enforces TLS, even if we're using Let's Encrypt at the Ingress level (instead of a certificate we've obtained ourselves from another certificate authority), this whole thing will break down without a TLS certificate of some kind in place from the beginning.</p>

<p>So, one solution to this is to just generate a new authority and certificate locally that lasts for 1,000 years and is for &#8220;<strong>localhost</strong>&#8221; or some other hostname never used in production:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">mkdir </span>tls

openssl genrsa <span class="nt">-des3</span> <span class="nt">-out</span> tls/ca.key 2048
openssl req <span class="nt">-x509</span> <span class="nt">-new</span> <span class="nt">-nodes</span> <span class="nt">-key</span> tls/ca.key <span class="nt">-sha256</span> <span class="nt">-days</span> 365000 <span class="nt">-out</span> tls/ca.key.pem <span class="nt">-subj</span> <span class="s2">"/CN=localhost"</span>

openssl req <span class="nt">-new</span> <span class="nt">-sha256</span> <span class="nt">-nodes</span> <span class="nt">-out</span> tls/ca.csr <span class="nt">-newkey</span> rsa:2048 <span class="nt">-keyout</span> tls/key.pem <span class="nt">-subj</span> <span class="s2">"/CN=localhost"</span>
openssl x509 <span class="nt">-req</span> <span class="nt">-in</span> tls/ca.csr <span class="nt">-CA</span> tls/ca.key.pem <span class="nt">-CAkey</span> tls/ca.key <span class="nt">-CAcreateserial</span> <span class="nt">-out</span> tls/crt.pem <span class="nt">-days</span> 365000 <span class="nt">-sha256</span></code></pre></figure>

<p>After generating these files, convert them to Base64 encoding and then copy-paste the output into the &#8220;<a href="https://github.com/benguild/gke-grpc-example/blob/master/manifest/ingress-secret.yaml"><strong>manifest/ingress-secret.yaml</strong></a>&#8221; file:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">cat </span>tls/crt.pem | <span class="nb">base64
cat </span>tls/key.pem | <span class="nb">base64</span></code></pre></figure>

<p>&#8230; OK, that's all the Ingress needs to be created and be &#8220;valid,&#8221; even though we won't serve anything on its default &#8220;frontend configuration&#8221; if we're using Let's Encrypt.</p>

<p>Note that these instructions for <a href="https://en.wikipedia.org/wiki/OpenSSL">OpenSSL</a> are for the <strong>current version of macOS</strong> at this time of writing (10.14.1), so if you're on another platform or version, you might need to update or adjust these slightly to get the same output.</p>


<h4>If not using Let's Encrypt, create a static IP address and modify the Ingress manifest file to use it</h4>

<p>If you're using a certificate signed by another certificate authority (not Let's Encrypt), you'll want to create a static IP address for your Ingress beforehand using the &#8220;<strong>gcloud</strong>&#8221; command-line tool, since you'll be using the default auto-generated frontend configuration for your service in production: <em>(Otherwise, an ephemeral IP address will be auto-assigned, and you could lose that address in the future for some unexpected reason.)</em> ⚠️</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">gcloud compute addresses create <span class="nt">--project</span><span class="o">=</span>example-grpc <span class="nt">--global</span> example-grpc-ipv4</code></pre></figure>

<p>&#8230; I included a commented out annotation line for assigning this in the &#8220;<a href="https://github.com/benguild/gke-grpc-example/blob/master/manifest/ingress.yaml"><strong>manifest/ingress.yaml</strong></a>&#8221; file, which you can uncomment and adjust based on the name given to the IP address. <em>(above)</em></p>


<h4>Finally, apply the secret and create the Ingress</h4>

<p>Copy and paste the Base64-encoded certificate and key into the &#8220;<a href="https://github.com/benguild/gke-grpc-example/blob/master/manifest/ingress-secret.yaml"><strong>manifest/ingress-secret.yaml</strong></a>&#8221; file of the repository if you haven't already, and apply this and the &#8220;<a href="https://github.com/benguild/gke-grpc-example/blob/master/manifest/ingress.yaml"><strong>manifest/ingress.yaml</strong></a>&#8221; files in that order:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">kubectl apply <span class="nt">-f</span> manifest/ingress-secret.yaml
kubectl apply <span class="nt">-f</span> manifest/ingress.yaml</code></pre></figure>

<p>Cool, now you should see an Ingress creating under your &#8220;<a href="https://console.cloud.google.com/net-services/loadbalancing/loadBalancers/list"><strong>Load balancers</strong></a>&#8221; list on Cloud Console, and your service should begin serving soon&#8230; unless you still need Let's Encrypt for TLS! ✅</p>


<h3>Configure Ingress for Let's Encrypt</h3>

<p>If you're dying to use <a href="https://en.wikipedia.org/wiki/Let%27s_Encrypt"><strong>Let's Encrypt</strong></a> (instead of a manually-renewed certificate obtained from another certificate authority) and have followed the steps above so far to do so, you can <strong>add another &#8220;frontend configuration&#8221;</strong> to the Ingress load balancer for your cluster at this point.</p>

<p>&#8230; This additional frontend configuration will use the same health check and backend that should already be healthy! &#8212; <strong>But</strong>, a few things to keep in mind:</p>

<ul>
    <li>You <strong>should not modify</strong> or remove the default &#8220;frontend configuration&#8221; or &#8220;backend configuration&#8221; that the redeployable &#8220;<a href="https://github.com/benguild/gke-grpc-example/blob/master/manifest/ingress.yaml"><strong>manifest/ingress.yaml</strong></a>&#8221; file created, but you should be OK by just adding another frontend configuration. <em>(At this time of writing, reapplying that manifest file will recreate or adjust these default frontend and backend configurations without affecting the other that you've created, but will revert any changes you made to the original configurations manually if you did so.)</em> 🛑</li>

    <li>It may eventually (later) become possible to use Let's Encrypt <strong>immediately when deploying Ingress using the manifest file</strong> instead of using the placeholder self-signed certificate, but it is not currently at this time of writing.</li>

    <li>The default ephemeral IP on the load balancer will still serve the backend using the self-signed certificate we created before, but this is redundant and would have to be explicitly trusted by clients, so it wouldn't be advertised or utilized in production and should just be ignored instead.</li>
</ul>

<p>&#8230; Adding another &#8220;frontend configuration&#8221; without modifying anything existing is actually super straightforward via &#8220;<a href="https://console.cloud.google.com/net-services/loadbalancing/loadBalancers/list"><strong>Load balancers</strong></a>&#8221; on Cloud Console:</p>

<figure>
    <img src="/upload/post/image/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/lets-encrypt-google-cloud-load-balancer-frontend-configuration.png" width="850" height="800" alt="Screenshot with suggested settings for creating a frontend configuration with Let's Encrypt using a Google Cloud managed certificate." />
    <figcaption>Screenshot with suggested settings for creating a frontend configuration with Let's Encrypt using a Google Cloud managed certificate.</figcaption>
</figure>

<p>When assigning a certificate to the &#8220;frontend configuration&#8221; that you're creating, to use Let's Encrypt you can simply select to use a &#8220;Google-managed certificate&#8221; rather than uploading one manually:</p>

<figure>
    <img src="/upload/post/image/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/google-cloud-managed-tls-certificate-lets-encrypt.png" width="550" height="450" alt="Let's Encrypt is available via Cloud Console using &#8220;Google-managed&#8221; certificate." />
    <figcaption>Let's Encrypt is available via Cloud Console by using a &#8220;Google-managed&#8221; certificate!</figcaption>
</figure>

<p>&#8230; Cool! Once everything has updated, you should now be serving gRPC over TLS with Let's Encrypt on Kubernetes with a passing health check. 👍🏻</p>
 ]]></description>
			<link>https://benguild.com/2018/11/11/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/</link>
			<guid>https://benguild.com-/2018/11/11/quickstart-golang-kubernetes-grpc-tls-lets-encrypt</guid>
			<pubDate>Sun, 11 Nov 2018 23:20:35 -0800</pubDate>
		</item>

		<item>
			<title>Wi-Fi and Cellular Data calling finally comes to Google Voice app on iOS?</title>
			<description><![CDATA[ <p>Today in the <a href="https://itunes.apple.com/us/app/google-voice/id318698524?mt=8">iOS app</a> for <a href="https://en.wikipedia.org/wiki/Google_Voice"><strong>Google Voice</strong></a>&#8230; I saw something that I'd been waiting for for a long time:</p>

<figure>
    <img src="/upload/post/image/google-voice-ios-mobile-data-wifi/modal.png" width="1125" height="965" alt="A modal offering the option to enable calling over Wi-Fi and Cellular Data in the Google Voice iOS app." />
    <figcaption>A modal offering the option to enable calling over Wi-Fi and Cellular Data.</figcaption>
</figure>

<p>&#8230; <strong>Finally!</strong> Is everyone else seeing this, too? <em>(although, probably not in Japanese&#8230;)</em></p>

<p>Now, we should be able to place calls just using our iPhone's active internet connection and not the traditional phone system when using the actual Google Voice app, and without needing <a href="https://en.wikipedia.org/wiki/Google_Hangouts">Hangouts</a>, third-party apps, or <a href="https://en.wikipedia.org/wiki/Google_Voice#Integration_into_Gmail_and_Google_Talk">Gmail</a> on the web. 🤩</p>

<p><strong>UPDATE:</strong> Users <a href="https://www.reddit.com/r/ios/comments/9evlr7/wifi_and_cellular_data_calling_finally_comes_to/e5s09rt/">on Reddit</a> are reporting that <a href="https://voice.google.com/settings">opting-into the beta for <strong>internet calling on the web version</strong></a> might be enough to trigger this modal. &#8212; If you don't see it appear on iOS, try that first, and then reboot the iOS app! 🤔</p>
 ]]></description>
			<link>https://benguild.com/2018/09/03/google-voice-ios-mobile-data-wifi/</link>
			<guid>https://benguild.com-/2018/09/03/google-voice-ios-mobile-data-wifi</guid>
			<pubDate>Mon, 03 Sep 2018 22:33:04 -0700</pubDate>
		</item>
	</channel>
</rss>