<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>uniontown labs</title>
    <description>uniontown labs is a repository for the projects of todd jeremy treece.</description>
    <link>https://uniontownlabs.org/</link>
    <atom:link href="https://uniontownlabs.org/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 14 Jan 2019 08:53:36 -0500</pubDate>
    <lastBuildDate>Mon, 14 Jan 2019 08:53:36 -0500</lastBuildDate>
    <generator>Jekyll v3.0.1</generator>
    
      <item>
        <title>Using dsh to Transfer Files to Multiple Machines</title>
        <description>&lt;p&gt;If you need to transfer a config file to multiple remote machines, &lt;code class=&quot;highlighter-rouge&quot;&gt;dsh&lt;/code&gt;
will make your life easier. Check out &lt;a href=&quot;https://uniontownlabs.org/notebook/2016/02/26/dsh/&quot;&gt;my previous post about dsh&lt;/a&gt; if
you need more info about setting up &lt;code class=&quot;highlighter-rouge&quot;&gt;dsh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, let’s look at the example file we plan on transferring:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat example-file
example file line 1
example file line 2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;We will pipe the output of that file to a &lt;code class=&quot;highlighter-rouge&quot;&gt;dsh&lt;/code&gt; group using the &lt;code class=&quot;highlighter-rouge&quot;&gt;-i&lt;/code&gt;
and &lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; options. Let’s look at the docs for those options first.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;--concurrent-shell | -c
       Executes shell concurrently.

--duplicate-input | -i
       Duplicates the input to dsh process to individual process
       that are remotely invoked.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now, let’s use those options to &lt;code class=&quot;highlighter-rouge&quot;&gt;tee&lt;/code&gt; the contents to a file on the remote servers:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat example-file | dsh -g ex-group -i -c &#39;tee ~/example-file&#39;
server-2: example file line 1
server-2: example file line 2
server-1: example file line 1
server-1: example file line 2
server-4: example file line 1
server-4: example file line 2
server-3: example file line 1
server-3: example file line 2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You will notice that the output is in random order due to the &lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; concurrent option.
Let’s check our work using &lt;code class=&quot;highlighter-rouge&quot;&gt;-w&lt;/code&gt; so that the output is in the proper order:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ dsh -g ex-group -w &#39;cat ~/example-file&#39;
server-1: example file line 1
server-1: example file line 2
server-2: example file line 1
server-2: example file line 2
server-3: example file line 1
server-3: example file line 2
server-4: example file line 1
server-4: example file line 2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</description>
        <pubDate>Thu, 13 Apr 2017 05:00:00 -0400</pubDate>
        <link>https://uniontownlabs.org/notebook/2017/04/13/dsh-file-transfer/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2017/04/13/dsh-file-transfer/</guid>
        
        <category>admin</category>
        
        <category>dsh</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>SSH Config Includes</title>
        <description>&lt;p&gt;OpenSSH version 7.3 introduced a very handy &lt;code class=&quot;highlighter-rouge&quot;&gt;Include&lt;/code&gt; feature, which is great for people who have to manage connection
info for multiple servers. This makes it easy for me to generate updated SSH configs via AWS CLI for the multiple EC2 instances that
serve Adafruit IO.&lt;/p&gt;

&lt;p&gt;Here is how you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;Include&lt;/code&gt; to pull in separate SSH config files from
your main &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.ssh/config&lt;/code&gt;. First, you will need to install OpenSSH version 7.3 or higher.
If you are using Linux, you will need to install version 7.3+ via your package manager (&lt;code class=&quot;highlighter-rouge&quot;&gt;yum&lt;/code&gt;,&lt;code class=&quot;highlighter-rouge&quot;&gt;apt-get&lt;/code&gt;, etc), or build it from source.&lt;/p&gt;

&lt;p&gt;On OS X, you can do this via homebrew:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ brew install homebrew/dupes/openssh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Confirm that you are now running 7.3 or higher by running &lt;code class=&quot;highlighter-rouge&quot;&gt;ssh -V&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ssh -V
OpenSSH_7.3p1, OpenSSL 1.0.2j  26 Sep 2016
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now you can create a new child config file in &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.ssh&lt;/code&gt; using a text editor. For example, we
can create an example child config at &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.ssh/pi_config&lt;/code&gt; and add configuration info just
as we would in the main SSH config file:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Host pi-1
  HostName 10.0.0.10
  User pi
  IdentityFile ~/.ssh/pi_cluster

Host pi-2
  HostName 10.0.0.11
  User pi
  IdentityFile ~/.ssh/pi_cluster

Host pi-3
  HostName 10.0.0.12
  User pi
  IdentityFile ~/.ssh/pi_cluster
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;From your main &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.ssh/config&lt;/code&gt;, add the following line at the top:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Include ~/.ssh/pi_config
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You should now be able to connect to your servers as you normally would:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ssh pi-1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;That’s it! Check out &lt;a href=&quot;https://bugzilla.mindrot.org/show_bug.cgi?id=1585&quot;&gt;this OpenSSH feature request&lt;/a&gt; if
you would like more info about the new feature.&lt;/p&gt;
</description>
        <pubDate>Tue, 27 Sep 2016 08:00:00 -0400</pubDate>
        <link>https://uniontownlabs.org/notebook/2016/09/27/ssh-config-includes/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2016/09/27/ssh-config-includes/</guid>
        
        <category>sysadmin</category>
        
        <category>ssh</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Tricking Instagram&#39;s Audio Fingerprint System with Infrasound</title>
        <description>&lt;p&gt;I recently attempted to share &lt;a href=&quot;https://vimeo.com/1308454&quot;&gt;an old video of one of my dogs&lt;/a&gt; on Instagram, and I immediately
received this notice via email after upload:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We’ve removed the video you posted at 12:24 PM on September 18, 2016 because it included the following content:&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Gun Street Girl&lt;/strong&gt; by &lt;strong&gt;Tom Waits&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;If you have permission to share everything in the video including the audio, like the soundtrack or music, you can appeal the removal and have your video re-posted. Remember that people should only post videos they have the right to share.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I understand why sites like YouTube and Instagram need to block unauthorized use of copyrighted works,
but I think that it’s overkill to block non-commercial use of a short piece of audio in a puppy video. I
can’t speak for Tom Waits, but doubt he’s going to mind if he doesn’t get license fees for the
small number of people (30-40) that hear a short clip of his song in my post. It’s not like
I’m uploading a full album, or even a full song.&lt;/p&gt;

&lt;p&gt;I normally would have just moved on and not posted the video, but I was curious if there were some ways around
the audio fingerprint system. My initial thought last night was that I could overlay a subaudible tone, and hopefully
that would be enough to trick their system. When I woke up this morning, I did a bit of searching
and I was unable to find any examples of this approach, but I was able to find some &lt;a href=&quot;http://www.scottsmitelli.com/articles/youtube-audio-content-id&quot;&gt;other methods that people have
tested with YouTube’s Content ID system&lt;/a&gt;. These methods include reversing the song, altering the pitch,
altering the playback speed, resampling, applying noise, volume changes, and tweaking the stereo imagery.&lt;/p&gt;

&lt;p&gt;The author of the article concluded:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is quite possible to thwart the YouTube Content ID system, but some methods mangle the song too much to be used in anything useful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I decided I’d give my infrasound method a try, so I loaded the video in &lt;a href=&quot;http://www.audacityteam.org&quot;&gt;Audacity&lt;/a&gt;. Here’s what the original
track looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/instagram/original.png&quot; alt=&quot;original track&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I then added a new stereo track, and generated a 1Hz sine wave at 0.8 amplitude. Here’s what that track looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/instagram/sine.png&quot; alt=&quot;sine&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s what the two tracks look like mixed together:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/instagram/mix.png&quot; alt=&quot;mixed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I played the track back on my laptop and phone, and couldn’t hear a difference between the source track and the new track with
the 1Hz sine wave overlaid. It passed the ear test, but the real test was to upload it to Instagram.&lt;/p&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-version=&quot;7&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 10px 0; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:8px;&quot;&gt; &lt;div style=&quot; background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;&quot;&gt; &lt;div style=&quot; background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/p/BKgqaPBD41F/&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;&quot; target=&quot;_blank&quot;&gt;A video posted by Todd Treece (@uniontownlabs_test)&lt;/a&gt; on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2016-09-18T20:16:28+00:00&quot;&gt;Sep 18, 2016 at 1:16pm PDT&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; defer=&quot;&quot; src=&quot;//platform.instagram.com/en_US/embeds.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;It works! You should see the 1Hz test video above unless Instagram manually removes the video. The nice thing about this approach is that there is no discernible difference between the source and modified tracks. I’ll be testing this method with YouTube’s
Content ID system in a follow-up post.&lt;/p&gt;

</description>
        <pubDate>Sun, 18 Sep 2016 04:00:00 -0400</pubDate>
        <link>https://uniontownlabs.org/notebook/2016/09/18/tricking-instagram-with-infrasound/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2016/09/18/tricking-instagram-with-infrasound/</guid>
        
        <category>hacks</category>
        
        <category>instagram</category>
        
        <category>audio fingerprint</category>
        
        <category>content id</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Hardware Testing with Jenkins &amp; TAP</title>
        <description>&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-jenkins.png&quot; alt=&quot;Jenkins CI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At Adafruit, when we push or merge changes to one of our &lt;a href=&quot;https://github.com/adafruit&quot;&gt;hundreds of Arduino libraries on GitHub&lt;/a&gt;,
we have to ask ourselves “Does this change work?”. In a lot of cases, the best answer we can give,
even after extensive manual testing, is “Maybe?”. For Adafruit IO, it is important that we are able to easily answer “Yes.” to that question.
A large portion of Adafruit IO users could be impacted by a bug in a library such as the &lt;a href=&quot;https://io.adafruit.com/blog/arduino/2016/08/10/new-arduino-library/&quot;&gt;Adafruit IO Arduino Library&lt;/a&gt;,
and we want to make sure our client code is reliable.&lt;/p&gt;

&lt;p&gt;The hard truth is that there aren’t enough hours in the day to test each change to every library
on all of the supported platforms, so most of the time we have to settle with testing one or two
platforms manually, and hoping for the best on the rest of the supported platforms.&lt;/p&gt;

&lt;p&gt;What do we mean by platform? In this case, we are talking about the multiple microcontrollers
that are supported in the Arduino IDE via the &lt;a href=&quot;https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification&quot;&gt;Arduino 1.6.x Third Party Hardware Specification&lt;/a&gt;.
Arduino Uno, ESP8266, Feather M0 (ATSAMD21), and the Feather WICED (STM32F205) are all good examples
different platforms that require separate testing.&lt;/p&gt;

&lt;h3 id=&quot;first-attempt-build-verification-using-travis-ci&quot;&gt;First Attempt: Build Verification using Travis CI&lt;/h3&gt;

&lt;p&gt;We had previously attempted &lt;a href=&quot;https://learn.adafruit.com/continuous-integration-arduino-and-you&quot;&gt;automated build checking using Travis CI&lt;/a&gt;, and that method works great
for checking if sketches build on multiple platforms.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-travis.png&quot; alt=&quot;Travis CI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you may have guessed, this leaves us with a huge blind spot. &lt;strong&gt;A passing build does not mean
the code will do what you expect it to do.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;second-attempt-manual-compatibility-testing&quot;&gt;Second Attempt: Manual Compatibility Testing&lt;/h3&gt;

&lt;p&gt;Our next attempt involved building a ‘compatibility matrix’ web app that allowed for easy logging
of the results of manual tests on supported platforms. The app pushes test results to a GitHub
repo as JSON (in case we need to access it programatically). It also automatically adds/updates
a compatibility table in the &lt;a href=&quot;https://github.com/adafruit/Adafruit_LED_Backpack#compatibility&quot;&gt;README&lt;/a&gt; of the target library.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-compat.png&quot; alt=&quot;Compatibility Matrix&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s an example of the README output:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-readme.png&quot; alt=&quot;Compatibility README Example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This method is very effective for answering “Does this change work?”, but it is very impractical
to manually test the daily changes to libraries on every supported platform.&lt;/p&gt;

&lt;h3 id=&quot;latest-attempt-running-tests-on-hardware&quot;&gt;Latest Attempt: Running Tests on Hardware&lt;/h3&gt;

&lt;p&gt;Our latest approach to this problem uses &lt;a href=&quot;https://jenkins.io/&quot;&gt;Jenkins CI&lt;/a&gt; running on an AWS EC2 instance,
and small fleet of local Raspberry Pi test nodes. Each node uploads and runs unit &amp;amp; integration tests
on hardware attached to USB ports.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-hardware.jpg&quot; alt=&quot;Hardware&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Each Raspberry Pi can support multiple platforms, and each platform can be targeted by the test. The tests
themselves output results using the &lt;a href=&quot;http://testanything.org/&quot;&gt;TAP protocol&lt;/a&gt; over a serial connection, and the Raspberry Pi sends
the results back to Jenkins so we are aware when builds fail.&lt;/p&gt;

&lt;p&gt;Here’s a visual overview of how it works:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-workflow.png&quot; alt=&quot;Jenkins Workflow&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Test status can be monitored in real time from anywhere, and we are able
to view the raw console output from the test nodes when needed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-stage.png&quot; alt=&quot;Jenkins Stage View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;TAP results are also parsed and attached to each build:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2016-08-31-tap.png&quot; alt=&quot;Jenkins TAP View&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We will be sharing the details of this in a tutorial soon, but if you would like to
take a look at our early experiments with Jenkins, head to &lt;a href=&quot;https://jenkins.adafruit.com&quot;&gt;jenkins.adafruit.com&lt;/a&gt; or
check out our &lt;a href=&quot;https://github.com/adafruit/jenkins-pipeline-library&quot;&gt;Jenkins Pipeline Library on GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Wed, 31 Aug 2016 08:00:00 -0400</pubDate>
        <link>https://uniontownlabs.org/testing/2016/08/31/hardware-testing-with-jenkins/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/testing/2016/08/31/hardware-testing-with-jenkins/</guid>
        
        <category>jenkins</category>
        
        <category>tap</category>
        
        <category>raspberry pi</category>
        
        <category>arduino</category>
        
        
        <category>testing</category>
        
      </item>
    
      <item>
        <title>Sending Commands to Multiple EC2 Instances Using dsh</title>
        <description>&lt;p&gt;We are moving &lt;a href=&quot;https://io.adafruit.com&quot;&gt;Adafruit IO&lt;/a&gt;’s node.js services to a group of &lt;code class=&quot;highlighter-rouge&quot;&gt;t2.nano&lt;/code&gt; EC2 instances, and
I wanted to document my &lt;code class=&quot;highlighter-rouge&quot;&gt;dsh&lt;/code&gt; setup. I used to use it for managing the backend workers
when working on &lt;a href=&quot;https://data.sparkfun.com&quot;&gt;data.sparkfun.com&lt;/a&gt;. It came in really handy
when I needed to quickly check or kick anything on the servers.&lt;/p&gt;

&lt;p&gt;First, you will need to install &lt;code class=&quot;highlighter-rouge&quot;&gt;dsh&lt;/code&gt; using &lt;code class=&quot;highlighter-rouge&quot;&gt;brew&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;apt-get&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;yum&lt;/code&gt;, etc:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ brew install dsh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You will need to make a couple config directories for your user:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ mkdir -p ~/.dsh/group
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Create a new &lt;code class=&quot;highlighter-rouge&quot;&gt;dsh.conf&lt;/code&gt; file using your favorite text editor:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ vim ~/.dsh/dsh.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Paste the following config into &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.dsh/dsh.conf&lt;/code&gt;, and save the file:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;showmachinenames = 1
remoteshell = ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now, you can create a &lt;code class=&quot;highlighter-rouge&quot;&gt;dsh&lt;/code&gt; group for the set of servers you would like to send commands to. Create
a new file called &lt;code class=&quot;highlighter-rouge&quot;&gt;example&lt;/code&gt; in the &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.dsh/group&lt;/code&gt; folder using your favorite text editor:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ vim ~/.dsh/group/example
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Enter the host names for your servers, and save the file:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;example-1.uniontownlabs.org
example-2.uniontownlabs.org
example-3.uniontownlabs.org
example-4.uniontownlabs.org
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now you can send commands (like &lt;code class=&quot;highlighter-rouge&quot;&gt;uptime&lt;/code&gt;) to all servers in the group using the following command:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ dsh -g example -w &#39;uptime&#39;
example-1.uniontownlabs.org:  14:17:44 up 17:15,  0 users,  load average: 0.08, 0.03, 0.05
example-2.uniontownlabs.org:  14:17:43 up 16:36,  0 users,  load average: 0.00, 0.01, 0.05
example-3.uniontownlabs.org:  14:17:45 up 16:41,  0 users,  load average: 0.00, 0.01, 0.05
example-4.uniontownlabs.org:  14:17:45 up 16:41,  0 users,  load average: 0.00, 0.01, 0.05
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Using &lt;code class=&quot;highlighter-rouge&quot;&gt;-w&lt;/code&gt; will send the command to each server in the order they appear in the group config,
and will wait for a response before continuing. If you want to send the commands concurrently
to all servers in the list, use the &lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; option to send the command:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ dsh -g example -c &#39;uptime&#39;
example-1.uniontownlabs.org:  14:22:42 up 17:20,  0 users,  load average: 0.00, 0.01, 0.05
example-3.uniontownlabs.org:  14:22:42 up 16:46,  0 users,  load average: 0.00, 0.01, 0.05
example-2.uniontownlabs.org:  14:22:41 up 16:41,  0 users,  load average: 0.00, 0.01, 0.05
example-4.uniontownlabs.org:  14:22:42 up 16:46,  0 users,  load average: 0.10, 0.05, 0.05
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You can get an interactive shell for all servers in the group by using
the &lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;-i&lt;/code&gt; commands together. Use CTRL-D to exit the session:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ dsh -g -c -i
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
        <pubDate>Fri, 26 Feb 2016 04:00:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2016/02/26/dsh/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2016/02/26/dsh/</guid>
        
        <category>admin</category>
        
        <category>dsh</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Caching YouTube Videos</title>
        <description>&lt;p&gt;Here are some quick instructions if you would like to grab high resolution copies
of all of the videos in a YouTube playlist.&lt;/p&gt;

&lt;p&gt;Install dependencies (replace &lt;code class=&quot;highlighter-rouge&quot;&gt;brew&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;yum&lt;/code&gt; for linux):&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;brew install youtube-dl ffmpeg
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You can get a muxed 1080P version of all videos in a playlist by running the following command and replacing the URL:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;youtube-dl -w --restrict-filenames -f 137+140 -o &lt;span class=&quot;s1&quot;&gt;&#39;~/%(playlist_title)s/%(upload_date)s.%(ext)s&#39;&lt;/span&gt; https://www.youtube.com/playlist?list&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;PLjF7R1fz_OOU08_hRcayfVZSmTpBCGJbL
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;-w&lt;/code&gt; option will disable overwrites, and &lt;code class=&quot;highlighter-rouge&quot;&gt;--restrict-filenames&lt;/code&gt; will make the saved filenames URL safe. The &lt;code class=&quot;highlighter-rouge&quot;&gt;-f 137+140&lt;/code&gt; option
tells &lt;code class=&quot;highlighter-rouge&quot;&gt;youtube-dl&lt;/code&gt; to grab the 1080P video and AAC audio files, and mux them using &lt;code class=&quot;highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 07 Jan 2016 04:00:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2016/01/07/youtube-video-cache/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2016/01/07/youtube-video-cache/</guid>
        
        <category>youtube</category>
        
        <category>podcast</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Raspberry Pi dwc2 Gadget Notes</title>
        <description>&lt;p&gt;Here are some notes from experiments with dwc2 otg enabled on
a Raspberry Pi Zero.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;dependencies&quot;&gt;Dependencies&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;sudo apt-get install -y device-tree-compiler bc ncurses-dev
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;files&quot;&gt;Files&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$  &lt;/span&gt;git clone --depth 1 --branch 4.1.y-dwc2 https://github.com/adafruit/adafruit-raspberrypi-linux.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gadget.dts&lt;/code&gt; - device tree overlay to enable dwc2&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/dts-v1/;
/plugin/;

/{
    compatible = &quot;brcm,bcm2835&quot;;

    fragment@0 {
        target = &amp;lt;&amp;amp;usb&amp;gt;;
        #address-cells = &amp;lt;1&amp;gt;;
        #size-cells = &amp;lt;0&amp;gt;;
        __overlay__ {
            compatible = &quot;brcm,bcm2835-usb&quot;;
            reg = &amp;lt;0x7e980000 0x10000&amp;gt;;
            interrupts = &amp;lt;1 9&amp;gt;;
            dr_mode = &quot;otg&quot;;
            clocks = &amp;lt;&amp;amp;clocks&amp;gt;;
            clock-names = &quot;otg&quot;;
        };
    };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;.config&lt;/code&gt; - kernel config&lt;/p&gt;

&lt;style type=&quot;text/css&quot;&gt; .gist {overflow:auto} .gist .gist-data {max-height: 200px;}&lt;/style&gt;

&lt;script src=&quot;https://gist.github.com/toddtreece/b51ffc5d4026adbc5f17.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;building&quot;&gt;Building&lt;/h2&gt;

&lt;p&gt;compile &lt;code class=&quot;highlighter-rouge&quot;&gt;gadget.dts&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo dtc -@ -I dts -O dtb -o /boot/overlays/usb-otg.dtb gadget.dts
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;enable the &lt;code class=&quot;highlighter-rouge&quot;&gt;usb-otg&lt;/code&gt; overlay by adding the following to your &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/config.txt&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;dtoverlay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;usb-otg
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 17 Dec 2015 11:48:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2015/12/17/dwc2-gadget-notes/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2015/12/17/dwc2-gadget-notes/</guid>
        
        <category>notebook</category>
        
        <category>raspberry pi</category>
        
        <category>kernel</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Message Queue Link Dump</title>
        <description>&lt;p&gt;We use Redis fairly heavily for &lt;a href=&quot;https://io.adafruit.com/&quot;&gt;Adafruit IO&lt;/a&gt;, and I started wondering
if there might be some better/faster options for our MQTT Redis message queue. It
looks like there are many options that are much faster than Redis.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Here are some of the options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://zeromq.org/&quot;&gt;ZeroMQ&lt;/a&gt; - No server needed, but setup might become more complex. Mosca support exists.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://nanomsg.org/&quot;&gt;nanomsg&lt;/a&gt; - Same as zeromq, but a younger project. Performs better in benchmarks. POSIX compliant &amp;amp; written in C.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://nats.io/documentation/&quot;&gt;NATS&lt;/a&gt; - Requires server, but still much faster than Redis. Can be scaled horizontally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think ZeroMQ might be our best bet. Here’s &lt;a href=&quot;http://bravenewgeek.com/dissecting-message-queues/&quot;&gt;a post with some benchmarks&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Thu, 17 Dec 2015 04:48:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2015/12/17/message-queue-linkdump/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2015/12/17/message-queue-linkdump/</guid>
        
        <category>notebook</category>
        
        <category>adafruit io</category>
        
        <category>message queues</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Redis Killed by OS?</title>
        <description>&lt;p&gt;Redis Server has been killed a few times this week by the OOM. More
info &lt;a href=&quot;http://stackoverflow.com/a/20218264&quot;&gt;in this post on stackoverflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After setting the &lt;code class=&quot;highlighter-rouge&quot;&gt;maxmemory&lt;/code&gt; limit to 3GB, and &lt;code class=&quot;highlighter-rouge&quot;&gt;maxmemory-policy&lt;/code&gt; to
&lt;code class=&quot;highlighter-rouge&quot;&gt;allkeys-lru&lt;/code&gt;, things should have gotten better. For some reason node.js
workers would die as soon as they were restarted, reporting that they
couldn’t connect to Redis.&lt;/p&gt;

&lt;p&gt;Starting the process manually would connect successfully, so the problem
seemed to be with &lt;a href=&quot;https://github.com/Unitech/pm2&quot;&gt;pm2&lt;/a&gt;, which manages the node cluster.&lt;/p&gt;

&lt;h2 id=&quot;fix&quot;&gt;Fix&lt;/h2&gt;

&lt;p&gt;I tried killing &lt;code class=&quot;highlighter-rouge&quot;&gt;pm2&lt;/code&gt; by running the following command.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;pm2 &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then, I restarted the node cluster, and everything was back to normal.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;sudo service io-server-cluster restart
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</description>
        <pubDate>Fri, 11 Dec 2015 19:00:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2015/12/11/redis-server-killed/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2015/12/11/redis-server-killed/</guid>
        
        <category>notebook</category>
        
        <category>adafruit io</category>
        
        <category>redis</category>
        
        <category>pm2</category>
        
        <category>nodejs</category>
        
        <category>postmortem</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Copy Raspbian Image to SD Card with Progress Bar on OS X</title>
        <description>&lt;p&gt;Find the SD card using &lt;code class=&quot;highlighter-rouge&quot;&gt;diskutil list&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;todd:~ todd&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;diskutil list
/dev/disk0
   &lt;span class=&quot;c&quot;&gt;#:                       TYPE NAME                    SIZE       IDENTIFIER&lt;/span&gt;
   0:      GUID_partition_scheme                        &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;251.0 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:          Apple_CoreStorage                         250.1 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.1 MB   disk0s3
/dev/disk1
   &lt;span class=&quot;c&quot;&gt;#:                       TYPE NAME                    SIZE       IDENTIFIER&lt;/span&gt;
   0:                  Apple_HFS Macintosh HD           &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;249.8 GB   disk1
                                 Logical Volume on disk0s2
                                 5931BF6D-C779-4723-94C6-5831AF22BC00
                                 Unlocked Encrypted
/dev/disk2
   &lt;span class=&quot;c&quot;&gt;#:                       TYPE NAME                    SIZE       IDENTIFIER&lt;/span&gt;
   0:     Apple_partition_scheme                        &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;17.5 MB    disk2
   1:        Apple_partition_map                         32.3 KB    disk2s1
   2:                  Apple_HFS Flash Player            17.4 MB    disk2s2
/dev/disk3
   &lt;span class=&quot;c&quot;&gt;#:                       TYPE NAME                    SIZE       IDENTIFIER&lt;/span&gt;
   0:     FDisk_partition_scheme                        &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;15.9 GB    disk3
   1:             Windows_FAT_32 boot                    62.9 MB    disk3s1
   2:                      Linux                         15.9 GB    disk3s2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Unmount the SD card:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;todd:~ todd&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;diskutil unmountDisk /dev/disk3
Unmount of all volumes on disk3 was successful
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Copy the image to the SD card using &lt;code class=&quot;highlighter-rouge&quot;&gt;dd&lt;/code&gt; and use &lt;code class=&quot;highlighter-rouge&quot;&gt;pv&lt;/code&gt; to display progress:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;todd:~ todd&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;pv ~/Downloads/2015-11-21-raspbian-jessie.img | sudo dd &lt;span class=&quot;nv&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;16m &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/disk3
837MiB 0:05:19 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;3.21MiB/s] &lt;span class=&quot;o&quot;&gt;[=======&lt;/span&gt;&amp;gt;                              &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; 22% ETA 0:18:29
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
        <pubDate>Fri, 11 Dec 2015 10:00:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2015/12/11/dd-image-pi-osx/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2015/12/11/dd-image-pi-osx/</guid>
        
        <category>notebook</category>
        
        <category>raspberry pi</category>
        
        <category>raspbian</category>
        
        <category>dd</category>
        
        <category>os x</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Notes on Raspberry Pi USB Gadget Mode</title>
        <description>&lt;p&gt;via &lt;a href=&quot;https://github.com/raspberrypi/linux/issues/884#issuecomment-78368062&quot;&gt;raspberrypi/linux#884&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use the upstream dwc2 driver in gadget mode - this in theory “should work” but
hasn’t really been tested in device mode. The hardware is similar to the type
underneath the s3c_hsotg driver in upstream and there is an ongoing effort to
consolidate both codebases. This would be the ideal solution if you just
require device mode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Other useful threads:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;dwc_otg can’t coexist with other USB drivers &lt;a href=&quot;https://github.com/raspberrypi/linux/issues/881&quot;&gt;raspberrypi/linux#881&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;RPi zero Support for USB Gadget Mode (pi shows up as keyboard, disk, net, camera etc) &lt;a href=&quot;https://github.com/raspberrypi/linux/issues/1212&quot;&gt;raspberrypi/linux#1212&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Fri, 11 Dec 2015 03:00:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2015/12/11/pi-device-notes/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2015/12/11/pi-device-notes/</guid>
        
        <category>notebook</category>
        
        <category>raspberry pi</category>
        
        <category>kernel</category>
        
        <category>otg</category>
        
        <category>usb</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Simple Let&#39;s Encrypt Instructions for nginx</title>
        <description>&lt;p&gt;Here are the steps I used to setup a new &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let’s Encrypt&lt;/a&gt; certificate for
&lt;code class=&quot;highlighter-rouge&quot;&gt;nginx&lt;/code&gt; running on a Debian EC2 instance.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Stop &lt;code class=&quot;highlighter-rouge&quot;&gt;nginx&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;sudo service nginx stop
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Clone the Let’s Encrypt client git repo:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;git clone https://github.com/letsencrypt/letsencrypt &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;letsencrypt
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Run the client using the following options:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;~/letsencrypt$ &lt;/span&gt;./letsencrypt-auto --standalone-supported-challenges tls-sni-01 certonly
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You will then be presented with a GUI that will ask you a couple questions about
the domain you wish to register.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/cYda9ad.png&quot; alt=&quot;letsencrypt gui&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After completing the registration, the client will
output the path to your new cert. You will need to copy the full path for the &lt;code class=&quot;highlighter-rouge&quot;&gt;nginx&lt;/code&gt;
config.&lt;/p&gt;

&lt;p&gt;Add the &lt;code class=&quot;highlighter-rouge&quot;&gt;fullchain.pem&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;privkey.pem&lt;/code&gt; to your nginx site config using the &lt;code class=&quot;highlighter-rouge&quot;&gt;ssl_certificate&lt;/code&gt;
and &lt;code class=&quot;highlighter-rouge&quot;&gt;ssl_certificate_key&lt;/code&gt; options. Here’s what my config looks like for uniontownlabs.org:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;server {
  listen 443;
  server_name uniontownlabs.org;

  root /var/www;
  index index.html index.htm index.txt;
  error_page    404 = /404;


  location ~ /\. { deny all; }
  location ~ ~$ { deny all; }

  ssl on;
  ssl_certificate /etc/letsencrypt/live/uniontownlabs.org/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/uniontownlabs.org/privkey.pem;

  ssl_ciphers &#39;AES256+EECDH:AES256+EDH:!aNULL&#39;;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_session_cache shared:SSL:10m;

  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 8.8.4.4 8.8.8.8 valid=300s;
  resolver_timeout 10s;

  ssl_prefer_server_ciphers on;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;

  add_header Strict-Transport-Security max-age=63072000;
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;

  location ~* \.(?:html)$ {
    expires 0;
  }

  location / {
    expires max;
    log_not_found off;
    add_header Cache-Control public;
    try_files $uri $uri/ =404;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Finally, start nginx:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;sudo service nginx start
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</description>
        <pubDate>Sun, 06 Dec 2015 04:00:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2015/12/06/lets-encrypt-nginx/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2015/12/06/lets-encrypt-nginx/</guid>
        
        <category>notebook</category>
        
        <category>ssl</category>
        
        <category>eff</category>
        
        <category>nginx</category>
        
        <category>let&#39;s encrypt</category>
        
        
        <category>notebook</category>
        
      </item>
    
      <item>
        <title>Mosca MQTT Persistence Performance</title>
        <description>&lt;p&gt;While quietly rolling out the public beta of &lt;a href=&quot;https://io.adafruit.com&quot;&gt;Adafruit IO&lt;/a&gt;, we noticed some
CPU spikes in our Node.js processes that handle MQTT. We currently use a slightly
modified version of &lt;a href=&quot;https://github.com/mcollina/mosca&quot;&gt;Mosca&lt;/a&gt; by &lt;a href=&quot;https://github.com/mcollina&quot;&gt;@mcollina&lt;/a&gt; as our MQTT broker. It is a very
solid package, but we seemed to be pushing the limits of Mosca’s Redis persistence
module.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;debugging&quot;&gt;Debugging&lt;/h2&gt;

&lt;p&gt;After examining the output of &lt;code class=&quot;highlighter-rouge&quot;&gt;node --perf&lt;/code&gt; in &lt;a href=&quot;chrome://tracing&quot;&gt;chrome://tracing&lt;/a&gt;, I seemed to
have found the culprit. It looked as if &lt;a href=&quot;https://github.com/mcollina/mosca/blob/8245c9ef43096d938646ae4dc9749dbb58df9c53/lib/persistence/redis.js#L220&quot;&gt;Mosca was waiting on Redis&lt;/a&gt; to return the
result of &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEYS&lt;/code&gt; for retained topics.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;v8  QuickSort native array.js:710:26
v8  QuickSort native array.js:710:26
v8  QuickSort native array.js:710:26
v8  QuickSort native array.js:710:26
v8  QuickSort native array.js:710:26
v8  QuickSort native array.js:710:26
v8  QuickSort native array.js:710:26
v8  InnerArraySort native array.js:670:24 {1}
v8  sort native array.js:885:19 {2}
v8  /home/deploy/io/releases/20151202164744/node/node_modules/mosca/lib/persistence/redis.js:220:44
v8  RedisClient.return_reply /home/deploy/io/releases/20151202164744/node/node_modules/redis/index.js:608:47 {2}
v8  /home/deploy/io/releases/20151202164744/node/node_modules/redis/index.js:306:34
v8  doNTCallback0 node.js:416:27
v8  _tickCallback node.js:335:27 {1}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;We retain all current values for topics, so they are immediately returned when a user connects
to Adafruit IO and subscribes to a topic. This enables the user to return to a previous state
on startup without making any calls to the REST API.&lt;/p&gt;

&lt;h2 id=&quot;improving-performance-with-redis-using-lua&quot;&gt;Improving Performance with Redis using Lua&lt;/h2&gt;

&lt;p&gt;In order to improve performance, I decided to do some preprocessing in Lua on the Redis
server before returning hash keys to Node.js. MQTT has two standard wildcard characters:
&lt;code class=&quot;highlighter-rouge&quot;&gt;+&lt;/code&gt; is a wildcard for matching one topic level, and &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt; can be used for matching multiple.
Given Lua’s limited string pattern matching abilities, I decided to narrow down the results
using Lua and do the final matching in Node.js.&lt;/p&gt;

&lt;p&gt;Here’s the Lua script:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hscan&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;repeat&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;hscan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;HSCAN&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEYS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;MATCH&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tonumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hscan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hscan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cursor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cjson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You can call the script from Node.js using Redis &lt;code class=&quot;highlighter-rouge&quot;&gt;EVAL&lt;/code&gt; like this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s1&quot;&gt;&#39;use strict&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// this example is es6, so it will only work in node &amp;gt;=4.0.0.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// it assumes you have a redis hash created with at least topic/1/1&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// and topic/2/1 as keys.&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;redis&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;redis&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;async&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6379&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;127.0.0.1&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// syntax highlighting is currently broken for es6 template strings :\&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;\
local cursor = 0 \
local response = {} \
local match \
local hscan \
repeat \
  hscan = redis.call(&#39;HSCAN&#39;, KEYS[1], cursor, &#39;MATCH&#39;, ARGV[1]) \
  cursor = tonumber(hscan[1]) \
  match = hscan[2] \
  if match then \
    for idx = 1, #match, 2 do \
      response[match[idx]] = match[idx + 1] \
    end \
  end \
until cursor == 0 \
return cjson.encode(response)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;test&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;topic/*/999997&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;topics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;found&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;topics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;HSCAN&lt;/code&gt; wildcard character is &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;, so I just replaced the MQTT wildcard
characters with &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt;, and did the final pattern matching in Node.js.&lt;/p&gt;

&lt;h2 id=&quot;benchmarks&quot;&gt;Benchmarks&lt;/h2&gt;

&lt;p&gt;I was benchmarking the change with 1 million retained hash keys, and it looks
like &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEYS&lt;/code&gt; method is about 258% slower than preprocessing with Lua. The results
are listed below so you can compare the results of the current &lt;code class=&quot;highlighter-rouge&quot;&gt;HKEYS&lt;/code&gt; method
vs the lua script:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;created 1000000 hash keys

real  0m23.453s
user  0m11.763s
sys  0m11.694s

--------------------------------------  HKEYS  --------------------------------------
retrieved 1000000 hash keys via HKEYS
JS matched topic: &#39;topic/0.09308264800347388/999997&#39;
got value via HGET: 0.3918832363560796
result {&quot;topic\/0.09308264800347388\/999997&quot;:&quot;0.3918832363560796&quot;}

real  0m3.460s
user  0m2.729s
sys  0m0.194s

----------------------------------------  LUA  ---------------------------------------
retrieved 1 hash keys via Lua
JS matched topic: &#39;topic/0.09308264800347388/999997&#39;
result {&quot;topic\/0.09308264800347388\/999997&quot;:&quot;0.3918832363560796&quot;}

real  0m1.337s
user  0m0.116s
sys  0m0.024s
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The pull request to &lt;a href=&quot;https://github.com/mcollina/mosca&quot;&gt;mosca&lt;/a&gt; can be found here: &lt;a href=&quot;https://github.com/mcollina/mosca/pull/379&quot;&gt;mcollina/mosca#379&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 02 Dec 2015 05:43:00 -0500</pubDate>
        <link>https://uniontownlabs.org/notebook/2015/12/02/mosca-redis-persistence/</link>
        <guid isPermaLink="true">https://uniontownlabs.org/notebook/2015/12/02/mosca-redis-persistence/</guid>
        
        <category>notebook</category>
        
        <category>mqtt</category>
        
        <category>node.js</category>
        
        <category>redis</category>
        
        <category>lua</category>
        
        <category>adafruit io</category>
        
        
        <category>notebook</category>
        
      </item>
    
  </channel>
</rss>
