<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Wordspeak</title><link>https://www.wordspeak.org/</link><description>Edwin's writings.</description><atom:link href="https://www.wordspeak.org/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Sun, 16 Apr 2023 04:24:50 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Exploring Australian indigenous bible translation</title><link>https://www.wordspeak.org/posts/exploring-australian-indigenous-bible-translation.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;Everyone deserves to have access to the bible in their native language.&lt;/p&gt;
&lt;p&gt;Given the large number of Australian indigenous languages, I wanted to see how far data could take me towards answering the question &lt;em&gt;Which Australian indigenous language group is most in need of bible translation?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It turns out that data can go a long way. As to the question itself:&lt;/p&gt;
&lt;p&gt;Spoiler: It's the &lt;a href="https://language-explorer.wordspeak.org/language/iso/kux.html"&gt;Kukatja&lt;/a&gt; people in Western Australia.&lt;/p&gt;
&lt;p&gt;I answered this question by writing some software to aggregate data on bible translation efforts, language groups, language relationships, speaker count and locations and English language competency. I used data from the &lt;a href="https://www.joshuaproject.net"&gt;Joshua Project&lt;/a&gt;, the &lt;a href="https://wals.info"&gt;World Atlas of Language Structure (WALS)&lt;/a&gt;, the &lt;a href="https://www.abs.gov.au/websitedbs/D3310114.nsf/Home/Census"&gt;2011 Australian Census&lt;/a&gt;, &lt;a href="https://iso639-3.sil.org/"&gt;SIL's ISO language code mappings&lt;/a&gt;, &lt;a href="https://find.bible"&gt;Find A Bible&lt;/a&gt;, &lt;a href="https://collection.aiatsis.gov.au/austlang/about"&gt;AIATSIS AUSTLANG&lt;/a&gt; and &lt;a href="http://archives.samuseum.sa.gov.au/tindaletribes/index.html"&gt;Tindale's Catalogue of Australian Aboriginal Tribes&lt;/a&gt; and after a bit of recent inspiration and rework I've published the output from the tool. Keep in mind that this was written as a proof-of-concept - the user interface has plenty of rough edges, and the data has only had limited review.&lt;/p&gt;
&lt;p&gt;You can see it at &lt;a href="https://language-explorer.wordspeak.org"&gt;https://language-explorer.wordspeak.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Why the Kukatja?&lt;/h2&gt;
&lt;p&gt;Most importantly, they don't have &lt;em&gt;any&lt;/em&gt; of the bible in their native language. Given the first book of the bible to be translated is usually one of the gospels, a language group with even a single book has access to the key parts of the message of Jesus. Given everyone should have some chance to read about Jesus, it's important to prioritise a giving that first opportunity.
Secondly, while the Kukatja aren't the largest language group without bible access (that would be the &lt;a href="https://language-explorer.wordspeak.org/language/iso/ddj.html"&gt;Jaru&lt;/a&gt;) their low (self-assessed) ability in Australia's national language, English, means that they can't use an English-language bible either, so they're quite stuck.&lt;/p&gt;
&lt;p&gt;Here's output from the software showing only language groups without a bible (filter by &lt;em&gt;No Scripture&lt;/em&gt;) and sorted by count of non English language readers.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://language-explorer.wordspeak.org/table.html" title="Language table showing Kukatja as most in need"&gt;
 &lt;img class="ri" src="https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_375/language-table" sizes="(max-width: 50em) 100vw,
          (min-width: 50em) 66vw" srcset="https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_180/language-table 180w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_375/language-table 375w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_768/language-table 768w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_1536/language-table 1536w" alt="Language table showing Kukatja as most in need"&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The software also has a map of language groups, with size corresponding to speaker count and colour showing scripture access (red circles are groups without any scripture, yellow circles are groups with only a single book of scripture)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://language-explorer.wordspeak.org/map.html" title="Filterable language map"&gt;
 &lt;img class="ri" src="https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_375/language-map1" sizes="(max-width: 50em) 100vw,
          (min-width: 50em) 66vw" srcset="https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_180/language-map1 180w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_375/language-map1 375w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_768/language-map1 768w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_1536/language-map1 1536w" alt="Filterable language map"&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The data shown in the software is a few years old, so it's quite possible that the Kukatja and the Jaru people have a bible translation underway, but I think there's promise in combining a data-driven approach with God's leading as we work towards giving everyone access to the bible in their native language. If you're interested in current Australian indigenous bible translation efforts, take a look at &lt;a href="https://www.aboriginalbibles.org.au"&gt;Aboriginal Bibles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The source code for the software is available on &lt;a href="https://github.com/edwinsteele/language_explorer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/exploring-australian-indigenous-bible-translation.html</guid><pubDate>Mon, 05 Aug 2019 06:53:00 GMT</pubDate></item><item><title>Accessibility - the old gray mare, she ain't what she used to be</title><link>https://www.wordspeak.org/posts/accessibility-the-old-gray-mare-she-aint-what-she-used-to-be.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;My eyes aren't getting any better as the years pass, and so I'm more aware of things that improve website usability for those who don't have awesome vision or who read the web through a screen-reader.&lt;/p&gt;
&lt;p&gt;I've recently improved the accessibility of this site by increasing the contrast of page elements and providing better information for a screen reader (removed redundant information and added it where it was missing).&lt;/p&gt;
&lt;p&gt;If you're wanting to improve the accessibility of your site (and it'd be great if you did), you'll get some practical, easy-to-implement guidance from the Accessibility section of Chrome's &lt;a href="https://developers.google.com/web/tools/lighthouse/"&gt;Lighthouse audit tool&lt;/a&gt; and from WebAIM's &lt;a href="http://wave.webaim.org"&gt;WAVE tool&lt;/a&gt;. Even if you pick a few higher priority suggestions and come back to the others, it'll be an improvement for someone with reduced vision who reads your site. &lt;em&gt;Future you&lt;/em&gt; says thanks.&lt;/p&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/accessibility-the-old-gray-mare-she-aint-what-she-used-to-be.html</guid><pubDate>Sat, 06 Oct 2018 07:34:51 GMT</pubDate></item><item><title>Making the Touch Bar useful</title><link>https://www.wordspeak.org/posts/making-the-touch-bar-useful.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;I've had a Touch Bar equipped Macbook Pro for about 6 months. Until recently, I only used the Touch ID sensor with any regularity; I couldn't see a use for the other buttons and the switch from buttons to sliders was a usability regression. I recently read &lt;a href="http://vas3k.com/blog/touchbar/"&gt;Making the Touch Bar finally useful&lt;/a&gt; and discovered how &lt;a href="https://folivora.ai/"&gt;BetterTouchTool&lt;/a&gt; can be used to customise the touch bar. Wow.&lt;/p&gt;
&lt;p&gt;Here's my new touch bar:.&lt;/p&gt;
&lt;p&gt; &lt;img class="ri" src="https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_375/touch-bar_ele2ra" sizes="(max-width: 50em) 100vw,
          (min-width: 50em) 66vw" srcset="https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_180/touch-bar_ele2ra 180w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_375/touch-bar_ele2ra 375w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_768/touch-bar_ele2ra 768w,
           https://res.cloudinary.com/wordspeak/image/upload/f_auto%2Cq_auto%2Cw_1536/touch-bar_ele2ra 1536w" alt="My customised Touch Bar"&gt;
&lt;/p&gt;
&lt;p&gt;From right-to-left:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The escape key. As a user of an Apple iPad keyboard that lacks an escape key, I've mostly retrained muscle memory to use &lt;code&gt;ctrl+[&lt;/code&gt; for escape. I still use the key occasionally (and I'd still rather have a physical key).&lt;/li&gt;
&lt;li&gt;Battery info. I had battery info in the menu bar but the Touch Bar is more noticeable, and the extra space allows for more info. This helps me be more aware of battery-hungry apps, and now my battery lasts longer.&lt;/li&gt;
&lt;li&gt;ADSL connection info. My ADSL model exposes this info by SNMP. It was previously in the menu bar as a &lt;a href="https://github.com/edwinsteele/bitbar-plugins/blob/13752ceb419d07bf7b6cf1f32d709ba06f7b4a10/Network/wan_status.10m.sh"&gt;custom plugin&lt;/a&gt; for &lt;a href="https://getbitbar.com/"&gt;BitBar&lt;/a&gt; and, like the battery info, it's more visible and useful in the Touch Bar.&lt;/li&gt;
&lt;li&gt;www.wordspeak.org ping time. This is an indication of my upstream connection quality, not as monitoring. When touched, it opens an &lt;a href="https://github.com/jwilm/alacritty"&gt;Alacritty&lt;/a&gt; terminal to my hosting machine, with the appropriate colour scheme.&lt;/li&gt;
&lt;li&gt;Gateway ping time. Like the other ping time widget, this gives me an indication of link congestion, which often happens when devices are doing cloud backups or uploads. When touched, it also opens a &lt;a href="https://github.com/edwinsteele/dotfiles/blob/master/alacritty-gateway.yml"&gt;custom Alacritty terminal&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Coffee time! Puts the laptop to sleep.&lt;/li&gt;
&lt;li&gt;Volume and brightness. Buttons, not sliders. The Touch Bar is a small target, and I found it hard to set the brightness or volume correctly with the sliders that Apple uses in the default configuration.&lt;/li&gt;
&lt;li&gt;Weather. Live, local weather from the &lt;a href="https://www.bom.gov.au"&gt;Bureau of Meteorology&lt;/a&gt; implemented in &lt;a href="https://github.com/edwinsteele/dotfiles/blob/master/btt-weather.sh"&gt;shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Clock. It's in the menu bar too, but I notice it more in this location.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Touch Bar is a well implemented piece of technology, with a poor default configuration. &lt;a href="https://www.folivora.ai/buy"&gt;Paying a few dollars for a BTT licence&lt;/a&gt; to make it useful is a good move.&lt;/p&gt;&lt;/div&gt;</description><category>Technology</category><guid>https://www.wordspeak.org/posts/making-the-touch-bar-useful.html</guid><pubDate>Fri, 11 May 2018 20:58:50 GMT</pubDate></item><item><title>Smaller images with single-step compression</title><link>https://www.wordspeak.org/posts/smaller-images-with-single-step-compression.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;I'm reviewing image sizes to improve download times in my photo galleries and I've obtained the smallest file sizes by performing a single compression step rather than allowing each tool to perform compression during my image pipeline.&lt;/p&gt;
&lt;p&gt;Each tool in my workflow has defaults that work well if there is no subsequent or proceeding compression, but produce sub-optimal results when used in an image pipeline where each tool performs compression. My workflow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load and edit photos in Apple's Photos.app. &lt;/li&gt;
&lt;li&gt;Export from Photos.app. I choose a "JPEG Quality" level at export time.&lt;/li&gt;
&lt;li&gt;Stamp copyright and licencing info using ExifTool&lt;/li&gt;
&lt;li&gt;Resize images as a part of image gallery creation using Pillow, which generally involves a compression step&lt;/li&gt;
&lt;li&gt;Perform final optimisation using imageOptim. I've used this tool in the past to reduce JPEG sizes with great success and it's consistently given me the best image compression. Adding this step was the trigger point for this investigation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I did experiments on my &lt;a href="https://images.wordspeak.org/arches/"&gt;Arches gallery&lt;/a&gt; whose photos have a total uncompressed size of 103.6MB. I tried four permutations of compression with the results below, noting that the Pillow step also includes resizing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compression by Photos.app, Pillow and imageOptim: Final size 4.6MB&lt;ul&gt;
&lt;li&gt;Photos.app (medium quality) &lt;em&gt;103.6MB → 19.1MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Pillow (75% quality) &lt;em&gt;19.1MB → 5.2MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;imageOptim (74% quality) &lt;em&gt;5.2MB → 4.6MB&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compression by Pillow and imageOptim: Final size 4.6MB&lt;ul&gt;
&lt;li&gt;Photos.app (maximum quality) &lt;em&gt;103.6MB → 103.6MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Pillow (75% quality) &lt;em&gt;103.6MB → 5.2MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;imageOptim (74% quality) &lt;em&gt;5.2MB → 4.6MB&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compression by JPEGmini and imageOptim: Final size 4.3MB&lt;ul&gt;
&lt;li&gt;Photos.app (maximum quality) &lt;em&gt;103.6MB → 103.6MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Pillow (100% quality) &lt;em&gt;103.6MB → 27.8MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;JPEGmini (no user-selectable settings) &lt;em&gt;27.8MB → 7.1MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;imageOptim (74% quality) &lt;em&gt;7.1MB → 4.3MB&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compression by imageOptim only: Final size 4.1MB&lt;/strong&gt; (best result)&lt;ul&gt;
&lt;li&gt;Photos.app (maximum quality) &lt;em&gt;103.6MB → 103.6MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Pillow (100% quality) &lt;em&gt;103.6MB → 27.8MB&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;imageOptim (74% quality) &lt;em&gt;27.8MB → 4.1MB&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The best result comes from performing a single compression step with the best compression tool. &lt;/p&gt;
&lt;p&gt;I expected that the individual compression tools would have complementary compression schemes and chaining them together would give the best compression. I suspect now that imageOptim uses all the types of compression schemes available in the other tools and gets the best result because it can take an image with low &lt;a href="https://en.wikipedia.org/wiki/Entropy_(information_theory)"&gt;entropy&lt;/a&gt; i.e. an uncompressed image and introduce all the entropy (compression) in a single step.&lt;/p&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/smaller-images-with-single-step-compression.html</guid><pubDate>Fri, 13 Apr 2018 20:38:45 GMT</pubDate></item><item><title>Disincentives and Photo Hosting</title><link>https://www.wordspeak.org/posts/disincentives-and-photo-hosting.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;I've been searching for a simple, scriptable photo hosting tool so that I can move off Flickr, and I've found one. Photo hosting tools don't seem to have evolved much in recent times, which is unsurprising given social media does the job of photo sharing for most people. There are a few tools, for sure, but there's something fun about hosting my own photos so I can optimise, meddle and learn and still have the joy of sharing (and indeed sharing without ads).&lt;/p&gt;
&lt;p&gt;So it's time for me to say goodbye to Flickr. It's been a good technical platform and seems to have a community in it, but I didn't like Flickr's injection of ads in my galleries and the platform does feel irreversibly stagnated - I was excited while when Yahoo gave it some focus a few years back, but there's nothing to indicate anyone's committed to developing it.&lt;/p&gt;
&lt;p&gt;So I've moved my photos across to a self-hosted instance of &lt;a href="https://github.com/saimn/sigal"&gt;Sigal&lt;/a&gt;. Performing the move was a delightful task as I relived some of the memories that I've previously published, and then discovered albums that I could have published, but never did. I realised my reservations about Flickr were a subconscious disincentive for publishing.&lt;/p&gt;
&lt;p&gt;So, with this renewed vigour, I've published photos from &lt;a href="https://images.wordspeak.org/cyprus/"&gt;my 2016 trip to Cyprus&lt;/a&gt; along with &lt;a href="https://images.wordspeak.org/"&gt;the rest of my photos&lt;/a&gt;. I hope you enjoy them.&lt;/p&gt;&lt;/div&gt;</description><category>Photography</category><guid>https://www.wordspeak.org/posts/disincentives-and-photo-hosting.html</guid><pubDate>Fri, 16 Feb 2018 19:01:16 GMT</pubDate></item><item><title>Yak shaving with Vagrant, Travis-CI and AWS</title><link>https://www.wordspeak.org/posts/yak-shaving-with-vagrant-travis-ci-and-aws.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;Tldr; Don't use the vagrant package from your distribution if you intent to build plugins.&lt;/p&gt;
&lt;p&gt;I've just finished setting up CI pipeline for a personal project. The project has an Ansible playbook that I want to exercise every time there's a commit or a PR. While completing the task I &lt;a href="http://catb.org/jargon/html/Y/yak-shaving.html"&gt;shaved a yak&lt;/a&gt; and narrowly avoided shaving a whole herd. I planned to use Vagrant in my Travis-CI pipeline to start an instance in AWS, run the playbook, look at the result and terminate the instance. Vagrant, Travis-CI and AWS are pretty common tools, so I was surprised at the wrangling involved before I ended up with a solution. I thought I'd document my findings to minimise the chance that others will have the same experience.&lt;/p&gt;
&lt;h2&gt;Finding #1: Travis' default build agent has an old Vagrant&lt;/h2&gt;
&lt;p&gt;The default build agent is based on Ubuntu 12.04 LTS Server which ships with Ansible 1.0 in its apt repo. The &lt;a href="https://github.com/mitchellh/vagrant-aws"&gt;vagrant-aws&lt;/a&gt; plugin requires Vagrant 1.2. Fortunately they have a &lt;a href="https://docs.travis-ci.com/user/trusty-ci-environment"&gt;Ubuntu 14.04 LTS Server beta&lt;/a&gt; which has a newer Vagrant in the apt repo.&lt;/p&gt;
&lt;h2&gt;Finding #2: The AWS-Vagrant plugin won't build with a newer Vagrant because of missing libraries and tools&lt;/h2&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="n"&gt;Installing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'vagrant-aws'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;few&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rubygems&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;562&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;rescue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build_extensions&lt;/span&gt;&lt;span class="s1"&gt;': ERROR: Failed to build gem native extension. (Gem::Installer::ExtensionBuildError)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;9.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;extconf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rubygems&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;custom_require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="s1"&gt;': cannot load such file -- mkmf (LoadError)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rubygems&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;custom_require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;extconf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Searching for the mkmf LoadError in the context of Debian and Ubuntu gives recommendations to install a few devel packages including ruby-dev (some will say a specific version of ruby dev). It seems that AWS-Vagrant is a Ruby gem, and gems are built on-box (at least it seems so - I'm a Ruby rookie), so libraries and the ruby compiler are needed. These aren't installed on the build agent.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get install build-essential libxslt-dev libxml2-dev zlib1g-dev ruby-dev&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Finding #3: The AWS-Vagrant plugin needs Ruby version 2.0+&lt;/h2&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ vagrant plugin install vagrant-aws
Installing the &lt;span class="s1"&gt;'vagrant-aws'&lt;/span&gt; plugin. This can take a few minutes...
/usr/lib/ruby/1.9.1/rubygems/installer.rb:388:in &lt;span class="sb"&gt;`&lt;/span&gt;ensure_required_ruby_version_met&lt;span class="s1"&gt;': json requires Ruby version ~&amp;gt; 2.0. (Gem::InstallError)&lt;/span&gt;
&lt;span class="s1"&gt;    from /usr/lib/ruby/1.9.1/rubygems/installer.rb:156:in `install'&lt;/span&gt;
    from /usr/lib/ruby/1.9.1/rubygems/dependency_installer.rb:297:in &lt;span class="sb"&gt;`&lt;/span&gt;block &lt;span class="k"&gt;in&lt;/span&gt; install&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="s1"&gt;    from /usr/lib/ruby/1.9.1/rubygems/dependency_installer.rb:270:in `each'&lt;/span&gt;
    from /usr/lib/ruby/1.9.1/rubygems/dependency_installer.rb:270:in &lt;span class="sb"&gt;`&lt;/span&gt;each_with_index&lt;span class="err"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;vagrant plugin install&lt;/code&gt; wants to use Ruby 1.9.1 to perform the gem build, but that version is too old. Fortunately the build agent has Ruby 2.3.&lt;/p&gt;
&lt;h2&gt;Finding #4: Ubuntu Vagrant can't load plugins built with Ruby 2.3&lt;/h2&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="n"&gt;Successfully&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gems&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;travis&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;rvm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gems&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;2.&lt;/span&gt;&lt;span class="n"&gt;gem&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rubygems&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;from_file_by_path&lt;/span&gt;&lt;span class="s1"&gt;': Cannot load gem at [/home.travis/.rvm/gems/ruby-2.3.1/cache/vagrant-aws-0.7.2.gem] in /home/travis/build/edwinsteele/biblebox-pi (Gem::Exception)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;share&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;install_gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vendor_ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;warden&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;share&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bundler_check&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vendor_ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;warden&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vendor_ruby&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;116&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now I'm running out of ideas and I'm considering less conventional means like building on the Travis MacOS environment where I can use Homebrew or moving to another CI hosting provider entirely. Fortunately I stumbled on the answer...&lt;/p&gt;
&lt;h2&gt;The Answer&lt;/h2&gt;
&lt;p&gt;From &lt;a href="https://www.vagrantup.com/docs/installation/"&gt;the Vagrant docs&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beware of system package managers! Some operating system distributions include a vagrant package in their upstream package repos. Please do not install Vagrant in this manner. Typically these packages are missing dependencies or include very outdated versions of Vagrant. If you install via your system's package manager, it is very likely that you will experience issues. Please use the official installers on the downloads page.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yeah, I experienced issues. Once I followed the advice it was smooth. Perhaps I should have looked at the official docs sooner!&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ wget -O /tmp/vagrant.deb https://releases.hashicorp.com/vagrant/1.8.7/vagrant_1.8.7_x86_64.deb
$ sudo dpkg -i /tmp/vagrant.deb
$ vagrant plugin install vagrant-aws
$
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And now I have a CI pipeline running on AWS after each commit. Nice.&lt;/p&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/yak-shaving-with-vagrant-travis-ci-and-aws.html</guid><pubDate>Fri, 18 Nov 2016 06:01:00 GMT</pubDate></item><item><title>Site Security Improvements</title><link>https://www.wordspeak.org/posts/site-security-improvements.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;I did some security work on this site recently. I was able to get some nice wins without a great investment of time, in part due to the great resources that are available. Here are the areas of work, the resources that I used, and the outcomes:&lt;/p&gt;
&lt;h2&gt;Content Security Policy (CSP)&lt;/h2&gt;
&lt;p&gt;A CSP constrains the actions that a web page can take or the actions that can be performed upon it. It allows one to apply the &lt;em&gt;principle of least privilege&lt;/em&gt; to a page and site. A CSP allows one to specify constraints like &lt;em&gt;"Only Load CSS from these sources"&lt;/em&gt;, &lt;em&gt;"Don't allow this site to be embedded in frames"&lt;/em&gt; and &lt;em&gt;"Don't allow inline JavaScript"&lt;/em&gt;. I developed a CSP after reading a &lt;a href="https://www.html5rocks.com/en/tutorials/security/content-security-policy/"&gt;the HTML5rocks CSP tutorial&lt;/a&gt; and &lt;a href="https://scotthelme.co.uk/content-security-policy-an-introduction/"&gt;Scott Helme's CSP intro&lt;/a&gt;. I validated my policy using &lt;a href="https://csp-evaluator.withgoogle.com"&gt;Google's CSP evaluator&lt;/a&gt; and &lt;a href="https://observatory.mozilla.org/analyze.html"&gt;Mozilla's Observatory tool&lt;/a&gt;. In order to apply best-practices, which include disabling inline JavaScript and CSS, I needed to make a simple changes to the site. I've been conscious to minimise JavaScript and CSS as I've developed this site, and it was great to see how that choice made the application of best-practices a simple task.&lt;/p&gt;
&lt;h2&gt;Miscellaneous security headers&lt;/h2&gt;
&lt;p&gt;I implemented &lt;code&gt;X-XSS-Protection&lt;/code&gt;, &lt;code&gt;X-Content-Type-Options&lt;/code&gt; and &lt;code&gt;X-Frame-Options&lt;/code&gt; and while the effect of these headers overlaps a little with CSP, providing them is still a good idea because of inconsistent CSP implementations and benefits unrelated to CSP. I learnt about them from &lt;a href="https://scotthelme.co.uk/hardening-your-http-response-headers/#x-frame-options"&gt;Scott Helme's Response Headers page&lt;/a&gt; and &lt;a href="https://wiki.mozilla.org/Security/Guidelines/Web_Security#X-Content-Type-Options"&gt;Mozilla's web security guidelines&lt;/a&gt;. I validated my setup with the &lt;a href="https://securityheaders.io/"&gt;SecurityHeaders validation tool&lt;/a&gt; and &lt;a href="https://observatory.mozilla.org/analyze.html"&gt;Mozilla's Observatory tool&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;SSL&lt;/h2&gt;
&lt;p&gt;I already had a reasonable SSL setup but while looking at the &lt;a href="https://wiki.mozilla.org/Security/Guidelines/Web_Security#HTTPS"&gt;Mozilla web security guidelines&lt;/a&gt;, I didn't consider how my list of cipher choices would need regular updating (I'd last reviewed them 2 years ago!). Mozilla are good enough to provide &lt;a href="https://wiki.mozilla.org/Security/TLS_Configurations#Nginx"&gt;nginx config snippets&lt;/a&gt; for to help with good cipher selection, and their config snippet included an &lt;a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security"&gt;HTTP Strict Transport Security (HSTS)&lt;/a&gt; directive. I'd considered HSTS before, but found the SSL certificate renewal process to be complex enough that I was unsure I wouldn't accidentally take my site offline around renewal time. Having recently switched my site certificates over to the (awesome) &lt;a href="https://letsencrypt.org"&gt;Let's Encrypt&lt;/a&gt; renewal process, I felt comfortable activating HSTS at the same time. I validated my setup with the &lt;a href="https://www.ssllabs.com/ssltest/analyze.html"&gt;Qualys SSL Report&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Outcome&lt;/h2&gt;
&lt;p&gt;It took about 4 hours to make the changes, and after the changes were applied, this site &lt;sup id="fnref:ssi-1"&gt;&lt;a class="footnote-ref" href="https://www.wordspeak.org/posts/site-security-improvements.html#fn:ssi-1"&gt;1&lt;/a&gt;&lt;/sup&gt; moved from an A to an A+ on the Qualys SSL Report. The Mozilla Observatory tool gives the site an A+ and the SecurityHeaders.io validator gives it an A. My &lt;a href="https://github.com/edwinsteele/setup-scripts/tree/master/ansible/roles/webhost/files"&gt;nginx config is available on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:ssi-1"&gt;
&lt;p&gt;Actually, I use Cloudflare as a CDN, so I ran the tests against (my origin server). &lt;a class="footnote-backref" href="https://www.wordspeak.org/posts/site-security-improvements.html#fnref:ssi-1" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/site-security-improvements.html</guid><pubDate>Thu, 06 Oct 2016 20:01:00 GMT</pubDate></item><item><title>Travelling through LAX just became a lot easier</title><link>https://www.wordspeak.org/posts/travelling-through-lax-just-became-a-lot-easier.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;I've transited Los Angeles Airport for many years on my way to conferences and family visits and on a recent trip I was thrilled to find that its no longer necessary to go through airport security when moving between international to domestic flights. While the signposting is pretty bad, one can now move between Tom Bradley International and Terminal 4 (and thus the rest of the domestic terminals) without exiting the terminal and subjecting oneself, and ones family, to security screening and potential delays.&lt;/p&gt;
&lt;p&gt;Hooray.&lt;/p&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/travelling-through-lax-just-became-a-lot-easier.html</guid><pubDate>Thu, 21 Jul 2016 06:39:00 GMT</pubDate></item><item><title>How I use the Apple Watch</title><link>https://www.wordspeak.org/posts/how-i-use-the-apple-watch.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;I bought an Apple Watch a little while ago. I really like it and I find it useful, but I use it differently to most people and I almost didn't buy one because I wasn't sure that it could fit with my need. Here are a few of my "workarounds" and a few findings that someone might find useful.&lt;/p&gt;
&lt;h2&gt;A non-standard user?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;I don't have an iPhone.&lt;/li&gt;
&lt;li&gt;I don't want to charge the Apple Watch at night; I want to wear it to bed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I'd love to report that the Apple Watch can connect to an iPad (I only have an iPad), but it doesn't. You still need an iPhone to configure it, and to serve as a conduit for data and App installs, but you don't need it to be with you all the time for the watch to be useful. In my case it doesn't even matter that the watch is hooked into my wife's Apple ID (she has the iPhone) because all the important data is still accessible&lt;sup id="fnref:hiutaw-1"&gt;&lt;a class="footnote-ref" href="https://www.wordspeak.org/posts/how-i-use-the-apple-watch.html#fn:hiutaw-1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2&gt;What I planned to use it for&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;silent alarms during the day, and to wake me up in the morning&lt;/li&gt;
&lt;li&gt;the time&lt;/li&gt;
&lt;li&gt;fitness tracker (mainly step count)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you're thinking that I could have used a Fitbit, you'd be right. I've had a few of them and the build quality on my bands was poor and the software gets in my way &lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="https://www.wordspeak.org/posts/how-i-use-the-apple-watch.html#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;, so I wanted something else.&lt;/p&gt;
&lt;h2&gt;What I also use it for&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;checking the weather when dressing the kids for bed and when getting clothes out for the next day&lt;/li&gt;
&lt;li&gt;countdown timers&lt;/li&gt;
&lt;li&gt;driving directions when I'm out with my family&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don't use it for anything else.&lt;/p&gt;
&lt;h2&gt;Battery Life&lt;/h2&gt;
&lt;p&gt;The big surprise is that battery life isn't a problem, even though the standard usage pattern involves charging at night and I have it on my wrist at night.&lt;/p&gt;
&lt;p&gt;My observations are that recording activity drains the battery a little, the attempts by the phone to connect to the iPhone drain the battery, and being in contact with the iPhone drains it quite a bit more.&lt;/p&gt;
&lt;p&gt;Battery drain rates:
* being a couch potato in aeroplane mode - 2% per hour
* normal activity levels in aeroplane mode - 2.5% per hour
* normal activity levels (not in aeroplane mode) when the iPhone is not around - 3.5% per hour
* normal activity levels (not in aeroplane mode) when the iPhone is around - 5% per hour&lt;/p&gt;
&lt;p&gt;I have a 42mm watch. Bluetooth is off, brightness is at the lowest setting and haptics are at the strongest setting). I have one third party complication &lt;a href="http://www.shiftyjelly.com/ios/pocketweatherau"&gt;Pocket Weather AU&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Charging&lt;/h2&gt;
&lt;p&gt;I've observed the watch charging at about 1% per minute, just like &lt;a href="https://david-smith.org/blog/2015/09/21/how-to-wear-your-apple-watch-24-slash-7/"&gt;David Smith&lt;/a&gt;, whose great idea allowed me to even consider an Apple Watch.&lt;/p&gt;
&lt;p&gt;I don't really need the watch while I'm preparing for my day, so I put it on charge within a few minutes of getting up so it's charging while I do my morning routine (shower, coffee, eat, read) so it has about 75 minutes to charge.&lt;/p&gt;
&lt;p&gt;I have the watch out of aeroplane mode in the evening (~4 hours) i.e. 4 x 3% = 12% and the rest of the non-charge time in aeroplane mode (~19 hours) i.e. 19 x 2.5% = 47.5%. This means I need about an hour of charge time to be constantly topped off. That has always been enough and consequently I'm not worried about battery life.&lt;/p&gt;
&lt;h2&gt;So, it works for me&lt;/h2&gt;
&lt;p&gt;Initially, I thought the watch would be of limited use without connectivity but it's not been a problem. Most of the features that require connectivity are for notifications and I'm happy to forgo them - I like not being interrupted, to be honest, and where connectivity is necessary for data updates, I've found a once-a-day refresh to be sufficient. So, I'm really happy with the watch. It's been a step-up from the Fitbit in terms of functionality and quality so it's been a good purchase and I'm looking forward to what's coming in watchOS 3.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:hiutaw-1"&gt;
&lt;p&gt;iCloud shared calendars for the win &lt;a class="footnote-backref" href="https://www.wordspeak.org/posts/how-i-use-the-apple-watch.html#fnref:hiutaw-1" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Why can't I set an alarm when I'm not connected to the internet? Why can't graphs render without an Internet connection? &lt;a class="footnote-backref" href="https://www.wordspeak.org/posts/how-i-use-the-apple-watch.html#fnref:2" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/how-i-use-the-apple-watch.html</guid><pubDate>Wed, 29 Jun 2016 20:09:59 GMT</pubDate></item><item><title>Making Ansible, Doas and OpenBSD play nicely</title><link>https://www.wordspeak.org/posts/making-ansible-doas-and-openbsd-play-nicely.html</link><dc:creator>Edwin Steele</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update 11 Jan 2020: The behaviour of doas changed in OpenBSD 6.6 so the environment specification described below is unnecessary. As of 6.6, doas sets HOME and USER to reflect the target user, like sudo. For details see &lt;a href="https://marc.info/?l=openbsd-tech&amp;amp;m=156039220904111&amp;amp;w=2"&gt;openbsd-tech&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A little while ago, OpenBSD replaced sudo in the base system with &lt;a href="http://www.tedunangst.com/flak/post/doas"&gt;doas&lt;/a&gt;. This transition follows a typical path in the OpenBSD community where a large, difficult to audit daemon or key application is replaced by an auditable and securable alternate implementation. At the same time, features are pruned if they add significant complexity. I don't need all the features of sudo and I like the simplicity and security of doas so I wanted to switch. I use Ansible to provision and manage my small collection of servers and when version 2.0 added support for privilege escalation via doas (via the &lt;a href="http://docs.ansible.com/ansible/become.html"&gt;become&lt;/a&gt; keyword) I started to make my switch. The main use-case in my Ansible playbooks is where I run as root and &lt;code&gt;become&lt;/code&gt; my non-admin user so that permissions are set correctly by default, and in this case doas behaves differently...&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# /usr/bin/env | grep -E '^(HOME|USER)='&lt;/span&gt;
&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/root
&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;span class="c1"&gt;# sudo -u esteele /usr/bin/env | grep -E '^(HOME|USER)='&lt;/span&gt;
&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;esteele
&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/esteele
&lt;span class="c1"&gt;# doas -u esteele /usr/bin/env | grep -E '^(HOME|USER)=' &lt;/span&gt;
&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/root
&lt;span class="nv"&gt;USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The key difference is which environment variables are propagated from the original session. I find doas to be a little unintuitive in this respect, particularly the inability in the new session to write to the directory specified by &lt;code&gt;$HOME&lt;/code&gt; and this behaviour causes problems with the Ansible &lt;em&gt;git&lt;/em&gt; and the &lt;em&gt;pip&lt;/em&gt; tasks. The &lt;em&gt;git&lt;/em&gt; task expects &lt;code&gt;$USER&lt;/code&gt; to correspond to the effective user id and the &lt;em&gt;pip&lt;/em&gt; task expects &lt;code&gt;$HOME&lt;/code&gt; to be the home directory of the effective user id. The unexpected doas environment meant the tasks were trying to write files, as my non-admin user, under /root which was unsurprisingly unsuccessful. Fortunately Ansible has a way to &lt;a href="http://docs.ansible.com/ansible/playbooks_environment.html"&gt;specify environment variables for a task&lt;/a&gt; and even though it'd be preferable for the &lt;code&gt;become&lt;/code&gt; mechanism to take care of this, it's an effective workaround (albeit a slightly dirty one).&lt;/p&gt;
&lt;p&gt;The example below, from my &lt;a href="https://github.com/edwinsteele/setup-scripts/blob/3374eb2d397f0880033e3dcac626b7b00eaf6afa/ansible/roles/webhost/tasks/nikola.yml#L32"&gt;web host playbook&lt;/a&gt;, ties the environment specification and the &lt;em&gt;doas become&lt;/em&gt; in a &lt;a href="http://docs.ansible.com/ansible/playbooks_blocks.html"&gt;block&lt;/a&gt;&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# doas preserves USER and HOME, which is different to sudo, however by                                                                                                    &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;#  specifying them as environment variables we can proceed.                                                                                                               &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="c1"&gt;# USER is required for git, and HOME is required for pip                                                                                                                  &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                                                  &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Checkout wordspeak repo&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                         &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;git&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;repo=ssh://git@github.com/edwinsteele/wordspeak.org.git&lt;/span&gt;&lt;span class="w"&gt;                                                                                                          &lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;dest=/home/esteele/Code/wordspeak.org&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                            &lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Setup nikola virtualenv&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                         &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;pip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;requirements=/home/esteele/Code/wordspeak.org/requirements.txt&lt;/span&gt;&lt;span class="w"&gt;                                                                                                   &lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;virtualenv_command=/usr/local/bin/virtualenv&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                     &lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;virtualenv=/home/esteele/.virtualenvs/wordspeak_n7&lt;/span&gt;&lt;span class="w"&gt;                                                                                                               &lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;become&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;True&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                                            &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;become_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;doas&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                                     &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;become_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;esteele&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                                    &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                                            &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;esteele&lt;/span&gt;&lt;span class="w"&gt;                                                                                                                                                         &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/esteele&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And having tied this together, I can remove the sudo package!&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Remove sudo&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;openbsd_pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;name=sudo-- state=absent&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><guid>https://www.wordspeak.org/posts/making-ansible-doas-and-openbsd-play-nicely.html</guid><pubDate>Wed, 29 Jun 2016 08:50:00 GMT</pubDate></item></channel></rss>