<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>inconshreveable</title>
		<description>inconshreveable's blog</description>
		<link>http://inconshreveable.com</link>
		<atom:link href="http://inconshreveable.com/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>Launching Equinox and the importance of software packaging</title>
				<description>&lt;p&gt;I was proud to launch &lt;a href=&quot;https://equinox.io&quot;&gt;Equinox&lt;/a&gt; last week: a service that makes it easy
to package and distribute production Go applications.&lt;/p&gt;

&lt;p&gt;In some ways, I’ve finally come full circle. My first job in the tech industry was packaging software.
The first company I worked for had offices in a converted warehouse - a squat, colorless building with
offices ringing a great, empty space. Perched against the back wall was a lone workstation on a
folding table with a mess of wires underneath. And that’s where I first began packaging software.
I would sit at that desk and burn copies of our compiler, one-by-one at 8x speed on to blank CD-Rs.&lt;/p&gt;

&lt;p&gt;Of course, packaged software needs good identifying metadata. So while each disc went through its
5 minute burn period, I printed CD labels - four at a time - onto special adhesive paper and then
carefully applied the label to a disc with a smoothing motion to avoid air bubbles. This was also my
earliest use of containers; I assembled pre-printed boxes, folding each of the four sides upright, slotting
hooked corner tabs together to lock the cardboard into holding its form.&lt;/p&gt;

&lt;p&gt;Boxes were packed with a pink flyer advertising upcoming product launches, a yellow flyer
encouraging you to visit a website (or mail in the form on the back) to register your software,
and the CD slipped into a simple paper and plastic case. End-to-end integrity of the transported
bits was enforced by shrinkwrap.&lt;/p&gt;

&lt;p&gt;Years later, everything is different and yet still the same. We still ship bits to customers, but it’s via
fiber and cable and wireless in our homes and offices. Containers are digital things with their own subindustry
and millions of VC dollars. Instead of shrinkwrap, we use cryptographic algorithms to sign and verify code. Maybe
the biggest change though is how little most software shops even think about these concerns.&lt;/p&gt;

&lt;p&gt;Developers don’t package their own software much anymore, even digitally. In the golden age of web services,
we ship software by adding a new resource endpoint. The bits are packed once for our rigidly
controlled datacenter environment. Even the web browser in 2016, for all its faults, is an impressively
consistent application delivery platform that makes it easy to safely deliver the most up-to-date version
of your app to customers. When we deploy to app stores, we run a vendor-provided tool that manages the
entire process, wrapping up our bits appropriately for the walled gardens before shipping it off to
an opaque vendor-specific cloud service.&lt;/p&gt;

&lt;p&gt;Three years ago I launched &lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok.com&lt;/a&gt; - a tunneling reverse proxy service. As part of that,
I distribute a command-line tool available on all major platforms (and many minor ones, too).
It has given me the privilege to watch users fail to install software in every possible way. Your users
will download your software for the wrong operating system, for the wrong computer architecture and they
will install older versions from third-party distributors. They will double-click command line apps, paste
terminal commands into web browsers and try to run unzip on tar.gz archives. They do not know how to change their
&lt;code&gt;PATH&lt;/code&gt; environment variable or what it even is. They will try to run your software on machines
built 20 years ago without the SSE2 instruction set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software packaging and installation is about first impressions.&lt;/strong&gt; It is the first step of the conversion
funnel after a customer agrees you have a solution to their problems. The UX of that installation experience is
crucially important. Installation sets the tone and expectations of your user before they’ve even begun using
your product. And if it fails, the rest of your product experience is moot because it will never run. A smooth
installation experience establishes the initial trust of your user in the quality of your software.&lt;/p&gt;

&lt;p&gt;For indie developers who aren’t shipping to the browser or an App Store, it is exceedingly difficult to build simple
installation experiences. Instead of the tedium of assembling cardboard boxes, packaging modern
software is a frustrating exercise in learning poorly-designed, vendor-specific toolchains, XML manifest files and
installation behaviors. You must buy code-signing certificates with week-long turnarounds after apply for
official business registrations. The tools for each platform often only run on that OS itself, adding additional burdens
in resources and ops. Updating software to the latest version is part of this experience as well and requires
an entirely new set of tools and knowledge of platform-specific behaviors and security mindfulness.&lt;/p&gt;

&lt;p&gt;And that’s why we built Equinox: to solve these problems and make creating first-class installations experiences
easier so that you can spend more time shipping your product and delighting your customers.
For any Go program, Equinox automatically builds Windows .msi, OS X .pkg, Linux .deb and .rpm
installers, .zip and .tgz archives in addition to publishing new releases to your own Homebrew tap. Equinox
adds a small package to your app that enables self-updating functionality with just a few lines of code.&lt;/p&gt;

&lt;p&gt;We’re starting with an admittedly small, but growing niche. Equinox only supports Go programs, but we’ve
got bigger plans down the road to widen the scope of what we can distribute and how.&lt;/p&gt;

&lt;p&gt;Ready to get started? Sign up at &lt;a href=&quot;https://equinox.io&quot;&gt;equinox.io&lt;/a&gt; or drop us a line at
&lt;a href=&quot;&amp;#109;&amp;#097;&amp;#105;&amp;#108;&amp;#116;&amp;#111;:&amp;#099;&amp;#111;&amp;#110;&amp;#116;&amp;#097;&amp;#099;&amp;#116;&amp;#064;&amp;#101;&amp;#113;&amp;#117;&amp;#105;&amp;#110;&amp;#111;&amp;#120;&amp;#046;&amp;#105;&amp;#111;&quot;&gt;contact@equinox&lt;/a&gt;.&lt;/p&gt;
</description>
				<pubDate>Tue, 26 Apr 2016 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/04-26-2016/launching-equinox-and-the-importance-of-software-packaging/</link>
				<guid isPermaLink="true">http://inconshreveable.com/04-26-2016/launching-equinox-and-the-importance-of-software-packaging/</guid>
			</item>
		
			<item>
				<title>Your live coding demo is boring</title>
				<description>&lt;p&gt;I’ve been to a lot of conferences and meetups over the past year and one thing I can say
categorically about the experience is that &lt;strong&gt;if you write code live on stage, then
you are boring me&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For better or worse, what we do as software engineers is not a good spectator sport.
I can think faster than you can type. Writing code doesn’t mix well with oration.
You will fumble your speech and settle into long, ennui-inducing pauses.
The demo will likely fail because of syntax errors in code, configuration,
or command lines. And while you eat up time in an impromptu debugging session, I will
reach for my phone. Live coding demos fail so often that I’m more surprised when
they work than when they fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That does not mean you shouldn’t show code on stage.&lt;/strong&gt; Please, by all means,
put code up there on your slide and talk about it. Just don’t write it in real time.
Live coding is lazy; it is a &lt;em&gt;shortcut for creating quality presentation material&lt;/em&gt;.
On well-prepared slides, your code will be magnified to ensure that I can read it.
It will be highlighted so that you can direct my attention appropriately.&lt;/p&gt;

&lt;p&gt;If you want to show something interactive do that &lt;em&gt;with a video&lt;/em&gt;.
You can talk over the video and guide my interest of what is happening on screen.
You can edit a video to add highlights, insert pauses, speed it up 1.5x,
or whatever else is needed to suit your speaking style and flow well with the presentation.
Oh right, and &lt;em&gt;it’s guaranteed to work&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This applies with equal force to demos of CLI tools or anything heavy with keyboard input.&lt;/p&gt;

&lt;p&gt;Less live demos, more high quality presentations.&lt;/p&gt;
</description>
				<pubDate>Fri, 13 Nov 2015 00:00:00 -0800</pubDate>
				<link>http://inconshreveable.com/11-13-2015/your-live-coding-demo-is-boring/</link>
				<guid isPermaLink="true">http://inconshreveable.com/11-13-2015/your-live-coding-demo-is-boring/</guid>
			</item>
		
			<item>
				<title>The Neomonolith</title>
				<description>&lt;h2 id=&quot;monolith-or-microservices&quot;&gt;Monolith or Microservices?&lt;/h2&gt;

&lt;p&gt;In the modern silicon-valley startup zeitgeist, the CTO of a formative software company
faces a pivotal decision. Will the fledgeling software service be a monolith or microservices?
The stakes are high: rewrites are measured in man-years.&lt;/p&gt;

&lt;p&gt;There is no industry consensus on whether either approach is
strictly-better. Despite that ambivalence, there is general
agreement of two points:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Monoliths that exceed some threshold will be broken apart eventually. At a large enough scale, only a service-oriented design is workable.&lt;/li&gt;
  &lt;li&gt;There are only two options. It’s either service-oriented or a monolith, and you must choose.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Much has been written contrasting the pros and cons of monolith and microservice designs in a general context. &lt;a href=&quot;#1&quot;&gt;[1]&lt;/a&gt;
This territory is well-covered so I omit discussion of it here. Instead we will take a more narrow focus
on the initial stage of the business. The neomonolith is most suited to this formative period: when scale is small and
code changes land at a breakneck pace.&lt;/p&gt;

&lt;p&gt;You can begin with a monolith. Sometimes this is called a sacrificial
architecture because of the inevitable transition to services at large scale. It
will enable immediate, rapid progress but inevitably ossify your codebase
in a Kafkaesque entanglement of broken interfaces. That may be a reasonable tradeoff:
technical debt is irrelevant if the business fails.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/monolith.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, you can construct a microservices architecture from the get-go.
Such a design comes with a steep premium. You must stand up and operate
new pieces infrastructure, most of which is non-standardized, alpha-quality and changing rapidly.
In addition, with a monolith, the work of monitoring, alerting, configuration, and
a local development is paid once. But with a microservice design,
that cost must be paid for &lt;em&gt;every service&lt;/em&gt;.
Starting with microservices is such a risky proposition for a startup that
many proponents of the design recommend building monolith-first.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/microservices.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-false-architectural-dichotomy&quot;&gt;A False Architectural Dichotomy&lt;/h2&gt;

&lt;p&gt;This architectural question is a false dichotomy: there is an intermediate, hybrid option.
I call it the &lt;strong&gt;The Neomonolith&lt;/strong&gt;. The neomonolith is a superior design for the nascent systems built by startups.
It combines strengths of both approaches with a less onerous set of
shortcomings. I’ve had great success applying this pattern building both &lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok.com&lt;/a&gt;
and &lt;a href=&quot;https://equinox.io&quot;&gt;equinox.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The ideas described herein are not new. Even this repackaging is not genuinely novel. Other
efforts have promoted similar architectural designs, but most are couched
in the context of a particular language or framework. &lt;a href=&quot;#2&quot;&gt;[2]&lt;/a&gt;
The neomonolith pattern is not limited to a particular language or framework – indeed,
one of its strengths is the tolerance of heterogeneity in implementation languages!&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-neomonolith&quot;&gt;What is the Neomonolith?&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A neomonolith can be conceptualized as a special-case deployment of a microservice architecture.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like a microservices architecture, a neomonolith is composed of small, independent services communicating with well-defined RPC interfaces. But like a monolith, each machine runs the same set of code. Each server is an identical image that runs &lt;em&gt;all&lt;/em&gt; of the individual services. All services start when a host boots and stop when it shuts down.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/neomonolith-one-many.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Services communicate with each other over ports bound on the loopback interface. A service listens on the same port regardless of which host it runs on, obviating the need
for any service discovery system.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/neomonolith-loopback.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Containers are optional.&lt;/em&gt;
They can be helpful if you are struggling with resource contention between misbehaving services, but
they are easy to add later if you are not prepared to pay their operational cost up front.&lt;/p&gt;

&lt;h2 id=&quot;the-best-of-both-worlds&quot;&gt;The Best of Both Worlds&lt;/h2&gt;

&lt;p&gt;The neomonolith provides the best of both worlds, combining benefits from both monolith and microservice styles:&lt;/p&gt;

&lt;h3 id=&quot;microservice-benefits&quot;&gt;Microservice benefits&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Independence of development&lt;/strong&gt; - services can be developed independently, even in different code repositories&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Independence of deployment&lt;/strong&gt; - services can be deployed without affecting others&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Independence of failure&lt;/strong&gt; - e.g. a file descriptor leak in one process will not affect others on the same host&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Enforced interfaces and the single responsibility principle&lt;/strong&gt; - services are small and must communicate over well-defined interfaces&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Heterogeneity of language/frameworks&lt;/strong&gt; - services communicate over APIs, allowing teams to “use the right tool for the job” if that would be more productive&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;monolith-benefits&quot;&gt;Monolith Benefits&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No orchestration complexity&lt;/strong&gt; - all boxes run an identical set of code and processes&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No dynamic service scheduling&lt;/strong&gt; - services start on boot, halt at shutdown&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No service discovery&lt;/strong&gt; - services listen on the same port on all hosts&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Easy scaling&lt;/strong&gt; - capacity can be added by deploying additional machines with the same image behind a load balancer&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Defer distributed systems problems&lt;/strong&gt; - use of loopback enables you to defer robust handling of uncommon failures caused by unreliable networks &lt;a href=&quot;#3&quot;&gt;[3]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three of the monolith benefits listed begin with the word ‘No’. Anyone who has operated large-scale systems understands why.
Every additional piece of infrastructure you run is an operational liability; each adds another point of failure.
Institutional knowledge and software must be built to configure, monitor and alert on these new pieces.&lt;/p&gt;

&lt;p&gt;But beyond that, the infrastructure of a typical microservice PaaS deployment bears yet another oft-ignored cost.
Complicating the production infrastructure makes a software system harder to reason about.
It becomes more difficult to build a mental model of how the entire system operates. This makes production incidents
harder to debug: “Is it our code, or a problem with the PaaS?” It also increases the knowledge and tools
required for development, ratcheting up the barrier to entry for new programmers.&lt;/p&gt;

&lt;p&gt;A neomonolith dispenses with all of that complexity, greatly simplifying the software system.
Problems that still plague microservice PaaS installations are often non-issues.
Deployment requires only tried-and-tested tools familiar to anyone who has done ops in the last 10 years.&lt;/p&gt;

&lt;h2 id=&quot;drawbacks&quot;&gt;Drawbacks&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No scaling independence&lt;/strong&gt; - you must scale out all services to meet the demand of the most resource-intensive one&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Requires investment in operational tooling&lt;/strong&gt; - monitoring, alerting, and local dev must be repeated or reusable for each service&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Refactoring across RPC interfaces&lt;/strong&gt; - it’s still more difficult than refactoring over a monolith’s functions&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limited failure isolation&lt;/strong&gt; - if a service dies, its consumers can’t talk to another one running on a different host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A neomonolith is not a panacea. It shares faults of both monoliths and service-oriented systems. It is more effort than starting
monolith-first and it will not scale out forever like service-oriented systems in place at Amazon or Google. On the other hand,
most of us don’t run at that scale. It’s important to remember that not running tens of thousands of machines &lt;em&gt;is an advantage&lt;/em&gt;
that you should leverage while it lasts.&lt;/p&gt;

&lt;h2 id=&quot;transitioning-to-a-complete-microservice-design&quot;&gt;Transitioning to a complete microservice design&lt;/h2&gt;

&lt;p&gt;Once the tools improve and mature, that there will be no question
about whether you begin with a sacrificial monolith first. But right now,
&lt;a href=&quot;http://blog.circleci.com/it-really-is-the-future/&quot;&gt;it is not the future yet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The single most important benefit of a neomonolith is the clear path
to a full microservices PaaS deployment&lt;/strong&gt;. The up-front investment in defining RPC interfaces and scoping
out services means that when you’re ready to deploy on a Mesos/CoreOS/Kubernetes cluster,
it will require no major architectural changes. Switching to service discovery or cluster proxies
instead of using loopback is a task measured in weeks, not years. Moreover, PaaS elements
can be added piecemeal for a smooth transition. For example, sketching the first two
steps of a hypothetical switch:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/neomonolith-transition.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In short, not only does starting with a neomonolith today provide you
with a whole host of development benefits, but it additionally positions
your engineering team for a painless transition in the future
if you must scale up to a more traditional service-oriented design.&lt;/p&gt;

&lt;h2 id=&quot;video&quot;&gt;Video&lt;/h2&gt;
&lt;p&gt;I gave a lightning talk at GopherCon 2015 that is the spiritual precursor
to this piece in which I called this design ‘The Lego Monolith’.&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/3Mvk3XkKiak?list=PL2ntRZ1ySWBeHqlHM8DmvS8axgbrpvF9b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt; &lt;/iframe&gt;

&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;span id=&quot;1&quot;&gt;Microservice/Monolith tradeoffs&lt;/span&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://martinfowler.com/articles/microservices.html&quot;&gt;http://martinfowler.com/articles/microservices.html&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://highscalability.com/blog/2014/4/8/microservices-not-a-free-lunch.html&quot;&gt;http://highscalability.com/blog/2014/4/8/microservices-not-a-free-lunch.html&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;span id=&quot;2&quot;&gt;Prior Art&lt;/span&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://www.erlang.org/&quot;&gt;http://www.erlang.org/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://zeromq.org/&quot;&gt;http://zeromq.org/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://fmontesi.blogspot.com/2015/06/non-distributed-microservices.html&quot;&gt;http://fmontesi.blogspot.com/2015/06/non-distributed-microservices.html&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://micromono.io/&quot;&gt;http://micromono.io/&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;span id=&quot;3&quot;&gt;Yes&lt;/span&gt;, loopback can fail when you overflow the kernel buffers, although I’ve yet to observe this ever happen in production. Whether you choose to defer error handling and retries for network problems depends of course on your failure tolerance for that particular operation.&lt;/li&gt;
&lt;/ol&gt;
</description>
				<pubDate>Wed, 07 Oct 2015 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/10-07-2015/the-neomonolith/</link>
				<guid isPermaLink="true">http://inconshreveable.com/10-07-2015/the-neomonolith/</guid>
			</item>
		
			<item>
				<title>Sweat the small stuff</title>
				<description>&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok&lt;/a&gt; is a tunneling, reverse proxy that establishes secure tunnels from a public endpoint to a locally running
network service while capturing all traffic for inspection and replay. It is an &lt;a href=&quot;https://github.com/inconshreveable/ngrok&quot;&gt;open-source project on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;When you double-click a command-line application on any modern Windows OS, a new cmd.exe window opens running the application that was launched. When the program terminates, &lt;strong&gt;Windows closes the whole cmd.exe window&lt;/strong&gt;. When executed with no arguments, almost all command line apps print their help text and then exit. From the perspective of a user who just double-clicked a command line app, they will see a command prompt open, display a large amount of text that they don’t have time to read and then close almost instantly. Anyone who didn’t initially learn on a command line will probably remember this initial growing pain.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/openclose.gif&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To say the least, this is a tremendously poor user experience. Not only did the program &lt;strong&gt;fail to work&lt;/strong&gt;, but it did so &lt;strong&gt;without displaying any error message or diagnostic text&lt;/strong&gt; that explains why it did not work. There isn’t even anything to search Google or Stack Overflow for!&lt;/p&gt;

&lt;p&gt;Authors of most CLI tools simply choose to ignore that they completely fail for this cohort of users. It’s easy to rationalize that it’s not &lt;em&gt;your problem&lt;/em&gt;, that it’s an error in how the user is interacting with their operating system. “All of my users are technical enough to understand that!” you cry. And I’ll tell you that they will have to be, if that’s your attitude.&lt;/p&gt;

&lt;p&gt;I don’t have that luxury. Until about six months ago, I received a support email almost every week from an ngrok user who was running into this exact issue. ngrok’s target userbase spans everyone from network engineers to designers to hobbyist gamers who want to host their own minecraft server. In short, ‘non-technical’ describes a large percentage of the userbase. Here are a few examples of support emails:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;i downloaded it and install it a terminal window poped out and nothing happened.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;hi i have download for window
when i try command prompt i suddent open and hide
can you help me?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;When I try to open the .exe file command prompt opens very briefly and then nothing happens. Please help?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Windows version for ngrok doesn’t work. I downloaded ngrok for windows 7 , 64 bit . When I click ngrok.exe , console flashes for half second and then vanishes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don’t get these support emails anymore. But before we dive into why that’s the case, I want to explain how I support ngrok.&lt;/p&gt;

&lt;h2 id=&quot;the-ngrok-support-strategy&quot;&gt;The ngrok support strategy&lt;/h2&gt;

&lt;p&gt;As of this writing, there are over 17,000 registered ngrok users, and undoubtedly more who use it without an account. I support ngrok entirely by myself. There is no IRC channel for ngrok. There is no mailing list. Instead, I have made it extraordinarily easy to reach me, personally. Every page of ngrok.com not only has an email address that goes directly to me, but it also has an Olark chat window that you can use to communicate with me directly and synchronously at any hour of the day when I’m in front of my computer. The next version of ngrok will include this direct chat on the local introspection interface of every ngrok client. I’ve even idly toyed with including a phone number.&lt;/p&gt;

&lt;p&gt;I have a secret, though: I hope you never use that chat window. I don’t want to talk to you. You’re all really lovely people, but I just don’t have the time for those interruptions. I’m being a bit hyperbolic here, of course. But think of it this way: when a new user has to talk to a human in order to fix a problem they’re having, the time-to-value of your product has just ballooned from seconds to minutes or possibly even hours or &lt;em&gt;days&lt;/em&gt;. If a user contacts me to solve an issue, &lt;strong&gt;ngrok is no longer self-service and it has failed them as a product&lt;/strong&gt;. Sometimes this is an error in the core functionality or an omission in the documentation. Those are the low hanging fruit of your support problem because they’re easy to fix. The more common cause is that the UX of the tool is not good enough, i.e. ngrok behaved in some way which did not match with the user’s expectations and it failed to communicate what change could be made to correct this expectation.&lt;/p&gt;

&lt;p&gt;The goal of this support system I’ve constructed is to attempt to visit upon myself every pain that using ngrok inflicts on my customers. When you really boil it down to its essence, &lt;strong&gt;ngrok’s support strategy is to make it as easy as possible to contact me and then to optimize that conversion rate to zero.&lt;/strong&gt; This is how you make a better product, this is how you &lt;em&gt;really&lt;/em&gt; listen to your customers.&lt;/p&gt;

&lt;p&gt;It works. I was sufficiently frustrated receiving these emails every week and typing the same explanation over and over again, that I decided to implement a fix.&lt;/p&gt;

&lt;h2 id=&quot;pursuing-a-technical-solution&quot;&gt;Pursuing a technical solution&lt;/h2&gt;

&lt;p&gt;The easy way to solve this problem is to create an FAQ or Knowledge Base item (or maybe in a larger organization to hire extra support staff). This is suboptimal. Users may not know to look in the FAQ. They may not know that it exists at all, or if it does what keywords they should search for. They may not even speak English! Even supposing neither of those apply and the user figures out what they were doing wrong, they will &lt;em&gt;still&lt;/em&gt; experience the initial frustration of ngrok inexplicably failing. Don’t forget that this is part of ngrok’s first impression. A user just downloaded my application and is trying to use it for the first time. Failing in any way will decrease the perceived value of the product and make them that much less likely to pay for it and recommend it to their network.&lt;/p&gt;

&lt;p&gt;With this in mind, I opted to pursue a technical fix. I initially didn’t know if this would work, but here’s the idea I experimented with:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If the parent process is named “explorer.exe” then display a message instructing users to open cmd.exe instead of running it from the command line, then sleep for a few seconds so they can read it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So at that point, I went into the Go standard library, and found &lt;code&gt;os.Getppid()&lt;/code&gt;. It returns the parent process’ id which I could check against the id of explorer.exe. I tried it out and . . . nothing; it didn’t work. I discovered that, strangely &lt;code&gt;os.Getppid()&lt;/code&gt; was always returning -1 until I read the source code of &lt;code&gt;syscall_windows.go&lt;/code&gt; to find:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// TODO(brainman): fix all needed for os&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Getppid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ppid&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&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;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Oops! So I read some of the Win32 API documentation, and a bunch more examples from the standard library about how to call those functions from Go. Finally I had a working implementation of os.Getppid(), and when I wired it all into the stdlib, the whole thing worked!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/instructions.gif&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Figuring that others could benefit from the work I’d done, I did two things. First, I submitted &lt;a href=&quot;https://codereview.appspot.com/102320044&quot;&gt;a changeset to the Go standard library to implement Getppid() for Windows&lt;/a&gt;. This was merged into Go 1.4. I then created a simple Go package which used os.Getppid() (and vendored the implementation for pre-1.4 versions) plus the other necessary Windows APIs to check if the parent process was explorer.exe. You can find it on github at &lt;a href=&quot;https://github.com/inconshreveable/mousetrap&quot;&gt;inconshreveable/mousetrap&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I released ngrok 1.7 three months ago with mousetrap integrated, and I’ve only had a single support email about this issue since then from someone who didn’t understand how to open cmd.exe and use a command prompt. Victory!&lt;/p&gt;

&lt;h2 id=&quot;sweat-the-small-stuff&quot;&gt;Sweat the small stuff&lt;/h2&gt;

&lt;p&gt;In the process of writing up this blog post, I wondered if I could do even better. Is there a way I could eliminate that last support email? After searching around for a bit, I stumbled on cmd.exe’s &lt;code&gt;/K&lt;/code&gt; option which instructs cmd.exe to run the program and &lt;em&gt;remain open after it terminates&lt;/em&gt;. The soon-to-released ngrok 2.0 contains the final iteration of this feature. When you double click it from explorer, it will relaunch itself using &lt;code&gt;cmd /K&lt;/code&gt; so that it executes as if you had opened the prompt yourself and run the command. Windows even launches the prompt in the directory of the double-clicked executable so there aren’t even problems with executable location. From there, a user can be instructed to type a proper incantation to invoke ngrok with the correct arguments. I’ve also wired it up to print out some additional help text that explains that you shouldn’t try double-clicking it from explorer again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/newprompt.gif&quot; /&gt;&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;ngrok lives in a crowded marketplace. There are any number of similar programs and services. In the end, what makes ngrok the best in class tool is the attention to detail. It is the uncompromising focus on user experience. Sweat the small stuff, it matters more than you think.&lt;/p&gt;
</description>
				<pubDate>Tue, 09 Sep 2014 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/09-09-2014/sweat-the-small-stuff/</link>
				<guid isPermaLink="true">http://inconshreveable.com/09-09-2014/sweat-the-small-stuff/</guid>
			</item>
		
			<item>
				<title>Principles of designing Go APIs with channels</title>
				<description>&lt;p&gt;&lt;em&gt;Channels are concurrent-safe queues that are used to safely pass messages between Go’s lightweight processes (goroutines). Together, these primitives are some of the most popularly touted features of the Go programming language. The message-passing style they encourage permits the programmer to safely coordinate multiple concurrent tasks with easy-to-reason-about semantics and control flow that often trumps the use of callbacks or shared memory.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Despite their power, channels are rare to find in public APIs. I combed through the Go standard library for examples. As of Go 1.3, there are more than 6,000 public APIs across 145 packages. Among those thousands, &lt;strong&gt;there are only 5 unique uses of channels&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There is little guidance on the tradeoffs and decisions to make when using channels in a public API. By “public API” I mean “any programmatic interface whose implementer and user are two different humans”. This article will go in depth to provide a set of principles and rationale on how to use channels appropriately in public APIs. Some cases that break the rules are discussed at the end.&lt;/p&gt;

&lt;h2 id=&quot;principle-1&quot;&gt;Principle #1&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;An API should declare the directionality of its channels.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;/h3&gt;
&lt;p&gt;#### time.After&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Time&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;signalnotify&quot;&gt;signal.Notify&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Although not commonly used, Go allows you to specify the directionality of a channel. The language spec says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The optional &amp;lt;- operator specifies the channel direction, send or receive. If no direction is given, the channel is bidirectional.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The important part is that a directional operator in your API signature will be &lt;em&gt;enforced by the compiler&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// won&amp;#39;t compile (send to receive-only type &amp;lt;-chan Time)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In addition to the safety granted by compiler enforcement, these directional operators help consumers of your API understand the direction of data flow just by looking at the type signature.&lt;/p&gt;

&lt;h2 id=&quot;principle-2&quot;&gt;Principle #2&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;An API that sends an &lt;strong&gt;unbounded&lt;/strong&gt; stream of values into a channel &lt;strong&gt;must&lt;/strong&gt; document how it behaves for slow consumers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;examples-1&quot;&gt;Examples&lt;/h3&gt;
&lt;p&gt;#### time.NewTicker&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// NewTicker returns a new Ticker containing a channel that will send the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// time with a period specified by the duration argument.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// It adjusts the intervals or drops ticks to make up for slow receivers.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NewTicker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Duration&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;nx&quot;&gt;Ticker&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;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;signalnotify-1&quot;&gt;signal.Notify&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Notify causes package signal to relay incoming signals to c.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Package signal will not block sending to c&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&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;/figure&gt;

&lt;h4 id=&quot;sshconnopenchannel&quot;&gt;ssh.Conn.OpenChannel&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// OpenChannel tries to open an channel. &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// On success it returns the SSH Channel and a Go channel for&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// incoming, out-of-band requests. The Go channel must be serviced, or&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// the connection will hang.&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;OpenChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Whenever an API sends an unbounded stream of values into a channel, the implementation will be faced with the decision about what to do if sending a value into the channel would block. This can occur either because the channel is full, or because it is unbuffered and no goroutine is ready to receive a new value. Choosing the appropriate behavior depends on the API, but an implementation must make a decision. For example, the ssh package chooses to block, and documents that if you don’t receive values that your connection will hang. &lt;code&gt;signal.Notify&lt;/code&gt; and &lt;code&gt;time.Tick&lt;/code&gt; choose not to block and instead drop values silently.&lt;/p&gt;

&lt;p&gt;Unfortunately, the language does not provide a way to specify the intended behavior as part of a type or function signature. As a designer of an API, you &lt;em&gt;must&lt;/em&gt; specify the behavior in your documentation, otherwise it is undefined. Since we are more often consumers of APIs than designers of them, it can be helpful to remember the converse rule, which is a warning:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can &lt;em&gt;never&lt;/em&gt; determine the behavior of an API that sends an unbounded stream of values over a channel to slow consumers without reading the documentation or the implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;principle-3&quot;&gt;Principle #3&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;An API that sends a &lt;strong&gt;bounded&lt;/strong&gt; set of values into a channel it &lt;em&gt;accepted as an argument&lt;/em&gt; &lt;strong&gt;must&lt;/strong&gt; document how it behaves for slow consumers.
### BAD Example
#### rpc.Client.Go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&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;o&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;Go&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serviceMethod&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; 
                         &lt;span class=&quot;nx&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; 
                         &lt;span class=&quot;nx&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Call&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;nx&quot;&gt;Call&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is similar to the second principle, except it’s for APIs sending a &lt;em&gt;bounded&lt;/em&gt; series of values. Unfortunately, there’s not a good example of this in the standard library. The only API in the standard lib which does this is &lt;code&gt;rpc.Client.Go&lt;/code&gt; and it &lt;em&gt;violates&lt;/em&gt; this principle. The documentation says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Go invokes the function asynchronously. It returns the Call structure representing the invocation. The done channel will signal when the call is complete by returning the same Call object. If done is nil, Go will allocate a new channel. If non-nil, done must be buffered or Go will deliberately crash.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Go&lt;/code&gt; sends a bounded number of values (just 1, when the remote call completes). But notice that because the channel is passed into the function, that it still suffers from the slow consumer problem. Even though you must pass a buffered channel to this API, sending on that channel could still block if the channel is full. &lt;em&gt;The documentation does not define the behavior under this circumstance&lt;/em&gt;. Time to read the source code:&lt;/p&gt;

&lt;h4 id=&quot;srcpkgnetrpcclientgo&quot;&gt;src/pkg/net/rpc/client.go&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;mi&quot;&gt;171&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;172&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;173&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Done&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;174&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;// ok&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;175&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;176&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;// We don&amp;#39;t want to block here.  It is the caller&amp;#39;s responsibility to make&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;177&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;// sure the channel has enough buffer space. See comment in Go().&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;178&lt;/span&gt;          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;debugLog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;179&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;nx&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;rpc: discarding Call reply due to insufficient Done chan capacity&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;181&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;182&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Uh oh! If the done channel isn’t appropriately buffered, your RPC replies may just disappear into the ether!&lt;/p&gt;

&lt;h2 id=&quot;principle-4&quot;&gt;Principle #4&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;An API that sends an unbounded stream of values into a channel should accept the channel as an argument instead of returning a new channel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;examples-2&quot;&gt;Examples&lt;/h3&gt;
&lt;p&gt;#### signal.Notify&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;sshnewclient&quot;&gt;ssh.NewClient&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NewClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;chans&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NewChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reqs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Request&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;nx&quot;&gt;Client&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When I first saw the API for &lt;code&gt;signal.Notify&lt;/code&gt;, I was confused. “Why does it take a channel as an input instead of returning a channel for my use?” I wondered. “Using the API requires the caller to allocate a channel, shouldn’t the API just do that for you, like this?”&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The documentation helps us understand why this is not a good choice:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Package signal will not block sending to c: the caller must ensure that c has sufficient buffer space to keep up with the expected signal rate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;signal.Notify&lt;/code&gt; takes the channel as an argument because it gives the caller control over the amount of buffer space. This allows the caller to choose how many signals it can safely miss while responding to a previous one by trading away memory to buffer those signals.&lt;/p&gt;

&lt;p&gt;This control of buffer size also matters for high-throughput systems. Imagine this interface to a high-throughput publish/subscribe system:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msgs&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The more messages pushed through that channel, the greater the chance that the channel synchronization could become a performance bottleneck. Because the API allows the caller to create the channel, it delegates the decision about buffering, and thus performance tuning to the caller. This is a more flexible design.&lt;/p&gt;

&lt;p&gt;If it’s just about controlling the size of the buffer, one could argue that an API like the following would suffice:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bufSize&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Signal&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A channel argument is still preferrable to this design because it allows the caller to wait for multiple types of signals dynamically with a single channel. This provides more flexibility to your callers both for the structure of the program and performance characteristics. As a thought experiment, let’s work with our &lt;code&gt;Subscribe&lt;/code&gt; API to build code for the requirements, “subscribe to the ‘newcustomer’ channel, and for each message, subscribe to the topic for that customer.” If the API allows us to pass the receiving channel as an argument, we might write:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;nx&quot;&gt;msgs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;newcustomer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Topic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;newcustomer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;handleCustomerMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;m&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;/figure&gt;

&lt;p&gt;But if the channel is returned, the caller is forced into a design with a separate goroutine for every subscription. This can cost additional memory and synchronization time in whatever piece is responsible for the demultiplexing:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;newcustomer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;subCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;subCustomer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;handleCustomerMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&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;/figure&gt;

&lt;h2 id=&quot;principle-5&quot;&gt;Principle #5&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;An API which sends a bounded number of values may do so safely by returning an appropriately buffered channel.
### Examples:
#### http.CloseNotifier&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloseNotifier&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// CloseNotify returns a channel that receives a single value&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// when the client connection has gone away.&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;CloseNotify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;timeafter&quot;&gt;time.After&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Time&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When an API sends a bounded number of values into a channel, it can return a buffered channel that has enough room for all the values it will send. The directionality indicator on the returned channel guarantees that the caller cannot break this contract. Channels returned by &lt;code&gt;CloseNotify&lt;/code&gt; and &lt;code&gt;After&lt;/code&gt; take advantage of this.&lt;/p&gt;

&lt;p&gt;On the other hand, be aware that these calls could be more flexible by allowing the caller to pass in a channel to receive values, but then they would be forced to cope with cases where the channel is full (Principle #3). For example, this an alternative, more flexible CloseNotifier:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloseNotifier&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// CloseNotify sends a single value with the ResponseWriter whose&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// underlying connection has gone away.&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;CloseNotify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ResponseWriter&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;/figure&gt;

&lt;p&gt;But the cost of the extra flexibility doesn’t seem worth paying since it is unlikely that a single caller would ever want to wait on multiple close notifications. After all, close notifications only make sense within the context of a single connection, and connections are typically largely independent.&lt;/p&gt;

&lt;h2 id=&quot;p-p-p-principle-breakers&quot;&gt;P-P-P-Principle Breakers&lt;/h2&gt;

&lt;p&gt;Some of the APIs we’ve examined break some of the principles. They warrant a closer look.&lt;/p&gt;

&lt;h2 id=&quot;breaking-principle-1&quot;&gt;Breaking Principle #1&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;An API should declare the directionality of its channels.
### Example
#### rpc.Client.Go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There’s no directionality indicator on the done channel you pass in:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&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;o&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;Go&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serviceMethod&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; 
                         &lt;span class=&quot;nx&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; 
                         &lt;span class=&quot;nx&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Call&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;nx&quot;&gt;Call&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Without diving in too deeply, this happens because the done channel is returned to you as part of the Call struct.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Call&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;Done&lt;/span&gt;          &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Call&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Strobes when call is complete.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This flexibility is required so that a done channel can be allocated for you if you pass &lt;code&gt;nil&lt;/code&gt;.
Fixing this would require removing Done from the Call struct and creating two functions:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&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;Go&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                    &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;)&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;nx&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&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;GoEx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
                      &lt;span class=&quot;nx&quot;&gt;reply&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
                      &lt;span class=&quot;nx&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Call&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;nx&quot;&gt;Call&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;breaking-principle-4&quot;&gt;Breaking Principle #4&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;An API that sends an unbounded stream of values into a channel should accept the channel as an argument instead of returning a new channel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;examples-3&quot;&gt;Examples&lt;/h3&gt;
&lt;p&gt;#### go.crypto/ssh&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NewClientConn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ClientConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NewChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;timetick&quot;&gt;time.Tick&lt;/h4&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Time&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The go.crypto/ssh package returns channels of unbounded streams nearly everywhere. &lt;code&gt;ssh.NewClientConn&lt;/code&gt; is just one of those APIs. A better API that gives the callers more control and flexibility would instead be:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NewClientConn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;nx&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ClientConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;nx&quot;&gt;channels&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NewChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;nx&quot;&gt;reqs&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;chan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Request&lt;/span&gt;
                   &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code&gt;time.Tick&lt;/code&gt; violates this principle as well, but it’s easy to forgive. It’s rare that you’ll ever be creating that many tickers, and you typically want to handle them independently anyways. Buffering doesn’t make much sense in this case either.&lt;/p&gt;

&lt;h2 id=&quot;more-and-revisions&quot;&gt;More and revisions&lt;/h2&gt;
&lt;p&gt;This material was eventually turned into a talk with some updates and changes, those start about half-way through.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=hFqXgmor74k&quot;&gt;Principles of designing Go APIs with channels talk from GopherCon India 2015&lt;/a&gt;&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/hFqXgmor74k&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;1&quot;&gt; &lt;/iframe&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Thanks to Kyle Conroy, Jeff Lindsay, shazow, and Blake Mizerany for feedback on drafts.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
</description>
				<pubDate>Tue, 08 Jul 2014 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/07-08-2014/principles-of-designing-go-apis-with-channels/</link>
				<guid isPermaLink="true">http://inconshreveable.com/07-08-2014/principles-of-designing-go-apis-with-channels/</guid>
			</item>
		
			<item>
				<title>Automate away your problems: combatting illegal abuses of ngrok</title>
				<description>&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok&lt;/a&gt; is a tunneling, reverse proxy that establishes secure tunnels from a public endpoint to a locally running network service while capturing all traffic for inspection and replay. It is an &lt;a href=&quot;https://github.com/inconshreveable/ngrok&quot;&gt;open-source project on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;At its core, ngrok is an &lt;strong&gt;open reverse proxy&lt;/strong&gt;. That shudder you heard was the sound of millions of network administrators crying out in horror. &lt;a href=&quot;https://en.wikipedia.org/wiki/Open_proxy&quot;&gt;Open proxies&lt;/a&gt;, sometimes called &lt;a href=&quot;https://en.wikipedia.org/wiki/Open_mail_relay&quot;&gt;open relays&lt;/a&gt; or &lt;a href=&quot;http://openresolverproject.org/&quot;&gt;open resolvers&lt;/a&gt; are excellent resources for spammers, cybercrime, DDOS attacks and more. “Open” in these cases means that the service is available without any authentication or payment.&lt;/p&gt;

&lt;p&gt;Open reverse proxies like ngrok are traffic sinks, and thus not prone to those types of abuses. They won’t relay any requests out to the public internet. Instead, ngrok.com suffers from the opposite problem that most hosting providers and Platforms-as-a-Service like Heroku, Azure and App Engine deal with: people like to host illegal content on us.&lt;/p&gt;

&lt;p&gt;Unlike most platform providers, ngrok doesn’t even require you to create an account before you can begin hosting content over a tunnel. I don’t require accounts because of my fanatical devotion to first-time user experience. Minimizing the time to value of the service is very important to me. Unfortunately, this lower barrier to entry does put me at a disadvantage when it comes to dealing with illegal content.&lt;/p&gt;

&lt;p&gt;Recently, some miscreants have taken to using ngrok for hosting phishing websites which attempt to collect login credentials for popular, high-value websites like Google Accounts. They’ve stuck to hosting these on the only types of tunnels you can create without signing up on ngrok.com which are randomly-chosen hexadecimal subdomains that look like &lt;strong&gt;39ac91f.ngrok.com&lt;/strong&gt;. And while most of us would immediately recognize a bank login page hosted on that domain as suspicious, there are plenty of humans without the internet-savvy to recognize the illegitimacy of such a site.&lt;/p&gt;

&lt;p&gt;It’s hard to blame them, honestly. URLs are often opaque, magic looking things with weird base64 encoded state passed around by the ColdFusion framework or ASP.NET web forms or something. Some mobile browsers have also taken to hiding them under certain circumstances. On top of all that, ngrok.com provides free TLS-secured tunnels, which means that an ngrok-hosted phishing site renders with that “secure lock” symbol we trained an entire cohort of internet users to believe meant a website was trustworthy.&lt;/p&gt;

&lt;p&gt;Inevitably, when illegal content gets hosted on ngrok.com, it is brought to the attention of my hosting provider and, in turn, to me. Hosting a phishing site (or any illegal content) is against the terms of service of pretty much every hosting provider out there and mine takes it pretty seriously.&lt;/p&gt;

&lt;h2 id=&quot;a-motivating-outage&quot;&gt;A motivating outage&lt;/h2&gt;
&lt;p&gt;During the first couple weeks encountering this phenomenon, I banned the sites manually as I was notified of them. But on Sunday, May 18th, ngrok suffered its first major outage (approximately five hours) in nearly nine months because I wasn’t fast enough. My hosting provider notified me of a phishing site and then subsequently powered down the ngrok servers because I failed to deal with it before the given deadline. The cause was as pedestrian and dull as they come: it was a Sunday morning and I was still asleep. After I got ngrok back online, I considered my next steps. What changes could I make to combat illegal content more effectively or reduce the incidence rate? I came up with a number of possible solutions.&lt;/p&gt;

&lt;h2 id=&quot;possible-solutions&quot;&gt;Possible Solutions&lt;/h2&gt;

&lt;h3 id=&quot;only-provide-tls-tunnels-to-paid-accounts&quot;&gt;Only provide TLS tunnels to paid accounts&lt;/h3&gt;
&lt;p&gt;Phishing sites are less attractive without TLS/HTTPS because our CA system has unfortunately conflated the fundamentally separate concepts of encryption and trust. In the end, other illegal content would be just as useful without https, and I’m a big proponent of encryption everywhere, so I decided against this.&lt;/p&gt;

&lt;h3 id=&quot;require-signup-before-you-can-create-any-tunnels&quot;&gt;Require signup before you can create any tunnels&lt;/h3&gt;
&lt;p&gt;This would severely compromise ngrok’s first-time user experience and ease of use, both of which are hugely important in ngrok’s popularity. Moreover, creating an ngrok account does not require anything besides an email address, which it doesn’t bother to verify since obtaining a valid one is so easy anyways. This wouldn’t help either really.&lt;/p&gt;

&lt;h3 id=&quot;inspect-tunnel-traffic-on-the-wire-for-illegal-content-patterns&quot;&gt;Inspect tunnel traffic on the wire for illegal content patterns&lt;/h3&gt;
&lt;p&gt;One of the core tenets of the ngrok.com service is that it does not inspect your traffic at all beyond reading the header field necessary to perform the multiplexing. All traffic inspection and replay is done client side. While I’m sure that this would likely be effective, it’s hard to get right and it’s not a route I want to go down unless I have no other options. This is a last resort.&lt;/p&gt;

&lt;h3 id=&quot;make-ngrok-a-completely-paid-service&quot;&gt;Make ngrok a completely paid service&lt;/h3&gt;
&lt;p&gt;Even if I did this and there was no free tier (which would fundamentally change ngrok’s audience and global appeal), it’s likely that I would still need some sort of free trial. I could keep upping the barrier to entry, (like requiring a credit card number) but it only hurts ease of use.&lt;/p&gt;

&lt;h2 id=&quot;automating-the-banhammer&quot;&gt;Automating the banhammer&lt;/h2&gt;
&lt;p&gt;In the end, I decided that what I really want to start with is just a more efficient way to respond to these illegal sites. Ideally, I’d like to give my hosting provider an administrative tool that they could use to shut down illegal tunnels without putting me in the critical path. Of course, it’s a little bit scary to hand a giant banhammer for your entire service over to someone else, but it’s a reasonable compromise so long as I get notifications and can asynchronously verify that it’s not being abused.&lt;/p&gt;

&lt;p&gt;These types of administrative interfaces aren’t all that uncommon, and I tried suggesting one to my provider, but they weren’t responsive to any deviation from their procedure of opening tickets against me. But I really wanted a completely automated workflow, so in the end, I got out my proverbial programmatic roll of duct-tape and automated around them. Let’s talk about how it works.&lt;/p&gt;

&lt;h2 id=&quot;the-anatomy-of-a-hack&quot;&gt;The anatomy of a hack&lt;/h2&gt;
&lt;p&gt;The goal was to automate the entire process so that as soon as I get a notification that an illegal site is hosted on an ngrok.com subdomain, the following will all happen, without a human involved:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Forward the email notification through to my personal email address&lt;/li&gt;
  &lt;li&gt;Hellban the offending subdomain and the IP address of the tunneling client. If someone was silly enough to do this with an account, ban their user id as well&lt;/li&gt;
  &lt;li&gt;Notify my hosting provider that the site was blocked through their ticketing system&lt;/li&gt;
  &lt;li&gt;Notify my phone so that I can verify that the site was indeed illegal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All total, I ended up writing a small custom extension to the ngrok server and about a hundred or so lines of Python to handle all of the blacklisting.&lt;/p&gt;

&lt;h3 id=&quot;automated-handling-of-email&quot;&gt;Automated handling of email&lt;/h3&gt;
&lt;p&gt;I get notifications of new tickets against me in email. So far as I can tell, this is the only mechanism by which I can automate the handling of them. I run my email through postfix, and it turns out that automatically handling email is super easy with postfix (sidenote: not exactly, it took me a while to figure out that you can only do this with aliases, not with virtual aliases). Here’s how you set up your /etc/aliases file to invoke a script for every email to an address:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; banhammer:        &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;/var/services/ngrok.blacklist/venv/bin/ngrok.ban&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now emails to banhammer on the local machine will invoke the given script. If you’re using virtual aliases you’ll need to properly point an entry in your virtual aliases file as well:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; banhammer@ngrok.com      banhammer@localhost&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;the-script&quot;&gt;The script&lt;/h3&gt;
&lt;p&gt;This is a slightly pared-down version of the script that I wrote up to automate the whole process. It walks through each of the important steps in the process: reading out the email, parsing it, forwarding it to my personal address, communicating with the blacklist service, and then posting an update back to my provider. It is a testament to the power of the Python language, standard library and its ecosystem of third-party modules that allow me to accomplish so much in ~60 lines of code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bs4&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# read in the message from stdin and parse it&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawmsg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message_from_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawmsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# send the mail through to my personal address&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smtp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smtplib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SMTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smtp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sendmail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;From&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;as_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smtp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# only process messages from the provider&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify_authenticity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticket_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;domains&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# look in each part of the email&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;walk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21&lt;/span&gt;         &lt;span class=&quot;c&quot;&gt;# find all the ngrok.com subdomains to ban&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;r&amp;quot;[\w]+\.ngrok\.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;24&lt;/span&gt;         &lt;span class=&quot;c&quot;&gt;# look for the ticket id in the email&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TICKET_ID_REGEX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27&lt;/span&gt;             &lt;span class=&quot;n&quot;&gt;ticket_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&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;lineno&quot;&gt;28&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29&lt;/span&gt;         &lt;span class=&quot;c&quot;&gt;# skip message parts without a payload&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;30&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# blacklist each domain requested&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blacklist_service_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&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;s&quot;&gt;&amp;quot;hostname&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;37&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ticket_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;38&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;https://provider.com/login&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&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;lineno&quot;&gt;40&lt;/span&gt;         &lt;span class=&quot;s&quot;&gt;&amp;quot;username&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider_username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41&lt;/span&gt;         &lt;span class=&quot;s&quot;&gt;&amp;quot;password&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;provider_password&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;42&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;43&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;44&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;45&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;# load the ticket page to pull out the CSRF token&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;46&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;https://provider.com/ticket/{}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ticket_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;47&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;48&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;49&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;# find the csrf token&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;50&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;soup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bs4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BeautifulSoup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;51&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;csrf_token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;soup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;#ticket input[name=csrf_token]&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;52&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;53&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;# construct and post the response&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;54&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;The site {} has been blocked&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;55&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;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;lineno&quot;&gt;56&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;The sites {} have been blocked&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;57&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;https://provider.com/ticket/{}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ticket_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&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;lineno&quot;&gt;58&lt;/span&gt;         &lt;span class=&quot;s&quot;&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;59&lt;/span&gt;         &lt;span class=&quot;s&quot;&gt;&amp;quot;csrf_token&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csrf_token&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;60&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;61&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raise_for_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;the-blacklist-service&quot;&gt;The blacklist service&lt;/h3&gt;
&lt;p&gt;The blacklist service is fairly simple. It just manages a database table of blacklist entries and pushes updates over HTTP into the ngrokd server to immediately update its blacklist maps. It’s also responsible for notifying my phone and doing some metrics collection. I’m not going to walk over this part of the code since it’s basically just a little CRUD service that talks to some database tables and external HTTP services.&lt;/p&gt;

&lt;h2 id=&quot;its-just-the-beginning&quot;&gt;It’s just the beginning&lt;/h2&gt;
&lt;p&gt;It’s my expectation that this problem will never go away so long as ngrok is a successful service and that I’m in for a long arms race and game of whack-a-mole. Automation will help, but to what extent it acts as a deterrent for those using ngrok to accomplish dishonorable ends remains to be seen. At the very least, I experience a brief moment of magical bliss whenever my phone lights up to notify me that my automation has done in a few seconds something which used to be a manual process that could have hours of latency before I could respond.&lt;/p&gt;

&lt;p&gt;For those of you who work at hosting providers and services platforms: how do you deal with this problem? What automation do you have in place?&lt;/p&gt;
</description>
				<pubDate>Thu, 29 May 2014 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/05-29-2014/automate-away-your-problems-combatting-illegal-abuses-of-ngrok/</link>
				<guid isPermaLink="true">http://inconshreveable.com/05-29-2014/automate-away-your-problems-combatting-illegal-abuses-of-ngrok/</guid>
			</item>
		
			<item>
				<title>gonative: Cross compiling Golang programs with native libraries</title>
				<description>&lt;p&gt;Go has execellent support for cross compilation. There are scores of tutorials
and helper tools on the internet with instructions on how to do it. This, in and
of itself, is rather astounding because of how simple it is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# set up cross compilation to windows_amd64&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# you only need to do this once&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GOROOT&lt;/span&gt;/src
&lt;span class=&quot;lineno&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;GOOS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;windows &lt;span class=&quot;nv&quot;&gt;GOARCH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;amd64 ./make.bash --no-clean
&lt;span class=&quot;lineno&quot;&gt;5&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# whenever you want to cross compile&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$YOUR_APP&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;GOOS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;windows &lt;span class=&quot;nv&quot;&gt;GOARCH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;amd64 go build&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There is a well-known limitation to this simplicty. If you want to use any C libraries,
you’ll need to compile and link them with Cgo and from there you’re back to the mess
that is cross-compiling C. Upon hearing this limitation you might say something
totally reasonable like:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;That’s not really a problem, there are tons of programs that don’t use Cgo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you’d be right, until I told you:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Some critical parts of the standard library use Cgo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What exactly are those parts exactly? It turns out that if you cross-compile Go
to Darwin or Linux your programs &lt;em&gt;won’t use the system DNS resolver&lt;/em&gt;. They also &lt;em&gt;can’t use the native host
certificate store&lt;/em&gt;. They also can’t look up the user’s home directory, either. For some applications,
that doesn’t matter so much. For &lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok&lt;/a&gt;, though, all three could
matter, and two have &lt;em&gt;already caused bugs&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;a-better-solution&quot;&gt;A better solution&lt;/h2&gt;
&lt;p&gt;When I spoke at GopherCon, I told everyone that if they want to distribute production applications
they should be compiling natively because of this limitation. I was wrong. It turns out that there’s
a clever workaround for this that has been mentioned occasionally on the mailing list
and which minux just reminded me of when he posted:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If the only cgo-dependent packages are in the standard library, then you do have workaround
without setting up a full cross compilation environment.&lt;/p&gt;

  &lt;p&gt;Download the binary distribution for the target platform, extract the pkg/$GOOS_$GOARCH directory
into your $GOROOT/pkg …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is quite clever and I want the simplicity of that build and deployment story.
But instead of doing this as a one off, I decided to automate the entire process since I want you
all to use it as well and because I’m going to need to do it again at every new release of Go.&lt;/p&gt;

&lt;h2 id=&quot;gonative&quot;&gt;gonative&lt;/h2&gt;

&lt;p&gt;gonative is a simple tool which creates a build of Go that can cross compile to all platforms 
while still using the Cgo-enabled versions of the stdlib packages. It’s a simple Go program of a few
hundred lines and is available on github! If you’re the author of a Go program doing multi-platform
distribution, please try it out and contribute:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/inconshreveable/gonative&quot;&gt;github.com/inconshreveable/gonative&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s really easy to install and run:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt;     go get github.com/inconshreveable/gonative
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt;     gonative&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I’ve tested targeting Windows/Linux/Darwin and they all work correctly!&lt;/p&gt;

&lt;h2 id=&quot;about-that-portabililty-thing---&quot;&gt;About that portabililty thing . . .&lt;/h2&gt;
&lt;p&gt;One of the oft-touted advantages of Go programs is that they’re “dependency-free”. They don’t
require a runtime or link against dynamic libraries which your users might not have installed on
their systems.&lt;/p&gt;

&lt;p&gt;So when I compiled my native-stdlib linux_386 test code and tried to run it on an amd64 machine,
I was rather confused when I was confronted with this error message:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt;     alan@inconshreveable:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./test 
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt;     -bash: ./test: No such file or directory&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;minux’s help on the mailing list helped me understand what was going on. Let’s read the ELF header
of this binary.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt;     alan@inconshreveable:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;readelf -l &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;pre&gt;&lt;code&gt;Elf file type is EXEC (Executable file)
Entry point 0x8066a50
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x1000
  INTERP         0x000bed 0x08048bed 0x08048bed 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x68e70 0x68e70 R E 0x1000
  LOAD           0x069000 0x080b1000 0x080b1000 0xc9403 0xc9403 R   0x1000
  LOAD           0x133000 0x0817b000 0x0817b000 0x10580 0x23554 RW  0x1000
  DYNAMIC        0x133080 0x0817b080 0x0817b080 0x00098 0x00098 RW  0x4
  TLS            0x000000 0x00000000 0x00000000 0x00000 0x00008 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
  LOOS+5041580   0x000000 0x00000000 0x00000000 0x00000 0x00000     0x4
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;See that INTERP section? The one that says:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[Requesting program interpreter: /lib/ld-linux.so.2]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;/lib/ld-linux.so.2 is the &lt;em&gt;32-bit&lt;/em&gt; ELF interpreter that is required for programs that need to
dynamically link libraries. My linux box didn’t have it installed, so the program was failing.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;But wait, you said that Go programs doesn’t require a runtime or dynamically linked libraries!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that’s totally true when you cross compile without native libraries. Those executables don’t
call anything via C APIs. But when you link against native libraries, there are a few libraries
you need to load dynamically. Let’s take a look at them:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alan@inconshreveable:~$ ldd test
        not a dynamic executable
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What!? This hung me up for a while too, until I remembered seeing that DYNAMIC section
in the ELF headers. It turns out that if you don’t have the 32-bit libraries on your system,
ldd can’t analyze the binary properly. minux explains:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;actually if you take a look at ldd’s source code you will find that it’s a script and just set some magic environment variable and then execute the program, essentially it’s asking the elf interpreter to dump needed shared library.&lt;/p&gt;

  &lt;p&gt;so if you don’t have the elf interpreter, the program will fail to execute and ldd mistake that as the program not being dynamically linked.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we use readelf instead, we can see what’s going on:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alan@inconshreveable:~$ readelf -d test
Dynamic section at offset 0x133080 contains 19 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
more …
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So now we can finally see that our natively compiled program now depends on the 32-bit
elf interpreter so that it can dynamically load libpthread and libc.&lt;/p&gt;

&lt;h2 id=&quot;a-fun-detour&quot;&gt;A fun detour&lt;/h2&gt;

&lt;p&gt;Understanding executable formats and the guts of how Linux sets up your program
to support dynamic linking is not an area I need to wander into frequently, so it was
rather enjoyable to push my knowledge deeper into this area. For those of
you who don’t wade there often either, I hope this was useful in understanding more
about exactly how your programs are loaded and run and what you need to be aware of
when you compile with native vs. non-native Go libraries. And for those of you who
need to distribute Go programs that use native libs, try out gonative! I hope it
makes things a little less painful.&lt;/p&gt;

&lt;h5 id=&quot;relevant-mailing-list-thread&quot;&gt;Relevant mailing list thread&lt;/h5&gt;
&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://groups.google.com/forum/#!msg/golang-nuts/2XoGUvBalcw/ErSWiTlO17kJ&quot;&gt;https://groups.google.com/forum/#!msg/golang-nuts/2XoGUvBalcw/ErSWiTlO17kJ&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
</description>
				<pubDate>Wed, 30 Apr 2014 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/04-30-2014/cross-compiling-golang-programs-with-native-libraries/</link>
				<guid isPermaLink="true">http://inconshreveable.com/04-30-2014/cross-compiling-golang-programs-with-native-libraries/</guid>
			</item>
		
			<item>
				<title>Code at 30,000 feet</title>
				<description>&lt;p&gt;A week ago, the post “&lt;a href=&quot;http://blogs.msdn.com/b/oldnewthing/archive/2013/10/08/10454920.aspx&quot;&gt;I wrote FAT on an airplane, for heaven’s sake&lt;/a&gt;”, on Raymond Chen’s popular blog &lt;em&gt;The Old New Thing&lt;/em&gt; was making the rounds on the interwebs. In the dramatized story, a frustrated Bill Gates wants his engineers to stop wasting time on some trivial performance tuning, don’t ya know he wrote a file system on an airplane, for heaven’s sake.&lt;/p&gt;

&lt;p&gt;And while Gates’ claim is obviously hyperbolic, it’s certainly not the first time I’ve heard “on a plane” used to exemplify programming virtuosity. A former colleague liked to say of another, “He doesn’t code much anymore, but he wrote the whole counters system on the plane flight back from a pitch meeting.” The boastful or reverent nature with which we say these things betrays the interesting assumption that airplanes are not conducive to effective work.&lt;/p&gt;

&lt;p&gt;So that got me thinking about when I’ve worked on airplanes (or buses, most recently). And for me, I think the exact opposite is true. Planes (and trains and automobiles) are a perfect incubator for productivity because they compose four major factors of a productive environment which rarely align in an office setting or at home:&lt;/p&gt;

&lt;h2 id=&quot;no-interruptions&quot;&gt;No interruptions&lt;/h2&gt;
&lt;p&gt;On an airplane, your interruptions are limited. You won’t get any text messages or phone calls. No one will come over to your desk for a quick question. Most of your fellow travelers are uninterested in conversation. They want to sleep, read or watch their in-flight entertainment. If you’re lucky enough to be on a flight without wifi, you won’t even get those pesky emails or chat messages.&lt;/p&gt;

&lt;!--
&lt;br /&gt;
&lt;center&gt;
&lt;img src=&quot;/img/countingonyou.jpg&quot; /&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;Good luck. We&#39;re all counting on you.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;/center&gt;
--&gt;

&lt;h2 id=&quot;few-distractions&quot;&gt;Few distractions&lt;/h2&gt;
&lt;p&gt;There’s just not that much to do on an airplane. You can’t get another cup of coffee down the street or step away to bang out a round of Foosball. Until recently, the lack of pervasive wifi on flights made it even better by curbing that impulsive news-feed, Twitter update scrolling habit you can’t seem to shake. Unless you brought along a book or really enjoy watching last year’s romantic comedy, there are just very few ways you can get yourself off-task. For breaks, I just close my computer and my eyes for five to ten minutes. I find it lends itself well to sub-conscious problem solving.&lt;/p&gt;

&lt;!--
&lt;br /&gt;
&lt;center&gt;
&lt;img src=&quot;/img/nightmare.jpg&quot; /&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Gremlins are the worst kind of air-travel distraction. Snakes are a close second.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;/center&gt;
--&gt;

&lt;h2 id=&quot;guaranteed-time&quot;&gt;Guaranteed time&lt;/h2&gt;
&lt;p&gt;I might have put this first because it’s perhaps the most important. On the ground, even if you manage to isolate yourself from interruptions and distractions, how long will it last? I have a serious mental aversion to entering a highly focused state when I know I won’t be able to maintain it. “I have that meeting at 11:30, so I don’t want to get started on anything big” is a variation on a thought that is all too common for me. On a plane flight though, you’ve got a printed receipt for a very real guarantee of uninterrupted time.&lt;/p&gt;

&lt;h2 id=&quot;a-brief-period-for-inspiration&quot;&gt;A brief period for inspiration&lt;/h2&gt;
&lt;p&gt;Excepting those with aviophobia, that meditative, relaxed, sub-conscious problem-solving mental state that’s credited with making the shower a place of great inspiration reminds me a lot of takeoff. It’s a 10-15 minute lull where no electronic devices are permitted and your mind can drift around solutions to problems you’re intending on solving when you hit cruising altitude.&lt;/p&gt;

&lt;!--
&lt;br /&gt;
&lt;center&gt;
&lt;img src=&quot;/img/eureka.jpg&quot; /&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;Running through the aisles naked is optional.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;/center&gt;
--&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;Of course, there are undesirable side-effects. An isolating flight might prevent me from accessing useful documentation and Stack Overflow. I find I end up reading the source code for third-party libs more frequently up in the air. And sometimes I really do want to take a break outside and unwind in the sun. But on balance, my creativity and productivity spikes frequently during my reclusive retreats in the sky. And judging by the &lt;a href=&quot;http://blogs.msdn.com/b/oldnewthing/archive/2013/10/08/10454921.aspx&quot;&gt;follow up post and comments&lt;/a&gt; on &lt;em&gt;The Old New Thing&lt;/em&gt;, I’m not the only one.&lt;/p&gt;

&lt;p&gt;The next time you want to impress someone with your coding chops, tell them about that time your hacked up an OS scheduler while on a conference call with a tab open to Reddit.&lt;/p&gt;
</description>
				<pubDate>Thu, 17 Oct 2013 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/10-17-2013/code-at-30000-feet/</link>
				<guid isPermaLink="true">http://inconshreveable.com/10-17-2013/code-at-30000-feet/</guid>
			</item>
		
			<item>
				<title>ngrok tunnels: better, faster, stronger</title>
				<description>&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok&lt;/a&gt; is a tunneling, reverse proxy that establishes secure tunnels from a public endpoint to a locally running
network service while capturing all traffic for inspection and replay. It is an &lt;a href=&quot;https://github.com/inconshreveable/ngrok&quot;&gt;open-source project on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;This week, I released a new version of ngrok that includes a number of significant improvements. This effort involved a serious refactoring of the ngrok codebase and a redesign of the ngrok protocol. The result is a cleaner, more extensible design as well as an improved set of core functionality that includes the following features and improvements.&lt;/p&gt;

&lt;h2 id=&quot;tunnels-behind-port-restricted-networks-and-firewalls&quot;&gt;Tunnels behind port-restricted networks and firewalls&lt;/h2&gt;
&lt;p&gt;ngrok’s raison d’etre is to selectively expose services behind firewalls and NATs to the public internet while capturing the traffic for inspection and replay. While most users have no problems getting ngrok to work, there was a small segment of users who reported that ngrok failed to connect on some restricted networks. The root cause of this issue is the implementation of network policies which block traffic on all ports except those responsible for the most commonly used internet protocols (HTTP, DNS, SMTP, etc). In an effort to make ngrok work absolutely everywhere, ngrok.com now combats these draconian filters by running all of its traffic over port 443 by default. Since all ngrok connections are encrypted with TLS, ngrok traffic looks sufficiently similar to regular HTTPS traffic to defeat all port/traffic restricting networks that I’ve been able to test.&lt;/p&gt;

&lt;h2 id=&quot;tunnels-behind-http-proxies&quot;&gt;Tunnels behind HTTP proxies&lt;/h2&gt;
&lt;p&gt;Unfortunately, the change to port 443 alone doesn’t allow ngrok to run on all restricted networks. Many corporate networks reject all traffic to the public internet and provide HTTP proxy servers as the only public gateway. Supporting ngrok tunnels in this environment was another common support request. The latest version of ngrok now supports establishing its tunnels through an HTTP proxy by honoring the traditional *nix environment variable “http_proxy” like other tools. You may also specify a value explicitly by setting the &lt;small&gt;http_proxy&lt;/small&gt; parameter in the configuration file. ngrok uses the same CONNECT method used by browsers to run HTTPS traffic over an HTTP proxy.&lt;/p&gt;

&lt;h2 id=&quot;multiple-simultaneous-tunnels&quot;&gt;Multiple simultaneous tunnels&lt;/h2&gt;
&lt;p&gt;One of the most major changes in this version of ngrok was adding the support to run multiple ngrok tunnels simultaneously. That means it’s now possible to run a single ngrok client which creates multiple public endpoints that route to multiple local services. Here’s a new ngrok client running:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ngrok

Tunnel Status                 online
Version                       1.3/1.3
Forwarding                    http://blog.ngrok.com -&amp;gt; 127.0.0.1:4000
Forwarding                    https://blog.ngrok.com -&amp;gt; 127.0.0.1:4000
Forwarding                    https://api.ngrok.com -&amp;gt; 127.0.0.1:8080
Forwarding                    http://hacks.inconshreveable.com -&amp;gt; 127.0.0.1:9090
Forwarding                    tcp://ngrok.com:44764 -&amp;gt; 127.0.0.1:22
Web Interface                 127.0.0.1:4040
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To enable you to easily create multiple tunnels without complicating the command-line interface, ngrok now supports reading from a simple YAML configuration file for starting multiple tunnels. Here’s the configuration file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;authtoken: &quot;...&quot;
tunnels:
  blog:
    proto:
      http: 4000
      https: 4000

  api:
    auth: &quot;user:password&quot;
    proto:
      https: 8080

  personal:
    hostname: &quot;hacks.inconshreveable.com&quot;
    proto:
      http: 9090

  ssh:
    proto:
      tcp: 22
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then you can start any number of your pre-configured tunnels from the command line, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ngrok start blog api personal
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here’s another example to just start remote access to my machine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ngrok start ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I expect this capability to be especially valuable to contractors and consultants working on multiple projects simultaneously as well as for developers who are working on distributed systems that involve running multiple public components. I already use it myself to switch between sharing different projects without rewriting command-line switches all of the time.&lt;/p&gt;

&lt;h2 id=&quot;faster-tunneled-connections&quot;&gt;Faster tunneled connections&lt;/h2&gt;
&lt;p&gt;Some users of ngrok in countries outside of the United States reported that requests routed through ngrok were very slow to complete. To understand why this was the case, I will briefly explain how ngrok tunnels a new connection to a public ngrok endpoint through to your local service.&lt;/p&gt;

&lt;p&gt;The old procedure ngrok used for tunneling a new connection was as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;ngrokd server receives a new public connection to your-app.ngrok.com&lt;/li&gt;
  &lt;li&gt;ngrokd server sends a Request Proxy Connection Message to the ngrok client&lt;/li&gt;
  &lt;li&gt;ngrok client establishes new connection to the ngrokd server&lt;/li&gt;
  &lt;li&gt;ngrokd server joins the public connection and the proxy connection to service the request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that you understand the ngrok protocol for tunneling a connection, it’s easy to understand why it was so slow. The setup time for tunneling
a new connection involved a significant amount of network latency, including:
- Sending a message from the server to the client (.5 network round trips)
- A new TCP connection (1 network round trip)
- A TLS negotiation (2 network round trips)&lt;/p&gt;

&lt;p&gt;In cases where the ngrok client was across the world or on slow connections, 3.5 network round trips could easily add over a full second to connection setup times!&lt;/p&gt;

&lt;p&gt;The latest version of ngrok now pre-fetches connections so it can immediately service new requests. The ngrokd implementation now executes the following logic:
1. ngrokd server receives a new public connection to your-app.ngrok.com
1. ngrokd server retrieves a proxy connection from a pool and joins it with the public connection to service the request
1. ngrokd server sends a new Request Proxy Connection Message to the ngrok client to replace the connection removed from the pool&lt;/p&gt;

&lt;p&gt;Now, in the common case when a connection is available in the pool, zero network round trips are required before the connection begins to tunnel. Avoiding the time to establish these connections makes tunneling connections feel much, much faster.&lt;/p&gt;

&lt;h2 id=&quot;better-crash-behavior&quot;&gt;Better Crash Behavior&lt;/h2&gt;
&lt;p&gt;When older versions of ngrok crashed, they destroyed your terminal. In order to display the terminal interface,
ngrok uses &lt;a href=&quot;https://github.com/nsf/termbox-go&quot;&gt;termbox-go&lt;/a&gt; which changes the mode of your terminal and needs to be cleanly shut down to return your terminal to a proper state. ngrok now wraps the execution of most of its goroutines with a function that will execute a controlled shutdown of the program if the goroutine panics. In the unfortunate but inevitable case that ngrok crashes, a user will no longer be confronted with a wrecked terminal. Instead, they will be presented with a clean stack trace and instructions for reporting the bug on Github.&lt;/p&gt;

&lt;h2 id=&quot;simplify-and-improve-self-hosting&quot;&gt;Simplify and improve self-hosting&lt;/h2&gt;
&lt;p&gt;All of the code for ngrok and ngrokd (the client and server) has always been open-source, but running your own ngrokd server wasn’t entirely trivial. I’ve made a great effort to make running your own ngrokd server easier than ever. I’ve posted a guide on how to self-host your own service that’s only six steps.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/inconshreveable/ngrok/blob/master/docs/SELFHOSTING.md&quot;&gt;Self-hosting: How to run your own ngrokd server&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;ngrok-on-linuxarm&quot;&gt;ngrok on linux/arm&lt;/h2&gt;
&lt;p&gt;Another common inquiry I received was whether ngrok ran on linux/arm devices and if so where the download was available. ngrok certainly runs on Linux/ARM (in fact, I developed this new version entirely on a Chromebook), and as of today, ngrok.com now has download links available for the builds. Now you can try it out on your Chromebook or RaspberryPi without compiling!&lt;/p&gt;

&lt;h2 id=&quot;https-for-everyone-by-default&quot;&gt;HTTPS for everyone, by default&lt;/h2&gt;
&lt;p&gt;Finally, I’d like to mention one last change to ngrok that is unrelated to the new version. Previously, ngrok only allowed you to use HTTPS tunnels after you signed up on ngrok.com. In response to the news regarding the surveillance of internet traffic and possible weaknesses in internet encryption implementations, I’m making my own small effort to improve the state of internet security. I’ve made the following changes to the ngrok.com service:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ngrok.com now issues https tunnels without any need to signup for an account on all versions of ngrok&lt;/li&gt;
  &lt;li&gt;ngrok.com now supports TLS1.2 for all capable clients&lt;/li&gt;
  &lt;li&gt;ngrok.com now supports TLS forward secrecy for all capable clients&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;get-it&quot;&gt;Get it&lt;/h2&gt;
&lt;p&gt;The latest version of ngrok is available for download on ngrok.com.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ngrok.com/download&quot; class=&quot;btn btn-primary&quot;&gt;Download ngrok&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;support-ngrok&quot;&gt;Support ngrok&lt;/h2&gt;
&lt;p&gt;If you love ngrok and would like to see more improvements, please show your support in any of the following ways:&lt;/p&gt;

&lt;h5 id=&quot;tweet-about-ngrok&quot;&gt;Tweet about ngrok&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/share&quot; class=&quot;twitter-share-button&quot; data-text=&quot;ngrok tunnels - better, faster, stronger&quot; data-via=&quot;inconshreveable&quot; data-size=&quot;large&quot; data-dnt=&quot;true&quot;&gt;Tweet&lt;/a&gt;
&lt;script&gt;!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?&#39;http&#39;:&#39;https&#39;;if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+&#39;://platform.twitter.com/widgets.js&#39;;fjs.parentNode.insertBefore(js,fjs);}}(document, &#39;script&#39;, &#39;twitter-wjs&#39;);&lt;/script&gt;&lt;/p&gt;

&lt;h5 id=&quot;file-a-bug-or-feature-request&quot;&gt;File a bug or feature request&lt;/h5&gt;
&lt;p&gt;You can file bugs or feature requests on the &lt;a href=&quot;https://github.com/inconshreveable/ngrok&quot;&gt;ngrok project on github&lt;/a&gt;.&lt;/p&gt;

&lt;h5 id=&quot;contribute-code&quot;&gt;Contribute code&lt;/h5&gt;
&lt;p&gt;ngrok is an open source project! Submit a pull request to fix a bug or add a new feature on the &lt;a href=&quot;https://github.com/inconshreveable/ngrok&quot;&gt;ngrok project on github&lt;/a&gt;.&lt;/p&gt;

&lt;h5 id=&quot;pay-for-ngrok&quot;&gt;Pay for ngrok&lt;/h5&gt;
&lt;p&gt;ngrok is a &lt;strong&gt;pay-what-you-want service&lt;/strong&gt;. You can pay for ngrok after you &lt;a href=&quot;https://ngrok.com/signup&quot;&gt;sign-up for an account on ngrok.com&lt;/a&gt;&lt;/p&gt;
</description>
				<pubDate>Wed, 25 Sep 2013 00:00:00 -0700</pubDate>
				<link>http://inconshreveable.com/09-25-2013/ngrok-tunnels-better-faster-stronger/</link>
				<guid isPermaLink="true">http://inconshreveable.com/09-25-2013/ngrok-tunnels-better-faster-stronger/</guid>
			</item>
		
	</channel>
</rss>
