<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jeff Lindsay on Medium]]></title>
        <description><![CDATA[Stories by Jeff Lindsay on Medium]]></description>
        <link>https://medium.com/@progrium?source=rss-55d1132d11df------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*cdxLT-9OBnDKPq1rY3Fu_w.png</url>
            <title>Stories by Jeff Lindsay on Medium</title>
            <link>https://medium.com/@progrium?source=rss-55d1132d11df------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 15 Jun 2026 05:43:07 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@progrium/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[The Spirit of Plan 9 on the Web]]></title>
            <link>https://progrium.medium.com/the-spirit-of-plan-9-on-the-web-de90309fc44d?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/de90309fc44d</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[plan-9]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[unix]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Tue, 06 May 2025 18:11:55 GMT</pubDate>
            <atom:updated>2025-05-06T18:38:44.118Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*C8SZHylnB1WrRvzX.jpg" /></figure><p>If you go back to the <a href="https://www.slideshare.net/slideshow/web-hooks/263894">first talk ever given on webhooks</a>, it opens on the command-line. Specifically the Unix shell, focusing on one of its defining features: pipes. The idea was that pipes brought a new level of compositionality to programs, and webhooks could bring a new level of compositionality to web apps. Perhaps you could say I was trying to bring the spirit of Unix to the web.</p><p>With this <a href="https://github.com/tractordev/wanix/releases/tag/v0.3-preview">last release of Wanix</a>, I’m at it again. This time with the successor to Unix, a little known operating system called <a href="https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs">Plan 9 from Bell Labs</a>.</p><p>Plan 9 has been on my mind for quite a while. In fact, around the time of that first talk on webhooks, the team behind Unix and Plan 9 was being re-assembled to create the Go programming language. I pretty instantly fell in love with the Go worldview, which turns out to be an outgrowth of the Unix and Plan 9 values of simplicity, pragmatism, economy, and ultimately compositionality.</p><p>Like Unix, the Plan 9 environment is really made for programmers and system operators. I’ll leave a deeper explanation of what makes Plan 9 great for another post, but I do get into it a bit in the demo video for Wanix:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FkGBeT8lwbo0%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DkGBeT8lwbo0&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FkGBeT8lwbo0%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/bb0da8d4239df0a928b21d2a89ef12ee/href">https://medium.com/media/bb0da8d4239df0a928b21d2a89ef12ee/href</a></iframe><p>While I wanted to incorporate Plan 9 ideas into Wanix from the beginning, it wasn’t until we rebuilt it from scratch with that intention that the magic really starts to come through. That’s what this preview release is about.</p><p>Wanix is a whole new beast now. It’s no longer a singular computing environment that runs in the browser. It’s now a primitive for building environments in general. The demo shows a shell environment, but this environment is not the point. It’s just a way to bootstrap Wanix so you can use and explore it interactively.</p><p>The point of this preview release is to get this primitive out there. I have my uses for Wanix, and I plan to share them with the final 0.3 release, but until then I wanted to let it all percolate. Maybe inspire people to get creative with their own use cases.</p><p>Here’s a quick rundown of Wanix features in this release:</p><h4>Plan 9 inspired design</h4><p>With the original intention to enable exploring Plan 9 ideas on modern platforms, we’ve ended up with a radically simple architecture around per-process namespaces composed of file service capabilities using similar design patterns to those found in Plan 9.</p><h4>Single executable toolchain</h4><p>The wanix executable includes everything needed to produce Wanix environments.</p><h4>Filesystem is the only API</h4><p>The Wanix microkernel is now simply a VFS module with several built-in file services exposed via a standard filesystem API. This ends up making the module itself a file service.</p><h4>Built-in Linux shell</h4><p>Using the built-in file service primitives, Wanix can bootstrap a Linux-compatible shell based on Busybox. It comes with several helper commands for working with built-in file services.</p><h4>Tasks and namespaces</h4><p>The Wanix unit of compute is a task, which is equivalent and compatible with POSIX processes but allows for different execution strategies. Each task has its own “namespace,” which is the customizable filesystem exposed to the task.</p><h4>Core file services</h4><p>Wanix includes two singleton file services: one to manage tasks (similar to procfs), and one to manage “capabilities” which are user allocated file services. Built-in capabilities include: tarfs, tmpfs, and loopback.</p><h4>Web file services</h4><p>With future non-browser deployments in mind, all web related file services are packaged in a web module, which is also built-in but not considered core. This module includes these work-in-progress file services:</p><ul><li><strong>opfs</strong>: For working with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system">OPFS</a> browser storage API</li><li><strong>dom</strong>: For inspecting and manipulating the DOM</li><li><strong>worker</strong>: For managing <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API">web workers</a></li><li><strong>pickerfs</strong>: Capability wrapping the window.showDirectoryPicker() method (not available yet in Safari and Firefox)</li><li><strong>ws</strong>: Capability for working with WebSocket connections</li><li><strong>sw</strong>: For configuring the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">service worker</a>, which is used by the system now to cache all resources needed to run Wanix allowing offline usage, as well as exposing virtual URLs to the root namespace.</li></ul><p>Go programmers might also appreciate the filesystem toolkit we’ve been working on since before Wanix. It builds on the fs.FS abstraction in the standard library and gives you DSL-like utilities for defining virtual filesystems like the file services in Wanix. More on that in a dedicated post as well.</p><p>So far, the feedback has been really positive. I appreciate everybody taking the time to process it. There’s still lots to do. Wanix is itself its own universe, but it’s just one layer of the Tractor project. After a little vacation I’ll be back to continue work on both fronts. As usual, I’d love help.</p><p>Speaking of help, shout-out to <a href="https://joel.franusic.com/">Joël Franusic</a> for the help and support. And as usual, big thanks to my <a href="https://github.com/sponsors/progrium">GitHub sponsors</a> for making this possible.</p><p><em>Originally published at </em><a href="https://progrium.xyz/blog/2025/spirit-of-plan9-on-the-web/"><em>https://progrium.xyz</em></a><em> on May 5, 2025.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=de90309fc44d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apptron Demo: HTML5 Background Apps]]></title>
            <link>https://progrium.medium.com/apptron-demo-html5-background-apps-39b875c56ec8?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/39b875c56ec8</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[electron]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[html5]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Fri, 19 Aug 2022 15:31:14 GMT</pubDate>
            <atom:updated>2022-08-19T15:33:11.418Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fi9nNUOHF7G4%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Di9nNUOHF7G4&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fi9nNUOHF7G4%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/1dd020ededc353b3b72d6662de419264/href">https://medium.com/media/1dd020ededc353b3b72d6662de419264/href</a></iframe><p>In this week’s demo I built a background application using HTML and <a href="https://progrium.com/blog/apptron-announcement/">Apptron</a>. It may seem weird to involve a webview at all for a background app, which is why this was not a use case I had planned, but I was pleasantly surprised to discover how this turned out.</p><p>In the last demo, we made an app indicator background utility as a shell script. This week we re-create it using JavaScript in an HTML file. With apptron build we can make it into a standalone executable. This workflow was originally intended for quick webview apps, but because we can let the JavaScript of the page run while the window is hidden, we effectively get a background application.</p><p>Apptron wasn’t made just for webview apps. I wanted to expose other native APIs that let you hook into more pre-built UI elements the operating system provides. For now this covers the common ones like app indicators, dialogs, and desktop notifications. In the future I’d like to expose APIs to let you add new items to system menus, control panels, and other places the OS lets developers hook into. These are important to make scriptable so you can easily leverage them for simple workflow hacks. When it’s easy to throw that kind of thing together, you don’t need to find “an app for that,” you can just imagine what would work for you and make it happen.</p><p>If this is also a world you want to see, <a href="https://tractor.dev/apptron/">join Apptron early access</a> and help us get this thing out. See you next demo!</p><p>-jeff</p><p><em>Originally published at </em><a href="https://dev.to/progrium/apptron-demo-html5-background-apps-35io"><em>https://dev.to</em></a><em> on August 19, 2022.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=39b875c56ec8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apptron Demo: Shell Scriptable Native APIs]]></title>
            <link>https://progrium.medium.com/apptron-demo-shell-scriptable-native-apis-b25d4d0b8795?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/b25d4d0b8795</guid>
            <category><![CDATA[shell]]></category>
            <category><![CDATA[shell-script]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[native-app]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Fri, 05 Aug 2022 21:45:37 GMT</pubDate>
            <atom:updated>2022-08-05T21:47:53.493Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FUHFS4STFYXM%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DUHFS4STFYXM&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FUHFS4STFYXM%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/365901d1b97481215586f2cbadc936d1/href">https://medium.com/media/365901d1b97481215586f2cbadc936d1/href</a></iframe><p>In this demo I show off the rest of the <a href="https://progrium.com/blog/apptron-announcement/">Apptron</a> CLI, which exposes most of the cross-platform APIs as commands. These commands make great shell scriptable utilities, and you can see how they’ve been designed to facilitate this. By the end, we’ll have created an app indicator (systray) icon and menu that’s created and driven by a shell script.</p><p>There is a whole class of power users that doesn’t always want to take the time to “write a program,” let alone learn how. Since the functionality is right there, why not expose it in a way that’s useful for these folks? Sysadmins and devops engineers often spend most of their time at a command prompt, so this feature goes out to all of them.</p><p>It turns out having a CLI interface to the APIs also makes it easy to do basic regression testing for the project. It also provides a kind of accessible listing of functionality. And! It helps with our polyglot primitive goal, working as a stopgap before having native library support for a language, since nearly all languages can “shell out”.</p><p>As I mentioned in the video, this is a strategy I like to use when making generative primitives: maximize utility by providing multiple ways to access features. Like being language agnostic, this helps meet users where they are, and reach users you might not otherwise. There’s also often a dominant context people think of for some functionality, so showing it in a new or surprising context helps break people out of existing thought patterns and imagine new possibilities.</p><p>Let’s see what possibilities open up with the next demo! In the meantime, <a href="https://tractor.dev/apptron/">join early access</a> to explore Apptron on your own.</p><p>-jeff</p><p><em>Originally published at </em><a href="https://dev.to/progrium/apptron-demo-shell-scriptable-native-apis-3o8p"><em>https://dev.to</em></a><em> on August 5, 2022.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b25d4d0b8795" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apptron Demo: Zero Config HTML5 Native Apps]]></title>
            <link>https://progrium.medium.com/apptron-demo-zero-config-html5-native-apps-9b6e4a04387f?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/9b6e4a04387f</guid>
            <category><![CDATA[api]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[native-app]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Thu, 21 Jul 2022 20:30:07 GMT</pubDate>
            <atom:updated>2022-07-21T20:32:41.124Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F7zsCQGOEBRU%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D7zsCQGOEBRU&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F7zsCQGOEBRU%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7cc78b02f20b368d22fcedb12c1ae3ee/href">https://medium.com/media/7cc78b02f20b368d22fcedb12c1ae3ee/href</a></iframe><p>In this demo I use the <a href="https://progrium.com/blog/apptron-announcement/">Apptron</a> build command to create an executable from a single HTML file. No setup, no configuration. If you want to change how the window looks, you can add a meta tag to your HTML file. It’s not only simple, it’s fast.</p><p>The way this works is actually similar to the last demo where we built a simple Go application to host our webview app, except Apptron is building it for us. Unlike all other commands of Apptron, this build command does have a dependency, which is the Go compiler. Luckily, this compiler is also a self-contained tool that’s easy to install on any platform.</p><p>At some point, I’d like to try embedding the Go compiler with Apptron, but this does introduce some complexity I don’t want to take on just yet. However, if anybody is interested in making this happen, please join early access so you can help us make Apptron great.</p><p>Speaking of help, this demo avoids talking about something that’s somewhat necessary on modern platforms: signing. If you’ve ever downloaded an executable on Windows, I’m sure you’ve gotten a giant alert asking if you really want to run it. This is because it’s an unsigned executable, which means it can’t be proven to not be tampered with by a non-author. Platforms are getting stricter and stricter about this.</p><p>On Mac, you have to not only sign but notarize your executable before another Mac will be allowed to run it. This is a tricky process that can be done on your own with an Apptron built executable, but I have preliminary tooling to help with the process. The same tooling can also bundle up your executable into a Mac app bundle.</p><p>However, I need help getting these last mile features in. So please consider <a href="https://tractor.dev/apptron/">joining early access</a>, not only to try Apptron, but if you have the experience, to help us finish these core features before it debuts as a public open source project.</p><p>See you with the next demo!</p><p>-jeff</p><p><em>Originally published at </em><a href="https://dev.to/progrium/apptron-demo-zero-config-html5-native-apps-29f8"><em>https://dev.to</em></a><em> on July 21, 2022.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9b6e4a04387f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apptron Demo: Webview Window Building Block]]></title>
            <link>https://progrium.medium.com/apptron-demo-webview-window-building-block-61cafbcd19cd?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/61cafbcd19cd</guid>
            <category><![CDATA[tools]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Fri, 08 Jul 2022 19:53:44 GMT</pubDate>
            <atom:updated>2022-07-08T19:58:57.573Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FXU-vrmA7j4U%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DXU-vrmA7j4U&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FXU-vrmA7j4U%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/06a7c2d94cf8837edbd8df5a4dd5b666/href">https://medium.com/media/06a7c2d94cf8837edbd8df5a4dd5b666/href</a></iframe><p>In this demo, I use the <a href="https://progrium.com/blog/apptron-announcement/">Apptron</a> API from Go to build a simple birthday card application defined by HTML and CSS. I also explore the architectural approach of Apptron compared to directly working with platform native APIs.</p><p>To expand on the demo, in this post I’ll discuss some of the motivation behind this project. There are a few reasons why I made Apptron.</p><p>I’ve always loved the option of using HTML5 in webviews to build cross-platform native app UIs. Native controls definitely have their benefit, but become tricky to do consistently across platforms. Rendering the UI the way a game would with a GPU accelerated render frame is consistent across platforms, but usually means you have to invent your GUI paradigm from the ground up. Using a webview gets you general consistency across platforms, while also getting a well known paradigm for building user interfaces. It also comes with the largest ecosystem of frameworks and pre-built components that can generally be used together, and is constantly improving as the web evolves. It’s not always the best approach, but it’s a nice option to have.</p><p>Using a webview for desktop UI is not new, but was not a well known practice until Electron. I’m pretty sure significant chunks of UI in Windows as far back as Windows 95 were actually webviews. Recently, I was surprised to discover some parts of macOS today are actually using webviews. But Electron popularized it by focusing on the cross-platform benefit and riding on the initial success of Node.js, empowering JavaScripters to do more than they ever could before.</p><p>Today, Node.js is considered by many, including its author, a bit of a mistake. This is a complex thing to explain, but for me it has a lot to do with the culture of dependencies and complexity in that ecosystem. So if you explicitly don’t want to use JavaScript, or maybe just avoid Node.js and NPM, you don’t have much choice when it comes to easily making webview based apps.</p><p>Luckily, we’ve been seeing alternative frameworks for a couple different languages recently. Unfortunately, any language-specific framework is limited to that language. This has always frustrated me as a polyglot programmer. Broad approaches to software design shouldn’t be silo’d off to specific languages, even though obviously you have to pick one for an implementation.</p><p>It’s also frustrating to see how complex these full service frameworks become. At their core, they’re just re-exposing platform APIs, but they end up being full-fledged platforms of their own. Keep in mind, it’s not just webviews and windows, there are a bunch of platform APIs for features across all major platforms that you might want to use. Native menus, dialogs, notifications, etc. If you wanted to use one of these in a script or simple program, Electron immediately feels like overkill.</p><p>Depending on your language, you may find one-off libraries for these cross-platform native features. A library for webviews, a library for desktop notifications, etc. The problem there is when you want to combine them. Not only are these libraries usually going to be fairly inconsistent in their design, if they even exist, but because of the nature of platform APIs, they often need to set up an event loop and take over the main thread. This makes them difficult if not impossible to use together.</p><p>With all this in mind, it seemed the only option was to build a language-agnostic bridge to cross-platform APIs. Apptron uniquely sits somewhere between a full framework like Electron or Tauri, and small one-off libraries you might find for specific APIs. It can be used like a library, a tool, or a micro-framework, but it’s small and self-contained. This is why I describe it as a software primitive or building block.</p><p>I hope Apptron is compelling, and I’d like to invite you to <a href="https://tractor.dev/apptron/">try it out in early access</a>. In the meantime, more demos are on the way.</p><p>-jeff</p><p><em>Originally published at </em><a href="https://dev.to/progrium/apptron-demo-webview-window-building-block-1e63"><em>https://dev.to</em></a><em> on July 8, 2022.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=61cafbcd19cd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Announcing Apptron: Cross-platform Native APIs Made Accessible]]></title>
            <link>https://progrium.medium.com/announcing-apptron-cross-platform-native-apis-made-accessible-da661f492541?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/da661f492541</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[cross-platform]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Fri, 01 Jul 2022 14:14:39 GMT</pubDate>
            <atom:updated>2022-07-01T14:21:23.395Z</atom:updated>
            <content:encoded><![CDATA[<p>I’m happy to announce the first major building block of the Tractor System: Apptron.</p><p>Apptron is a software primitive that lets you drive native platform APIs using the technologies you’re already familiar with, enabling cross-platform webview apps and more.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YcX51tKGOitFtryw4HBYuQ.png" /></figure><p>Usable from any language, or even the command line, Apptron gives you webview windows and common platform APIs for your simple scripts, homebrew utilities, or full applications. Building cross-platform programs that leverage native functionality (menus, dialogs, notifications, global shortcuts, etc) has never been more accessible.</p><p>These APIs are packed into a single executable that can be used a number of different ways, making Apptron a simple but powerful software primitive.</p><h4>Zero Config Native Apps from HTML</h4><p>Turn an HTML file and any other web assets into a self-contained, cross-platform executable in a single command.</p><h4>Language Agnostic Building Block</h4><p>Use the Apptron APIs like a library from any language without C extensions or tricky event loops.</p><h4>Shell Scriptable Native APIs</h4><p>Use platform APIs from CLI commands to break shell scripting out of the terminal and into the desktop GUI.</p><p>These features and more will be showcased in a series of demos coming out over the next few weeks. Given all the different ways it can be used, Apptron is something you really have to see in action. Catch their <a href="https://www.twitch.tv/progrium/schedule?segmentID=97869319-25d1-428f-bedb-4b372ca0db6a">premieres on Twitch</a>, or <a href="https://www.youtube.com/c/progrium">subscribe on YouTube</a> so you don’t miss them.</p><h3>Early Access</h3><p>In the meantime, we’re opening up Early Access via the Apptron website:</p><p><a href="https://tractor.dev/apptron">https://tractor.dev/apptron</a></p><p>By joining early access, you’ll be invited to the currently private GitHub repository where you can try Apptron out, submit issues, and help bring Apptron to a stable 1.0 release and become public open source software.</p><h3>Help Wanted</h3><p>I’ve been using Apptron in some form with the rest of the Tractor System for over a year now, but to make this a solid standalone primitive I’ll need your help. Please consider joining early access to play around with it and give feedback.</p><p>Anybody interested in contributing or helping drive this project should definitely request access and get involved. This is one of many projects I have in the pipeline and I can only do so much.</p><h3>Thanks</h3><p>A huge thanks to my <a href="https://github.com/sponsors/progrium">sponsors</a>, who I depend on to fund this early R&amp;D work on the Tractor System. As long as it’s sustainable, I look forward to releasing more powerful open source primitives.</p><p>Excited to tell you more next week with the first Apptron demo!</p><p>-jeff</p><p><em>Originally published at </em><a href="https://dev.to/progrium/cross-platform-native-apis-made-accessible-with-apptron-2cli"><em>https://dev.to</em></a><em> on July 1, 2022.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=da661f492541" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Progrium Technology Thesis]]></title>
            <link>https://progrium.medium.com/progrium-technology-thesis-3b287b07723e?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/3b287b07723e</guid>
            <category><![CDATA[design]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[tools]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Wed, 16 Mar 2022 04:17:39 GMT</pubDate>
            <atom:updated>2022-03-16T15:21:19.263Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FyKZ15O7zeHY%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DyKZ15O7zeHY&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FyKZ15O7zeHY%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c1412979139b2e207a6c1948b78bc596/href">https://medium.com/media/c1412979139b2e207a6c1948b78bc596/href</a></iframe><p>Creating software is too complicated. Our options are overwhelming, our tooling is too bloated, and things rarely ever “just work”.</p><p>8 years ago, the late Joe Armstrong, co-creator of Erlang, gave one of his last talks at the 2014 Strange Loop conference. It was titled “The Mess We’re In” and talked about the increasing over-complexity of software. “Software entropy increases with time,” he said, “we need to reverse entropy.” He said in his conclusion:</p><p>“Computing was about controlling complexity, and we have failed miserably.”</p><p>An echo of what Djikstra said in the <em>90s</em>: “It is time to unmask the computing community as a Secret Society for the Creation and Preservation of Artificial Complexity.”</p><p>Alan Kay, another computing pioneer, has a great word for this “artificial complexity”: complication.</p><p>“The amount of complication [in software today] can be 100s, maybe 1000s of times more than the inherent complexity.”</p><p>For 25 years, Kay has been giving talks saying, “The computer revolution hasn’t happened yet!” I believe that this is true, in part, because complication has gotten in the way.</p><p>The risks with complexity, complication, and bloat are also only growing. It’s 2022 and we’re still deploying software with logging libraries that let attackers run arbitrary code. We barely understand our own software systems, let alone the tools we use to make them. And yet, underneath this mess is probably the most powerful and constructive tool humanity has ever seen.</p><p>Building software today <em>could</em> be as fast and intuitive as we made desktop publishing by the 1990s. Suddenly designers could sit with a client in front of the computer and just execute an idea, making iterations together in real-time. Watching the designer use these intuitive tools, the client might think, “Oh, I could do that”.</p><p>To get there with building software we need to reduce the complexity, and especially the complications, of making modern software. It’s not just about user friendliness or making the process more visual. And it’s not about adding more. If anything, it’s about revisiting the idea of doing more with less.</p><p>Recently, tools falling under the label of “nocode” have shown the demand for non-programmers to be able to just make what they need, with “no code”. It does sort of hint at what a photoshop of software might be. But every nocode tool is a facade, another layer of complication. They exist for a particular market, offered as a service, with no path to a more general means of making software.</p><p>These tools have their place, the same way image macro generators have their place when Photoshop and Canva exist. I think if “nocode” tools could really save us, you’d be able to build a nocode tool with itself. They lack the true generativity of programming.</p><p>That said, I am all for <em>anything</em> that can be used as a building block in a software system. Whether it’s a library or a web API or a tool repurposed because you like its workflow. The less you have to build yourself, the better.</p><p>My experience has shown me time and again that there is a powerful strategy or guiding principle in what I call “generative building blocks.”</p><p>This philosophy applies to more than libraries and web APIs. I design everything as a building block, and everything out of building blocks. This is about more than just modularity, it’s about <em>high-leverage generativity</em>.</p><p>I’ve worked on a number of projects and technologies trying to bring out this particular generative building block approach and there are a couple of standout hits that I’ll use as examples.</p><p>In 2006 I was working on a hosted source control web app (like GitHub) trying to expose repository hooks. These are shell scripts triggered by your local source control tool that can be used to customize and extend your workflow, almost like registering a callback for an event. Running user code in a web app, especially shell scripts, was not safe or practical. However, imagining if somehow it was, a flood of possibilities hit me. If only web apps could have extension points! Without over formalizing it, I called this “webhooks” and started giving talks about what we could have if we added webhooks to our apps. Throwing a PHP script online was an easy and common activity for web developers then, so functioning as “hook” script with a URL, you just needed to have web apps let you register it and then hit it on certain events.</p><p>Webhooks did end up making web apps more extensible and integratable, increasing their value and potential as building blocks. It also allowed for new kinds of API building blocks to emerge like Twilio, where the initial core product was built around webhooks to integrate with telephony and SMS. Webhooks also started creating a demand for what would later manifest as AWS Lambda for event handling, which quickly inspired “serverless” architecture. The concept of functions as a service was something I was pioneering in 2009 because of webhooks.</p><p>Later I found myself helping to prototype and conceptualize Docker, which turned software into a kind of standard unit, hence the shipping container metaphor. Although many interpreted it as a lightweight VM to throw whole systems into, I was in love with microservices designed to be run in tiny containers, configured by APIs instead of files, acting more like a little appliance than traditionally managed software. More like a simple building block.</p><p>Docker itself became a building block, opening up a whole new generation of platforms and developer tooling. One of my initial use cases for it was as a primitive to create a simple, hackable version of Heroku. Sure enough, using Docker and a few other components I made, I was able to “throw together” a simple Docker-powered Heroku alternative in 90 lines of Bash. This was called Dokku.</p><p>Dokku was a bit of a revelation. An expensive, top-tier platform valued in the hundreds of millions of dollars recreated with the right primitives in a weekend as a free Bash script…</p><p>I want to have the primitives to create more software like Dokku — extremely simple, hackable alternatives to the world’s best software. The resulting software isn’t really the important part, though. The resulting building blocks are. Why use Figma if you could somewhat easily throw together your ideal figma?</p><p>Building simpler alternatives by disaggregating into new primitives expands the adjacent possibility space of what can be thrown together, while also simplifying and commoditizing what it takes to build great software. It actually is a sort of reversing of entropy! Repacking down our abstractions, engineering for generativity.</p><p>What I love is how “building blocks” brings to mind a sort of ideal vision we have of building software. Like LEGO, we snap together little components to realize something new. But this metaphor also points out how far that ideal is from reality. LEGO is fun. Building software really <em>isn’t</em> most of the time. And I think trying to express the reality through this metaphor is helpful:</p><p>While ALL our software is made of many small bricks or components or modules, most come to us fully assembled with the bricks glued in place and painted over. We can’t change it or even get a sense of how it was composed. Most apps come as an opaque “turn-key solution” to a specific problem because… that’s how you sell things.</p><p>Perhaps there’s nothing wrong with that, but we can at least try to find ways to build more software like LEGO kits, where the focus is on good, re-usable, re-combinable building blocks and showcasing what you can do with them. In the long term, I believe this is the way that provides the most value.</p><p>A few years ago I realized that I need to build a single cohesive system that lets me express all these ideas, and really show the potential I’ve been talking about. I need to create my own LEGO system for software that would be an umbrella for all my work and let me build the way I want to build, while being at the very least a useful example to others…</p><p>So I’m announcing the Tractor System. It’s not one project, but a system of projects building towards a malleable software environment and ecosystem. It is my take on a “Photoshop of software”.</p><p>Tractor is a system of building blocks and pragmatic ways of building centered around simplicity and generativity. It’s not a new language, it helps glue languages together. It’s not just an app builder, it’s a systems playground. Much of the tool is made with itself, out of the same primitives you use in it. It’s designed to be as simple and understandable as it can be, top to bottom, inside and out.</p><p>The aim is to blur the line between user and developer. The computer is a computer because it can be <em>programmed</em>. The rushed, over-productization of shrink-wrapped software, pandering to enterprise organizations and the computer illiterate, has created so much complication it would seem we NEED big companies to build our software. But we don’t. I’m constantly surprised how simple things really are or can be. With Tractor, eventually we’ll just have operators: people that can use the computer in its full potential to solve problems, create systems, play, learn, work, and have fun.</p><p>Some say we live in a post PC era, but the original idea of the PC, the <em>personal</em> computer, is and will always be significant because it puts a magical form of <em>leverage</em> into the hands of <em>individuals</em> for their own use. But instead of this becoming more obvious with time, this generative power has been centralized and sterilized into “pop computing”. Today we consume and are defined by software that benefits engines of capitalism more than engines of equity. You could say, instead of being able to program our own leverage, we are being programmed by those that can.</p><p>Luckily, underneath all the apps, social media, and enterprise “solutions”, there is still an <em>imagination compiler</em>. This is what I focus on. This is what I’d love to dust off, polish up, and see where that takes us in the proper hands.</p><p>Even if that doesn’t happen, I’d love to just get some better building blocks. Perhaps just find simpler, more enjoyable ways to build software.</p><p>As an independent creator, I depend on people like you to help fund this work, and I’d love you to come along and help make the Tractor System a reality by <a href="https://github.com/sponsors/progrium">becoming a sponsor</a>.</p><p>Onward!<br> -jeff</p><p><em>Originally published on </em><a href="https://progrium.com/blog/progrium-technology-thesis/"><em>progrium.com</em></a><em> and </em><a href="https://www.youtube.com/watch?v=yKZ15O7zeHY"><em>YouTube</em></a><em> on March 15, 2022</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3b287b07723e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Towards the Personal Potential of Software]]></title>
            <link>https://progrium.medium.com/towards-the-personal-potential-of-software-3c89486bd65b?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/3c89486bd65b</guid>
            <category><![CDATA[no-code]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[golang]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Mon, 05 Jul 2021 20:31:00 GMT</pubDate>
            <atom:updated>2021-07-05T19:13:24.166Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*Sxq8csrGcfhsGZWU.jpeg" /></figure><p>Hello everybody!</p><p>Since starting work under <a href="http://progrium.com">Progrium Technology Company</a>, I’ve had a single objective: to build a system called Tractor. It’s what I’ve been slowly approaching in my dense 22 year career as an independent programmer. The Tractor System is still hard for me to describe at this stage, but its goal is to make building production-grade personal software systems like building with Legos: fast and fun.</p><p>After a difficult year heads-down in 2020, I started posting here in January with the intention of sharing and officially releasing components I’ve been working on that are building up to Tractor. Besides warming up for when I’d eventually talk about Tractor, I wanted people to see how I make independently useful building blocks towards an ideal. That’s a core part of the Tractor philosophy.</p><p>Let’s review what I’ve shared so far, and then set up what’s coming in the next 6 months.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/522/1*PZsjwp54g6on4MyuzoGDjw.gif" /></figure><p>The first big release of the year was the <a href="https://github.com/progrium/macdriver">macdriver</a> project, which got a significant response on Hacker News. It gives us Objective-C and Apple framework bindings for Go, letting you build simple Apple apps entirely from Go. That wasn’t possible before, so it was a little exciting, but it was early. The native Go APIs included for commonly used Foundation and Cocoa classes were far from complete. They could still be used if you knew what you were doing, but with an ideal of total coverage of Apple frameworks, that wouldn’t be enough. There were also unresolved issues just deciding how to best manage memory and pointers, which I knew would fall on me to figure out and take some time.</p><p>Luckily I’ve since gotten a colleague involved in the company, and he’s been helping push <a href="https://github.com/progrium/macdriver">macdriver</a> towards a real beta. Part of this was made possible by a project I spun out of macdriver and <a href="https://dev.to/progrium/apple-api-schemas-for-code-generation-and-more-1phj">posted about</a>, though again in a very early state. That project was <a href="https://github.com/progrium/macschema">macschema</a>.</p><p>With the <a href="https://github.com/progrium/macschema">macschema</a> toolchain, you can generate API schemas about any Apple framework, class, function, etc based on their documentation and header declarations. This is useful for us in generating framework bindings in macdriver, but it would be useful for any project doing similar work, such as bindings for other languages. This sort of approach will play a big part in how Tractor will integrate with “whatever we want” down the line.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*AiBTARkAlDK9f4T_6F8VZQ.gif" /></figure><p>Along the way, I released a few demos for macdriver to make clear the possibilities and provide reference examples. One of them I spun off into a standalone project called <a href="https://github.com/progrium/topframe">topframe</a>, which is also serving as a test bed for how we are doing cross-platform support. More on that in a moment.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*og8jIffppOFeMYeG0It25w.jpeg" /></figure><p>Unrelated to macdriver, I released a protocol called <a href="https://github.com/progrium/qmux">qmux</a> with <a href="https://dev.to/progrium/the-history-and-future-of-socket-level-multiplexing-1d5n">a post</a> explaining why this (but really any) muxing protocol, including and especially QUIC, is such a great primitive for network programming. The project came with two implementations, Go and TypeScript, with more on the way. The post about it was the first explainer article I’d done in a while and people liked it. This was important because the idea behind it was really more valuable than the protocol itself, but regardless we still need implementations of it to exist. To show the idea in action, I built a <a href="https://github.com/progrium/qmux/tree/main/demos/groktunnel">130 line version of Ngrok</a>, which turned into <a href="https://dev.to/progrium/building-your-own-ngrok-in-130-lines-2lif">a great post</a> walking through how it works.</p><p>That brings us to today, half way through 2021. Let’s talk about what’s coming.</p><p>More exciting than qmux is what we built on top of it, which is called <em>qtalk</em>. This is my meticulously designed, re-written-several-times network/IPC programming stack. While not <em>that</em> different from just JSON-RPC with pluggable codecs, its two unique features are callbacks and streams. Callbacks means its bi-directional, either side can expose or call methods. Just that alone is something rare in existing RPC stacks, but necessary for callbacks. And while streaming RPC is not new, our streams are full virtual connections, so you can stream more RPC results, arbitrary byte streams, or tunnel something else like a database connection. I’ll share more about the possibilities when it’s released.</p><p>So in the second half of 2021, we’ll see qtalk, we’ll see a major update to macdriver, and then with those two parts we can release <em>shelldriver</em>. This is a cross platform API to platform specific resources like windows, dialogs, menus, etc. It’s about native GUI shell integration, it’s not about having every native UI component. In fact, for app UI it really focuses on windows and webviews, a la Electron. Unlike Electron, it can be used from any language that has a qmux and qtalk implementation. The result should be a simple “Electron but as a library” that you can use from Go and other languages.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*IhVc8oJwAfW_4oEliQ6bJw.png" /></figure><p>Once shelldriver is out I will be able to finally start talking about the first <em>real</em> piece of the Tractor system: Tractor Toolkit. I hope to have at least a public demo by the end of the year. The Toolkit is already being shown to friends and sponsors. The work I’ve shared so far is just the tip of the iceberg.</p><p>Which reminds me …</p><p>I really, really have to thank my <a href="https://github.com/sponsors/progrium">sponsors</a>. As things are ramping up, I’m starting to work with more people and this is all self funded. Not only does sponsorship support all this open source work, but sponsors get early access to Tractor Toolkit <em>and</em> see more of what I’m up to.</p><p>Also to you, thanks for reading and following along. I’ll be back soon with more releases.</p><p><em>Originally published at </em><a href="https://dev.to/progrium/towards-the-personal-potential-of-software-5g5a"><em>https://dev.to</em></a><em> on July 5, 2021.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3c89486bd65b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building your own Ngrok in 130 lines]]></title>
            <link>https://progrium.medium.com/building-your-own-ngrok-in-130-lines-627c2aaa6247?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/627c2aaa6247</guid>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[ssh]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[ngrok]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Thu, 27 May 2021 19:45:23 GMT</pubDate>
            <atom:updated>2021-05-27T20:06:36.577Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*g10S9EwuXytRHbZD.png" /></figure><p>Running a local development server for your app is pretty common, but what if you wanted somebody else to access it? Maybe for a demo, or maybe to debug webhook integrations. If you’ve ever used <a href="https://ngrok.com/">Ngrok</a> (or perhaps the original <a href="https://github.com/progrium/localtunnel">localtunnel</a>), you know what I’m talking about.</p><p>Today we’re going to make a system that emulates the core functionality of Ngrok, exposing a localhost HTTP server via a public URL.</p><p>Just to make sure we’re clear on how this all works, let me explain the problem. Normally when you run a server on your computer it can only be accessed by other computers on the same local network. However, many development servers will bind to 127.0.0.1, or localhost, which means only a client or browser on your computer can access it. Either way, there are times when you might want another party to access it temporarily, but it&#39;s simply not addressable from the public Internet.</p><p>In the olden days, this could only be achieved if you had access to a server running SSH. You could configure SSH to have publicly addressable ports on that server be “forwarded” to a local port on your computer over an SSH connection. This is called <a href="https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding">remote port forwarding</a> or sometimes SSH tunneling. Assuming SSH is configured properly, the command to set this up looks like this:</p><pre>$ ssh -R 8080:localhost:3000 your-ssh-server.com</pre><p>This would allow somebody to point their browser to your-ssh-server.com:8080 and get your localhost server running on port 3000 as long as this command was running. Around 2009, I noticed how useful this is (especially in the context of webhooks), but was annoyed you had to have an SSH server properly configured for it before you could even do it. Even then, I always found the SSH command difficult to remember how to use for forwarding. I envisioned a command as simple as:</p><pre>$ localtunnel 3000</pre><p>So I set about making this happen. My <a href="https://github.com/progrium/localtunnel/tree/prototype">first attempt</a> at building a system with Twisted Python failed because I didn’t quite grok <a href="https://dev.to/progrium/the-history-and-future-of-socket-level-multiplexing-1d5n">multiplexing</a>. So I ended up wrapping SSH. I wrote a localtunnel client in Ruby that behind the scenes would just run the above SSH command using a server I had set up. The only difference being that I didn&#39;t give you a port like 8080 to connect to, I wanted you to get a generated subdomain. Instead of localtunnel.com:8080, you&#39;d get wxyz.localtunnel.com, which was unique to your tunnel as it was intended as a public service.</p><p>To achieve that, I just ran a Twisted web server with the SSH server that would figure out which port to use based on the subdomain and proxy to it. This would only work with HTTP because the idea of virtual hosts was actually invented for HTTP. Normally when you get a TCP connection you have no idea what hostname is used.</p><p>So I had something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/716/1*SX3cNvftnBjzWDn-yFNv9w.png" /></figure><p>And it worked! It became rather popular. However, due to the slight complexity (Ruby+Python+OpenSSH) and the fact I was too busy to maintain the service, people started cloning it. One jackass even called <a href="https://theboroer.github.io/localtunnel-www/">his clone</a> “localtunnel”.</p><p>Another jackass, who was less of a jackass and more my friend and coworker, decided to clone it as a way to learn Go. He called it Ngrok and decided to commercialize it. This bothered me for a number of reasons I won’t get into, but eventually decided it was fine since he really improved on the idea and would at least listen to me if I had anything to say about it.</p><p><em>Anyway</em>, today we’re exploring a toy recreation of this system that we’ll call groktunnel. It removes SSH from the setup and using two libraries lets us build the whole thing in about 130 lines of Go. You’d still need a server to run this on, so it’s not as convenient as a hosted service, but this is more for educational purposes. After all, groktunnel is actually a demo for qmux, one of the libraries I mentioned. Let me explain these libraries.</p><p>What <a href="https://github.com/progrium/qmux">qmux</a> does is give us a subset of the SSH protocol to multiplex many connections over a single connection. It was the missing piece of my original Twisted prototype. You can read more about how there aren’t a lot of these protocols (but perhaps with QUIC are also the future of the Internet) in my <a href="https://dev.to/progrium/the-history-and-future-of-socket-level-multiplexing-1d5n">previous post</a>.</p><p>The other library is actually by the author of Ngrok. Called <a href="https://github.com/inconshreveable/go-vhost">go-vhost</a>, it helps with the problem of virtual hosts. Ideally when a connection comes in to be forwarded down the tunnel we can hand it off wholesale. This way the other end can just start reading off the connection as if received directly. But in order to figure out the hostname used, we have to start reading off the connection up to the Host header. Then we have to hand it off prepended with what was read. What Alan wrote is an abstraction that gives you a virtual listener that lets you accept new connections for a particular virtual host and get the connection as if it hadn’t been read yet. A lot of this is due to the interface based approach of the Go standard library.</p><p>This brings us to the main utility function we’ll be writing for this system. A TCP connection established between two peers cannot (easily) be handed off between programs, let alone hosts. We don’t literally “forward” the connection, we sit in the middle and relay the bytes. This is how every proxy, load balancer, gateway, or tunnel works. It sort of welds two sockets together. I call this socket joining. And if you <em>can</em> avoid doing any protocol level stuff, building a proxy just becomes opening a connection to the backend when a connection comes in and then joining them.</p><p>Since Go models connections using the interfaces of the io package, we can use io.Copy to simplify reading off bytes from one connection into another until an EOF. A two-way copy like this that closes when finished looks like this:</p><pre>func join(a io.ReadWriteCloser, b io.ReadWriteCloser) {<br>    go io.Copy(b, a)<br>    io.Copy(a, b)<br>    a.Close()<br>    b.Close()<br>}</pre><p>This minimal approach benefits from setting a deadline on the connections in case one end doesn’t hang up. You can see we copy bytes from a to b in the background, then wait for copying bytes from b to a before making sure both sides are closed once b gets an EOF.</p><p>We’ll also define a helper to generate random subdomains. In a production service this is a big deal. You’d want an incredibly large space of possible subdomains, but here we generate 10 random alphanumeric characters using crypto/rand:</p><pre>func newSubdomain() string {<br>    b := make([]byte, 10)<br>    if _, err := rand.Read(b); err != nil {<br>        panic(err)<br>    }<br>    letters := []rune(&quot;abcdefghijklmnopqrstuvwxyz1234567890&quot;)<br>    r := make([]rune, 10)<br>    for i := range r {<br>        r[i] = letters[int(b[i])*len(letters)/256]<br>    }<br>    return string(r) + &quot;.&quot;<br>}</pre><p>Another quick utility to define that’s common in simple Go programs is a fatal function. This helps simplify all the error checking.</p><pre>func fatal(err error) {<br>    if err != nil {<br>        log.Fatal(err)<br>    }<br>}</pre><p>Now we can get started in our main() which will contain both client and server modes. However, first we&#39;ll define some flags useful to both:</p><pre>var port = flag.String(&quot;p&quot;, &quot;9999&quot;, &quot;server port to use&quot;)<br>var host = flag.String(&quot;h&quot;, &quot;vcap.me&quot;, &quot;server hostname to use&quot;)<br>var addr = flag.String(&quot;b&quot;, &quot;127.0.0.1&quot;, &quot;ip to bind [server only]&quot;)<br>flag.Parse()</pre><p>The default hostname of vcap.me is a way to make the server easy to run locally. Since localhost doesn&#39;t allow subdomains, but any domain can resolve to 127.0.0.1, there are a few public domains that have wildcard subdomains all resolving to 127.0.0.1. If we ran this on a real server, we&#39;d need a domain with wildcard subdomains resolving to the server IP, and we&#39;d probably use port 80 instead of 9999.</p><p>Now we’ll start with the client, which is quite simple. It’s activated when there is an argument specifying the local port to expose publicly.</p><pre>// client usage: groktunnel [-h=&lt;server hostname&gt;] &lt;local port&gt;<br>if flag.Arg(0) != &quot;&quot; {<br>    conn, err := net.Dial(&quot;tcp&quot;, net.JoinHostPort(*host, *port))<br>    fatal(err)<br>    client := httputil.NewClientConn(conn, bufio.NewReader(conn))<br>    req, err := http.NewRequest(&quot;GET&quot;, &quot;/&quot;, nil)<br>    req.Host = net.JoinHostPort(*host, *port)<br>    fatal(err)<br>    client.Write(req)<br>    resp, _ := client.Read(req)<br>    fmt.Printf(&quot;port %s http available at:\n&quot;, flag.Arg(0))<br>    fmt.Printf(&quot;http://%s\n&quot;, resp.Header.Get(&quot;X-Public-Host&quot;))<br>    c, _ := client.Hijack()<br>    sess := session.New(c)<br>    defer sess.Close()<br>    for {<br>        ch, err := sess.Accept()<br>        fatal(err)<br>        conn, err := net.Dial(&quot;tcp&quot;, &quot;127.0.0.1:&quot;+flag.Arg(0))<br>        fatal(err)<br>        go join(conn, ch)<br>    }<br>}</pre><p>Here we set up the tunnel over HTTP since we’ll be serving HTTP anyway, and we need a way to tell the client what the generated public host is to display a URL to the user. You can see we get the hostname from a response header.</p><p>We’re manually creating an HTTP request with httputil.NewClientConn because it lets us hijack the connection. This simply means it stops processing as HTTP and treats it like a raw TCP connection. Alternatively we could upgrade to WebSocket for this, but this simply works just as well with fewer dependencies.</p><p>After hijacking we create a qmux session to start multiplexing virtual connections, or channels, over this connection. This is the tunnel. We don’t open channels, we just sit in a loop waiting for incoming channels and assume they want to be joined with a connection to our localhost server. That’s it.</p><p>Now the server. First we set everything up using our flags, including a TCP listener, which we wrap in a virtual host muxer. The meat of our server is in serve() but that runs asynchronously as we listen for vhost muxing errors until shutdown.</p><pre>// server usage: groktunnel [-h=&lt;hostname&gt;] [-b=&lt;bind ip&gt;]<br>l, err := net.Listen(&quot;tcp&quot;, net.JoinHostPort(*addr, *port))<br>fatal(err)<br>defer l.Close()<br>vmux, err := vhost.NewHTTPMuxer(l, 1*time.Second)<br>fatal(err)<br><br>go serve(vmux, *host, *port)<br><br>log.Printf(&quot;groktunnel server [%s] ready!\n&quot;, *host)<br>for {<br>    conn, err := vmux.NextError()<br>    fmt.Println(err)<br>    if conn != nil {<br>        conn.Close()<br>    }<br>}</pre><p>Now the heart of this system in serve():</p><pre>func serve(vmux *vhost.HTTPMuxer, host, port string) {<br>    ml, err := vmux.Listen(net.JoinHostPort(host, port))<br>    fatal(err)<br>    srv := &amp;http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {<br>        publicHost := strings.TrimSuffix(net.JoinHostPort(newSubdomain()+host, port), &quot;:80&quot;)<br>        pl, err := vmux.Listen(publicHost)<br>        fatal(err)<br>        w.Header().Add(&quot;X-Public-Host&quot;, publicHost)<br>        w.Header().Add(&quot;Connection&quot;, &quot;close&quot;)<br>        w.WriteHeader(http.StatusOK)<br>        conn, _, _ := w.(http.Hijacker).Hijack()<br>        sess := session.New(conn)<br>        defer sess.Close()<br>        log.Printf(&quot;%s: start session&quot;, publicHost)<br>        go func() {<br>            for {<br>                conn, err := pl.Accept()<br>                if err != nil {<br>                    log.Println(err)<br>                    return<br>                }<br>                ch, err := sess.Open(context.Background())<br>                if err != nil {<br>                    log.Println(err)<br>                    return<br>                }<br>                go join(ch, conn)<br>            }<br>        }()<br>        sess.Wait()<br>        log.Printf(&quot;%s: end session&quot;, publicHost)<br>    })}<br>    srv.Serve(ml)<br>}</pre><p>Here we create a new listener using the vhost muxer passed in to listen for connections on the naked domain ( vcap.me). Then we make an HTTP handler that sets up a new tunnel. First it generates a new hostname and tells our vhost muxer to start listening on it. We&#39;ll hold on to that listener.</p><p>Then we start writing headers. Remember this is how we tell the client what our generated hostname is. We also use Connection: close to tell the other end we&#39;re going to be done and normally hang up, but after we flush the headers with a 200 OK status, we hijack the connection like we know is going to happen on the other end. We make a new qmux session and start a new goroutine.</p><p>This goroutine handles connections coming in from our new vhost listener. All this does is sit in a loop accepting new connections, then using our qmux session opens a channel, then joins the new connection with that channel.</p><p>Back in our HTTP handler, we call Wait() on our session, which will just block until the connection breaks. This handler has been put into an HTTP server instance set to serve on our original vhost listener for the naked vcap.me domain.</p><p>Now we can try it out. You can follow the instructions in the <a href="https://github.com/progrium/qmux/tree/main/demos/groktunnel#readme">groktunnel README</a> to see it in action.</p><p>This little program gives us a client and server that recreates the original localtunnel system. It showcases the use case of tunneling for connection multiplexing, but also how easily you can wire things up in Go using mostly the standard library.</p><p>If you wanted to tunnel normal TCP connections, not HTTP connections, this program gets even simpler. However, that’s an exercise for the reader. You can see the <a href="https://github.com/progrium/qmux/tree/main/demos/groktunnel">full groktunnel source</a> in the demos directory of the <a href="https://github.com/progrium/qmux">qmux</a> project. Hope you were inspired to build something cool!</p><p><em>For more posts like this sent directly to your inbox and to find out what all I’m up to, get on the list at </em><a href="http://progrium.com"><em>progrium.com</em></a><em>. ✌</em></p><p><em>Originally published at </em><a href="https://dev.to/progrium/building-your-own-ngrok-in-130-lines-2lif"><em>https://dev.to</em></a><em> on May 27, 2021.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=627c2aaa6247" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The History and Future of Socket-level Multiplexing]]></title>
            <link>https://progrium.medium.com/the-history-and-future-of-socket-level-multiplexing-28d1de822dc5?source=rss-55d1132d11df------2</link>
            <guid isPermaLink="false">https://medium.com/p/28d1de822dc5</guid>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[multiplexing]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[api]]></category>
            <dc:creator><![CDATA[Jeff Lindsay]]></dc:creator>
            <pubDate>Thu, 06 May 2021 16:50:41 GMT</pubDate>
            <atom:updated>2021-05-06T17:08:24.737Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*og8jIffppOFeMYeG0It25w.jpeg" /></figure><p><strong>Towards a socket API with builtin stream muxing</strong></p><p>Multiplexing (sometimes called muxing) has a long history of innovation in telecommunications. In 1875, Edison figured out how to transmit four separate telegraph lines over a single wire. In 1910, multiplexing technology was brought to telephony. Within 30 years the Bell System could multiplex 2,400 channels over a single cable. Then in the 1960s we got packet switching, a kind of unlimited multiplexing, which led to computer networking and Internet protocols.</p><p>I think of layered protocols and packet switching as “virtualized” multiplexing. Every layer provides a new medium or virtual transport for more protocols to operate, all over the same physical medium. Ethernet allows IPX, IP, and other packets. IP then allows UDP, TCP, and other packets. TCP allows HTTP, SSH, and so on.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/816/1*w_gR1hx2hUvS5VgptzjB_g.png" /></figure><p>The layers nearly end at TCP and UDP because these two protocols provide most of what any programmer would need to make two programs talk. This was more or less set in stone in the 1980s when Unix introduced the <a href="https://en.wikipedia.org/wiki/Berkeley_sockets">socket API</a>, which has been the de facto standard interface for network programming ever since.</p><p>This API layer is where you get “application protocols” like FTP, Telnet, DNS, and so many others. TCP, UDP, and the layers below were typically concerned with just getting bytes from one point to another. Application protocols introduced more specific interactions like request-reply, such as with HTTP. Some, like SSH and DNS, became rather multifaceted protocols to serve their domain with many different flows and message types. Rarely would you find an application protocol multiplexed to provide a generic transport for another.</p><p>An early exception was SSH. One of the reasons SSH fascinates me is that it was designed for many uses and breaks down into its own set of layers and internal protocols, almost mirroring TLS, TCP, and UDP.</p><p>First, SSH establishes an authenticated and encrypted line, similar to TLS. Then you have two very generic ways to communicate over this line. You can send “request” messages that may or may not get a response, similar to use cases of UDP. Or you can establish “channels” that were bidirectional streams just like TCP.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/663/1*5pxkOTgnSjb-leyshBNBpQ.jpeg" /></figure><p>From there, SSH defines how to use these requests and channels as primitives to set up remote shells. But besides that, you could also forward TCP connections, which worked by bridging a TCP socket with an SSH channel, “tunneling” the TCP connection through the SSH connection.</p><p>If you’ve ever used <a href="https://ngrok.com/">Ngrok</a> to open a public endpoint to a localhost server, you may not know it was one of several clones of a tool I made in 2010 called <a href="https://github.com/progrium/localtunnel/">localtunnel</a>. The original localtunnel was just a wrapper around SSH, literally using OpenSSH on the server side.</p><p>So SSH was one of the earliest application level protocols designed to further multiplex its transport protocol. The next protocol to multiplex at the application/socket level was HTTP/2. It was introduced in 2015, which is still quite recent, but 20 years after SSH.</p><p>HTTP has become a new de facto API, like Unix sockets did. What was originally used to just download files into the browser has been co-opted and repurposed. As people started building more and more complex applications in the browser and with the emergence of web APIs, HTTP evolved into a sort of “do everything” protocol.</p><p>In 2012, Google started an effort to design a more efficient HTTP with what they called SPDY, which they were able to test end-to-end in Chrome without people even knowing. After proving its improved performance and cost saving benefits, SPDY basically became HTTP/2. They did a number of things to make it more efficient, but the core idea was multiplexing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/795/1*86jI8fOOjgXmaD1q6G8oDw.png" /></figure><p>HTTP/2 was more or less HTTP, but over newly introduced multiplexed “streams” similar to normal TCP connections. This makes them roughly the equivalent of SSH “channels”. At least at one point, Ngrok used a subset of HTTP/2 as their muxing protocol the same way localtunnel used SSH.</p><p>Both HTTP/2 and SSH do more than multiplexing, and usually aren’t thought of specifically for muxing. Most library implementations don’t expose the lower level multiplexing primitives. SSH at least has clear separation in its specification, where multiplexed channels are a specific and independent layer of the protocol architecture.</p><p>After spending so much time with these multiplexing protocols, I noticed something. Most other application protocols also did something similar to multiplexing without realizing.</p><p>For example, any request-reply protocol that allows more than one request over a connection is “multiplexing” each request and reply pair. A request and response will often get a shared ID so they can be correlated as other requests and responses can be sent in the meantime. This is the heart of any muxing protocol!</p><p>I’ve found implementing RPC or any request-reply over a muxing API not only simplifies the protocol, but opens up the ability to also stream or tunnel other protocols on the same connection. You could “attach” a tunneled database connection with an RPC response.</p><p>Working with muxed streams over a single connection gives me a similar feeling to discovering green threads for the first time. Suddenly, streams between the same two points are so cheap and lightweight you can think about using them in totally different ways.</p><p>I now believe a socket API with muxing builtin is a better base layer and API for designing application protocols. I was validated when Google pushed multiplexing into <a href="https://en.wikipedia.org/wiki/QUIC">QUIC</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/732/1*1_K0rQv7_hpAqvPcc6Ifug.png" /></figure><p>If Google could redesign HTTP, why not also TCP+TLS? Their experience with SPDY started them down a path to reduce the overhead of TLS and TCP session establishment even more by creating a more efficient kind of encrypted TCP over UDP. Built into this layer is stream muxing that would not suffer from <a href="https://en.wikipedia.org/wiki/Head-of-line_blocking">head of line blocking</a>.</p><p>It’s taken nearly 10 years for QUIC to be refined and adopted in the wild and we’re basically there. There’s even a new browser API in the works called <a href="https://w3c.github.io/webtransport/">WebTransport</a>.</p><p>In the meantime, I started designing protocols on the assumption of having a muxing layer directly beneath. Eventually, that would be QUIC, but as a stopgap, what would it be? Remember, there haven’t been any standard TCP muxing protocols.</p><p>I considered what Ngrok did with a stripped down HTTP/2, but then I realized… SSH has been around a lot longer. SSH is known to work on nearly every platform and is trusted by basically everybody. Best of all, the SSH muxing layer is simpler and more straightforward than HTTP/2.</p><p>So I literally took the muxing chapter of the SSH spec, cut out a couple of SSH specific identifiers, stripped it down to just the channels, and I’ve been using it for the past 5 years. It’s called <a href="https://github.com/progrium/qmux">qmux</a>.</p><p>The great thing is that even with QUIC available, qmux can be used over any streaming transport and provide the same foundation for higher level protocols built on muxing. Whether the transport is WebSocket or stdio, as long as it can reliably transport ordered byte streams, qmux drops right in. Protocols built on it will translate easily to QUIC.</p><p>I’m only releasing this now because it might not have made sense without all this context or more awareness of QUIC. The banality of a brand new subset of a 25 year old protocol does not escape me, but it’s the shift in perspective that brings potential. In that way it’s similar to webhooks!</p><p>If nothing else, I hope people use this story as inspiration to think about using multiplexing to design better, simpler application protocols. Even if for some reason QUIC isn’t the future, you now have <a href="https://github.com/progrium/qmux">qmux</a>!</p><p><em>Originally published at </em><a href="https://dev.to/progrium/the-history-and-future-of-socket-level-multiplexing-1d5n"><em>https://dev.to</em></a><em> on May 6, 2021.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=28d1de822dc5" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>