<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title></title>
    <description></description>
    <link>/README/</link>
    <atom:link href="/README/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 25 Sep 2023 22:34:14 -0700</pubDate>
    <lastBuildDate>Mon, 25 Sep 2023 22:34:14 -0700</lastBuildDate>
    <generator>Jekyll v4.3.1</generator>
    
      <item>
        <title>Best Gear for Biking in the Rain</title>
        <description>&lt;p&gt;I live car-free in the Pacific Northwest, and this year all my friends are asking “what gear do I need so I can keep biking all winter long?” That’s so awesome! Biking is great fun and it can be fun in the winter too, with the right gear. The short answer is “shop at Showers Pass. You can’t go wrong.” They’re a Portland-based company and all they do is make rain gear for biking. They’re good at it. Here’s the long answer, in order by what you should invest in first:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ridepdw.com/collections/fenders/products/poncho-fenders&quot;&gt;Fenders&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lightandmotion.com/collections/road-biking/products/vis-pro-1000-blacktop&quot;&gt;Front&lt;/a&gt; and &lt;a href=&quot;https://cygolite.com/product/hotshot-pro-150-usb/&quot;&gt;back&lt;/a&gt; lights&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://showerspass.com/products/mens-crosspoint-waterproof-softshell-glove-ts&quot;&gt;Gloves&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Hat or &lt;a href=&quot;https://www.amazon.com/gp/product/B007UDF5AK/&quot;&gt;balaclava&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://showerspass.com/products/mens-transit-pant&quot;&gt;Pants &lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://showerspass.com/products/mens-elite-2-1-jacket&quot;&gt;Jacket&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://showerspass.com/products/club-shoe-covers&quot;&gt;Shoe covers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First up, fenders. Always fenders. Cheap &lt;a href=&quot;https://ridepdw.com/collections/fenders/products/poncho-fenders&quot;&gt;plastic ones will do fine&lt;/a&gt;. &lt;a href=&quot;https://ridepdw.com/collections/fenders&quot;&gt;Metal ones&lt;/a&gt; will last longer and be less fussy, for twice the price. Make sure to get front and back full coverage ones. It’s worth buying from your local bike shop and paying for installation them because it can be fiddly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lightandmotion.com/collections/road-biking/products/vis-pro-1000-blacktop&quot;&gt;Front&lt;/a&gt; and &lt;a href=&quot;https://cygolite.com/product/hotshot-pro-150-usb/&quot;&gt;back&lt;/a&gt; lights are critical, and moreso when it’s raining. Use them in daylight too so drivers can see you better.&lt;/p&gt;

&lt;p&gt;Gloves are the most important. Your hands are exposed to the wind and the rain and the cold, and by the end of a ride they can make you miserable. Any gloves you have lying around are great, if they give you enough dexterity to shift and brake. I like having lightweight gloves for autumn and spring since they give you more dexterity. Also over-heavy gloves can quickly overheat you. When the weather gets colder or the rain gets heavier I like the Showers Pass &lt;a href=&quot;https://showerspass.com/products/mens-crosspoint-waterproof-softshell-glove-ts&quot;&gt;Crosspoint Waterproof Softshell&lt;/a&gt;. On a long enough ride all gloves will wet through, so carrying a second pair is a great way to make the ride home more fun, particularly if you’re commuting.&lt;/p&gt;

&lt;p&gt;You’ll need something to keep your head warm. All those nice airy holes in your helmet keep you cool in summer but in winter the raindrops will be falling on your head. These &lt;a href=&quot;https://www.amazon.com/gp/product/B007UDF5AK/&quot;&gt;Blackstrap convertible balaclavas&lt;/a&gt; are absolutely fantastic. They’re thin so they can fit under your helmet and they’re not too hot. They also convert easily between full face coverage, just the head and neck, or neck gaiter only. If you wear glasses, consider a &lt;a href=&quot;https://www.brooksrunning.com/en_gb/base-running-sun-hat/280454.html?dwvar_280454_color=329&quot;&gt;runner’s cap&lt;/a&gt; instead. They’re thin and have no buttons or big seams, so they’ll fit nicely under your helmet and keep some of the rain off your face.&lt;/p&gt;

&lt;p&gt;Rain pants are key. If you have ski or hiking rain pants they’ll work alright, but biking rain pants often have better reinforcement in the crotch and thighs, bigger darts at the knees, more room in the butt so they don’t slide down, and reflective piping. They’ll last longer and perform better on the bike than other rain pants. I have the Showers Pass &lt;a href=&quot;https://showerspass.com/products/mens-transit-pant&quot;&gt;Transit Pant&lt;/a&gt; and love it.&lt;/p&gt;

&lt;p&gt;For a rain jacket, again whatever rain jacket you already have will work, but you will be more comfortable with a biking one. Look for: great ventilation under the armpits and across the back; a tail that hangs down a little lower to keep your butt covered when you’re leaning over; comfort when your arms are extended forward. I have the Showers Pass &lt;a href=&quot;https://showerspass.com/products/mens-elite-2-1-jacket&quot;&gt;Elite 2.1 Jacket&lt;/a&gt; and love it.&lt;/p&gt;

&lt;p&gt;Lastly, footgear. You can bring dry shoes and socks in your bag and change when you get where you’re going. But if your ride is long enough, cold wet shoes will make you miserable before you get there. &lt;a href=&quot;https://showerspass.com/products/club-shoe-covers&quot;&gt;Shoe covers&lt;/a&gt; solve that problem and are a surprisingly valuable piece of gear for rain riding. You can try &lt;a href=&quot;https://showerspass.com/products/crosspoint-essentials-waterproof-socks?_pos=1&amp;amp;_sid=1fc04ebde&amp;amp;_ss=r&quot;&gt;waterproof socks&lt;/a&gt; instead. They work! But they can be overly hot and they’re not nearly as comfortable as regular socks.&lt;/p&gt;

&lt;p&gt;Do you need to put special tires on your bike for traction in the rain? Nope! &lt;a href=&quot;https://bicycles.stackexchange.com/a/6459&quot;&gt;Slick tires work just fine on roads in the rain&lt;/a&gt;. But whether you’re riding knobbies or slicks, you have less traction and less visibility than usual, so slow down. It’s a good idea to test your brakes and your tires, too. Find an empty section of road, get up to a nice speed, then brake as hard as you are comfortable - with both brakes. If you start to feel a skid, release the brakes; now you know how hard you can safely brake in those conditions.&lt;/p&gt;

&lt;p&gt;One last tip: the key is to make biking in the rain fun. Blasting your favorite tunes from a portable, waterproof, &lt;a href=&quot;https://www.jbl.com/power-sports-audio/WIND3-.html&quot;&gt;Bluetooth speaker&lt;/a&gt; can add a lot of joy (avoid earbuds, though; they are dangerous).&lt;/p&gt;
</description>
        <pubDate>Wed, 20 Sep 2023 11:00:00 -0700</pubDate>
        <link>/README/2023/09/20/best-gear-for-biking-in-the-rain.html</link>
        <guid isPermaLink="true">/README/2023/09/20/best-gear-for-biking-in-the-rain.html</guid>
        
        
      </item>
    
      <item>
        <title>Error Handling in C</title>
        <description>
</description>
        <pubDate>Mon, 21 Dec 2020 10:00:00 -0800</pubDate>
        <link>/README/2020/12/21/error-handling-in-c.html</link>
        <guid isPermaLink="true">/README/2020/12/21/error-handling-in-c.html</guid>
        
        
      </item>
    
      <item>
        <title>I Will Not Attend - Climate Change and Conference Travel</title>
        <description>&lt;p&gt;California is burning and I am not attending the 105th meeting of the Internet
Engineering Task Force in Montreal. I won’t attend the 106th in Singapore
either. As the climate crisis crests higher each month, our responsibility for
an urgent coordinated response mounts with it. I will not fly to any conference
more than 100 miles from my home and I ask you to do the same.&lt;/p&gt;

&lt;p&gt;Instead, I will attend conferences online when there are options to do so. I
will ask for better online access when it’s not available. I am writing now as
the Montreal meeting approaches because the IETF combines frequent meetings,
long-distance travel, and superlative support for remote attendance. It’s an
ideal case for participating online, and I plan to do so. But the same logic
applies to all professional conferences.&lt;/p&gt;

&lt;p&gt;This is a collective action problem. Our governments will soon need to tax
carbon emissions, and our culture of collaboration will need to change to one
that emphasizes communication at a distance over expensive physical travel.&lt;/p&gt;

&lt;p&gt;But we can’t wait. We need to start building the culture and tools of online
work and online collaboration long before it becomes the only viable option. We
need to cut our industry’s emissions from flying immediately and drastically.&lt;/p&gt;

&lt;p&gt;The tech industry is the ideal leader for a movement to reduce professional
flying. Many of our conferences are already professionally recorded, closed
captioned, and live-streamed. The most advanced, like the IETF, have two-way
participation with a queue for remote participants to readily speak or ask
questions. Since the 90s the Internet has promised to bring us closer together
without the messy and inconvenient business of travel. It is time we asked the
Internet to fulfill that promise.&lt;/p&gt;

&lt;p&gt;There’s a long way to go. Some conferences have slow Internet; some homes have
slow Internet. There’s no replacement for face-to-face contact, and we need to
find an online replacement for the valuable “hallway track” (impromptu
conversations between sessions). But we won’t find the solutions, as a
community, until we take the hard first step that makes them necessary, and
reject flying.&lt;/p&gt;
</description>
        <pubDate>Fri, 19 Jul 2019 11:00:00 -0700</pubDate>
        <link>/README/2019/07/19/i-will-not-attend.html</link>
        <guid isPermaLink="true">/README/2019/07/19/i-will-not-attend.html</guid>
        
        
      </item>
    
      <item>
        <title>Using Git Blame on Refactored or Reformatted Code</title>
        <description>&lt;p&gt;Update: &lt;code&gt;git log -G some_pattern&lt;/code&gt; is often a much easier way to do this!&lt;/p&gt;

&lt;p&gt;If you want to know when or why a particular line of code was written, “git blame” is a powerful tool. However, very often you’ll find that “git blame” leads you to a routine refactor or reformatting of code, rather than the meaningful change you were looking for. Fortunately, there’s a fairly quick way to chase down the true origin of a line of code, using git blame’s revision argument and the ~ (parent) operator.&lt;/p&gt;

&lt;p&gt;First, you run the normal “git blame” command. These examples are from the Signal Desktop repo.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ export PAGER=less
$ git blame main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I’m interested in the commit that commented out “sandbox: true”, so I search for that using less’ “/” (search) operator.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1dd87ad19 (Daniel Gasienica 2018-04-27 17:25:04 -0400 215)         // sandbox: true,
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now I want to see what happened in that commit:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git show 1dd87ad19
commit 1dd87ad1971f86dc954bbfbf365673172a5ba990
Author: Daniel Gasienica &amp;lt;daniel@signal.org&amp;gt;
Date:   Fri Apr 27 17:25:04 2018 -0400

    Format all source code using Prettier
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is pretty clearly a reformat. If I want to be sure of what changed, I could use “/” (search) again to find the diff and check for myself.&lt;/p&gt;

&lt;p&gt;Now I want to find: What was the commit that touched that line &lt;em&gt;before&lt;/em&gt; that? In other words, if I stepped back to the commit one before &lt;code&gt;1dd87ad19&lt;/code&gt;, what is the output of &lt;code&gt;git blame&lt;/code&gt;? Fortunately git has a convenient syntax: &lt;code&gt;1dd87ad19~&lt;/code&gt; (note the tilde) means “the commit one before &lt;code&gt;1dd87ad19&lt;/code&gt;”. Also, &lt;code&gt;git blame&lt;/code&gt; has a parameter that specifies the revision to look at. So instead of checking out the whole repo to &lt;code&gt;1dd87ad19~&lt;/code&gt;, we just do:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git blame 1dd87ad19~ main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Searching for sandbox: true shows an earlier commit:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;64fe9dbfb (Scott Nonnenberg 2018-01-08 13:19:25 -0800 187)       // sandbox: true,
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we repeat the process:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ git show 64fe9dbfb
commit 64fe9dbfb2ef3e8c9afbc78c9879ad596c3fb43b
Author: Scott Nonnenberg &amp;lt;scott@nonnenberg.com&amp;gt;
Date:   Mon Jan 8 13:19:25 2018 -0800

    Clean logs on start - and eslint/mocha with code coverage (#1945)
…
-      //sandbox: true,
-      preload: path.join(__dirname, &apos;preload.js&apos;)
+      // sandbox: true,
+      preload: path.join(__dirname, &apos;preload.js&apos;),
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is another formatting diff, so we chase it further with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git blame 64fe9dbfb~ main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And so forth. Eventually, we get to the original commit, which has a meaningful explanation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;commit 77d5ef2f6888079356222fec96fa9bd155f28f7e
Author: lilia &amp;lt;liliakai@gmail.com&amp;gt;
Date:   Wed Apr 19 15:25:03 2017 -0700

    Add spellcheck
    
    As of Electron 1.6.5, this requires disabling the sandbox in order to
    get access to the `webFrame` api.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It’s possible to speed up the process somewhat by piping the output of &lt;code&gt;git blame&lt;/code&gt; through a grep for the thing of interest, but sometimes this doesn’t work as well if lines got split up at some point.&lt;/p&gt;

&lt;p&gt;If files were renamed or split up at some point in the git history, this will show up in your &lt;code&gt;git show&lt;/code&gt; commands. In that case you’ll need to change the name of the file that you pass to &lt;code&gt;git blame&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hopefully this helps you understand code bases faster. I’ve thought about writing a tool to semi-automate the process but haven’t come up with a great workflow. Let me know if you’ve got one!&lt;/p&gt;
</description>
        <pubDate>Fri, 07 Dec 2018 10:01:31 -0800</pubDate>
        <link>/README/2018/12/07/chasing-git-blame.html</link>
        <guid isPermaLink="true">/README/2018/12/07/chasing-git-blame.html</guid>
        
        
      </item>
    
      <item>
        <title>Moderation in Open Source</title>
        <description>&lt;p&gt;Yesterday I attended the Maintainerati un-conference. It was neat! I joined a breakout session on moderation in open source communities, or keeping your community nice. Below are some of the things we talked about, plus a few of my own later thoughts. These focus largely on GitHub-centric communities.&lt;/p&gt;

&lt;h2 id=&quot;start-building-a-good-culture-early&quot;&gt;Start building a good culture early&lt;/h2&gt;

&lt;p&gt;There were two examples of big projects that had trouble with people being jerks, and attempts to fix it were stymied by disagreement on what to do, often from within the community of project leaders or admins. In one of the projects, frequently a moderator would remove a negative post, only to have their action undone by another moderator, escalating the conflict rather than healing it! It’s harder to turn a large ship than a small one.&lt;/p&gt;

&lt;h2 id=&quot;delete-early-delete-often&quot;&gt;Delete early, delete often&lt;/h2&gt;

&lt;p&gt;Every moment a jerky post stays up, more people in your community see it. That slightly changes their perception of what is normal in your community. For some people that will push them towards more jerkiness; for others it will push them slowly away from your project.&lt;/p&gt;

&lt;p&gt;Many maintainers (myself included) worry that people with moderated comments will cry censorship, but remember that your project’s issues and mailing lists are a community of purpose, and every unkind comment impairs that purpose. There are plenty of other venues on the Internet to debate philosophy; your issue tracker doesn’t need to be one of them. And remember that in most cases people whose comments are deleted never show up again.&lt;/p&gt;

&lt;p&gt;Commenting on a thread saying “that comment was unacceptable” can be a useful tool for publicly setting standards of behavior, but keep it to a minimum; It can easily wind up escalating the conflict and derailing the original conversation. In most cases, a delete and a private follow-up message suffices.&lt;/p&gt;

&lt;h2 id=&quot;there-are-different-kinds-of-bad-actors&quot;&gt;There are different kinds of bad actors&lt;/h2&gt;

&lt;p&gt;One project that was mentioned has a specific category for sockpuppet accounts; those with no activity other than a single unkind comment. These are easy to identify, and anyone with moderation privileges is encouraged to delete them on sight with no further process.&lt;/p&gt;

&lt;p&gt;There are also brigades and drive-bys, when someone on another platform encourages lots of people to come to your project, generally to leave rude comments on one of your issues. A good strategy to deal with these: edit the top post to say [Moderator’s note: If you want this issue fixed, use the :thumbsup: emoji reaction on the first post. All “me too” and similar comments will be swiftly deleted]. The earlier you do this the better. You may also need to temporarily restrict commenting on the issue.&lt;/p&gt;

&lt;p&gt;Then there are the persistent bad actors who are part of your community or an adjacent community. Consistent and frequent reminders of how to behave, combined with willingness to delete comments when necessary, may have an effect here, but there are often tricky issues about respect and clout and standing in a community (see above re: setting a good culture early).&lt;/p&gt;

&lt;h2 id=&quot;discussing-moderation-produces-more-conflict-than-doing-it&quot;&gt;Discussing moderation produces more conflict than doing it&lt;/h2&gt;

&lt;p&gt;Nerds love nothing more than a good bikeshed, and every discussion of adopting a Code of Conduct has resulted in endless bikeshedding. Set a deadline for decisions on all major moderation-related changes (which should still be public). Also, provide some amount of private space for moderators to discuss pending enforcement decisions. Remember that on GitHub, trolls don’t get notified when their comment is deleted, so most of them will never even come back to raise a stink.&lt;/p&gt;

&lt;h2 id=&quot;good-tools-are-important&quot;&gt;Good tools are important&lt;/h2&gt;

&lt;p&gt;Tools really shape how you do moderation. For instance, I’m a huge fan of the Discourse forum software, because the authors put a lot of thought into good moderation. For instance, any community member can flag a post, and three flags hides a post. Admins can of course review and un-hide posts. Also, most moderation involves hiding a post behind a light gray link, which feels like an easier decision to make than deleting it outright. Also, the first time a post is hidden, the author is given the option to wait ten minutes, then edit it to automatically unhide it. This gives people an automatic second chance to try and improve their behavior.&lt;/p&gt;

&lt;p&gt;GitHub could provide better moderation tools. I also highly recommend running a Discourse forum alongside your repos for more in-depth conversations, question-asking, and support.&lt;/p&gt;

&lt;h2 id=&quot;write-enforcement-guidelines-to-go-along-with-your-codes-of-conduct&quot;&gt;Write enforcement guidelines to go along with your codes of conduct&lt;/h2&gt;

&lt;p&gt;Enforcement is the hard part, and the larger your community (and the more moderators you have), the more specific you need to get.&lt;/p&gt;

&lt;p&gt;Figure out who bears the brunt of bad behavior, and give them moderation privileges&lt;/p&gt;

&lt;p&gt;On some projects, newbies receive more of the rude behavior, and it’s harder for them to take since they may feel like they don’t have enough social capital to deal with the trolls, especially if the trolls are more experienced or have more social capital than they do. Make sure the people who are most affected by harassment are directly empowered to do something about it.&lt;/p&gt;

&lt;h2 id=&quot;dont-be-afraid-to-delete-comments-criticizing-yourself&quot;&gt;Don’t be afraid to delete comments criticizing yourself&lt;/h2&gt;

&lt;p&gt;This is one I struggle with a lot, since it feels like an abuse of power. Still, if participants in the community see people being abusive to project leaders and that behavior going unchecked, they can assume they’ll be on the receiving end eventually, and this can drive them away from the project. Criticism is fine and important, but you can demand that your critics treat you as kindly as they would a beloved colleague, or take their criticism somewhere other than your project’s issue tracker and mailing lists.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In game design, they say “when you think you’ve made a level just hard enough, make it half as hard.” Level designers have a closeness to the game that makes it hard to see how difficult their levels are to real players. Similarly, project maintainers have a closeness to their project that makes it hard to see what it looks like to others. When you think you’ve made your community nice enough, make it twice as nice.&lt;/p&gt;
</description>
        <pubDate>Wed, 11 Oct 2017 11:21:00 -0700</pubDate>
        <link>/README/2017/10/11/moderation-in-open-source.html</link>
        <guid isPermaLink="true">/README/2017/10/11/moderation-in-open-source.html</guid>
        
        
      </item>
    
      <item>
        <title>HTTP/2, the Twitter API, and Node</title>
        <description>&lt;p&gt;Update 2017-01-04: Twitter has &lt;a href=&quot;https://twittercommunity.com/t/improving-the-twitter-api-support-for-http-2/98728&quot;&gt;announced an update&lt;/a&gt; to HTTP/2 support in the API
so the Accept-Encoding header is honored, and the workaround below is no longer
required. However, you may want to do it anyhow in order to reduce your
bandwidth needs when talking to the Twitter API. By default, Twitter will not
compress responses.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://http2.github.io/&quot;&gt;HTTP/2&lt;/a&gt; is a major revision of the HTTP protocol,
most notable for multiplexing requests across a single TCP connection. Twitter
was an early supporter of HTTP/2 and its predecessor SPDY. Now that HTTP/2
is going to be natively supported in Node 8, I wanted to use it in &lt;a href=&quot;https://blocktogether.org/&quot;&gt;Block
Together&lt;/a&gt; to make more efficient use of resources.&lt;/p&gt;

&lt;p&gt;Unfortunately, the Twitter API servers implement an earlier draft of HTTP/2.
Originally, client support for gzip and deflate was mandatory, so Twitter could
send deflated content even if the client did not send Accept-Encoding: deflate.
That requirement was removed in a later draft, so many clients, including Node’s
&lt;code&gt;http2&lt;/code&gt; package, don’t support deflate natively. In order to use Node’s HTTP/2
with Twitter, you’ll need to add a little intermediate layer to deflate or
gunzip:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;const http2 = require(&apos;http2&apos;);
const zlib = require(&apos;zlib&apos;);
const client = http2.connect(&apos;https://api.twitter.com&apos;);
const req = client.request({
  &apos;accept-encoding&apos;: &apos;gzip,deflate&apos;,
  &apos;:path&apos;: &apos;/robots.txt&apos;
});

let data = []
let expander = input =&amp;gt; input;
req.on(&apos;response&apos;, response =&amp;gt; {
  console.log(response)
  switch (response[&apos;content-encoding&apos;]) {
    case &apos;deflate&apos;:
      expander = zlib.inflateSync; break;
    case &apos;gzip&apos;:
      expander = zlib.gunzipSync; break;
  }
})
req.on(&apos;data&apos;, d =&amp;gt; data.push(d))
req.on(&apos;end&apos;, (r) =&amp;gt; {
  var buffer = Buffer.concat(data);
  console.log(expander(buffer).toString(&apos;utf8&apos;));
  client.destroy()
});
req.end();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that it’s easy to mess up the encoding by converting the data chunks to
strings too early using the wrong encoding. When you do that, the binary data
can get interpreted as UTF-8 and mangled.&lt;/p&gt;

&lt;p&gt;To really use the Twitter API, of course, you’ll need to use an OAuth library.
I’ve forked the node-oauth package and have an &lt;a href=&quot;https://github.com/jsha/node-oauth/tree/http2&quot;&gt;http2
branch&lt;/a&gt; that implements the
deflation code above. You can use it by adding a line like this to your
package.json:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    &quot;oauth&quot;: &quot;git://github.com/jsha/node-oauth#http2&quot;,
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The upstream maintainer of node-oauth does not appear to be taking pull
requests, so this probably won’t be upstreamed. And I don’t intend to actively
maintain this branch other than for my own use, so consider this more along
the lines of example code than a package ready to be used.&lt;/p&gt;

&lt;p&gt;The issue with Twitter and Content-Encoding: deflate has also been noticed and
&lt;a href=&quot;https://github.com/golang/go/issues/18779&quot;&gt;fixed by Go&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Tue, 10 Oct 2017 11:13:31 -0700</pubDate>
        <link>/README/2017/10/10/http2-twitter-api-node.html</link>
        <guid isPermaLink="true">/README/2017/10/10/http2-twitter-api-node.html</guid>
        
        
      </item>
    
      <item>
        <title>How Not To Get Phished</title>
        <description>&lt;h4 id=&quot;password-managers-are-your-best-defense-against-phishing-and-phishing-is-one-of-the-best-reasons-to-use-a-password-manager&quot;&gt;Password managers are your best defense against phishing, and phishing is one of the best reasons to use a password manager.&lt;/h4&gt;

&lt;p&gt;A &lt;a href=&quot;https://www.wordfence.com/blog/2017/01/gmail-phishing-data-uri/?utm_source=list&amp;amp;utm_campaign=011217&amp;amp;utm_medium=email&quot;&gt;clever phishing campaign&lt;/a&gt; is making the rounds, using a &lt;code&gt;data:text/html&lt;/code&gt; URI to disguise itself. The usual advice is also making the rounds in its wake, suggesting that everyone learn exactly what to look for in the URL bar before typing a password. I’d like to propose a much more effective and systematic fix instead:&lt;/p&gt;

&lt;p&gt;Forget your passwords. No, really— forget them, and instead use a password manager with autofill.&lt;/p&gt;

&lt;p&gt;Backing up a bit: Phishing is what happens when someone tricks you into typing the right password into the wrong website. Unfortunately, it’s all too easy to fall victim to phishing, because it takes advantage of a mismatch between what humans are good at and what computers are good at.&lt;/p&gt;

&lt;p&gt;Computers are very good at comparing two things to see if they are exactly equal. If you ask a computer whether &lt;code&gt;www.paypal.com&lt;/code&gt; and &lt;code&gt;ww.wpaypal.com&lt;/code&gt; are the same site, it can easily determine that they are different, unrelated sites. Most humans can tell the difference most of the time, but if they are tired, or stressed, or in a rush, or have any number of other common obstacles to computer use, there’s a good chance they won’t notice the difference, will type their password into the wrong site, and will have their account taken over by bad guys.&lt;/p&gt;

&lt;p&gt;As an individual, your entire defense against phishing hinges on the ability to make that distinction with the same inhuman perfection your computer does: before you type your password into a web site, you need to check the URL bar of your browser to make sure the domain name is exactly correct, and do it with 100% accuracy.&lt;/p&gt;

&lt;p&gt;Fortunately, there’s a better way: Use a password manager with autofill, and generate unique, complex passwords for every site. When a password manager fills in a password for you on a site, it will only fill in passwords that match that site’s domain name. If you land on a lookalike domain name as part of a phishing attack, it will not fill in any passwords, because that lookalike domain name isn’t an exact match for any of your saved passwords.&lt;/p&gt;

&lt;p&gt;Faced with a password manager that is refusing to fill in a password, you may, in a weaker moment, go ahead and type in your password anyhow. This is why it’s also important to use a unique, complex password for every site, ideally one randomly generated by your password manager. You want to make it impossible to remember your passwords, so you have to rely on your computer to autofill them.&lt;/p&gt;

&lt;h2 id=&quot;saving-passwords-in-the-browser&quot;&gt;Saving passwords in the browser&lt;/h2&gt;

&lt;p&gt;Some outdated security advice recommends against using your browser’s built-in saved passwords list. This advice is typically intended to defend against someone with in-person access to an unlocked computer. These days, phishing is a much more common attack than in-person access, and screen lock passwords are much more ubiquitous, so the tradeoff is different.&lt;/p&gt;

&lt;p&gt;Since a browser’s saved password list is effectively a built-in password manager, it’s a good defense against phishing. The main thing browsers lack in the password management department is an easy way to generate random passwords, though &lt;a href=&quot;https://www.chromium.org/developers/design-documents/password-generation&quot;&gt;Chrome is working on it&lt;/a&gt;. Browsers also tend to lack some of the fancier features of dedicated password managers, like auditing your passwords for quality, or automatically changing them. That said, they have the important advantage of already being installed.&lt;/p&gt;

&lt;h2 id=&quot;vulnerabilities-in-password-manager-extensions&quot;&gt;Vulnerabilities in password manager extensions&lt;/h2&gt;

&lt;p&gt;In July and August of 2016, &lt;a href=&quot;https://twitter.com/taviso&quot;&gt;Tavis Ormandy&lt;/a&gt; investigated password managers, and quickly &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=884&quot;&gt;found&lt;/a&gt; &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=888&quot;&gt;several&lt;/a&gt; &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=890&quot;&gt;critical&lt;/a&gt; &lt;a href=&quot;https://bugs.chromium.org/p/project-zero/issues/detail?id=917&quot;&gt;vulnerabilities&lt;/a&gt;, which were then fixed.  These vulnerabilities demonstrated the unfortunate truth that software marketed for security purposes often introduces its own security flaws.&lt;/p&gt;

&lt;p&gt;This doesn’t mean you should avoid password managers altogether. For most users, the benefit from using a password manager with autofill features is so large that it outweighs the risks from installing additional software that may be vulnerable. But it all depends on your estimation of how vulnerable the common password manager extensions are. The presence of past vulnerabilities is not always a good predictor of software quality, so it’s hard to say. Using a browser’s built-in password manager is probably a fairly safe bet, since browser security teams are generally larger and better-funded than password manager companies.&lt;/p&gt;

&lt;h2 id=&quot;two-factor-authentication&quot;&gt;Two-factor authentication&lt;/h2&gt;

&lt;p&gt;2FA is also commonly touted as a strong defense against phishing. It can be, but as &lt;a href=&quot;https://www.schneier.com/blog/archives/2005/04/more_on_twofact.html&quot;&gt;Bruce Schneier pointed out in 2005&lt;/a&gt;, any 2FA that depends on the user typing in a secondary code (as most 2FA implementations today do) can be defeated by a phishing site that simply asks for the code and passes it along to the real site. So using a password manager with autofill and random passwords is actually a stronger phishing defense than most common two-factor authentication.&lt;/p&gt;

&lt;p&gt;The recent &lt;a href=&quot;https://en.wikipedia.org/wiki/Universal_2nd_Factor&quot;&gt;Universal 2nd Factor&lt;/a&gt; (U2F) standard solves this problem by requiring a small hardware device (a “security key”) that you insert instead of typing in a secondary code. Your browser talks to the security key, and includes the identity of the site you’re trying to log into, so if you are trying to log into a phishing site, the security key will not send any data that could be used to log into your real account. This technique offers much stronger security, but is currently implemented only by a small number of browsers and sites, and requires purchase of an extra device.&lt;/p&gt;
</description>
        <pubDate>Sun, 15 Jan 2017 03:13:31 -0800</pubDate>
        <link>/README/2017/01/15/how-not-to-get-phished.html</link>
        <guid isPermaLink="true">/README/2017/01/15/how-not-to-get-phished.html</guid>
        
        
      </item>
    
      <item>
        <title>The Three Types of Email Encryption</title>
        <description>&lt;p&gt;There are three types of encryption relevant to email:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SMTP STARTTLS: This is server-to-server, and is implemented by your email provider, not you as an end user. Uses SSL certificates.&lt;/li&gt;
  &lt;li&gt;S/MIME: This is person-to-person, and is implemented by you. Uses email certificates.&lt;/li&gt;
  &lt;li&gt;PGP: Person-to-person, implemented by you. Doesn’t use certificates, uses PGP-format public keys. Includes both inline PGP and PGP/MIME.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that there’s a difference between SSL certificates and email certificates.
The first identifies a server, and can be used for HTTPS, or SMTP STARTTLS, or a
number of other TLS (aka SSL) protocols. The second identifies a specific email
address.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.instantssl.com/ssl-certificate-products/free-email-certificate.html&quot;&gt;Some CA pages&lt;/a&gt;
use the phrase “SSL email certificate,” but as far as I know the “SSL” part
of that is inaccurate. They appear to be offering email certificates on that page.&lt;/p&gt;

&lt;p&gt;S/MIME and PGP are more or less mutually exclusive in that you’d receive and
send encrypted mail using one or the other. And most likely your mail software
is easiest to configure one way or the other.  However, it’s entirely a matter
of local configuration. Just the act of getting an email certificate for use
with S/MIME doesn’t prevent you from using PGP, but installing it may lead your
email client to think you want to use S/MIME rather than PGP.&lt;/p&gt;

&lt;p&gt;Note that an email certificate for S/MIME is like your public key for PGP. It
tells other people how they can encrypt mail to you. It doesn’t help you encrypt
mail to them. For that, you need &lt;em&gt;their&lt;/em&gt; email certificate. My understanding is
that S/MIME encryption is most common within organizations, where there is a
shared infrastructure for fetching people’s addresses and corresponding
certificates. PGP is more common between people who don’t work for the same
organization.&lt;/p&gt;

&lt;p&gt;STARTTLS works at a different level. So long as STARTTLS is implemented by both your
email provider and the provider of the person you are sending mail to, your
email will be encrypted in transit between those two providers. However, each
email provider will be able read your messages. STARTTLS interoperates invisibly
with S/MIME, PGP, and unencrypted email.&lt;/p&gt;
</description>
        <pubDate>Sun, 28 Aug 2016 00:00:00 -0700</pubDate>
        <link>/README/2016/08/28/types-of-email-encryption.html</link>
        <guid isPermaLink="true">/README/2016/08/28/types-of-email-encryption.html</guid>
        
        
      </item>
    
      <item>
        <title>How to set up Sonic.net VPN service on Ubuntu 15.04</title>
        <description>&lt;p&gt;Sonic.net offers a free &lt;a href=&quot;https://wiki.sonic.net/wiki/VPN_Service&quot;&gt;VPN service to all customers&lt;/a&gt;. Here are instructions on how to set it up on Ubuntu:&lt;/p&gt;

&lt;p&gt;# sudo apt-get install network-manager-vpnc vpnc&lt;/p&gt;

&lt;p&gt;Click on wireless icon in toolbar, select “Edit connections…”&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/network-connections.png&quot;&gt;&lt;img alt=&quot;network connections&quot; src=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/network-connections.png&quot; width=&quot;400&quot; height=&quot;328&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click “Add”&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/add-connection.png&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-275&quot; alt=&quot;add connection&quot; src=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/add-connection.png&quot; width=&quot;544&quot; height=&quot;565&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select “Cisco Compatible VPN (vpnc)”&lt;/p&gt;

&lt;p&gt;Fill in the details from &lt;a href=&quot;https://wiki.sonic.net/wiki/VPN_Service&quot;&gt;Sonic’s help page&lt;/a&gt;. At the time of writing, that’s:&lt;/p&gt;

&lt;p&gt;Gateway: ipsec.vpn.sonic.net&lt;/p&gt;

&lt;p&gt;Username: your username from members.sonic.net.&lt;/p&gt;

&lt;p&gt;Password: your password from members.sonic.net. You need to change the dropdown on the right-hand side from “Always ask” to “Saved” if you want to store your password here. Otherwise you will be prompted when you connect.&lt;/p&gt;

&lt;p&gt;Group name: Standard VPN&lt;/p&gt;

&lt;p&gt;Group password: standard. You need to change the dropdown on the right-hand side from “Always ask” to “Saved” to store your password here. Since it’s a publicly-known password, there’s no downside choosing “Saved.”&lt;/p&gt;

&lt;p&gt;Set Connection name to “sonic.net” or anything you like.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/add-connection.png&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-275&quot; alt=&quot;add connection&quot; src=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/add-connection.png&quot; width=&quot;544&quot; height=&quot;565&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/edit-vpn.png&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-276&quot; alt=&quot;edit vpn&quot; src=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/edit-vpn.png&quot; width=&quot;538&quot; height=&quot;506&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click “Save.” Click “Close” on the Network Connections window. Click the wireless icon in the top bar, select “VPN Connections,” and then “sonic.net” (or whatever you named your VPN config). The wireless icon will make a “connection” animation for a few seconds, and then a small lock icon should appear in the lower right. If the lock icon does not appear, the VPN connection failed and you need to do some troubleshooting.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/lock-icon.png&quot;&gt;&lt;img class=&quot;alignnone size-full wp-image-277&quot; alt=&quot;lock icon&quot; src=&quot;https://jacob.hoffman-andrews.com/README/wp-content/uploads/2015/10/lock-icon.png&quot; width=&quot;29&quot; height=&quot;23&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to auto-connect to VPN for certain networks (e.g. open wifi), click the wireless icon in the top bar, select “Edit connections,” select the network you are interested in. Click “Edit” and select the “General” tab. Select “Automatically connect to VPN when using this connection,” and choose your VPN from the drop-down below (if it is not already chosen).&lt;/p&gt;
</description>
        <pubDate>Mon, 05 Oct 2015 15:27:31 -0700</pubDate>
        <link>/README/how-to-set-up-sonic-net-vpn-service-on-ubuntu-15-04/</link>
        <guid isPermaLink="true">/README/how-to-set-up-sonic-net-vpn-service-on-ubuntu-15-04/</guid>
        
        
        <category>Uncategorized</category>
        
      </item>
    
      <item>
        <title>Emulate X-Frame-Options: ALLOW-FROM using postMessage</title>
        <description>&lt;p&gt;Clickjacking is a a web-based attack that can be mounted on a website when the attacker is able to display that website inside an iframe on another website. In short, the attacker tricks users into clicking a button on their target site, e.g. [delete account] or [&lt;a href=&quot;http://www.theregister.co.uk/Print/2009/03/20/twitter_viral_xss_flaw/&quot;&gt;tweet&lt;/a&gt;].&lt;/p&gt;

&lt;p&gt;Websites prevent clickjacking by sending the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options&quot;&gt;X-Frame-Options: SAMEORIGIN header&lt;/a&gt;, which tells browsers not to display their pages inside an iframe on any site other than their own. Since the iframe doesn’t display anything, there’s nothing to click.&lt;/p&gt;

&lt;p&gt;However, sometimes a site want to allow limited framing by other hostnames (origins). For instance, Twitter shows extended tweet information from cards.twitter.com in an iframe loaded on twitter.com. Since there are sometimes buttons inside those cards, like ‘share this,’ they want to make sure cards.twitter.com can &lt;strong&gt;only&lt;/strong&gt; be displayed by twitter.com.&lt;/p&gt;

&lt;p&gt;The X-Frame-Options header has another setting: X-Frame-Options: ALLOW-FROM &lt;uri&gt;. This allows, e.g. cards.twitter.com to serve &amp;#8216;X-Frame-Options: ALLOW-FROM https://twitter.com&amp;#8217;. That prevents iframing by third parties but not by twitter.com itself. Unfortunately, ALLOW-FROM is only supported on Firefox and IE. Eventually ALLOW-FROM will be supplanted by [CSP&amp;#8217;s frame-ancestors directive](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html), but that&amp;#8217;s not ready for prime time yet.&lt;/uri&gt;&lt;/p&gt;

&lt;p&gt;So, what can we do today? It turns out you can get the equivalent ALLOW-FROM behavior today if you’re willing to require Javascript. We start with the antiClickjack CSS approach from Rydstedt, Burzstein, Boneh, and Jackson, described in short at &lt;a href=&quot;https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#Best-for-now_Legacy_Browser_Frame_Breaking_Script&quot;&gt;OWASP&lt;/a&gt; and with more detail in the original paper, &lt;a href=&quot;http://seclab.stanford.edu/websec/framebusting/&quot;&gt;“Busting Frame Busting”&lt;/a&gt;. On top of that, we require the host frame to send a postMessage to the child frame. The content of the message doesn’t matter. The child frame will check the ‘origin’ attribute of the received message, which is guaranteed by the browser, against a list of acceptable parent frames. If the sending origin is acceptable, the child frame removes the antiClickjack style node, and the content (including buttons) becomes visible. If the sending origin is not acceptable, the child frame does nothing and the content remains hidden and non-interactable.&lt;/p&gt;

&lt;p&gt;Note: It’s important that the parent frame is not itself frameable. Typically that means the parent frame sends X-Frame-Options: SAMEORIGIN, though one could also implement the ALLOW-FROM strategy recursively if necessary.&lt;/p&gt;

&lt;p&gt;You can see an example in action at &lt;a href=&quot;http://xfoaf1.crud.net&quot;&gt;xfoaf1.crud.net&lt;/a&gt;. It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parent frame&lt;/strong&gt;:&lt;/p&gt;

&lt;pre style=&quot;border: 1px light grey;&quot;&gt;function loadFrame(url) {                                                                                                                                      
  var ifr = document.createElement(&apos;iframe&apos;);                                                                                                                  
  ifr.src = url;                                                                                                                                               
  ifr.onload = function() {                                                                                                                                    
    // Send a dummy message just to prove we&apos;re here.                                                                                                          
    ifr.contentWindow.postMessage(&quot;openSesame&quot;, &quot;*&quot;);                                                                                                          
  }                                                                                                                                                            
  document.body.appendChild(ifr);                                                                                                                              
};
&lt;/pre&gt;
&lt;p&gt;&amp;lt;/p&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Child frame&lt;/strong&gt;:&lt;/p&gt;

&lt;pre style=&quot;border: 1px light grey;&quot;&gt;&amp;lt;style id=&quot;antiClickjack&quot;&amp;gt;body{display:none !important;}&amp;lt;/style&amp;gt;                                                                                               
                                                                                                                                                               
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;                                                                                                                                
   var validParents = {                                                                                                                                        
     &quot;http://xfoaf1.crud.net&quot;: 1                                                                                                                               
   };                                                                                                                                                          
   window.addEventListener(&quot;message&quot;, function(message) {                                                                                                      
     if (validParents[message.origin] &amp;amp;&amp;amp; message.data == &quot;openSesame&quot;) {                                                                                       
       var antiClickjack = document.getElementById(&quot;antiClickjack&quot;);                                                                                           
       antiClickjack.parentNode.removeChild(antiClickjack);                                                                                                    
     }                                                                                                                                                         
   }, false);                                                                                                                                                  
&amp;lt;/script&amp;gt;
&lt;/pre&gt;
</description>
        <pubDate>Sat, 17 May 2014 06:31:23 -0700</pubDate>
        <link>/README/emulate-x-frame-options-allow-from-using-postmessage/</link>
        <guid isPermaLink="true">/README/emulate-x-frame-options-allow-from-using-postmessage/</guid>
        
        
        <category>Uncategorized</category>
        
      </item>
    
  </channel>
</rss>
